Control Linear Actuator By Position (Actuonix with position feedback)

Hello,

I have a small Actuonix (Firgelli) Linear Actuator with built in potentiometer for position feedback (which shows position of the linear actuators tip 0-1000,). I would like to program it to move to position A, then position B, then position C.

I have it successfully running backward and forward. My program allows me to set the speed and duration the linear actuator should move forward and backward, but I cannot figure out how to control it by position. I tried with a series of If and else statements but this did not work and in theory could only make it move to position a and back to position b in a loop. I require 3 positions.

Does anyone have any ideas how I could do this? I am new to Arduino, so would very much appreciate and be grateful for your advice.

Extra Info: I am using an Uno, I have attached my code, the Linear actuator specifications (I am using the 6V L12-P 30cm stroke), and I am also using a TB6612FNG Dual Motor Driver Carrier.

#define AIN1 2
#define BIN1 7
#define AIN2 4
#define BIN2 8
#define PWMA 5
#define PWMB 6
#define STBY 9

const int feedback = A0; //potentiometer from actuator
int position_LA = 0; //potentiometer from actuator
int prev;



void setup()
{
  pinMode(feedback, INPUT);//feedback from actuator
  pinMode(PWMA, OUTPUT);
  pinMode(AIN1, OUTPUT);
  pinMode(AIN2, OUTPUT);
  pinMode(STBY, OUTPUT);
  Serial.begin(9600);
  enable();
}

void loop()
{

prev = position_LA;
position_LA = analogRead(A0);  // read the input on analog pin 0:

  
if((position_LA >= prev) && position_LA < 750)
  {
    fwd(150);
  }

else if (( position_LA < prev) && position_LA > 250)
{
  rev(150);
}
delay(20);
  
  Serial.print("  Position LA  = ");
  Serial.println(position_LA);


}//end void loop


void fwd(int speed)
{ digitalWrite(AIN1, HIGH);
  digitalWrite(AIN2, LOW);
  analogWrite(PWMA, speed);
}
void rev(int speed)
{ digitalWrite(AIN1, LOW);
  digitalWrite(AIN2, HIGH);
  analogWrite(PWMA, speed);
}
void brake()
{ digitalWrite(AIN1, HIGH);
  digitalWrite(AIN2, HIGH);
  analogWrite(PWMA, 0);
}
void standby()
{ digitalWrite(STBY, LOW);
}
void enable()
{ digitalWrite(STBY, HIGH);
}

If I wanted to move backward or forward I would put...

//reverse for 2 seconds

rev(155); //reverse at speed 155
delay(2000); //for 2 seconds

or

fwd(155);
delay(2000)

As I mentioned, new to Arduino so If you have a better way of doing this would appreciate the advice.

Thanks for reading this and for future advice!
Melissa :slight_smile:

I didn’t read your code, but i’ve driven multiple Firgelli (or any servo) simultaneously.

I used an array of currentPosition[], and targetPositio[] values - then using a millis() timer, ensured they always attempted to bring currenPosiotion to match targetPosition for each servo.

No work other than to set a new targetPosition whenever you need a move. You could add other functions like speed, and end limits etc as needed.

Thanks for the reply!

lastchancename:
I used an array of currentPosition[], and targetPositio[] values - then using a millis() timer, ensured they always attempted to bring currenPosiotion to match targetPosition for each servo.

I read up on current and target position, overshoot and millis. However I still wasn't able to make it move to 3 separate positions. Using this code I could get it to move to position A. But I am unsure where to implement millis and also this code. Should I have this in the update loop or as its own void function (I tried both with position a,b,c but didn't work and stopped at position A)? Can you give any advise on how I can get this to work for 3 positions?

From what I read I need it to move to position A, then use Millis to allow the linear actuator to get to this position before then reading the code for move to Position B, just haven't been able to implement this (or I am wrong).

  if (position_LA > target_A)
{
  rev(155);
}
else
{
  brake();
}

You're perfectly right.

As there is no absolute feedback of the servo positions in real-time - you need to keep track of the estimated time to complete each of the axis movements you're attempting.
Unfortunately, these can vary under different voltages/loads etc (esp with multiple axes and long throws) - so you need to allow 'worst case' durations on each stroke.

millis() timing will be a fundamental part of your methods, keeping track of when each axis/motion starts, and forward estimating how long each axis/motion will take to complete.

With that knowledge (plus a fraction extra for tolerance), you can run each axis back-to-back continuously. or you may prefer to stall the slower movements until the 'last one' has completed - then start the next leg of the motion sequence as needed.

I mentioned arrays[] earlier, and you should read up on them - as well as functions() so you can get the most from code efficiency and readability.

As there is no absolute feedback of the servo positions in real-time - you need to keep track of the estimated time to complete each of the axis movements you're attempting.

I don't think this is correct. The L12-P series has an analog position sensor. I see no reason why this actuator needs to be controlled with timed moves instead of moves controlled by position feedback. A big factor is whether or not the code can be "blocking" during a move. If so, the architecture could involve while() loops.

The starting and current position as well as positions A-B-and C are known. You should know which direction to move. Slowing down for no overshoot, or correcting for overshoot are possible refinements.

For example if you start at position 200 and targetA is 600, targetB is 400, and targetC is 900

while (analogRead(A0) <= targetA)
{ 
  //move out
}
//stop moving
while (analogRead(A0) >= targetB)
{ 
  //move in
}
//stop moving
while (analogRead(A0) <= targetC)
{
//move out
}
//stop moving

I tried with a series of If and else statements but this did not work

It is often easier to create sequential code with switch() case statements instead of the nested conditionals. This is also known as a finite state machine. Sequencing tasks is simplified because the completion of a state can cause the switch to the next. The while() loops could be placed within separate cases in the switch() design.

The while() loops are "blocking" like using delay() or for() and the code will be non responsive to inputs other than the position sensor during the move.

Non blocking code could handle the sequencing with switch() and small moves within each case using if() conditionals until the target was achieved. These small moves might use timed moves, or they could be designed around small changes in the analogRead values.

OK, great - the L12-P has feedback - that's excellent.
Makes the control much tighter.
Link to L12-P actuiator

Your pseudo-code in Replay #4 is sequential of course, but if you want to run all the servos simultaneously - we'll need to get rid of those while() loops...

Also - may be worth considering what +/0 rails you will provide to the actuators and feedback pots (more critical) - note theSTALL current listed for your L12-P in the datasheet - for multiple runnign simultaneously.

If you want to get fancy, use a PID control loop so that the motor slows down and stops right on the mark without overshooting.

If you don't want to get that fancy you can use simple proportional control:

  int currentPosition = analogRead(A0);
  int error = TargetPosition - currentPosition;
  if (error < 0)  // Target is lower than current
 {
    // The speed is proportional to how far from Target we are
    int speed =  constrain(-error, MinSpeed, 255);
    rev(speed);
  }
  else if (error > 0) // Target is higher than current
 {
    // The speed is proportional to how far from Target we are
    int speed =  constrain(error, MinSpeed, 255);
    fwd(speed);
  }
else  // error == 0
  brake();

This will fade down the speed as current approaches Target. It will not go slower than MinSpeed to try to keep the motor from stalling before it reaches the target. Set MinSpeed by experiment. :slight_smile:

Thank you all for your responses! I was able to get it working with the code provided. Also thank you for introducing me to Finite State machines, I was able to create a state machine where I can switch between states (force control or proportional position control).

All the best with your projects :slight_smile:

p.s
Sorry for the delay I have been travelling with work,I have been dying to get back so I could finish my arduino project.