I know this topic has been posted about already in this forum but after studying several threads I still haven't been able to solve this problem. I'm trying to program a linear actuator (with internal potentiometer) to open and close a door based on different inputs. This code here is just preliminary because all I'm trying to do is get the positional control figured out first. I'm so frustrated because I know this should be pretty basic!
This code is an adaptation of the Progressive Automations example code available in the public domain.
What I've done so far is I've mapped the actual analog output of the internal potentiometer on the linear actuator and gotten an output range of 332-686, and I've tried using numbers between that range as destinations. I've also tried mapping that range from 332-686 to 0-1023, yet both always result in sporadic movements of the actuator.
For this preliminary code, I just want to be able to tell the linear actuator to extend to position X, delay a second, then retract to position Y, delay a second. If I can just get that basic position-command code figured out I believe I can take it from there. Any help is greatly appreciated!
Thanks,
Henri
const int feedback = A0; //potentiometer from actuator
const int pwm = 6;
const int dir = 7;
int actMax = 686;
int actMin = 332;//positions of actuator
int precision = 2;//how close to final value to get
int checkingInterval = 50;//how often position is checked (milliseconds)
int currentPosition = 0;
int destination = 0;
int difference = 0;//values for knowing location
void setup()
{
pinMode(feedback, INPUT);//feedback from actuator
pinMode(pwm, OUTPUT);
pinMode(dir, OUTPUT);
digitalWrite(pwm,HIGH);
Serial.begin(9600);
}
void loop()
{
pushActuatorUntilStop(600);
delay(1000);
pullActuatorUntilStop(400);
delay(1000);
}
void pushActuatorUntilStop(int destination)
{
int temp = analogRead(feedback);
difference = destination - temp; //check difference to see if continue moving, or stop
while (difference > precision || difference < -precision)
{
temp = analogRead(feedback); //continue checking difference
difference = destination - temp;
pushActuator();
}
delay(25);
stopActuator();
}//end pushActuatorUntilStop
void pullActuatorUntilStop(int destination)
{
int temp = analogRead(feedback); //check difference to see if continue moving, or stop
difference = destination - temp;
while (difference > precision || difference < -precision)
{
temp = analogRead(feedback); //continue checking difference
difference = destination - temp;
pullActuator();
}
delay(25);
stopActuator();
}//end pullActuatorUntilStop
void stopActuator()
{
digitalWrite(pwm,LOW);
}
void pushActuator()
{
digitalWrite(pwm,HIGH);
digitalWrite(dir,HIGH);
}
void pullActuator()
{
digitalWrite(pwm,HIGH);
digitalWrite(dir,LOW);
}
First, and for future reference, get rid of the pinMode statement for A0. All pins are input by default and unless you have a specific reason for doing so, you're just adding about 100 bytes to your code.
Second, change out your test conditions in pushActuatorUntilStop and pullActuatorUntilStop. Declare difference = abs(destination - temp). This will give you a positive value which you can then test - while (difference > precision)....
Third, and last for now, change your precision away from 0. Even in the best lab conditions the odds of getting an A to D conversion that hits spot on a value are pretty slim. Try a small number like 3 or 5. Better yet, start with 10 or 20 and get it working, then you can tune it.
Fourth (counting was never my best subject) change your variable name away from pwm as this has other meanings which may only serve to confuse others.
I'm amused by people that write code this way. Press the gas pedal, and then select a gear.
My transmission, clutch, and engine are much happier if I select a gear (direction) FIRST.
Calling this function when the actuator is ahead of where you want it AND when the actuator is behind where you want it, as you do in pushActuatorUntilStop(), hardly makes sense, either.
By the way, setting the speed of the actuator to HIGH, or LOW, hardly makes for smoothly approaching a desired position. Changing the speed of the actuator to something other than stopped-dead-its-tracks or balls-to-the-wall would make more sense.
The motor is so slow on its own that I've never had a problem with this code in the years that I've run this particular motor controller with exactly that code. If you have any advice on positional feedback, like I asked for, I'd appreciate that.
If you have any advice on positional feedback, like I asked for, I'd appreciate that.
Have you ever walked a dog? Sometimes the dog runs ahead, and you have to pull back on the leash. Sometimes (way too damned often), the dog stops to smell where some other dog pissed, and you have to pull forward on the leash.
Your code is pulling on the leash in the same direction regardless of where the dog is.
johnwasser:
Sounds to me like a bad connection.
Could you provide information about what model linear actuator you are using and how you have it wired?
Yes I'm using a Progressive Automations (PA - 14P - 24 - 50) 12v linear actuator. I have a 12v power source for the motor driver and motor wires of the actuator, and then I'm running a 5v circuit through a breadboard to my arduino mega. Now that you mention it I can't remember if there is a 10k resistor inside the device or if I'm supposed to add that to my circuit. I think there's one in there but I'll try adding one. Otherwise I've just had the potentiometer direct-wired with poor results and I've also tried a capacitor between the 5v and ground wires. I realize describing it isn't as good as a photo, if you want I can try to post a photo of my circuit.
Maybe I did a bad job posing my question at first, I'll try to state it more clearly.
Basically I'm trying to take this example code from Progressive Automations (which allows you to control the position of the actuator by turning an external potentiometer):
/* Sample code to control the position of an actuator with potentiometer feedback using a MegaMoto.
The main loop of this program checks the potentiometer, and moves the actuator accordingly.
Written by Progressive Automations
This example code is in the public domain.
*/
const int feedback = A0; //potentiometer from actuator
const int pot = A1; //pot from throttle
const int enable = 6; //motor enable pin
const int dir = 7; //motor direction pin (HIGH = forwards LOW = backwards)
int actMax = 686;
int actMin = 332;// max/min positions of actuator
int potMin = 0;
int potMax = 1023;
int precision = 5;//how close to final value to get
int checkingInterval = 50;//how often position is checked (milliseconds)
int rawcurrentPosition = 0;
int currentPosition = 0;
int rawdestination = 0;
int destination = 0;
int difference = 0;//values for knowing location
void setup()
{
pinMode(feedback, INPUT);//feedback from actuator
pinMode(pot, INPUT);//feedback from potentiometer
pinMode(enable, OUTPUT);
pinMode(dir, OUTPUT);
digitalWrite(enable,HIGH);
Serial.begin(9600);
}
void loop()
{
destination = getDestination();
currentPosition = analogRead(feedback);//check where you are
Serial.print("Position ");
Serial.println(analogRead(feedback));
difference = destination - currentPosition;//find out how far you are from the destination
if (currentPosition > destination) pullActuatorUntilStop(destination);// choose what action to take
else if (currentPosition < destination) pushActuatorUntilStop(destination);
else if (difference < precision && difference > -precision) stopActuator();
}//end void loop
int getDestination()
{
rawdestination = analogRead(pot);//read the potentiometer to get the destination
destination = map(rawdestination, potMin,potMax,actMin,actMax);//convert the potentiometer feedback to match the actuator
return(destination);
}//end getDestination
void pushActuatorUntilStop(int destination)
{
destination = getDestination();
int temp = analogRead(feedback);
difference = abs(destination - temp);//check difference to see if continue moving, or stop
while (difference > precision || difference < -precision)
{
destination = getDestination();
temp = analogRead(feedback); //continue checking difference
difference = abs(destination - temp);
pushActuator();
}//end while
delay(25);
stopActuator();
}//end pushActuatorUntilStop
void pullActuatorUntilStop(int destination)
{
destination = getDestination();
int temp = analogRead(feedback); //check difference to see if continue moving, or stop
difference = destination - temp;
while (difference > precision || difference < -precision)
{
destination = getDestination();
temp = analogRead(feedback); //continue checking difference
difference = destination - temp;
pullActuator();
}//end while
delay(25);
stopActuator();
}//end pullActuatorUntilStop
void stopActuator()
{
digitalWrite(enable,LOW);
}//end stopActuator
void pushActuator()
{
digitalWrite(enable,HIGH);
digitalWrite(dir,HIGH);
}//end pushActuator
void pullActuator()
{
digitalWrite(enable,HIGH);
digitalWrite(dir,LOW);
}//end pullActuator
and I want to modify it to allow me to input two different destinations, let's say 400 & 600, instead of turning a potentiometer to extend and retract the actuator. If I could change the above example code to allow me to send the actuator to position 400, delay 1 second, then send it to position 600, delay a second.... I'd be thrilled. For some reason I'm having a heck of a time getting it to work.
hpwatso:
Now that you mention it I can't remember if there is a 10k resistor inside the device or if I'm supposed to add that to my circuit. I think there's one in there but I'll try adding one. Otherwise I've just had the potentiometer direct-wired with poor results and I've also tried a capacitor between the 5v and ground wires.
That documentation isn't very helpful. It shows the three potentiometer wires going into the cable with the two motor wires, but only shows two motor wires coming out the other end! There is no indication of color coding on the wires or anything.
According to the PA-14P Data Sheet you connect Arduino Ground to one end of the potentiometer and Arduino +5V to the other end. Then connect the wiper to your analog input pin. If you disconnect the three wires from your circuit you can use a multimeter to measure the resistance between pairs of wires. The two that measure near 10k Ohms will be the outer ends. The wiper should measure different values to each end but those two values should add up to about 10k.
The expression inside the while() is problematic. Since difference was calculated using the abs() function, difference can only be a positive number. precision is also a positive number. So how can difference ever be less than -precision?
In your previous code, you didn't have abs(). So if the actuator went right past the destination very quickly, the second part of that expression would become true and it would keep on going to the end of the physical travel.
The two functions "pull until stop" and "push until stop" are basically the same thing copy-pasted. If this was my code, I would re-write it so that I didn't have two copies of the same thing in my code. Here's my preferred loop:
MorganS:
In your previous code, you didn't have abs(). So if the actuator went right past the destination very quickly, the second part of that expression would become true and it would keep on going to the end of the physical travel.
Oops, I just realized that abs() makes it do this anyway. If it overshoots, then it keeps going to the end.
MorganS:
Oops, I just realized that abs() makes it do this anyway. If it overshoots, then it keeps going to the end.
It doesn't need to. The arguments to the abs() function, and the while statement using the output define whether the actuator is at the target position, or not. The direction to move should be calculated independent of the need to move. Then, the appropriate function can be called to begin moving in the proper direction, at a speed that is proportional to the distance to move. Long way, move faster. Almost there, creep up on it.
Okay I've made some good progress now and I'm able to get the actuator to extend and retract between two positions using two bush buttons on my breadboard hard coded to their own destinations. I've also slowed the motor down and increased the "precision" slack to allow anything within 20 to be acceptable. The problem I'm having now is that even though the actuator is stopping exactly on the position I want, or at least within 1, it is still sporadically bumping around in tiny little movements (even when it is exactly on the right position). Any ideas? Could it be a wiring problem or do you see something in this code that would make it do anything other than stop and sit still?
/* Sample code to control the position of an actuator with potentiometer feedback using a MegaMoto.
The main loop of this program checks the potentiometer, and moves the actuator accordingly.
Written by Progressive Automations
This example code is in the public domain.
*/
const int feedback = A0; //potentiometer from actuator
const int pwm = 6; //motor enable pin
const int dir = 7; //motor direction pin (HIGH = forwards LOW = backwards)
int precision = 10;//how close to final value to get
int checkingInterval = 50;//how often position is checked (milliseconds)
const int buttonPin = 2;
const int buttonPin2 = 4;
int buttonPinState = 0;
int buttonPin2State = 0;
int currentPosition = 0;
int destination = 350;
int difference = 0;//values for knowing location
void setup()
{
pinMode(feedback, INPUT);//feedback from actuator
pinMode(pwm, OUTPUT);
pinMode(dir, OUTPUT);
pinMode(buttonPin, INPUT);
pinMode(buttonPin2, INPUT);
digitalWrite(pwm,HIGH);
Serial.begin(9600);
}
void loop()
{
buttonPinState = digitalRead(buttonPin);
buttonPin2State = digitalRead(buttonPin2);
difference = destination - currentPosition;//find out how far you are from the destination
currentPosition = analogRead(feedback);//check where you are
Serial.print("Position ");
Serial.println(analogRead(feedback));
Serial.print("\t Goal = ");
Serial.println(destination);
if (buttonPinState == HIGH) {
// set new goal position
destination = 400;
}
if (buttonPin2State == HIGH) {
// set new goal position
destination = 500;
}
if (currentPosition > destination) pullActuator();// choose what action to take
else if (currentPosition < destination) pushActuator();
else if (difference < precision) stopActuator();
}//end void loop
void stopActuator()
{
digitalWrite(pwm,LOW);
}//end stopActuator
void pushActuator()
{
digitalWrite(dir,HIGH);
analogWrite(pwm,100);
}//end pushActuator
void pullActuator()
{
digitalWrite(dir,LOW);
analogWrite(pwm,100);
}//end pullActuator
[\code]
if (currentPosition > destination)
pullActuator();// choose what action to take
else if (currentPosition < destination)
pushActuator();
else if (difference < precision)
stopActuator();
The first two if statements don't use 'precision'. If currentPosition is not exactly equal to destination the actuator will start to move. If you put the third if first, the actuator will remain stopped as long as difference is less than precision.
if (difference < precision)
stopActuator();
else if (currentPosition > destination)
pullActuator();// choose what action to take
else if (currentPosition < destination)
pushActuator();