Autonomous Robot, problem with rc and gps

Hi all,

I'm new to Arduino and not sure where to post this. I'm working with an engineering friend to build an autonomous gps robot. The goal is to create an robot that will navigate a course using only gps coordinates, but also have a switch for manual control. We are using a Dagu Wild Thumper 4WD Chassis with a 7.2v battery, an Arduino Uno R3, two Pololu Dual VNH5019 Motor Shields, a Sparkfun GPS Shield with EM-406A GPS Module, and a Turnigy 9x v2 8 channel remote. We stacked all the boards on top of each other with the two motor shields on top of the Arduino and the gps shield on top of the motor shields. Individually, everything works fine. The motor shields run the motors fine with hard coded values. The remote connects fine and the Arduino IDE serial monitor shows the correct values when throttle/steering are pressed, and the gps reads position fine as well. The problem is trying to add in movement code to the test code.

With the remote, the robot moves but it doesn't move correctly. Either it will crawl, regardless of how much the throttle is pressed and have random amperage spikes, or it will sit there like the throttle isn't being pressed but I'll hear the motors trying to move. The motor lights will flicker but nothing happens. Using the getM1CurrentMilliamps() functions in the DualVNH5019MotorShield library, I found that the motors were spiking at upwards of 6000-7000ma, normal operation without the remote is 300-500ma(at full speed). I don't believe this is a programming error because, again, what I see in the serial monitor is the correct value for the speed. Basically, I'm taking the throttle amount and subtracting the idle throttle from it, roughly 1400 idle with max 1800 min 1000. My engineer friend tells me it looks like the voltage is randomly going through the motor shield unresisted because 6a x 1.2ohms = 7.2v and 7.2v / 1.2ohms = 6a(I just assume he knows what he's talking about). I can post my code for this but I don't know if that would belong in this thread.

With the gps shield, putting even simple movement code into the TinyURL test examples does nothing. I believe this is because of a pin problem. The gps wants/needs to use pin 2 but so does the motor shield. I've tried changing the pins for both the gps and the motor shield in the dualVNH5019MotorShield.cpp but it only breaks the gps and motor. I've tried changing the gps from DLINE to UART but i don't see a difference.

I feel that with everything we're trying to do, there might be a lack of pins available. The motor shield needs pins 2,4,6,7,8,12, A0,A1. The gps needs 2 pins, and the remote will need 3 pins. I've completely hit the wall on debugging this thing. It feels like a hardware problem to me, but I'm just the programmer and the hardware may as well be latin written backwards for how well I understand it. Can this be done with the Arduino Uno R3 or would we need something else like the Mega2560?

Thank you so much anyone who has any advice. :blush:

This sounds like a complex system and you could have problems in either the hardware and/or software design.

I suggest you divide the project down into simple parts and make sure each part is bullet proof before you add in the next part.

If I were you I'd start with a simple hard-coded sequence of motor movements and confirm that the motor power supplies, wiring and software are working correctly and reliably in your final vehicle configuration.

Once you know the output part is working correctly, you can start thinking about how the speed ought to be controlled and what inputs you need to control that - and start implementing those inputs. Confirm they're working correctly and reliably before you actually use them to control the motors. I'd suggest you focus on manual control first, and add the autonomous controls later. By the way, you don't mention having a compass. Isn't that essential? I'd have thought a gyro for closed loop yaw control would also be very helpful.

Still able be done with the Arduino Uno R3.

EM-406A GPS Module is using software-serial, if you change the pin connection, you have to change the soft-serial pins definition in the software.

Which Dagu Wild Thumper 4WD Chassis you used 34:1 or 75:1, the 34:1 had torque of roughly 5 kg-cm (70 oz-in) and 75:1 had higher torque of roughly 11 kg-cm (150 oz-in). What is the total weight of your car?
What type battery? AH?
Post your code will help.

I'm really wondering how you have these shields hooked up; do you have two of the Pololu shields stacked on top of each other, so that you are controlling them in parallel? Or have you somehow rewired the pins?

Of course, you only state the pins for the one Pololu shield - not both; if you have them hooked up in parallel - how do you have the motors wired to them? If the motors are wired in parallel (that is, you are trying to use two shields in parallel to drive one motor for each side - ie, double the current) - does the datasheet for the h-bridge say it can be hooked up this way, and if it does, does the schematic for the shield allow for this?

Have you contacted Pololu regarding this? Furthermore, what are the current requirements of the Dagu Wild Thumper's motors? Do you need that much current handling?

What pins do the GPS shield plug into, and where do you have the RC receiver plugged into on the stack?

Lastly, if you are intending on pulling so many amps, what are you doing about heat sinking the h-bridge IC's?

I've done a good amount of testing with the hardware, and i know individually its good. I will look into the software-serial for gps; i never thought to look into the .cpp files. The chassis we're using is the 75:1 motors. All i know about them is that they're really high torque. I've had them rip the wires out of the motor shields because it turned so fast. I don't know the weight, but i know it's light enough that if it goes from forward to reverse at max speed it will flip itself over. The battery we're using is a 7.2v 5000mAh NiMH.

The motor shields are stacked on top of each other and, to my knowledge, we didn't do anything with the pins. As i understand it, the first shield is controlled by the arduino, and then the motors on the second shield get the same voltage as the first. so when setM1Speed() is used both motors plugged into motor 1 on both boards move at the same time. The only draw back is that we can't control 1 motor individually without moving the other, as i understand it. The pins on the gps would go into the motor shield, and the RC receiver would plug into that somewhere. At present i'm not testing the remote with the gps shield on because it is very very difficult to get the receiver wires into the gps headers. They're a poorer quality header, i feel, and the wire needs to be thinned down some to fit better into the header. I'm not intending for it to draw so many amps. It should be no more than 500ma at its max speed.

Most of the code i'm using I found in other examples and have modified. I have no idea if this is a good place to start. This is a completely different universe of programming from what I'm used to. I've tested it with hardcoded values, but strangely it doesn't seem to improve it much. What happens then, is the motors will move but it wont be smooth. The amperage comes in pulses, but also spikes in pulses.

 // MultiChannels
// rcarduino.blogspot.com
#include <PinChangeInt.h>

#include <Servo.h>

#include "DualVNH5019MotorShield.h"
   
DualVNH5019MotorShield md;


/*********************************************

  MOTOR SHIELD PIN MAP REFERENCE
  _INA1 = 2;
  _INB1 = 4;
  _EN1DIAG1 = 6;
  _CS1 = A0; 
  _INA2 = 7;
  _INB2 = 8;
  _EN2DIAG2 = 12;
  _CS2 = A1;

**************************************/

#define THROTTLE_IN_PIN 3
#define STEERING_IN_PIN 5
#define AUX_IN_PIN 11

#define THROTTLE_OUT_PIN 9
#define STEERING_OUT_PIN 10
#define AUX_OUT_PIN 11

Servo servoThrottle;
Servo servoSteering;
Servo servoAux;

#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

volatile uint8_t bUpdateFlagsShared;

volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;

uint32_t ulThrottleStart;
uint32_t ulSteeringStart;
uint32_t ulAuxStart;

volatile uint16_t throt;
volatile uint16_t reverse;
volatile uint16_t steering;


void setup()
{
  Serial.begin(9600);
 
  Serial.println("multiChannels");

  servoThrottle.attach(THROTTLE_OUT_PIN);
  servoSteering.attach(STEERING_OUT_PIN);
  servoAux.attach(AUX_OUT_PIN);

  PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
  PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
  PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);
}

void loop()
{

  Serial.println("Loop Begin");

  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t unAuxIn;
  static uint8_t bUpdateFlags;

  Serial.print("Throttle: ");
  Serial.println(unThrottleInShared);

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
   
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
   
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
   
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }
    
    bUpdateFlagsShared = 0;
   
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
  }
 
 
 
 // Start: my test code
 if(unThrottleIn < 1500 && unThrottleIn > 1400){
 
    
    Serial.println("Stop Case");
    Serial.print("Throttle Val: ");
    Serial.println(unThrottleIn);
    
    md.setBrakes(400,400);
  
  }
  
  if(unThrottleIn > 1500){

    Serial.println("FORWARD");    

      Serial.print("Throttle forward: ");
      Serial.println(unThrottleIn);
      throt = unThrottleIn - 1400;
      
      if (throt > 400){throt=400;}
      
      Serial.print("Throt var: ");
      Serial.println(int(throt));
//      md.setSpeeds(throt, -throt);
        md.myForward(int(throt));  // my own function added to the library to see if it functions better
      
      int motor = md.getM1CurrentMilliamps();
      Serial.println(motor);


  }
  
  if (unThrottleIn < 1400){
    
      Serial.println("REVERSE");
      Serial.print("Throttle reverse: ");
      Serial.println(unThrottleIn);      
    
      reverse = unThrottleIn - 1400;
      Serial.print("Reverse var: ");
      Serial.println(int(reverse));
      md.setSpeeds(int(reverse), -int(reverse));  
  
  }// End: my test code

  if(bUpdateFlags & THROTTLE_FLAG)
  {
    if(servoThrottle.readMicroseconds() != unThrottleIn)
    {
      servoThrottle.writeMicroseconds(unThrottleIn);
    }
  }
 
  if(bUpdateFlags & STEERING_FLAG)
  {
    if(servoSteering.readMicroseconds() != unSteeringIn)
    {
      servoSteering.writeMicroseconds(unSteeringIn);
    }
  }
 
  if(bUpdateFlags & AUX_FLAG)
  {
    if(servoAux.readMicroseconds() != unAuxIn)
    {
      servoAux.writeMicroseconds(unAuxIn);
    }
  }
 
  bUpdateFlags = 0;
}


// simple interrupt service routine
void calcThrottle()
{
  // if the pin is high, its a rising edge of the signal pulse, so lets record its value
  if(digitalRead(THROTTLE_IN_PIN) == HIGH)
  {
    ulThrottleStart = micros();
  }
  else
  {
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
    bUpdateFlagsShared |= THROTTLE_FLAG;
  }
}

void calcSteering()
{
  if(digitalRead(STEERING_IN_PIN) == HIGH)
  {
    ulSteeringStart = micros();
  }
  else
  {
    unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
    bUpdateFlagsShared |= STEERING_FLAG;
  }
}

void calcAux()
{
  if(digitalRead(AUX_IN_PIN) == HIGH)
  {
    ulAuxStart = micros();
  }
  else
  {
    unAuxInShared = (uint16_t)(micros() - ulAuxStart);
    bUpdateFlagsShared |= AUX_FLAG;
  }
}

serev:
I've done a good amount of testing with the hardware, and i know individually its good.

serev:
I've tested it with hardcoded values, but strangely it doesn't seem to improve it much. What happens then, is the motors will move but it wont be smooth. The amperage comes in pulses, but also spikes in pulses.

I'm trying to figure out what these two quotes mean. Start from a minimal system - just an Arduino, and a motor shield, and a power supply for the shield, and a motor. With hard-coded values for the motor controls, does the motor behave correctly with none of the funny behaviour you describe in the second quote above?

If so, add in the hardware connections to your remote control receiver - does the motor control still swork correctly?

If so, add in the code to read and decode the manual control commands from the receiver - I suggest just printing them out to confirm correct receipt; don't use them to control the motor at this stage. Do both functions (receiving commands, and hard-coded motor control) still work correctly?

I got the remote problem resolved somehow. I rewrote my code from scratch and copied only what looked important from the semi-working code. The programming gods were merciful because i have no idea why it works and the old code didn't. Now it's just tweaking here and there to make it respond better.

That just leaves the gps which looks like it just needs a pin tweak somewhere. Hopefully there wont be too many more surprises left. Thank you all for the help. :smiley:

#include "DualVNH5019MotorShield.h"

#include <PinChangeInt.h>
   
DualVNH5019MotorShield md;

/*********************************************

  MOTOR SHIELD PIN MAP
  _INA1 = 2;
  _INB1 = 4;
  _EN1DIAG1 = 6;
  _CS1 = A0; 
  _INA2 = 7;
  _INB2 = 8;
  _EN2DIAG2 = 12;
  _CS2 = A1;

**************************************/

#define THROTTLE_IN_PIN 3
#define STEERING_IN_PIN 5

#define THROTTLE_FLAG 1
#define STEERING_FLAG 2

volatile uint8_t bUpdateFlagsShared;

volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;

uint32_t ulThrottleStart;
uint32_t ulSteeringStart;

volatile uint16_t throttle;
volatile uint16_t steering;

void setup(){

  Serial.begin(9600);
  md.init();

  PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle, CHANGE);
  PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering, CHANGE);
}

void loop(){

  Serial.println("Loop Start");
  
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint8_t bUpdateFlags;

//  Serial.print("update flags: ");
//  Serial.println(bUpdateFlags);  

//  Serial.print("throttle start: ");
//  Serial.println(ulThrottleStart);  
//  Serial.print("throttle shared: ");
//  Serial.println(unThrottleInShared);
  Serial.print("throttle in: ");
  Serial.println(unThrottleIn);  
  
  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    Serial.println("inside flag shared check");
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
   
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
   
    if(bUpdateFlags & THROTTLE_FLAG){
      unThrottleIn = unThrottleInShared;
    }
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
    
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
   
    interrupts();
    
    
    if(unThrottleIn < 1500 && unThrottleIn > 1400){
    
//      Serial.println("Stop Case");
//      Serial.print("Throttle Val: ");
//      Serial.println(unThrottleIn);
    
      md.setBrakes(400,400);
  
    }
    
    if(unThrottleIn > 1500){
//      Serial.println("FORWARD");

      throttle = unThrottleIn - 1450;  //should be able to define this sooner
      forwardSpeed(throttle);          //need to double check the idle number
//      forward(1000, 400);
//      brake(1000);    
    }
    
    if(unThrottleIn < 1400){
      
      throttle = unThrottleIn - 1450;
      forwardSpeed(throttle); //this should work for reverse too
      
    }
    
    if (unSteeringIn > 1500){
    
//      Serial.println("RIGHT TURN");
      steering = unSteeringIn - 1400;
      right(steering);
    
    }
    
    if (unSteeringIn < 1400){
    
//      Serial.println("LEFT TURN");
      steering = unSteeringIn - 1400;
      left(steering);
      
    } 
  }
  
  Serial.println("Loop End");

}

// simple interrupt service routine
void calcThrottle()
{
  // if the pin is high, its a rising edge of the signal pulse, so lets record its value
  if(digitalRead(THROTTLE_IN_PIN) == HIGH)
  {
    ulThrottleStart = micros();
  }
  else
  {
    // else it must be a falling edge, so lets get the time and subtract the time of the rising edge
    // this gives use the time between the rising and falling edges i.e. the pulse duration.
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
    // use set the throttle flag to indicate that a new throttle signal has been received
    bUpdateFlagsShared |= THROTTLE_FLAG;
  }
}

void calcSteering()
{
  if(digitalRead(STEERING_IN_PIN) == HIGH)
  {
    ulSteeringStart = micros();
  }
  else
  {
    unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
    bUpdateFlagsShared |= STEERING_FLAG;
  }
}

void forwardSpeed(int motorSpeed){

  md.setM1Speed(motorSpeed);
  md.setM2Speed(-motorSpeed);
  Serial.println();
}

void left(int motorSpeed){

  md.setM1Speed(-motorSpeed);  
  md.setM2Speed(0);      
  Serial.println();
}

void right(int motorSpeed){

    md.setM1Speed(0);
    md.setM2Speed(-motorSpeed);  
    Serial.println();
}


void forward(int time, int motorSpeed){

  unsigned long forwardStart = millis();

  while(millis() - forwardStart <= time){
    md.setM1Speed(motorSpeed);
    md.setM2Speed(-motorSpeed);
    Serial.println();
  }
}



void brake(int time){

  unsigned long stop = millis();

  while(millis() - stop <= time){
    md.setM1Brake(400);
    md.setM2Brake(400);
  }
}