How to Work Two Linear Actuator Synchronously by Using Arduino and VNH5019 Motor Driver?

I am newbie and I want to build a project that contains two linear actuator that move synchronously. Two of them will lift up and down at same time. I use arduino mega and VNH5019 motor driver. I have thompson electrac ELD linear actuator. I have never tried to synchronize two linear actuator. Linear actuator have encoder output ( it is hall effect encoder -( not absolute. So it cannot keep last position in case of power cut )). Encoder type is hall effect type. Encoder output voltage levels low (logical zero), typical / max is 0.1/0.25 Vdc. Also it has output fully extented/retracted signal. I tested it with H-Bridge motor driver made of 4 SSR relays and push button switches to retract and extract. There is a synchronization problem. How should I solve that synchronozation problem could you help me ? What should I do?

We do not have any specific suggestions for synchronizing your system. In general, tasks for synchronizing a pair of actuators like reading the encoders, reading the limit switches, and determining how to move the motor in response to those inputs are performed by the microcontroller, not the motor driver. Synchronizing such a system generally might involve writing code for the microcontroller that moves both actuators in one direction in a controlled manner, monitoring the limit switches to determine when the actuators are fully extended, and then adjusting for any discrepancies in the position of the two actuators.

-Nathan

Thank you for your answer. I want to ask you something about VNH5019 sample code in github. When I tested VNH5019 wit your sample code, can I control the actuators by using pushing buttons by adding some snippets for push buttons? I want actuators to extract 100 mm ( for that they must work during 60 sec). If I add some codes to your codes, can I achive what I want ?

Best Regards

The demo code we include in the library for our Dual VNH5019-motor shield runs a test cycle that ramps the power up and down to both motors in forward and reverse. In general, it demonstrates how to use the functions in the library that determine how much power is sent to the motors. It sounds like you would have to integrate those functions with your own code that reads the state of the push buttons you mention (and perhaps reads the encoders and limit switches in your actuators).

-Nathan

Thank you for your answer. I try vnh5019 library and test code. It works perfectly. I want to ask about code. what do 400,-400 mean? Can I change these calues to drive motor at full speed or half speed? How can I arrange speed to control driving ? I think like that for synchronization (as you said): Linear actuators have encoder that can give 0.5mm/pulse. If I count pulses and use differentation of motor speed or position, I can equalize motor position in loop continously in ANY LOAD. Am I right ?

The value 400 corresponds to a 100% duty cycle in the forward direction. To move forward with a 50% duty cycle (half speed), you would use the value 200. With feedback from encoders, it should be possible to write code that implements something like a PID loop that continuously varies the motor speed based on the distance from the desired position.

-Nathan

Hi again, I found different code to synchronize two linear actuator by using another motor driver named MegaMoto. This code drive two linear actuator simultaneously. How can I adapt this code for VNH5019 driver module ? Could you give any idea?

Best Regards

#define amp0 A5
#define PWMA0 6
#define PWMB0 5
#define enable0 13   //pins for first MegaMoto

#define amp1 A4
#define PWMA1 9
#define PWMB1 10
#define enable1 12   //pins for second MegaMoto

#define hall0 52
#define hall1 53   //interrupt pins for hall effect sensors

#define switch0 8   //Up button
#define switch1 7   //Down button

int amp[] = {0, 0}; //current readings
int maxAmps = 100;
int minAmps = 0;
int spd[] = {0, 0, 0, 0}; //speeds (fwd, rev, fwd, rev)
int sw[] = {1, 1}; //switch up, switch down
int prev[] = {0, 0};//previous switch state

volatile int count[] = {0, 0};//Act0, Act1
int ave[] = {0, 0}; //average speed values
int timedelay[] = {750, 50}; //first, regular delay for current readings
int FWDspd = 255, REVspd = 255;//default speed values (max)
int maxSpeed = 255;

int hitLimits = 0;
int hitLimitsmax = 20;//value to know when at limits

bool forwards = false;
bool backwards = false;
bool dontExtend = false;
bool dontRetract = false;
bool fullyRetracted = true;//trigger for homing
bool firstRun = true;//first run of the motor once the button is pushed

void setup() {
  pinMode(amp0, INPUT); 
  pinMode(amp1, INPUT);
  
  digitalWrite(amp0, LOW);
  digitalWrite(amp1, LOW);//set Current sensors

  pinMode(PWMA0, OUTPUT); 
  pinMode(PWMA1, OUTPUT); 
  pinMode(PWMB0, OUTPUT);
  pinMode(PWMB1, OUTPUT);//set PWM outputs

  pinMode(enable0, OUTPUT); 
  pinMode(enable1, OUTPUT); 
  digitalWrite(enable0, LOW);
  digitalWrite(enable1, LOW); //set enable and turn boards off

  pinMode(switch0, INPUT); 
  pinMode(switch1, INPUT); //set up/down switch
  digitalWrite(switch0, HIGH);
  digitalWrite(switch1, HIGH);//enable enternal pullups

  pinMode(hall0, INPUT);
  pinMode(hall1, INPUT);//set interrupts
  digitalWrite(hall0, LOW);
  digitalWrite(hall1, LOW);//set low, to wait for rising edge

  attachInterrupt(digitalPinToInterrupt(52), speed0, RISING);
  attachInterrupt(digitalPinToInterrupt(53), speed1, RISING); //enable the two hall effect interupts

  dontExtend = false;
  dontRetract = false;
  fullyRetracted = true;//start assuming retracted

  Serial.begin(9600);
}//end setup

void loop() {
   ReadInputs();//check input button, calculate speeds

  if (sw[0] == 0 && sw[1] == 1) goForwards();
  else if (sw[0] == 1 && sw[1] == 0) goBackwards();
  else stopMoving();

  for (int i = 0; i <= 1; i++) prev[i] = sw[i]; //store switch values as previous values
}//end loop

void ReadInputs() {
  amp[0] = analogRead(amp0);
  amp[1] = analogRead(amp1);//read current sensors
  sw[0] = digitalRead(switch0);
  sw[1] = digitalRead(switch1);//read switches
}//end read inputs

void goForwards()
{
  if (prev[0] == 1 && sw[0] == 0) firstRun = true;
  else firstRun = false;//when the switch changes, use firstRun delay
  forwards = true;
  backwards = false;
  fullyRetracted = false;
  //Serial.println("Moving forwards");
  spd[0] = FWDspd;
  spd[1] = 0;
  spd[2] = FWDspd;
  spd[3] = 0; // set base speeds
  FWDsync();//update speeds based on hall effect counts
  getFeedback();//check current draw

  digitalWrite(enable0, HIGH);
  digitalWrite(enable1, HIGH);//enable both boards
  //Serial.print("Dont Extend"), Serial.println(dontExtend);
  if (dontExtend == true)
  {
    digitalWrite(enable0, LOW);
    digitalWrite(enable1, LOW);//disable both boards
  }
  delay(10);//slight delay

  Serial.print(" Speeds "), Serial.print(spd[0]), Serial.print(", "), Serial.print(spd[1]);
  Serial.print(", "),       Serial.print(spd[2]), Serial.print(", "), Serial.println(spd[3]);

  analogWrite(PWMA0, spd[0]);
  analogWrite(PWMB0, spd[1]);
  analogWrite(PWMA1, spd[2]);
  analogWrite(PWMB1, spd[3]);//set speeds
  
  if (firstRun == true) delay(timedelay[0]);
  else delay(timedelay[1]); //small delay to get to speed

  dontRetract = false;//once you have moved forwards, you can move back again.
  firstRun = false;
}//end goForwards

void goBackwards()
{
  if (prev[1] == 1 && sw[1] == 0) firstRun = true;
  else firstRun = false;//when the switch changes, use firstRun delay
  forwards = false;
  backwards = true;
  //Serial.println("Moving backwards");
  spd[0] = 0;
  spd[1] = REVspd;
  spd[2] = 0;
  spd[3] = REVspd;  // set base speeds
  REVsync();//update speeds based on hall effect counts
  getFeedback();//check current draw

  digitalWrite(enable0, HIGH);
  digitalWrite(enable1, HIGH);//enable both boards
  //Serial.print("Dont Retract"), Serial.println(dontRetract);
  if (dontRetract == true)
  {
    digitalWrite(enable0, LOW);
    digitalWrite(enable1, LOW);//disable both boards
  }
  delay(10);//slight delay

  Serial.print(" Speeds "), Serial.print(spd[0]), Serial.print(", "), Serial.print(spd[1]);
  Serial.print(", "),       Serial.print(spd[2]), Serial.print(", "), Serial.println(spd[3]);

  analogWrite(PWMA0, spd[0]);
  analogWrite(PWMB0, spd[1]);
  analogWrite(PWMA1, spd[2]);
  analogWrite(PWMB1, spd[3]);//set speeds
  
  if (firstRun == true) delay(timedelay[0]);
  else delay(timedelay[1]); //small delay to get to speed

  dontExtend = false;//once you have moved backwards, you can move forwards again.
  firstRun = false;
}//end goBackwards

void stopMoving()
{
  forwards = false;
  backwards = false;
  //Serial.println("Stopped");
  analogWrite(PWMA0, 0);
  analogWrite(PWMB0, 0);
  analogWrite(PWMA1, 0);
  analogWrite(PWMB1, 0);//set speeds to 0

  digitalWrite(enable0, LOW);
  digitalWrite(enable1, LOW);//disable both boards
}//end stopMoving

void FWDsync() {
  Serial.println("FWD");
  int diff = count[0] - count[1];
  diff = abs(diff);
  //Serial.print(" Diff - "), Serial.print(diff);
  if (diff >= 5)
  {
    if (count[0] > count [1]) spd[0] = FWDspd - 10;
    else spd[2] = FWDspd - 10;
    if (diff >= 10)
    {
      if (count[0] > count [1]) spd[0] = FWDspd - 25;
      else spd[2] = FWDspd - 25;
      if (diff >= 20)
      {
        if (count[0] > count [1]) spd[0] = FWDspd - 50;
        else spd[2] = FWDspd - 50;
        if (diff >= 40)
        {
          if (count[0] > count [1]) spd[0] = 0;
          else spd[2] = 0;
        }//end if diff>40
      }//end if diff>30
    }//end if diff>20
  }//end if diff>10

   else {
    spd[0] = maxSpeed;
    spd[2] = maxSpeed;
  }
  Serial.print(" ACT1 counts - "), Serial.print(count[0]);
  Serial.print(" ACT2 counts - "), Serial.print(count[1]);
}//end FWDSync

void REVsync() {
  Serial.println("REV");
  int diff = count[0] - count[1];
  diff = abs(diff);
  //Serial.print(" Diff - "), Serial.print(diff);
  if (diff >= 5)
  {
    if (count[0] < count [1]) spd[1] = REVspd - 10;
    else spd[3] = REVspd - 10;
    if (diff >= 10)
    {
      if (count[0] < count [1]) spd[1] = REVspd - 25;
      else spd[3] = REVspd - 25;
      if (diff >= 20)
      {
        if (count[0] < count [1]) spd[1] = REVspd - 50;
        else spd[3] = REVspd - 50;
        if (diff >= 40)
        {
          if (count[0] < count [1]) spd[1] = 0;
          else spd[3] = 0;
        }//end if diff>40
      }//end if diff>30
    }//end if diff>20
  }//end if diff>10

  else {
    spd[1] = maxSpeed;
    spd[3] = maxSpeed;
  }
  Serial.print(" ACT1 counts - "), Serial.print(count[0]);
  Serial.print(" ACT2 counts - "), Serial.print(count[1]);
}//end REVsync

void speed0() {
  //Serial.println("Update 1");
  if (forwards == true) count[0]++; //if moving forwards, add counts
  else count[0]--; //if moving back, subtract counts
}//end speed0

void speed1() {
  //Serial.println("Update 2");
  if (forwards == true) count[1]++; //if moving forwards, add counts
  else count[1]--; //if moving back, subtract counts
}//end speed1

void getFeedback()
{
  amp[0] = analogRead(amp0);
  amp[1] = analogRead(amp1);
  //Serial.print(" Amp readings - "), Serial.print(amp[0]), Serial.print(", "), Serial.println(amp[1]);

  if ((amp[0] <= minAmps|| amp[1] <= minAmps) && hitLimits < hitLimitsmax) hitLimits = hitLimits + 1;
  else hitLimits = 0;
  if (hitLimits == hitLimitsmax && backwards == true) fullyRetracted = true;
  else fullyRetracted == false;

  if (fullyRetracted == true)
  {
    for (int e = 0; e <= 4; e = e + 1) count[e] = 0;
    hitLimits = 0;
  }//end fullyretracted

  if (amp[0] > maxAmps)
  {
    //Serial.println(amp[0]);
    spd[0] = 0;
    spd[1] = 0;
    spd[2] = 0;
    spd[3] = 0;//stop all motion
    //Serial.println("TRIPPED 0");
    if (forwards == true) dontExtend = true;
    else if (backwards == true) dontRetract = true;//disable movement in the direction the limit was tripped
  }//end if

  if (amp[1] > maxAmps)
  {
    //Serial.println(amp[1]);
    spd[0] = 0;
    spd[1] = 0;
    spd[2] = 0;
    spd[3] = 0;//stop all motion
    //Serial.println("TRIPPED 1");
    if (forwards == true) dontExtend = true;
    else if (backwards == true) dontRetract = true;//disable movement in the direction the limit was tripped
  }//end if
}//end getFeedback


I skimmed that code and I don’t see any obvious problems with it, but there is no reason to expect the control input pins on the motor driver it was written for to function the same as control the pins on a different motor driver. In general, you might look carefully at the logic structure in the code and read the code itself and the comments to understand what decisions it is making and what actions it takes. Then you can write similar code to implement some of the same behavior for the hardware you have.

Is there anything specific in that code that you do not understand?

-Nathan

Actually, I dont understand void FWDsync() function. what does diff >= 5,diff >= 10 etc. mean ? Why this numbers(5,10,20,40). Does This function of code work with VNH5019 ( synchronization of speed and position will be provided by this part) ? If I change code according to VNH5019 pinout and dont change other parts of code, will it work ( hall effect sensor pinout etc.)?
What is your advices?

Best Regards.

The best way to determine what the function is doing is to trace out the input and output variables in the function to see how they are used.

The diff variable is defined and its value is set in the function in line 189 of the code (int diff = count[0] - count[1];). To determine how the elements of the array count[] are set, you can search the code to see where they are used. The speed0 and speed1 functions change the value of count[0] and count[1]. If you look at lines 67 and 68 of the code, you can determine that the speed0 and speed1 functions are ISR functions for pins 52 and 53. According to lines 11 and 12, pins 52 and 53 appear to be connected to hall effect sensors. You can learn more about the attachInterrupt() function used in lines 67 and 68 on its Arduino Reference page.

It appears that the nested if statements in the function all set the spd[2] array element to different values based on the value of the count[0] and count[1] values. You could look through the rest of the code to see where the spd[2] array element is used. In general, it appears that the FWDsync() function sets the speed of one of the motors based on the difference between the number of times the encoder pins have been toggled (presumably, it sets the speed of one of the motors slower if it is too far ahead).

In general, using code written to work with one hardware configuration is often not directly usable with a similar but different piece of hardware and it can take a considerable bit of work to understand what the code does and modify it for your own purposes. I have taken the time to outline a little of the process here to help you understand what is generally involved, though interpreting and explaining all of the code is outside the scope of our technical support. If you are having trouble parsing the code, you might try to find a C++ course or someone who is a more experienced coder to help you out.

-Nathan

Hello Nathan,

I achieve to control two linear actuator by using button but I have a problem about encoder counting. I use Linak LA25 for testing. The actuator encoder gives 1 pulses 0.5mm ( 0.5mm/pulse). I write a code just counting pulses from encoder. The system stop motors when encoder is counting up to 250 pulses. Motors’ stroke extends up to 125 mm and that is right. I tested it with different speeds. When I arrange speed to 400 ( md.setSpeeds(400, 400);, encoder counting is right. But when I arrange the speed to 250 , encoder dont count right. Motors stop when encoder counting is 250 as above, but moto stroke is 108 mm that encoder counts 216 pulses. There is a problem when motors work at lower speed. But when I power motor by using only power supply(disconnecting VNH5019 from system) and connect encoder pin to arduino, I can count pulses perfectly ( at high speed ). Could you help me about that problem? Could you give an opinion? By the way I seperate the power source for “motor INA- INB” and " actuator encoder power". I add the test code for counting encoder pulses:

#include "DualVNH5019MotorShield.h"

DualVNH5019MotorShield md(13, 4, 9, 6, A0, 7, 8, 10, 12, A1);


void print(int val1, int val2)
{
	Serial.print(val1);
	Serial.print("-");
	Serial.println(val2);
}


// Hall settings

#define M1_HALL_PIN	2
#define M2_HALL_PIN 3

int16_t m1_hall_count = 0;
int16_t m2_hall_count = 0;

void initHallSensors()
{
	pinMode(M1_HALL_PIN, INPUT);
	pinMode(M2_HALL_PIN, INPUT);//set interrupts
	digitalWrite(M1_HALL_PIN, LOW);
	digitalWrite(M2_HALL_PIN, LOW);//set low, to wait for rising edge

	attachInterrupt(digitalPinToInterrupt(M1_HALL_PIN), onM1HallInterrupt, RISING);
	attachInterrupt(digitalPinToInterrupt(M2_HALL_PIN), onM2HallInterrupt, RISING); //enable the two hall effect interupts
}

void onM1HallInterrupt()
{
		m1_hall_count++;
}

void onM2HallInterrupt()
{
		m2_hall_count++;

}

void setup()
{
	Serial.begin(115200);
	Serial.println("Dual VNH5019 Motor Shield");
	initHallSensors();
	md.init();

	md.setSpeeds(400, 400);
	delay(20000);
	md.setSpeeds(0, 0);
	print(m1_hall_count, m2_hall_count);
}

void loop()
{

}

Did you increase the delay time when you decreased the speed? From the typical speed specifications in the datasheet for the linear actuator you mentioned, I would expect the 25060xxxxxxxxA (which appears to be what you are using based on the 0.5mm/tick pulse rate you mentioned) to take about 20 seconds to travel 125mm at full power. If you decrease the power by using md.setSpeeds(250, 250), then I would expect the actuator to take longer than 20 seconds to move 125mm.

-Nathan