Global Domination Robot (GDR) Mk III

As it's been some time since the completion of the GDR Mk II, and many parts of the world have not yet fallen under the shadow of my power, I have decided to begin work on the Mk III version to work out many of the details that will be needed in future larger and more heavily weaponized platforms, especially regarding navigation.

I plan to build the Mk III sequentially to develop its navigation capability, and would like input from all of the smart folks on here. Here is the game plan:

1. Chassis, power, sensors, and processor.

2. Receive coordinate and Navigate to location (dead recon).

3. Detect obstacles, move around obstructions,

and still keep track of where it is and where it needs to go.

4. Establish wireless connectivity to receive movement orders.

5. Video relay through wireless connection

and ability to directly control the GDR Mk III from my lair (switch between autonomous and remote control)

 

To begin, I'm going after steps 1 and 2 above. I have an extra Uno here, as well as a motor shield and various other components. I think unlike the previous GDR versions, I will use a pre-made chassis and engine. So, my materials list:

a) Tamiya tracked chassis with dual motors and gearbox. (open to input that doesn't get too expensive)

b) Arduino Uno

c) arduino motor shield

d) LIPO Battery (input on this greatly appreciated)

e) Voltage regulator so I can power arduino and

motors on same battery

f) LCD Keypad to input movement order coordinates (never worked with this before, would like input on whether or not this will conflict with other components I am attaching to the arduino, like the motorshield)

g) Encoders for motors

h) Compass (I see these for sale from anywhere from about $5 to $150... am I wrong to go cheap here?) h)

Update 17 JAN 2016:

I've ordered the chassis from Sinoning.com, and have just placed my order for the components that I don't have laying around the workshop. Since I'm waiting on the post from China to the US, I've worked up some schematics for how I think I'm going to build this thing. The only part that I should have to fabricate myself is a plastic plate to mount on top of the chassis and hold the arduino and sensors.

 

I've also been working out this plan for wiring. You will note that I dont have any of the power worked out for my components yet (except the motors). I've ordered a 7.4v 2000mAh LIPO battery. Can I just run the power for all of the sensors and servos out of the 5V and 3.3V pins on the arduino motor shield?

 

Update 4 FEB 2016:

The chassis finally came in from Sinoning. Wow, that was slow. I chose the cheap (free) option to have it go through the post from China, and the torture (no disrespect meant for those undergoing actual torture) of waiting was made worse by having the ability to watch its progress through various sorting facilities via usps.com.

The chassis exceeds my expectations. It came fully assembled, which was a surprise for me, and I could immediately wire up the motors to a battery pack to make it run. It has a switch built into the bottom, which I don't remember from the description, but is an added bonus. I usually include a power switch on my projects, so this just makes it easier. The front compartment of the chassis fits the mini-servo that I'm planning on using for an ultrasonic rangefinder just about perfectly.

Areas that need some work:

1) The battery compartment isn't long enough for my LIPO battery (I think it was originally designed for several AA batteries, but there are no terminals built in). I'm going to have to cut out a hole in the front of the battery box, which should work fine since there is extra room in front of it, but my dremel recently died and I have to aquire a new one.

2) The disks for the optical encoders that I bought are too thick to fit in between the drive sprocket for the tracks and the chassis. I've stencilled the shape of the disks (more like sprockets themselves actually) onto some thin cardboard and will cut them out and see if I can mount them in that thin gap. Otherwise I'm going to have to put them on the outside of the sprockets and fabricate something to hange the encoders out there. More to follow on this one. 

 

Update 8 FEB 2016:

I built encoder disks that fit between my drive sprockets and the chassis out of cardboard (I used the original thick plastic ones as a stencil), and tested that they work.

 

I also built a casing out of polystyrene to cover all of the upcoming mess of wires. This was midway through the process, it looks a bit nicer now that I've peeled off the tape (used to keep it together while I was cementing it) and hit it with a coat of black spray paint. Unfortunately, the only plastic I could find at Hobby Lobby was this really thin and flimsy stuff which will be okay for cosmetic purposes, but I'm going to have to replace before I try mounting anything up on the roof. I found thicker plastic today at Home Depot, so it will work out in the long run.

 

20 FEB 2016:

Designed and built a turret for the servo and ultrasonic rangefinder on the front of the GDR from the thicker plastic that I found at Home Depot. Cutting the pieces with my dremel was adequate and created a solid structure (I designed interlocking components), but cosmetically isn't very impressive. The dremel created slag which had to be sanded off, and resulted in less than perfect edges where the pieces come together. Fully functional, sturdy and durable, but a CNC cutter or 3D printer might be in my future. 

I used a small breadboard to create a power system for the GDR. 7.4V comes in from the LIPO battery in the chassis, and the board puts out 7.4V for the motors, and a strip of 5V pins for all my components (also integrating a pair of capacitors to hopefully stabilize power flow (especially for the use of the ultrasonic sensor). The power board works great as far as I can see so far, but the prototyping board I used is a little big to fit comfortably on the deck of my chassis (it's on the desk beside the GDR in the photo). before field testing I may use the dremel cutter and take down the size of the board to just the sections I am using.

I rigged up the encoders on the back of the GDR and they work, but I'm not happy with the stability of the way they are mounted. I'm going to have to design and cut a frame for them out of plastic to get them stable enough for field testing. I've started on the code, and have it ready to run as a standard obstacle avoider (this will be a loop running while the GDR navigates). I'll post a video when I load it onto the arduino and get the robot zipping around.

 

24 FEB 2016:

Well, it "zips" sufficiently at this point (sorry, no video yet... but then again, who really wants to see just another obstacle avoider at this point?) acting as an obstacle avoider. I was able to fit the power board up onto the main deck without cutting it down. Had to play with the wiring to get everythnig to work, and realized that I have a couple dead pins on my homemade power board that don't work. I'll have to check the soldering to see where I scewed that up. The ultrasonic works great, but there wasn't room for my jumper wires in the configuration from the above picture. I had to flip the ultrasonic upside-down to fit the wires, which is a major bummer. I'll fiddle with it in the future to see if I can get them to comfortably come out of the bottom.

For my next trick I will have to straighten up the mounting for the encoders and the compass. After that it should be a fun little exercise to work through the mathematics of navigation, and how to make the machine do it. Looking ahead beyond that will come questions of destination input (ie LCD keypad vs bluetooth or wifi. This will be autonomous, so no remote control required.

 

27 FEB 2016:

I figured out how to use the compass (3-axis magnetometer), and wrote some code to make the GDR turn to an azimuth and drive that direction. Very happy with the results. For initial testing (as you will see in the video) I just taped the magnetometer to the back of the robot. This worked to prove the concept of the robot turning to an azimuth and traveling along it, but what you don't know is that I've set the target azimuth at 270 degrees, and the robot was consistently heading at about 225 or so degrees (our historic home is laid out with 19th century military precision, so this robot should be heading directly for the west wall, which it is not). As you'll see in the picture I cut a hole in the top of the GDR covering case and mounted the magnetometer on top (you see it held there with electrical tape). Now the GDR Mk3 turns in precisely the correct direction and keeps moving in the correct direction (as verified by my trusty M2 compass with declination constant). I have noticed a slight variance when the GDR drives past a certain electrical socket in my second floor hallway, so I may have to integrate some of the code that I see available online to compensate for external magnetic fields. I will save this for the end of the GDR 3 project though, because I am starting to fear that my coding for this many planned functions may exceed the capacity of the single arduino that I am using.

My next step will be more securely mounting the encoders and integrating data from them into the main loop. I want the GDR to turn to a directed azimuth and drive a directed distance at this azimuth. Right now my device to input this into the GDR is a USB link to the arduino - I need to start planning for either an LCD and keypad interface or a wifi connection to send movement directions to the GDR.

 

29 FEB:

Mounted one of the encoders on the port-side drive sprocket. My plan was to run some tests to figure out how far the robot moved for a set number of "hits" on the optical encoder. I was getting some pretty drastically different distances (for 100 hits it varied from 96" traveled down to only 72" traveled). I tightened down the reference disc on the drive sprocket and got slightly better results, but it wasn't until I slowed down the speed of the motors to half speed that my distances started coming back consistently. It should be said that at half speed the distance traveled for 100 hits is significantly less (16.5" every time), so that leads me to believe that at full speed the counter was missing counts at full speed. With an accurately moving robot, I am ready to move to my next problem which is determining a path from current location to a destination coordinate.

Navigates to Coordinate in order to control territory/enforce my will on the populace

  • CPU: arduino uno
  • Programming language: C++
  • Sensors / input devices: encoders, compass

This is a companion discussion topic for the original entry at https://community.robotshop.com/robots/show/global-domination-robot-gdr-mk-iii

Tank Chassis

Observation: Compass probably does not want to be near motors.

Question: Which encoders?

Agree with all, and the encoders…

Yeah, I hear you on the compass. Not sure how far away the compass needs to be from everything else. Was thinking of starting it in the back to keep it clear of the other electronics. I think you’re probably right to point out that the motors will affect the compass more than anything else on the robot. If it doesn’t work back where it’s at I will try elevating it up above the main deck. Put it up like we used to do with our GPS receivers on HMMWVs.

For the encoders, I’ve ordered these things: https://www.robotshop.com/en/cytron-simple-rotary-encoder-kit.html

I expect that I’ll have to do a fair amount of fiddling with them to get them to work with the chassis that I ordered (if they work at all). There’s lots of pics of the chassis at the Sinoning website, but I really need to get my hands on it and the sensors to see what I can do with them.

Power is really what I’m worried about right now… do I get a voltage regulator, or just run all the servers and sensors off of the arduino motor shield power pins?

Power

I am building a slightly smaller machine based on the Tamiya gearbox and treads. It is being powered with 5 AAA recharagables. 6.25V (fresh batteries) for the motors while a linear voltage regulator provides 3.3V and 5.0V for the logic. Last week I found 3.7V LiPo’s in AAA package which may fit my needs.

Motor shield will handle motors and logic… I think
I think the motor shield will handle how are regulation for the logic and the motors. I’m wondering how many components I can power off of the power output from the motor shield.

It’s been a while since I’ve set up anything like this. I really need to reteach myself how voltage amperage and current work.

Compass

Which compass did you decide on? How does it preform?

 

I went with this one:

I went with this one: https://www.robotshop.com/en/hmc5883l-bb-3-axis-magnetometer-compass.html

Just starting to figure out how to use the thing. Seems awfully complex.

For your earlier question about the encoders I went with a pair of these:

https://www.robotshop.com/en/cytron-simple-rotary-encoder-kit.html

These seem really easy to get to work, haven’t included them in my code yet.

Compass

I have the compass working, as in providing input. Still working through the code to provide data in actual degrees. Right not I think I need to make my math more comprehensive since it displays accurately from 1-90 degrees, then gives screwy readings in the 91-359 range.

Here is my code if you have any ideas:

 #include <Wire.h> //I2C Arduino Library

#define address 0x1E //0011110b, I2C 7bit address of HMC5883

void setup(){
  //Initialize Serial and I2C communications
  Serial.begin(9600);
  Wire.begin();
 
  //Put the HMC5883 IC into the correct operating mode
  Wire.beginTransmission(address); //open communication with HMC5883
  Wire.write(0x02); //select mode register
  Wire.write(0x00); //continuous measurement mode
  Wire.endTransmission();
}


 void loop(){
 
  int x,y,z,az; //triple axis data

  //Tell the HMC5883L where to begin reading data
  Wire.beginTransmission(address);
  Wire.write(0x03); //select register 3, X MSB register
  Wire.endTransmission();
 
 
 //Read data from each axis, 2 registers per axis
  Wire.requestFrom(address, 6);
  if(6<=Wire.available()){
    x = Wire.read()<<8; //X msb
    x |= Wire.read(); //X lsb
    z = Wire.read()<<8; //Z msb
    z |= Wire.read(); //Z lsb
    y = Wire.read()<<8; //Y msb
    y |= Wire.read(); //Y lsb

    az=90-atan(y/x)*180/PI; //this is my equation to turn the y and x readings into an azimuth
  }
 
  //Print out values of each axis
  Serial.print("x: “);
  Serial.print(x);
  Serial.print(”  y: “);
  Serial.print(y);
  Serial.print(”  z: “);
  Serial.println(z);
  Serial.print(”  Degrees: ");
  Serial.print(az);
 
  delay(250);
}

Compass

Good luck. Some observations: 

1. x & y are integers.

2. If x == 0 it may crash

3. PI & atan typically use floating point math so you might as well use all floats.

if (x != 0)

    float fx, fy;

    fx = x;
    fy = y;
    az = atan(fy / fx) * 180.0 / PI;
}
else
{
    az = XXXXX;

I think I have a HMC5883 so I should be able to duplicate/verify your efforts.


 

Here’s the code that worked!

I ditched that code when I found the Arduino-HMC5883L library here: https://github.com/jarzebski/Arduino-HMC5883L

This is the code that I used that is currently working perfectly with my GDR3:

 

//COMPASS STUFF
#include <Wire.h>
#include <HMC5883L.h>
HMC5883L compass;

// MOTOR STUFF give the motor control pins names:
const int pwmA = 3;
const int pwmB = 11;
const int brakeA = 9;
const int brakeB = 8;
const int dirA = 12;
const int dirB = 13;

void setup() {
// Compass Stuff
  compass.setRange(HMC5883L_RANGE_1_3GA); // Set measurement range
  compass.setMeasurementMode(HMC5883L_CONTINOUS);  // Set measurement mode
  compass.setDataRate(HMC5883L_DATARATE_30HZ);  // Set data rate
  compass.setSamples(HMC5883L_SAMPLES_8);  // Set number of samples averaged
  compass.setOffset(0, 0);  // Set calibration offset. See HMC5883L_calibration.ino

}

void loop() {
  int TGTazimuth = 275;
  int azimuth = currentheading();
  int azdif = TGTazimuth-azimuth;
  int absoluteazdif = abs (azdif);
  if (absoluteazdif < 2){
    forward();
    delay (100);
  }
  else if (azdif>0){
    left_turn();
    delay (10);
  }
  else if (azdif<0){
    right_turn();
    delay (10);
  }
  else{
    forward();
    delay (100);
  }
}

int currentheading(){
    Vector norm = compass.readNormalize();
  // Calculate heading
  float heading = atan2(norm.YAxis, norm.XAxis);
  // Set declination angle on your location and fix heading
  // You can find your declination on: http://magnetic-declination.com/
  // (+) Positive or (-) for negative
  // For Bytom / Poland declination angle is 4’26E (positive)
  // Formula: (deg + (min / 60.0)) / (180 / M_PI);
  float declinationAngle = (2.0 + (4.0 / 60.0)) / (180 / M_PI);
  heading += declinationAngle;
  // Correct for heading < 0deg and heading > 360deg
  if (heading < 0)
  {
    heading += 2 * PI;
  }
  if (heading > 2 * PI)
  {
    heading -= 2 * PI;
  }
  float headingDegrees = heading * 180/M_PI;   // Convert to degrees
  Serial.print(" Heading = “);   // Output
  Serial.print(heading);
  Serial.print(” Degress = ");
  Serial.print(headingDegrees);
  Serial.println();
  //delay(100);
  return (headingDegrees);
}

void forward()
{
  //Motor A forward. 255 is max speed
  digitalWrite(dirA, LOW); //Establishes forward direction of Channel
  digitalWrite(brakeA, LOW); //Disengage the Brake for Channel A
  analogWrite(pwmA, 255); //Spins the motor on channel A
  //Motor B forward. 255 is max speed
  digitalWrite(dirB, LOW); //Establishes forward direction of Channel B
  digitalWrite(brakeB, LOW); //Disengage the Brake for Channel B
  analogWrite(pwmB, 255); //Spins the motor on channel B  
}

void left_turn()
{
  digitalWrite(dirA, LOW); //Establishes backward direction of Channel A
  digitalWrite(brakeA, LOW); //Disengage the Brake for Channel A
  analogWrite(pwmA, 255); //Spins the motor on channel A
  digitalWrite(dirB, HIGH); //Establishes forward direction of Channel B
  digitalWrite(brakeB, LOW); //Disengage the Brake for Channel B
  analogWrite(pwmB, 255); //Spins the motor on channel B
}

void right_turn()
{
  digitalWrite(dirA, HIGH); //Establishes backward direction of Channel A
  digitalWrite(brakeA, LOW); //Disengage the Brake for Channel A
  analogWrite(pwmA, 255); //Spins the motor on channel A at quarter speed
  digitalWrite(dirB, LOW); //Establishes forward direction of Channel B
  digitalWrite(brakeB, LOW); //Disengage the Brake for Channel B
  analogWrite(pwmB, 255); //Spins the motor on channel A at quarter speed
}

Compass

Looking good.

1. You have some extra code in the main loop. You can probably remove the absolute calculation and test.

2. I think you need to add some code when the heading is near zero. -1 degree would be 355.

3. Comments in turn_right() are incorrect. 255 is full speed, not quarter.

4. You can reduce the lines of code further by writing a routine that controls both motors. 

SetMototorSpeed(int leftSpeed, int rightSpeed);

5. Now that you can go in a “straight” line. How “straight” is it for a 5 meter run?

 

 

 

Okay, learning a bit on the coding here

Thanks for the help. I’m kind of learning to code this as I go along.

1. I put the absolute calculation and test in the code because I guessed that without a little tolerance (2 degrees is what I threw in there) that the robot might never have things perfect enough to keep driving forward. I plan to refine that once I have all my basic functions working.

2. I see where I don’t have anything that covers zero or 360 itself. I don’t get how -1 degree becomes 355.

3. You’re right on the speed comments. That is an old cut-paste error.

4. I see what you mean with making a single function for the motors and plugging the variables in when it gets used. I would need to add something in for motor direction too, and maybe the brakes.

5. It was fairly straight. It made some minor corrections here and there, but it looked like it made a straight line overall. I plan to test it during my refinement phase, but from playing with it so far the only thing I noticed was it made a bit of a turn as it passed near one of the outlets in my hallway.

Single function

Use signed integers for speed. Inside the function test for zero, plus & minus. You can simplify even further by adding a more primative function that gets passed the port I/O pins such as:

setMotorSpeed(int speed, uint8_t port1, uint8_t port2, uint8_t port3)
{
if (speed < 0)
{
}
else
{