Linear Actuator Motor Problem

Hello Arduino forums!

I've been struggling with a problem for the last few days that I just can't work out, maybe someone here has the expertise to help.

I'm attempting to run a linear actuator (Actuonix L12-50-210-6-I datasheet here) very, very slowly. The eventual application is to push a syringe full of fluid, and I'm looking to achieve around 100 microliters per minute as a minimum. Pushing on a 5mL syringe this ends up needing the actuator to run at around 0.6 mm/min, which through trial and error calculations is close to 0.2% of full speed of the actuator. I fully appreciate this is probably impossible to get accurately but this is the absolute lower limit of flow I'll need, and the fluid in the syringe will provide some elasticity, and help smooth out small steps-per-second. So, I decided to run the motor off an Arduino Nano which was bought off Amazon before I realised there were such things as fake Arduinos, so quite possibly a fake/copy/knock-off - but seems to run as expected in every other respect.

The basic idea I had was to loop through a function, with an in-built delay, and step the motor forward by increasing a Servo.writeMicroseconds command by 1 on each loop. Now here's the bit I don't understand - at very low delays, this runs fine, so at 100% - 20% of the actuator's top speed, it's nice and smooth, but at longer delays (so lower speeds) the shaft will randomly stop in the middle of operation, wait an apparently random amount of time, and then "catch up" with where it should be.

I then tried a different approach - setting the delay very short (9ms), but decreasing the step size, so rather than adding +1 to the step per loop, I'm adding +0.1 (or less, depending on the desired speed) into a float variable and then sending the (int) version of this variable to Servo.writeMicroseconds - long story short, this increases the delay by a factor of 10. This approach has been way more successful, allowing me to get down to around 8% of the servo's full speed, but still starts the "freeze and catch-up" behaviour at lower speeds.

The main thing I don't understand is why the servo seems to be having more problems receiving fewer, more spaced out commands than it does when it receives a greater number of commands per second. I have two of this model of servo and both behave the same. I'm powering the actuator from a L7806CV voltage regulator powered from a 24V mains supply, but have also tried powering it from the 5V pin on the Arduino, with identical results. I'll post my most successful code below.

Any ideas on how to better code it will be welcome, as will any ideas on how to do this smarter, whether that's different hardware or dedicated motor controller board or whatever. I know that a DC motor attached to a threaded shaft and fixed nut to move a block along the shaft would probably give me more control via the rpm and thread pitch etc etc, but the goal here was to simplify/minimise the size by using a linear actuator as a single package.

Most successful code so far:

#include <Servo.h>

Servo myServo;
#define PIN_SERVO (6)                   //motor is connected to pin 6 on the Arduino

long unsigned lastUpdate = 0;           //variable used for non-blocking delay function
long unsigned currentTime = millis();   //variable used to store current time for non-blocking delay function

int Started = 0;                        //flag used to control loop
unsigned long StartTime = 0;            //variable to store time motor movement starts
unsigned long EndTime = 0;              //variable to store time motor movement ends

float i = 0;                            //variable used to count loops for SetStrokeMMSpeed function
float usec = 1050.0;                    //variable used to set position 0 for actuator

const float syringeDiameter = 15.0;      //syringe internal diameter for volume and flow rate calculations

void SetStrokeMMSpeed(float strokeMM, float strokeSpeed)                                    //Servo movement function, inputs are final position expressed as "mm from 0", speed as "% of full speed"
{
    int interval = 9;                                                                       //interval time (in mS) for updating motor position
    int finalPos = 1020 + strokeMM * (2005 - 1020) / 50;                                    //calculates final position in uS based on entered mm position value
    if (strokeMM >= 0.0 && strokeMM <=50.0 && strokeSpeed > 0.0 && strokeSpeed <= 100.0)    //checks that mm value and speed values given are in range
    {   
        if (i <= finalPos-1020){                                                            //checks that i has not reached limit
          currentTime = millis();                                                           //records current run time 
          if (currentTime - lastUpdate >= interval) {                                       //checks whether "interval" time has been exceeded since last motor position update
              lastUpdate = currentTime;                                                     //updates lastUpdate variable to current time
              i = i + (strokeSpeed/100);                                                    //increases counter "i" by strokeSpeed
              usec = 1050 + (int)i;                                                         //adds the integer value of i to the motor position variable
              myServo.writeMicroseconds(usec);                                              //moves the motor to this position
          }
        }
      }
}

void setup() {
  Serial.begin(9600);
  myServo.attach(PIN_SERVO, 1020, 2005);
  myServo.writeMicroseconds(1020);                        //moves servo back to 0 position for start of test
  delay(20000);                                           //waits for servo to get to 0
}

void loop() {
  SetStrokeMMSpeed(50, 1);                                //Moves to 50mm (full travel) at 1% full speed
  if (Started == 0) {StartTime = millis();}               //Records start time
  Started = 1;                                            //Changes "started" flag to avoid overwriting StartTime variable
  int sensorValue = analogRead(A0);                       //Positional feedback from actuator recorded (3.3V proportional signal)
  float travelPos = (1 - ((sensorValue-12)/654.0))*100;   //Calculates current position as a percentage of full travel - the "12" and the "654" values were determined experimentally for this servo

if (travelPos>99.5 ) {                                    //Checks if actuator has reached 99.5% of total travel
    EndTime = millis();                                   //Records end time
    Serial.print("Run time: ");                           
    Serial.print(EndTime - StartTime);                    //Prints total run time to serial monitor
    Serial.println("mS");
    Serial.println("############################");
    usec = 1050;                                          //Resets usec value to initial position (useful when looping through different speeds etc)
    i = 0;                                                //Resets i to 0 (useful for looping through different speeds etc)
    Started = 0;                                          //Resets "Started" flag to 0 
  }

  if (Started == 0) {exit(0);}                            //Stops everything once the test is run. (Failsafe to avoid accidentally crashing the servo by looping movement commands through "void loop()"
}

Time to show a schematic with all the connections, including the power supply. I certainly hope you are not trying to power the motor from an Arduino pin.
Paul

That linear actuator, driven by a brushed DC motor, is a very poor choice to drive a syringe, and you may find that it is not possible to get the desired volume per unit time delivery.

It is quite easy to build your own micro syringe pump, using a lead screw coupled to a geared down stepping motor.

The dirt cheap 28BYJ-48 can be directly coupled to an ordinary threaded rod, and used to push the plunger. Servocity.com and others have the required shaft couplers, or make your own using a metal lathe and a bit of aluminum rod stock. The shaft diameter of the motor shown below is 5 mm.

Quick responses as always from the Arduino forum!

Paul_KD7HB:
Time to show a schematic with all the connections, including the power supply. I certainly hope you are not trying to power the motor from an Arduino pin.
Paul

Paul - very fair, I should supply a schematic and will when I can. But to quickly answer your question - the servo is being driven from an L7806 voltage regulator (which provides a steady 6V), which is itself connected to a 24V mains supply, so no, not powering the motor off the Arduino pin (although I did have a cheeky go at powering it off the 5V pin just once to make sure I got the same results 0- and I've tried varying the Arduino board to make sure I didn't fry anything, same results again).

That linear actuator, driven by a brushed DC motor, is a very poor choice to drive a syringe, and you may find that it is not possible to get the desired volume per unit time delivery.

It is quite easy to build your own micro syringe pump, using a lead screw coupled to a geared down stepping motor.

The dirt cheap 28BYJ-48 can be directly coupled to an ordinary threaded rod, and used to push the plunger. Servocity.com and others have the required shaft couplers, or make your own using a metal lathe and a bit of aluminum rod stock. The shaft diameter of the motor shown below is 5 mm.

I totally agree! I'm trying the linear actuator to save space, as this is all going to fit into a more complex and space-critical system. As I said at the bottom of my question, I'm aware I could have used the tried-and true method of a lead screw driven by a motor, but the neatness of the linear actuator wooed me a bit. More fool me, I guess! At this point I suppose this question is slightly less about making it work and more about learning what's going on and why the motor is freezing up like this at slower speeds.

What voltage does your DVM show for the output of the regulator while you are running the motor?
Paul

1ml syringe, 180ul +-2ul, with the Actuonix you use - just the servo version.

zwieblum:
1ml syringe, 180ul +-2ul, with the Actuonix you use - just the servo version.

Very cool! When you say "the servo version" do you mean the L12-P - that's the one with just the 3 wires (Vcc, Ground and position feedback potentiometer)? And if so are you running that off the Linear Actuator Control board from Actuonix? Or are you controlling directly with an Arduino? And if so how? And that's achieving 180 +/-2uL per... second? minute?

Paul_KD7HB:
What voltage does your DVM show for the output of the regulator while you are running the motor?
Paul

Just double checked, its consistently 5.96V, whether the motor is running or not. No voltage sags when it stops mid-cycle or anything like that.

Yes, that's the name. But they are simple RC servos with a linar potentiometer glued in the inside of the aluminium tube. The 0 to ~ 1.05 ml equals a signal of 1.00ms to 1.62ms, using the servo library I get a resolution of +-1ul. The function pulsewidth --> µl needs to be carefully calibrated with a capable scale, as the linear servo is not 100% linear :slight_smile: ... and always move in one direction when distributing fluid ...

Thanks Zwieblum - I think this might be the route for me to go down. :smiley: :smiley: :smiley:

Just two questions left if you would be so kind!!!

  1. Are you driving it straight from the Arduino, or through the Actuonix control board? I know you can send a PWM signal from Arduino > Control Board > Servo, I'm curious if you actually need the control board or can go from Arduino directly!

  2. What speed are you dispensing at? I know you said you can dispense 180 uL to +/- 2 uL accuracy, but the flow rate is really important in my application, so I need to dispense this volume over the course of a minute, not in a second or two!

@1: Just a normal Nano - the actuator is a normal rc servo. The "controller board" is just that - only a bit more expensive.

@2: the actuator takes ~ 17 seconds from 0 to fully extended - no special speed control needed there. IMO it could be faster. My next pump will not use that actuator, but a nema8.

Oh, and whats not in the photo: I attache a 0.3mm needle for operation.

Just to close this out, thank you everyone for your bug hunting with this. I spoke with the tech support team from Actuonix and they believe that the stall protection which is present on the I-series is kicking in at very small increments/slow drive speeds, they have suggested I try their P-series driven by their actuator control board which will allow me to disable this stall protection feature.

I'll probably move forward with both a simpler Actuonix L12 actuator (P- or R-series), as well as a "home-made" syringe driver system using 28BYJ-48 motors (or similar) as suggested by jremington.

Thanks again everyone!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.