Hey everyone, I'm new here and have a question about servo control. I've done two Arduino projects (specifically with ICLEDs) and this is my first project with servos.
The Use Case
The goal is to create a linear actuator powered by a servo. Why? So I would be able to move the linear actuator to a specific point (height) based on how I drive the servo. Ex: run the servo for X ms to move the linear actuator Y mm.
I have opted for a continuous servo to allow for maximum length of the linear actuator rod, and a more compact design. A 180* servo would require additional gearing (piston-style design) to achieve the same linear travel for the rod. I have avoided the idea of a screw-based linear actuator to achieve speed of linear movement.
In theory, I should be able to run the continuous servo in one direction at full speed for X ms, and then run it at full speed in the opposite direction from X ms and have the linear actuator at the same height as when it started.
The final project requires these components to be as small as possible, and I need many of them - that's why I've started with the micro servo's and 3D-printed linear actuators (the small pre-made linear actuators cost $80-100 each).
Parts
- 3D printed a linear actuator that I found online that is powered by a small servo.
- A continuous rotation Tower Pro Servo
- ESP8266-D1 Arduino Compatible Board
- A 6v 2a PSU
The Issue
I have coded the servo to run at different speeds in both directions without issue, the issue is with the consistency in the precision of the resting position of the linear actuator, powered by the servo.
When I run the servo for X ms in one direction, and then X ms in the opposite direction, the linear actuator does not always return to the same spot. Sometimes it is closer than others, and sometimes it is off quite a bit (5-15mm).
The project I am working on requires precision (1-2mm tolerances) for a linear actuator that can travel about 100mm
I don't think it's an issue with the 3D-printed components as things feel stable with no unwanted movement. I don't think it is my code, but perhaps I am missing some servo command or setting that affects precision? Perhaps I'm using a bad pin on the controller? Perhaps too much power (although I see people giving them 6v to achieve best performance)? Perhaps a poor servo? Perhaps the wrong type of servo? Or maybe I'm going about this the wrong way to achieve the desired outcome?
Hopefully I've provided all of the necessary information - but please let me know if I am missing anything.
Any ideas would be greatly appreciated!
Code
#include <Servo.h>
Servo servoA;
int currentHeight = 0; // Global variable to store the current height
// Define your constants
const int MIN_HEIGHT = 0;
const int MAX_HEIGHT = 100; // 100mm height
const int CLOSE_MAX = 1000; // go to MIN_HEIGHT at full speedn
const int OPEN_MAX = 2000; // go to MAX_HEIGHT at full speed
const int NEUTRAL = 1500; // stop
const float MM_PER_SECOND = 89; // 8.9mm/0.1s at full speed (1000 or 2000)
// clockwise is to open (180 deg, 2000 microseconds)
// counter-clockwise is to close (0 deg, 1000 microseconds)
int pos = 0;
void setup() {
servoA.attach(D5);
Serial.begin(9600);
}
void loop() {
delay(2000);
controlHeight(90); // Set to 90mm
delay(5000);
controlHeight(20); // Set to 20mm
delay(5000);
controlHeight(50); // Set to 50mm
delay(5000);
controlHeight(0); // Set to 0mm
delay(5000);
}
void controlHeight(int desiredHeight) {
Serial.println();
Serial.print("Desired Height: ");
Serial.print(desiredHeight);
Serial.println();
Serial.print("Current Height: ");
Serial.print(currentHeight);
Serial.println();
// Calculate the delay based on the desired change in height and the speed
int delayTime = abs(desiredHeight - currentHeight) / MM_PER_SECOND * 1000;
Serial.print("Delay Calculated: ");
Serial.print(delayTime);
Serial.println();
// Set the servo speed and direction
if (desiredHeight > currentHeight) {
servoA.writeMicroseconds(OPEN_MAX);
} else if (desiredHeight < currentHeight) {
servoA.writeMicroseconds(CLOSE_MAX);
}
// Wait for the linear actuator to move
delay(delayTime);
// Stop the servo by setting it to the neutral position
servoA.writeMicroseconds(NEUTRAL);
// Update the current height
currentHeight = desiredHeight;
}
Wiring Diagram