Controlling position of linear actuator

Hi all,

I am working on an animatronics project and need to control two linear actuators. The linear actuators are DC Motors connected to a gearbox and output through a leadscrew. On the shaft of the motor is an encoder wheel. Unfortunately, it is not quadrature. Only a single channel. 4 openings on the disk. Through extensive testing and experimenting, I have found the gear ratio is 1:10.

There are 4 pulses per revolution of the DC motor, 40 pulses = 1 turn of the output shaft. That translates into 5mm of linear travel. So, I am settling on 1mm resolution of control (8 pulses).

At the extremes of movement are limit switches which go low when activated. Again, the encoder is only a single channel (basically a tachometer) so I cannot derive direction or absolute position from it.

So, I have devised a plan to track the position using the limit switches and pulses using interrupts:

void CountLR(){
 //Rightmost position = 64
 //Leftmost position = 0 
 
 // 8 pulses per 1mm of travel. Count every 8 pulses for 64 positions
	if (RevLR == 8){
    		RevLR=0;  //if pulses = 8, resent revolution counter
    
 //if moving Left to Right, increase position
 		if (Dir == LU && digitalRead(L_LIM) == 1){
  	 		CURRPOS[8]++;
  		}//Else if moving Right to Left, count down
 	 	else  if (DirLR ==RD && digitalRead(R_LIM) == 1){
  		 	CURRPOS[8]--;
 	 	}
	}

  	RevLR++;
  
 	 if(digitalRead(R_LIM) == 0){
   		 CURRPOS[8]=0; 
    		RevLR=0;
  	}
  
  	if(digitalRead(L_LIM) == 0){
  		CURRPOS[8]=64; 
    		RevLR=0;
  	}
  	  
}

This snippet is activated by the interrupt on an encoder count. I have it ignoring counting until it has been triggered 8 times. Depending on the direction variable which is kept track of in code, it will count up or down. If it hits a limit switch, it will reset the min and max count to a fixed number. This is needed because the count is not always exactly the same (off only by a few.) So I constrain it to 0 to 64.

I have written code to move to a position. I will paste what I have in the next post.

Here is the code to move to an absolute position:

void MoveLR(int pos){

	
	if (pos == CURRPOS[8]){
     
   
	}  
     
   
	else if (pos > CURRPOS[8]) {

     		while (pos != [CURRPOS[8]){

       			stepRight();

       			delay(5);

       
     	}

     		stop();

   	}
   
	else if (pos < CURRPOS[8]) {

     		while (pos != CURRPOS[8]) {

       			stepLeft();

       			delay(5);
       
     
		}

     		stop();

   	}
 

}

This works, but it's ugly. I am basically calling a step function which turns on the motors, delays, then stops to check the encoder pos again. And repeats until pos == currpos. It triggers on the position match, but of course the motor overshoots by quite a bit. some overshoot is ok, but we are talking actually stopping at position 16 when it is suppose to be 32 for example.

The larger project will not really have enough PWM to control all of the motors (10 motors total, but 8 of them use potentiometers for feedback), and the original controller didn't seem to use PWM either.

So, I am trying to figure out how to control the position cleanly without using PWM. Is this possible? Hopefully I included enough information. If not, just ask. If I can get something steady, I will be posting it up for posterity for others to use in similar situations. I know motor control is one of the most commonly asked questions.

I have only a little control over the design itself because I am modifying existing hardware and want to keep it as stock as possible, so simply using a servo is not an option. In fact, the end application here is to turn all of the motors into servos and provide a 10 servo serial servo controller interface to the end-user (compatible to Mini-SSC II.) Again, all code will be released to the public when completed.

how about using softPWM?

I was just investigating that. I may have to go that route. I guess I am a bit stubborn. I don't see how they are using PWM in the original application since the motor signals are all connected to latches. I almost wonder if they are actually just sending pulse widths (not PWM) directly (meaning turning the motors on for a certain length of time, then off.) When I was first experimenting with these, I found there was actually a pretty good correlation between pulse widths and position. For example, I was moving the head to one limit switch, then moving it to center with a 400ms pulse and it would land in the center every time. That would mean that they are using the position information to determine the direction to move and a pulse width based on how far off it is.

Honestly, for this application, it really doesn't need to 'hold' the position, because there is no load on the motors when they are not moving, with the exception of the mouth and eyebrows which spring back when power is removed. However, these both move from a centered position. I think they were just meant to spring one way or the other quickly and let it return to nuetral on it's own.

However, doing it this way is a hack and makes my ultimate code much less useful for other projects.

Linear actuators with position feedback use internal sliding pots. If the travel distance isn't too long, sliding pots might be used external to the actuators you have.

Agreed and I could probably just use a multiturn pot on the output shaft as well. However, I do not want to modify the stock design. I am shooting for plug and play. This will not be a one-off thing or I would just replace everything with RC servos and call it done. And even with the potentiometer, I still will have the problem with overshooting if I just run full-speed.

Tracking the position of these encoders is not really the problem. I have already written pretty robust code to do that. It will provide a value between 0 and 32 (or 0 and 64 for the longer movement) which will correspond to the actual position of the linear actuator. That part is nailed down and solid. Since it is not an absolute position (meaning it won't know where it is on power-up) I have to run an initial routine to move to a home position at first power up. Not really a problem, though and the position is accurate from then on.

Getting them to reliably run to a position and stop on that position without using PWM and and an integral (speeding up, constant speed, and slowing as it approaches) is the problem I am having now. While experimenting, I did find that I could somewhat reliably move them to a desired position by using delays (and the motor would basically coast into position.) To do it that way, I would need to calculate the delay on the fly based on the amount of change from the current position to the desired position. Some overshoot is just fine for this particular application. But again, it is a hack way to do it.

I guess I am really answering my own question here, aren't I? I really need to use PWM and PID to do it properly. The only reason I am stuck on doing it without PWM and PID is that the original circuitry obviously did without it. I guess I need to hook up a scope or logic analyzer to the output of one of the motor control signals to see what it is really doing. But, I suspect that it is simply running full speed for an amount of time, ocassionally coming back and checking on the position (probably every 1ms or so) to limit overshoot. The H-bridges use selective resistors to control the current through the motors for a constant speed. The h-bridges don't seem to respond well to PWM signals either. Each h-bridge has a forward and reverse signal, btw. 10 is forward, 01 is reverse, 00 is off, and 11 is a no-no (no braking.)

So, this topic is basically done, I guess. Unless someone can think of an ingenious way to do this without PWM and without being a complete hack. I realize it is going to need some form of PID-like control and even just doing with delays is a form of PD control (the D being calculating the amount of time to be on based on the amount of change) at least. I am only just learning about PID theory, however.

You'll probably get reasonable behaviour with just the P (proportional) part of the PID loop - reducing the drive
as you approach the destination has a strong braking effect if the H-bridge is set up for fast decay mode. Just
switching it completely off doesn't have active braking.

Resistors in the H-bridge sound like a poor idea - current controls torque more than speed, resistors waste
battery power too, and reduce the braking effect. Using PWM isn't to be discarded lightly I think.

MarkT:
You'll probably get reasonable behaviour with just the P (proportional) part of the PID loop - reducing the drive
as you approach the destination has a strong braking effect if the H-bridge is set up for fast decay mode. Just
switching it completely off doesn't have active braking.

Resistors in the H-bridge sound like a poor idea - current controls torque more than speed, resistors waste
battery power too, and reduce the braking effect. Using PWM isn't to be discarded lightly I think.

I agree regarding the resistors. Not my design, though. lol

When I experimented with using PWM on the motors, they didn't really move at all until the PWM value was pretty much maxed out. I am going to set up an experiment, I think, to see if I can find the usable ranges. Also, PWM made the motors whine A LOT which is not something it does in the stock application. If only these h-bridges actually had a brake mode! That would make life so much simpler. I will reverse engineer them a bit more. Maybe they do have a brake mode and I just don't know it. Any suggestions on how to safely test that without blowing them?

Also, interesting information about the braking effect of reducing the drive. That would mean that even if the usable range of PWM is very low, I could still use that just to brake.

The last hold-out for PWM is the fact that I would need 20 PWM signals total. The softPWM library may do this, I am still reading and trying to understand the code.

MarkT wouldn't stand for Mark Tilden, would it? If so, you actually designed this beast, give up the goods! :wink:

I just hooked up a logic analyzer to one of the forward and reverse motor outputs. It is exactly as I expected. No PWM going on. Signals are never both on (11) as in braking, either. They are simply on for a varying period of time depending on the amount of travel. How they do this smoothly completely escapes me!!!!

Basically, exactly like a proportional servo: a longer pulse width means more travel. Except that instead of controlling direction that way, it puts the pulse width on one of the lines to control direction. So, something like using two servo channels for each motor would sort of work, or hacking a servo routine to change pins for direction would work.

Also, I learned that the "homing function" takes 503ms (from one extreme to the other.) So, pulse widths of a servo would obviously need to be modified.

hello Retroplayer
i guess this would not be a good idea but if your willing to try,

Each h-bridge has a forward and reverse signal, btw. 10 is forward, 01 is reverse, 00 is off, and 11 is a no-no (no braking.)

if you do decide to use pwm to control the speed your set up one way to do it is by controlling one of the pin
for example if you want to go forward, you have to pwm the first pin.

for braking, you could for instant if you go forward pwm the second pin but use only 50 that way you have total braking.

Unfortunately, as I understand it, doing that would simply short out the power supply, not the motor. Turning both lines high at the same time shorts out the power supply. Braking shorts out the motor itself. For brake control, you need access to each quarter of the bridge individually and you activate the lower two halves at the same time, connecting to motor leads together. But since the upper half is not turned on, the positive end of the power supply is disconnected.

Turning both on at the same time for any length of time is a very bad idea.

Is that what you meant, or am I misunderstanding?

nm, I think you meant, remove the PWM from the first pin, and then pwm the second pin to kind of shunt it into reverse, but at a low enough speed that it won't actually go backwards.

Hmm, if so, that is a great suggestion!

well it seem that you understand what i mean..... btw what do you mean by no no

sigh... I just looked at the signals again with the logic analyzer and it is using a form of PWM. I must not have been triggering correctly before or the pulses were too long for my sampling rate.

Longer pulses at the beginning of movement and shorter pulses at the end of movement. The minimum pulse width is 1ms, and it looks like a full period is 8ms (125Hz). It can be on for the whole period or just 1 ms. Does this mean 8 PWM values?

Now that I have this data, what else should I learn from it to use in my own control? I will not be using hardware PWM because I don't have enough (I would need 20) so I will be emulating it in software. So understanding how it is currently working will likely help me with that.

ash901226:
well it seem that you understand what i mean..... btw what do you mean by no no

A "no-no" means "Don't do that! Very bad!" because what I originally thought you were suggesting was to raise both signals high at the same time and this would short out the power supply through the driving transistors and blow them.

ok, doing some math...

PWM frequency is 125Hz. Minimum duty cycle is 0%. The steps are in 12.5% for 8 steps = 100%

If I were to translate that to 0 to 255, I would have duty cycles of 32, 64, 98, etc... (256/8)

Assuming I keep the PWM frequency at 125Hz
32 would give me 1ms
64 would give me 2ms
and so forth...

Is my math correct?

hello retroplayer.

i am working on a similar project.
i have a linear actuator that uses a brushless dc motor, that has internal hall sensors to track count its steps.
i too get my position info by counting the hall sensor steps. i too have external limit switches to know when i reach the extreme positions.

i am wondering about your success to implement PID control to get better position control.
i am hoping to constantly give the arduino or teensy new position data over OSC or serial communication. and i am hoping for smooth motion from source to target positions, even if new position data is constantly send.

do you have any test code i could test on my setup?

thanks,
stephan.