Go Down

### Topic: Differential Steering using a joystick / programming question.... (Read 16511 times)previous topic - next topic

#### TimCoyne ##### Jun 17, 2013, 06:43 pm
Hi Guys,

I am brand new here, so please forgive my "noobie"ness.  This should be quite a simple problem, but it is driving me loopy...

Basically, I want to control two motors (forward fast / slow and steering via differential speed).

The inputs are: X-axis potentiometer (turn) is connected to pin A1 and Y-axis (move) pot is connected to A0.
0,-128 is bottom left and 255,128 is top right.  There is a "forward only" strip in the centre from -10 to +10, where turning signal is ignored (just to make it not too squirrely).

Outputs are: left motor digital pin 10 and right motor digital pin 9.

I have written a sketch which sort of works, but whenever the "turn" value is greater than the "move" value, it starts behaving erratically.

I have been trying to figure this out all day, and am starting to lose the plot!!  Please find the code below... any ideas? This is my first real go at programming, so please be kind....  :-)   Thanks, Tim

/* This script is intended to operate a differential steering system, but is
a little buggy.  Whaenever the Y value is greater than the X value, it reverses
it's operation.
*/

const int LMotor=10;
const int RMotor=9;
int val = 0;
int off = 0;

int move;
int turn;

void setup() {

pinMode(A0, INPUT);
pinMode(A1, INPUT);

pinMode(LMotor,OUTPUT);
pinMode(RMotor,OUTPUT);

Serial.begin(9600);
}

void loop() {
// read the X (turning) input on analog pin A1 and Y (ahead) input
//on analog pin A0:

move = map(aheadY, 0,1023, 0, 255);
move = constrain(move, 0, 255);

turn = map(turnX, 0,1023,-128,127);
turn = constrain(turn, -128,127);

Serial.print("Move   ");
Serial.println(move);

Serial.print("              Turn     ");
Serial.println(turn);

if (turn >=-10 && turn <=10)  // go forward
{
analogWrite (LMotor,move);
analogWrite (RMotor,move);
}
if (turn > move)  //  this sequence seems to
{
analogWrite (LMotor,0);  // have absolutely no effect..
analogWrite (RMotor,0);
}

if (turn <-10)   // forward left
{
analogWrite (LMotor,move + turn);
analogWrite (RMotor,move);
}

if (turn >10)     // forward right
{
analogWrite (LMotor,move);
analogWrite (RMotor,move - turn);
}
}

#### TimCoyne #1
##### Jun 17, 2013, 06:55 pm
Here is a photo of the offending item....

#### groundFungus #2
##### Jun 17, 2013, 07:41 pm
Your circuit will only turn the motor on or off.  there is no way to get reverse with that circuit.  a one one the Arduino output will turn the motor one,  zero off.  you will need  an H-bridge to get forward and reverse.  there are lots of examples for dc motor control.

#### TimCoyne #3
##### Jun 17, 2013, 08:42 pm
Hi GF,

For this application, I don't need reverse, just to be able to control speed and rate of turn.

If I was really clever, I would even be able to program the radius of turn at a given speed, but I am just not that smart.....  :-(

:-)

Tim

#### keeper63 #4
##### Jun 18, 2013, 06:47 amLast Edit: Jun 18, 2013, 06:49 am by cr0sh Reason: 1
I'm going to try to explain something - and once I do, it will either get your brain running again - or it just might be a "doh!" moment...

You have two axes on the joystick - an x-axis and a y-axis, right? So - let's imagine you have the potentiometers of each axis connected to an analog input on the Arduino. So - what happens then if you push the joystick all the way up into the upper-left hand corner?

That's right - your Arduino is going to read "0" for the x-axis, and "0" for the y-axis (note: I am ignoring any possibility that calibration of the joystick will be needed - in reality, this is most likely - I don't know if you are old enough or not to remember old DOS games, and how you had to calibrate the analog joystick before playing a game - the routine was a part of the game itself).

Now - what happens if you then push the joystick to the lower-right corner?

Yes - your Arduino will read "1023" for the x-axis, and "1023" for the y-axis...

So - what does this mean? Well - here are the values (more or less):

0,0     511,0   1023,0
\       |       /
\      |      /
\     |     /
\    |    /
\   |   /
\  |  /
\ | /
\|/
0,511-----+-----1023,511
/|\
/ | \
/  |  \
/   |   \
/    |    \
/     |     \
/      |      \
/       |       \
0,1023 511,1023 1023,1023

Now - if you subtract 511 from each axis - that'll "center-up" the values - which means that the upper-left will be "-511,-511", the center will be "0,0", and the lower-right will be "512, 512"...and the other values will change similarly - understand?

Now - given that - what can you do?

Well - what if you said "my x-axis value will drive my left wheel" and "my y-axis value will drive my right wheel" - and then you scaled the values for the PWM, and based on the sign of the value, switched the PWM from one side or the other of the h-bridge...

So - let's suppose positive values mean "forward" and negative values mean "reverse". So now, at rest - the joystick (auto-centered) would read "0,0" - and a PWM values of "0" would be sent to each motor, and they would remain "off".

Then, when you pushed the joystick to the bottom-right corner, you are reading "512,512", then after scaling and sign checking - you apply PWM values of "255,255" to both motors - you're now going forward!

If you pushed the joystick to the upper-left corner, you are reading "-511, -511", then scaled and sign checked to mean "apply PWM values of 255,255 to the other pin of the h-bridge" to drive both motors in the opposite direction - and you're going backward!

So what happens if you push the joystick to the *upper-right* side? Well, you would then read "512, -511" - and after scaling and sign-checking - hey look! One motor turning one way, and the other the opposite! Turning in place, so to speak. For the "lower-left" side, you would read "-511, 512" - wow! Spinning in the opposite direction.

Take a look at the other ordinal points - and notice what they do: That's right, you can turn one motor off, and have the other spinning - that is, one wheel moving, and the other stationary - so instead of turning on the center-point of the "axle" between the wheels, you are now rotating around the center of one of the wheels.

All fine and well - for the most part - but! Everything is on a diagonal - how can I use my joystick this way (you're asking yourself)? Simple:

Rotate the joystick 135 degrees counter-clockwise - so that the normally "bottom-right" corner of the joystick faces "up"...

Note that this isn't a perfect solution; it has it's bugs - but maybe you (or someone else) can make use of it. I hope it helps, or at least gives you some ideas. It's an example of thinking outside of the box (the main part being to not look at the joystick as needing to be explicitly oriented in an X/Y position as given by the potentiometers). I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

#### TimCoyne #5
##### Jun 18, 2013, 02:26 pmLast Edit: Jun 18, 2013, 02:38 pm by TimCoyne Reason: 1
cr0sh, you are a genius, mate.

I have done what you suggested (turned the stick by 45 degrees) and have a "stick plan" which looks like this:
L    R
max        255,255
left wheel    /
pot here    /
min/                   |
L   R             |               L   R
255,0         - - + - -          0,255
max                  |
\                    |
rt wheel    \
pot here     \                L R
min             0,0

One potentiometer controls the left wheel, and the other controls the right.  There is currently no reverse, although by remapping the pots  to say, -10 to 245 and using an if statement that could easily be handled.

The right stick values ("R" in the diagram) are then fed to the left motor, and the left stick values ("L" in the diagram) are then fed to the right motor.....  simples!

It works a treat, the only thing it needs now is some way of "proportionaly slowing it down"...  in other words, having both motors reading 255 and running at full chat is fine, but having one reading 0 and stopped and the other reading 255 and at running full blat is a bit harsh.  I have tried subtracting the 2 values and dividing them, but I'm just not getting it right....

Anyway, here is the code if anyone needs it....

// This script operates a differential steering system. By Tim Coyne

const int LMotor=10;
const int RMotor=9;
int val = 0;

void setup() {

pinMode(A0, INPUT); // connects #1 potentiometer which senses stick inputs on the right of center to Arduino
pinMode(A1, INPUT); // connects #2 potentiometer which senses stick inputs on the left of center

pinMode(10,OUTPUT);  // connects left motor to Arduino
pinMode(9,OUTPUT);   // connects right motor

Serial.begin(9600);  // sends stick left and right values back to computer
}

void loop() {

leftStick = map(leftStick, 0,1023, 0, 255);  // changes 1023 steps to 256 steps which Arduino can read
leftStick = constrain(leftStick, 0, 255);

rightStick = map(rightStick, 0,1023, 0,255);  // changes 1023 steps to 256 steps which Arduino can read
rightStick = constrain(rightStick, 0,255);

Serial.print("Left Stick   ");    // This makes the left and right values easier to read on the computer screen.
Serial.println(leftStick);
Serial.print("                   Right Stick     ");
Serial.println(rightStick);

{
analogWrite (LMotor,rightStick);  // the left motor is driven by right stick value
analogWrite (RMotor,leftStick);   // the right motor is driven by left stick value
}}

// simples.  :-)

#### drazha #6
##### Mar 01, 2016, 06:05 pm
Hi all,

I have read the above article, and I have a similar setup:

|-255,+255 | +000,+255 | +255,+255 |
|-255,+000 | +000,+000 | +255,+000 |
|-255, -255 | +000,-255 | +255, -255|

Is there a way to programatically "twist" this by 45deg CCW so that +255,+255 points north so to account for analog inbetween values as well?

BR//D

#### tohatsu #7
##### Dec 30, 2016, 02:50 pm
Thought I'd share my code as I did not find anythig that exactly matched what I wanted.

I'm running two RC servo outputs into my arduino one on pins 12 and 13 and using 3,9, 10,11 as pwm outputs for my two H-bridges. Enable-signal on the bridges are constantly enabled. My code is a mix and match from several sources+some own adaptions. I'm sure some things can be improved, but it works as is.

Maybe I'll rewrite it to use digital control channels for direction and only two PWM outputs (pins 5-6)in the future (to be able to increase the PWM frequency).

Code: [Select]
`const int chA=12;  //Fwd-rev servo input const int chB=13;  //Left-right servo input//RX signal massaging valuesconst int RXLo=1000;const int RXHi=2000;const int RXDeadLo=1490;const int RXDeadHi=1510;const int RXCenter=1500;const byte controllerFA = 11; //PWM FORWARD PIN for OSMC Controller A (left motor)const byte controllerRA = 10;  //PWM REVERSE PIN for OSMC Controller A (left motor)const byte controllerFB = 9;  //PWM FORWARD PIN for OSMC Controller B (right motor)const byte controllerRB = 3;  //PWM REVERSE PIN for OSMC Controller B (right motor)int analogTmp = 0; //temporary variable to store int throttle, direction = 0; //throttle (Y axis) and direction (X axis) int leftMotor,leftMotorScaled = 0; //left Motor helper variablesfloat leftMotorScale = 0;int rightMotor,rightMotorScaled = 0; //right Motor helper variablesfloat rightMotorScale = 0;float maxMotorScale = 0; //holds the mixed output scaling factorint deadZone = 10; //jostick dead zone int ch;  //Array to store and display the values of each channelvoid setup()  {  //initialization of pins   Serial.begin(115200); pinMode(controllerFA, OUTPUT); pinMode(controllerRA, OUTPUT); pinMode(controllerFB, OUTPUT); pinMode(controllerRB, OUTPUT);   pinMode(chA, INPUT); pinMode(chB, INPUT);} void loop()  {   ch = pulseIn (chA,HIGH);  //Read and store channel 1  ch = pulseIn (chB,HIGH);  for (int i=0; i<=2; i++)      //Signal Conditioning loop  {   if (ch[i] <= RXLo)             //Trim Noise from bottom end   {    ch[i] = RXLo;   }     if (ch[i] <= RXDeadHi && ch[i] >= RXDeadLo)     //Create Dead-Band   {    ch[i] = RXCenter;   }     if (ch[i] >= RXHi)            //Trim Noise from top end   {     ch[i] = RXHi;   }   ch[i]=map(ch[i],RXLo,RXHi,-255,255);  } throttle = ch; direction = ch; //mix throttle and direction leftMotor = throttle+direction; rightMotor = throttle-direction; //print the initial mix results Serial.print("LIN:"); Serial.print( leftMotor, DEC); Serial.print(", RIN:"); Serial.print( rightMotor, DEC); //calculate the scale of the results in comparision base 8 bit PWM resolution leftMotorScale =  leftMotor/255.0; leftMotorScale = abs(leftMotorScale); rightMotorScale =  rightMotor/255.0; rightMotorScale = abs(rightMotorScale); Serial.print("| LSCALE:"); Serial.print( leftMotorScale,2); Serial.print(", RSCALE:"); Serial.print( rightMotorScale,2); //choose the max scale value if it is above 1 maxMotorScale = max(leftMotorScale,rightMotorScale); maxMotorScale = max(1,maxMotorScale); //and apply it to the mixed values leftMotorScaled = constrain(leftMotor/maxMotorScale,-255,255); rightMotorScaled = constrain(rightMotor/maxMotorScale,-255,255); Serial.print("| LOUT:"); Serial.print( leftMotorScaled); Serial.print(", ROUT:"); Serial.print( rightMotorScaled); Serial.print(" |"); //apply the results to appropriate uC PWM outputs for the LEFT motor: if(abs(leftMotorScaled)>deadZone) {   if (leftMotorScaled > 0)   {     Serial.print("F");     Serial.print(abs(leftMotorScaled),DEC);     analogWrite(controllerRA,0);     analogWrite(controllerFA,abs(leftMotorScaled));               }   else    {     Serial.print("R");     Serial.print(abs(leftMotorScaled),DEC);     analogWrite(controllerFA,0);     analogWrite(controllerRA,abs(leftMotorScaled));     } }   else  { Serial.print("IDLE"); analogWrite(controllerFA,0); analogWrite(controllerRA,0); }  //apply the results to appropriate uC PWM outputs for the RIGHT motor:   if(abs(rightMotorScaled)>deadZone) {   if (rightMotorScaled > 0)   {     Serial.print("F");     Serial.print(abs(rightMotorScaled),DEC);     analogWrite(controllerRB,0);     analogWrite(controllerFB,abs(rightMotorScaled));               }   else    {     Serial.print("R");     Serial.print(abs(rightMotorScaled),DEC);     analogWrite(controllerFB,0);     analogWrite(controllerRB,abs(rightMotorScaled));     } }   else  { Serial.print("IDLE"); analogWrite(controllerFB,0); analogWrite(controllerRB,0); }  Serial.println(""); //To do: throttle change limiting, to avoid radical changes of direction for large DC motors and alter PWM base frequency. delay(10);}`

Go Up