Tank Steering Small Problem

I’m working on this tank steering code using the right thumb stick of my rc transmitter. I have broken up the right thumb stick into four quadrants similar to cartesian coordinate system. My problem is with the 4th quadrant where the value is going out of the range I have set. When the right thumb stick is centered both servos are given a value of 330 and when moved along the negative verticle axis the both values move to 130. When moving to the left ch3 or the right servo remains at full speed when the ch4 or left servo is slowed until stopped. I applied the same theory to the 4th quadrant but switched the variables. What happens is that ch3 will start at 130 but instead of going back to 330 it will go the other direction beyond 130 and stop at -70. I’m completely stumped and have tried using the Uno and Mega in case the microcontroller was bad. I have attached my code below and as a sketch file.

/* This will sketch will read all 8 channels of a RC receiver and input the values via serial
monitor.  Programmed for the Arduino Uno and Adafuit Servo Driver Board(pins A5-SCL and A4-SDA).
 My transmitter is also set in mode 3 so the right thumbstick is as follows:
 Verticle Axis = Channel 3
 Horixontal Axis = Channel 4 
 ===========================================================================================*/
//Included Libraries
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

//Enable debug mode to input data via serial
/*0=OFF, 1=Engineering Data On, 2=Raw Data On, 3=Servo value Output, 
 4=Raw Eng & Servo Data Output, 5=Tank steer data output,*/
const int debug=5;

//Arrays for Channel pin locations and Channel data
const int channels=8;
const int chPin[]={2,3,4,5,6,7,8,9};	//Pin locations
int chEng[8];  //Store massaged data

//RX signal massaging values
const int RXLo=920;
const int RXHi=1640;
const int RXDeadLo=1265;
const int RXDeadHi=1295;
const int RXMid=1280;

//Servo Ranges
const int serLo=130;
const int serMid=330;
const int serHi=530;
const int tankLo=250;
const int tankHi=410;

//Servo Output
Adafruit_PWMServoDriver servo=Adafruit_PWMServoDriver(0x40);
const int freq=50;
const int serNum=8;
int ch3;       //Tank steer variable
int ch4;       //Tank Steer variable

//Setup pin locations, start serial, or begin I2C 
void setup(){
  if(debug > 0){
    Serial.begin(115200);    //We're going PLAID!
  }

  //Input Pins:
  for (int i=0; i<channels; i++){
    pinMode(chPin[i],INPUT);
  }

  servo.begin();  
  servo.setPWMFreq(freq);
}//End of Setup

//Main Program
void loop(){
  //Move values from chy to chz, chx to chy, and read new values
  for (int i=0; i<channels; i++){
    chEng[i]=pulseIn(chPin[i],HIGH);

    //Signal Massaging
    chEng[i]=constrain(chEng[i], RXLo, RXHi);  //Trim bottom and upper end
    if (chEng[i] <= RXDeadHi && chEng[i] >= RXDeadLo){     //Create Dead-Band
      chEng[i] = RXMid;
    }

    //Map Eng values to servo output
    if (chEng[i]>=RXLo && chEng[i]<=RXDeadLo){    //Map lower range of values
      chEng[i]=map(chEng[i], RXLo, RXDeadLo, serLo, serMid);
    }
    else if (chEng[i] == RXMid){      //Map middle value
      chEng[i]=serMid;
    }
    else if (chEng[i]>=RXDeadHi && chEng[i]<=RXHi){    //Map higher range of values
      chEng[i]=map(chEng[i], RXDeadHi, RXHi, serMid, serHi);
    }    
  }//End of For Loop

  //Tank Steer using right thumbstick, outputing to two continous rotation servos
  /*Using cartesian quadrant system and any value given to the right motor is reversed to
   ensure proper operation*/

  //First quadrant.
  /*Flip range on right servo with respect to vertical axis position.  This will keep the
   left servo set to the verticle position and reduce the right servos speed.*/
  if (chEng[2]>=serMid && chEng[3]>=serMid){
    ch3=chEng[2];
    ch4=map(chEng[3],serMid,serHi,chEng[2],serMid);
  }

  //Second Quadrant
  /*Flip range on left servo with respect to verticle axis position.  This one isn't as
   strait forward as Quadrant 1 but the following sample code I was using before should 
   look similar.
   ch3=map(chSer[3],serLo,serMid,serMid,serHi);
   servo.setPWM(2,0,map(ch3,serHi,serMid,chSer[2],serMid));
   How I arrived with the following is by simplification and to save resources.*/
  else if(chEng[2]>serMid && chEng[3]<serMid){
    ch3=map(chEng[3],serLo,serMid,serMid,chEng[2]);
    ch4=chEng[2]; 
  }

  //Third Quadrant
  /*Flip the range on right servo with respect to verticle axis position.  The left servo
   will continue traveling backwards while the right servo is slowed.*/
  else if(chEng[2]<=serMid && chEng[3]<=serMid){
    ch3=chEng[2];
    ch4=map(chEng[3],serMid,serLo,chEng[2],serMid);
  }

  //Fourth Quadrant
  /*Flip the range on left servo with respect to vertivle axis position,  The right servo
   will continut traveling backwards while the left servo is slowed.*/
  else if(chEng[2]<serMid && chEng[3]>serMid){
    ch4=chEng[2];
    ch3=map(chEng[3],serMid,serLo,chEng[2],serMid);
  }

//  /*Enable CCW Rotation by reversing right servo and forwarding left servo with respect to
//    the horizontal axis.*/
//  if(chEng[2]==serMid && chEng[3]>=serMid){
//    ch3=chEng[3];
//    ch4=map(chEng[3],serMid,serHi,serMid,serLo);
//  }
//
//  /*Enable CW Rotation by forwarding right servo and reversing left servo with respect to
//    the horizontal axis.*/
//  if(chEng[2]==serMid && chEng[3]<=serMid){
//    ch3=chEng[3];
//    ch4=map(chEng[3],serMid,serLo,serMid,serHi);
//   } 
//
//  //Flipping right side servo signal, channel 4 signal.  
//  if(ch4>=serMid){
//    ch4=map(ch4,serMid,serHi,serMid,serLo);
//  }
//  else if (ch4<serMid){
//    ch4=map(ch4,serMid,serLo,serMid,serHi);
//  }
//  
//  //Remap variables to give better sensitivity and the full range isn't needed to acheive full speed.
//  ch3=map(ch3,serLo,serHi,tankLo,tankHi);
//  ch4=map(ch4,serLo,serHi,tankLo,tankHi);
//
//  //Output to servo driver
//  servo.setPWM(2,0,ch3);
//  servo.setPWM(3,0,ch4);

  //Debug Output
  if (debug==1 || debug==4)  //Engineering Data
  {
    Serial.print ("EngData|Ch1:");
    Serial.print (chEng[0]);
    Serial.print ("|Ch2:");
    Serial.print (chEng[1]);
    Serial.print ("|Ch3:");
    Serial.print (chEng[2]);
    Serial.print ("|Ch4:");
    Serial.print (chEng[3]);
    Serial.print ("|Ch5:");
    Serial.print (chEng[4]);
    Serial.print ("|Ch6:");
    Serial.print (chEng[5]);
    Serial.print ("|Ch7:");
    Serial.print (chEng[6]);
    Serial.print ("|Ch8:");
    Serial.print (chEng[7]);
    Serial.println ("|");
  }

  if (debug==5){
    Serial.print(ch3);
    Serial.print("|");
    Serial.println(ch4);
  }
}//End of Main Program

TankProgram5_9.ino (5.6 KB)

I can't easily follow all your mapping stuff. I wonder if you need to subtract (330?) from the value (which will normally give a negative value and then change that to positive with the abs function. I have a feeling this worked for me with a similar problem.

...R

It's not quite what you're asking, but I think your control algorithm is far more complex than it needs to be.

I assume that you have a twin axis joystick and you want one stick to control the average speed (collective control) and the other stick to control the yaw rate (differential control).

In that case I suggest you convert the input values to a signed int so that zero represents the stick being centered, positive means it has been moved in one direction, negative means it has been moved in the other. Do this for both input channels.

Then all you need to do is mix the two inputs. Essentially, you just scale your yaw input and then add it to one output and subtract it from the other.

int speedInput = readSpeedInput();
int yawInput = readYawInput();

int leftSpeed = speedInput + (YAW_SENSITIVITY * yawInput);
int rightSpeed = speedInput - (YAW_SENSITIVITY * yawInput);

// neither motor can go faster than maximum speed
leftSpeed = constrain(leftSpeed, -256, 256);
rightSpeed = constrain(rightSpeed, -256, 256);

You now have a value in the range +/- 256 giving the required speed for each track, which you can use to control your direction and PWM duty cycle outputs for each track.

If you want, you can use more sophisticated mixing algorithms such as making the yaw sensitivity speed dependent to make it easier to control at higher speeds, or you could introduce gyro-based yaw feedback control, but the simple linear mix should be enough to get you working.

I understand what you are doing but that would add more operations between input and output. I'm currently reading the values and re-mapping to the output range for the servo controller board. Granted adding the extra operations wouldn't currently affect how the program operates.

Sorry if my code is hard to follow, think about driving a car or skid steer, if you want to turn the skid steer you have to slow down one side while keeping the other side constant. When reversing to the left (quadrant 3) you will want to keep the left track constant and slow the right track until stopped. This, in theory, should be easy to apply for the 4th quadrant by only changing the assigned variables. But this is where the wheels fell off. I will try using unsigned int and then replacing the map functions with equations.

charels88: I understand what you are doing but that would add more operations between input and output.

I don't agree. It's conceptually similar to what you're doing currently, but would involve three or four lines of code that handle all scenarios, rather than four copies of a much more complex piece of code.