Smoother movement of servos while using joystick - Arduino

41 Views Asked by At

I have an Arduino project in which I move 2 servos using a joystick module. It works pretty good, but the movement is not smooth at all. I watched many videos about interpolation, but couldn't figure out how to implement it in my existing code. Can you please help me?

This is the code I use:

#include <Servo.h>

#define SERVO_PIN 9
#define GROUND_JOY_PIN A3            //joystick ground pin will connect to Arduino analog pin A3
#define VOUT_JOY_PIN A2              //joystick +5 V pin will connect to Arduino analog pin A2
#define XJOY_PIN A1                  //X axis reading from joystick will go into analog pin A1
#define YJOY_PIN A0

Servo myservoX;
Servo myservoY;

 
void setup()
{
 Serial.begin(9600);
 pinMode(VOUT_JOY_PIN, OUTPUT) ;    //pin A3 shall be used as output
 pinMode(GROUND_JOY_PIN, OUTPUT) ;  //pin A2 shall be used as output
 digitalWrite(VOUT_JOY_PIN, HIGH) ; //set pin A3 to high (+5V)
 digitalWrite(GROUND_JOY_PIN,LOW) ; //set pin A3 to low (ground)
 myservoX.attach(9);
 myservoY.attach(6);
}
 
void loop()
{                    
 int joystickXVal = analogRead(XJOY_PIN) ;  //read joystick input on pin A1
 Serial.print(joystickXVal);                //print the value from A1
 Serial.println(" = input from joystick");  //print "=input from joystick" next to the value
 Serial.print((joystickXVal+520)/5);       //print a from A1 calculated, scaled value
 Serial.println(" = output to servo");      //print "=output to servo" next to the value
 Serial.println() ;

 myservoX.write((joystickXVal+520)/10);      //write the calculated value to the servo  
                    
 int joystickYVal = analogRead(YJOY_PIN) ;  //read joystick input on pin A1
 Serial.print(joystickYVal);                //print the value from A1
 Serial.println(" = input from joystick");  //print "=input from joystick" next to the value
 Serial.print((joystickYVal+520)/5);       //print a from A1 calculated, scaled value
 Serial.println(" = output to servo");      //print "=output to servo" next to the value
 Serial.println() ;
 myservoY.write((joystickYVal+520)/10);      //write the calculated value to the servo  
}

I tried using delays, but it did not work

1

There are 1 best solutions below

2
Adrian McCarthy On

Mapping the Input to the Output

The return from analogRead will be a number from 0 to 1023 on the most common Arduino boards. Check the documentation if you're not sure about the range of analog values for your specific microprocessor.

The output you write to the servo is supposed to be a value from 0 to 180, which corresponds to an angle in degrees. If you write 90, that should turn the servo to its middle position. Many servos can rotate an entire 180 degrees (or even a little more), but some have a more limited range.

An easy way to reliably turn the analog input into the servo output is the Arduino map function:

int joystickXVal = analogRead(XJOY_PIN);
int servoX = map(joystickXVal, 0, 1023, 0, 180);
myservoX.write(servoX);

Low Pass Filtering

To get smooth motion, you can apply filtering to the joystick input, the servo output, or both.

Since the control circuitry and the physics of the servo mechanism, there's already some filtering at the output side, so I usually start by smoothing the input.

A simple and effective method is to use exponential smoothing.

// We use a static (or global) variable to persist the filtered
// position of the joystick.
static int xVal = 512;

// Update the filtered position by doing a weighted average
// with the "instantaneous" reading.
int joystickXVal = analogRead(XJOY_PIN);
xVal = (7*xVal + joystickXVal) / 8;

// Note that we now use xVal, the filtered position, when
// figuring out where we want the servo.
int servoX = map(xVal, 0, 1023, 0, 180);
myservoX.write(servoX);

This approach works well if you update the position at approximately regular intervals.

The more you weight you give to the existing position versus the weight you assign to the instantaneous reading, the smoother the changes will be. Go too far in that direction, though, and you'll find the servo noticeably lags the joystick.

Note that since we're using int values, we have to be wary of the pitfalls of averaging integers and of possible overflow. That's why I set up the expression to do the division last, and why I chose the coefficients to ensure the intermediate result won't exceed 2^15.