Need assistance with robot control

Hello. Currently I'm working on joystick controlled Arduino robot and found a big problem when programming the direction controls for it, specifically with diagonal movement.

Basically, most programs of Arduino joystick robot control only do forwards and backwards with the y-axis and pivot steering with the x-axis, but I wanted do to diagonal turning- basically, when you direct the analog stick at say, 2 o'clock, using both the x and the y-axis, there will be force going for both motors, but the left motor will be stronger and the right motor will be weaker. This way it allows you to do turns without just stopping dead to do it.

I tried to come up with a solution to it- the Arduino only uses the x-axis for mapping, then I wrote it so the innermost motor to the curve (again with the 2 o'clock example, in that case it would be the right motor) works as a "mirror" of the outermost motor of the curve, that way the farthest to the x-axis you pull the joystick the faster the outermost motor gets and the slower the innermost motor gets.

However that has bunch of problems; it's quite wonky and unreliable. The main problem is when the x-axis reading is low, like when you want to make a slight diagonal turn, because of the mirrored nature of the map function of the innermost motor, it will go stronger than the outermost motor, making you curve to the wrong way.

I thought about it for a long while and haven't really come up with anything to substitute the function- the two ideas I have is:

  1. make some kind of equation that grabs both y and x-axis reading and does some kind of math operation to them to come up with a number; that number is translated into a PWM command and sent to the motor shield for the outermost motor, and it is proportionally divided by another function to be sent to the innermost motor.

  2. Make a matrix of some sort for all the joystick values and have each position in the matrix (like 1024x1024) have a speed assigned to it, either by a function or manually, which sounds like a nightmare.

However I don't really have a clue on how to implement either of these of if they will even work. Do any of you guys have an idea of something I could do or use or base myself on to accomplish what I'm trying to do? Because I'm really at at loss here and any help would be appreciated.

Here is my code:

int potPin1= A4;//Y axis
int potPin2 = A5; //X axis

//these are the motor shield inputs
int ena = 5;//right motor power input
int en1 = 7;//right motor direction input 1
int en2 = 8;//right motor direction input 2
int enb = 6;// left motor power input
int en3 = 4;//left motor direction input 1
int en4 = 9;//left motor direction input 2

int joyValue1= 0; 
int joyValue2= 0;
int joyValueMax = 1023;
int joyValueMin = 0;
int joyValueMid = 512;
int joyValueMidUpper = 550; //these two values are the necessary tolerances so that the car won't go crazy on idle
int joyValueMidLower = 470; //they also double has fixed values in between which the straight forward, straight backward and pivot steer left and right functions operate

byte motorSpeed1 = 0;
byte motorSpeed2 = 0;
byte motorSpeedMin = 0;
byte motorMaxMin = 90; //not in use, but this is here because the motor only starts to move reliably at 90; anything bellow that and it's really finicky and might not move
byte motorMaxHigh = 255;//maximum motor speed

void setup() {
pinMode (ena, OUTPUT);
pinMode (en1, OUTPUT);
pinMode (en2, OUTPUT);
pinMode (en3, OUTPUT);
pinMode (en4, OUTPUT);
pinMode (enb, OUTPUT);

}

void loop() {

joyValue1 = analogRead(potPin1); //reads value of y axis potenciometer
joyValue2 = analogRead(potPin2);// reads value of x axis potentiometer

//straight forward
if (joyValue1 >= joyValueMidUpper && joyValue2 <= joyValueMidUpper && joyValue2 >= joyValueMidLower)  {//this uses the "tolerance" values to set a course in which both motors will reliably operate with the same strengh without fluctiations  
 motorSpeed1 = map(joyValue1, joyValueMidUpper, joyValueMax, motorSpeedMin, motorMaxHigh); //map function that translates joystick readings to pwm input
MotorForward1 (motorSpeed1);
motorSpeed2 = map (joyValue1, joyValueMidUpper, joyValueMax, motorSpeedMin, motorMaxHigh);
MotorForward2 (motorSpeed2);
  digitalWrite (en1, LOW);
  digitalWrite (en2, HIGH);
  digitalWrite (en3, LOW); 
  digitalWrite (en4, HIGH);
}

else if (joyValue1 <= joyValueMidLower && joyValue2 <= joyValueMidUpper && joyValue2 >= joyValueMidLower) {//staight backwards reverse 
motorSpeed1 = map(joyValue1, joyValueMidLower, joyValueMin, motorSpeedMin, motorMaxHigh);
MotorForward1 (motorSpeed1);
motorSpeed2 = map (joyValue1, joyValueMidLower, joyValueMin, motorSpeedMin, motorMaxHigh);
MotorForward2 (motorSpeed2);
digitalWrite (en1, HIGH);
digitalWrite (en2, LOW);
digitalWrite (en3, HIGH); 
digitalWrite (en4, LOW); 
}


else if (joyValue1 <= joyValueMidUpper && joyValue1 >= joyValueMidLower && joyValue2 >= joyValueMidUpper ) {// pivot steer right
 motorSpeed2 = map(joyValue2, joyValueMidUpper, joyValueMax, motorSpeedMin, motorMaxHigh);//while in the pivot steer range the opposite motor will be shut off
MotorSides2 (motorSpeed2);
  digitalWrite (en1, HIGH);
  digitalWrite (en2, HIGH);
 digitalWrite (en3, LOW); 
  digitalWrite (en4, HIGH);
}

else if (joyValue1 <= joyValueMidUpper && joyValue1 >= joyValueMidLower && joyValue2 <= joyValueMidLower ) {//pivot steer left
motorSpeed1 = map(joyValue2, joyValueMidLower, joyValueMin, motorSpeedMin, motorMaxHigh);
MotorSides1 (motorSpeed1);
digitalWrite (en1, LOW);
  digitalWrite (en2, HIGH);
digitalWrite (en3, HIGH); 
  digitalWrite (en4, HIGH);
} 

else if (joyValue2 > joyValueMidUpper && joyValue1 > joyValueMidUpper ){
motorSpeed2 = map(joyValue2, joyValueMidUpper, joyValueMax, motorSpeedMin, motorMaxHigh);// has you can see the left and right motors have got their map function mirrored
MotorSides2 (motorSpeed2);//this way, the more to the right the controller stick is, the stronger the left motor is and the weaker the right motor is
motorSpeed1 = map(joyValue2, joyValueMax, joyValueMidUpper, motorSpeedMin, motorMaxHigh);
MotorSides1 (motorSpeed1);
  digitalWrite (en1, LOW);
  digitalWrite (en2, HIGH);
  digitalWrite (en3, LOW); 
  digitalWrite (en4, HIGH);
}

else if (joyValue2 < joyValueMidLower && joyValue1 > joyValueMidUpper ){//diagonal northwest 
motorSpeed2 = map(joyValue2, joyValueMin, joyValueMidLower, motorSpeedMin, motorMaxHigh);
MotorSides2 (motorSpeed2);
motorSpeed1 = map(joyValue2, joyValueMidLower, joyValueMin, motorSpeedMin, motorMaxHigh);
MotorSides1 (motorSpeed1);
  digitalWrite (en1, LOW);
  digitalWrite (en2, HIGH);
  digitalWrite (en3, LOW); 
  digitalWrite (en4, HIGH);
  
}

else if (joyValue2 < joyValueMidLower && joyValue1 < joyValueMidLower) {// diagonal southwest
motorSpeed2 = map(joyValue2, joyValueMin, joyValueMidLower, motorSpeedMin, motorMaxHigh);
MotorSides2 (motorSpeed2);
motorSpeed1 = map(joyValue2, joyValueMidLower, joyValueMin, motorSpeedMin, motorMaxHigh);
MotorSides1 (motorSpeed1);
   digitalWrite (en1, HIGH);
  digitalWrite (en2, LOW);
  digitalWrite (en3, HIGH); 
  digitalWrite (en4, LOW);
}

else if (joyValue2 > joyValueMidUpper && joyValue1 < joyValueMidLower) {// diagonal southeast
motorSpeed2 = map(joyValue2, joyValueMidUpper, joyValueMax, motorSpeedMin, motorMaxHigh);
MotorSides2 (motorSpeed2);
motorSpeed1 = map(joyValue2, joyValueMax, joyValueMidUpper, motorSpeedMin, motorMaxHigh);
MotorSides1 (motorSpeed1);
   digitalWrite (en1, HIGH);
  digitalWrite (en2, LOW);
  digitalWrite (en3, HIGH); 
  digitalWrite (en4, LOW);
}

else { digitalWrite (en1, LOW); //idle mode
  digitalWrite (en2, LOW);
  digitalWrite (en3, LOW); 
  digitalWrite (en4, LOW); 
}


}
void MotorForward1(byte Spd){
 analogWrite(ena, Spd);}//this function is the one that actually inputs the pwm commands to the motors

void MotorForward2(byte Spd) {
 analogWrite(enb, Spd);
 }
 void MotorSides1 (byte Spd){// these two  "motorSides" functions do the exact same thing has the "motorForward" functions and are intechangeable with them 
 analogWrite(ena, Spd);}//but they help me distinguish what function does what more quickly so I keep them
  
  void MotorSides2 (byte Spd){
  analogWrite(enb, Spd);
 }

I know I'm not too good at explaining myself but I really am trying. If there's any confusion ask me about it and I'll try to clarify it the best way I can.

I know I'm not too good at explaining myself

You got that right.

The simplest solution for your problem is the use the X value to determine the speed for both motors, assuming you want to move straight ahead at some speed. Then use the Y value to determine how much to increase the speed of one motor and decrease the speed of the other.

Suppose that you read 800 from X and 600 from Y. The 800 value is greater than the midpoint, so you want to go forward. It's beyond halfway to maximum, so you want to go just over half the maximum speed forward.

The 600 value is greater than the midpoint, so you want to steer to the right. It's only a little to the right of the midpoint, so you want the left motor to spin just a little faster, and the right motor to spin just a little slower. How much faster and slower will be something you experiment with.

Suppose that you read 600 from X and 1000 from Y. Now, you don't want to be traveling very fast, but you want a large difference between the left (faster) and right (slower) motors.

If you read 800 and 400, the turn is to the left, so the right motor goes faster (a little) and the left motor goes slower (a little).

joyValue1 = analogRead(potPin1); //reads value of y axis potenciometer
joyValue2 = analogRead(potPin2);// reads value of x axis potentiometer

Big mistake right here. Name the variables for X and Y, NOT 1 and 2. You want to move in the X and Y direction, not the 1 and 2 direction.

if (joyValue1 >= joyValueMidUpper && joyValue2 <= joyValueMidUpper && joyValue2 >= joyValueMidLower)

Create a function to determine if a value is in the do-nothing range.

if(InDoNothingRange(joyValueX) && InDoNothingRange(joyValueY))
{
   DoNothing();
}

See how much easier that code is to follow?

 motorSpeed1 = map(joyValue1, joyValueMidUpper, joyValueMax, motorSpeedMin, motorMaxHigh); //map function that translates joystick readings to pwm input
MotorForward1 (motorSpeed1);
motorSpeed2 = map (joyValue1, joyValueMidUpper, joyValueMax, motorSpeedMin, motorMaxHigh);
MotorForward2 (motorSpeed2);

Do you really expect that mapping the same value to the same ranges might result in different values?

Thanks for the help Paul!

PaulS:
You got that right.

The simplest solution for your problem is the use the X value to determine the speed for both motors, assuming you want to move straight ahead at some speed. Then use the Y value to determine how much to increase the speed of one motor and decrease the speed of the other.

Suppose that you read 800 from X and 600 from Y. The 800 value is greater than the midpoint, so you want to go forward. It's beyond halfway to maximum, so you want to go just over half the maximum speed forward.

The 600 value is greater than the midpoint, so you want to steer to the right. It's only a little to the right of the midpoint, so you want the left motor to spin just a little faster, and the right motor to spin just a little slower. How much faster and slower will be something you experiment with.

Suppose that you read 600 from X and 1000 from Y. Now, you don't want to be traveling very fast, but you want a large difference between the left (faster) and right (slower) motors.

If you read 800 and 400, the turn is to the left, so the right motor goes faster (a little) and the left motor goes slower (a little).

Well, the problem with that is, say you're pointing the stick at between 2 and 3 o'clock. Because the speed is measured in the Y axis, both motors would be going pretty slow, when what wold be more practical is that when you're pushing the stick to that in between 2 and 3, the left motor would be going quite strong and the right one quite weak.

PaulS:
Do you really expect that mapping the same value to the same ranges might result in different values?

That was actually intentional of my part, I knew it was redundant. Not too useful though so I'll delete it.

DBSN:
Well, the problem with that is, say you're pointing the stick at between 2 and 3 o'clock. Because the speed is measured in the Y axis, both motors would be going pretty slow, when what wold be more practical is that when you're pushing the stick to that in between 2 and 3, the left motor would be going quite strong and the right one quite weak.

Turning using differential power is always tricky. That's why Paul was saying that you use the Y axis to set the basic speed and then you make the turn by adding a value derived from the X-axis to one motor and subtracting it from the other motor. The technique is right you just need to fine tune it by working out how much to add/subtract.

And you need to think about the extremes too e.g. joystick hard to one corner so calling for full speed and a hard turn at the same time or the version when it seems to be calling for no speed but a hard turn (one motor forward, one in reverse?).

Steve

Well, the problem with that is, say you're pointing the stick at between 2 and 3 o'clock. Because the speed is measured in the Y axis, both motors would be going pretty slow, when what wold be more practical is that when you're pushing the stick to that in between 2 and 3, the left motor would be going quite strong and the right one quite weak.

You can't change the strength of the motors. You can change the speed.

Yes, when the stick is at 2:30, you are not asking for much forward speed. But, you are asking for a big differential in speed. Suppose that you decide that the center speed (PWM value) should be 30. Suppose that you decide that, for that stick position, the differential should be 100. 30 + 100 = 130. 30 - 100 = -70. So, spin one motor at 130, and the other at -70. Why is that a problem? You have control over both speed and direction for each motor (or you should).