Robotic arm with joystick control

Hello all.

I've been working on a robotic arm and was planning on using 2-axis parallax joysticks to control the joint servos. I've gotten the arm to work with the joysticks but the problem I'm having is when I let go of the joystick the arm returns to the beginning position. It makes sense since my code does an analogRead and then tells the servos to move to that position, but how do I get the arm to stay hold a position until I wanna move it again? Similar to how a robotic arm should work.

Below is my code so far. Any advice?

Thanks!

#include <Servo.h>


int LR;
int UD;
int ebPos;
int gripPos;

Servo base;
Servo base2;
Servo elbow;
Servo grip;

void setup() {
  
  base.attach(9);
  base2.attach(10);
  elbow.attach(11);
  grip.attach(6);
  Serial.begin(9600);
 
  
}

void loop() {
  //outputJoystick();
  UD = analogRead(1);
  LR = analogRead(0);
  ebPos=analogRead(2);
  gripPos=analogRead(3);
  UD = map(UD,0,1023,70,180);
  LR = map(LR,0,1023,0,180);
  ebPos = map(ebPos,0,1023,0,180);
  gripPos = map(gripPos,0,1023,0,180);
  base.write(LR);
  base2.write(UD);
  elbow.write(ebPos);
  grip.write(gripPos);
  
}

void outputJoystick(){
 Serial.print("UD = ");
 Serial.print(UD);   
 Serial.print(", LR = ");
 Serial.println(LR);  
}

but how do I get the arm to stay hold a position until I wanna move it again?

Somehow remove/defeat the spring return in the joystick, or have the arm move incrementally when the joystick is off center.

First off - change your mapping of UD to 0-180 (instead of 70-180).

So now, UD (and your other vars) all correspond to 0-180, right? Subtract 90 from them - so now they map -90 to 90. When the joystick is in the middle, it should be at 0,0 (ok, in reality it won't be; you'll need to make something to account for this - perhaps a "window" that ignores values from -5 to 5 or something like that - there are better solutions as well).

So - let's take UD as our variable; have another variable that represents the servo - let's call it "servoUD" - and set it to a value in setup (maybe set it to 0, or some other value - whatever is the "home position" for that joint). Then, just add UD to servoUD in the loop (ie - servoUD += UD). The farther you move the joystick, the faster servoUD will be updated (you may have to or will want to apply some scaling factors). Also do some if-then checks to keep servoUD between 0 and 180 (or whatever the end-points are for the servo position). Set your servo position to "servoUD".

Do something similar with all of your other joints. As long as when you release the joystick to let it go back to "center" - and you filter out the values on the window around the middle in some manner (reducing them to "0" - so when the joystick is released, it is effectively at "0,0") - this should work.

You could also later set something up so that when you pressed a button, it would store the values of the servo positions to home values for each servo (perhaps store them on EEPROM) - so when you power up the Arduino next time, the arm "homes" back to that pose you stored.

Thanks!!

It seems to be doing what I want. It would have been nice to had the joystick calibrated to "0,0" but oh well. Anyway below is my code after your suggestions... I hope I understood what you meant and put it in my code correctly. This is the program for only 2 joints, I didn't want to do the whole arm until I heard back from you.

Another question: how would I make the joystick less sensitive? I hardly push the joystick one way and the servo will spin to that position way too fast. Is there a way to make the joystick have more precise control over the arm? Im guessing that the solution has something to do with the scaling factor you were talking about?

#include <Servo.h>


int LR;

int UD;

int servoLR=90;
int servoUD=90;

Servo base;
Servo base2;

void setup() {
  
  base.attach(9);
  base2.attach(10);
  Serial.begin(9600);

}

void loop() {

  UD = analogRead(1);
  LR = analogRead(0);
  
  UD = map(UD,0,1023,0,180)-90;
  LR = map(LR,0,1023,0,180)-90;
  
  servoLR=constrain(servoLR,0,180);
  servoLR += LR;
  base.write(servoLR);
  if((UD<-3) || (UD>-0)) {
  servoUD=constrain(servoUD,0,180);
  servoUD += UD;
  base2.write(servoUD);
  }

  outputJoystick();
  
}
void outputJoystick(){
 Serial.print("UD = ");
 Serial.print(UD);   
 Serial.print(", LR = ");
 Serial.println(LR);  
}

Some pot test code for moving servos.

//zoomkat dual pot/servo test 12-29-12
//view output using the serial monitor

#include <Servo.h> 
Servo myservo1;
Servo myservo2;

int potpin1 = 0;  //analog input pin A0
int potpin2 = 1;

int newval1, oldval1;
int newval2, oldval2;

void setup() 
{
  Serial.begin(9600);  
  myservo1.attach(2);  
  myservo2.attach(3);
  Serial.println("testing dual pot servo");  
}

void loop() 
{ 
  newval1 = analogRead(potpin1);           
  newval1 = map(newval1, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval1 > (oldval1+2)){  
    myservo1.write(newval1);
    Serial.print("1- ");
    Serial.println(newval1);
    oldval1=newval1;
  }

  newval2 = analogRead(potpin2);
  newval2 = map(newval2, 0, 1023, 0, 179);
  if (newval2 < (oldval2-2) || newval2 > (oldval2+2)){  
    myservo2.write(newval2);
    Serial.print("2- ");    
    Serial.println(newval2);
    oldval2=newval2;
  }
  delay(50);
}

cflo2k:
It seems to be doing what I want. It would have been nice to had the joystick calibrated to "0,0" but oh well. Anyway below is my code after your suggestions... I hope I understood what you meant and put it in my code correctly. This is the program for only 2 joints, I didn't want to do the whole arm until I heard back from you.

Yes, you seem to have it right - I would suggest a couple of changes, though:

  1. Do the "constrain" after the addition of the value from the joystick.
  2. You only seem to have a limit on UD - what about LR?

cflo2k:
Another question: how would I make the joystick less sensitive? I hardly push the joystick one way and the servo will spin to that position way too fast. Is there a way to make the joystick have more precise control over the arm? Im guessing that the solution has something to do with the scaling factor you were talking about?

Yes - by a "scaling factor", you can just do a simple division on UD and LR, prior to adding them to the servo value; because they are integer values, you'll lose some precision (because of the rounding on the divide), but that's ok. For instance, if you divide UD and LR by "4", those values will be only a 1/4 as large. Note that you can only go so far with this method (for instance, if you divided by 90 - you would have effective values of +1/-1 - and nothing else!).

If you wanted finer operation, you could move to floating point variables - or you could perform fixed-point arithmetic using all integers (something similar would be to multiply your values by a larger number - say 1000 - then at the end, divide by the same amount - which will increase precision; it's essentially a variant on fixed-point), perhaps coupled with a modified form of Bresenham's algorithm (tough to explain what I mean here, but it's basically a method to advance things slowly over a slope to make angled lines using all integer math, along with error correction - something similar could be done to allow you to slowly add a value to a servo, over time).

this may be a bit late but if your looking to control the arm without it moving back look at inverse kinematics it will allow to figure out were your robotic arm is currently(in x.y.z dimensions) and store the position and keep it there.

here's and example although this method is a lot more complex most robotic arms use it