Welcome, this log is for my first foray into robotoics and I'm making a robot to map an area and explore. The intent is to use Arduino related componets for the robot, sensors and low level management of things and a Raspberry Pi for the mapping and high level logic. My background is in hobby programming not electronics so I've gone for mostly prebuilt components. My hope that this wil be useful for others who are also having a go at robotics.
Fistly here are the components that I'm using.
Hardware
Seeed studio's shieldbot - nice little robot platform with motor and controllers; battery and charging system; 5 reflective sensors; led indicator lights for everything; 6 ports for expansion; pins for directly mounting an Arduino or clone; and a plastic top to mount additional items. All for well under $100.
http://www.seeedstudio.com/wiki/Shield_Bot
Arduino Uno
Raspberry Pi
Dagu encoders - gives 8 changes per rotation and the sensors for them are pretty tolerant for placement
SRF05 Ultrasonic Range Finder
Software
Arduino IDE
CodeBlocks
Unittest++
My first steps were just to use the Arduino and shieldbot and run through some of the basic examples that were supplied. All of it worked out of the box but it was clear that prescision was an issue with movement. The distance travelled in a straight line was roughly the same for consecutive tests but decreased when the charge level in the battery dropped , and a straight line was only roughly straight (as the output power of each motor was not exactly the same). All of these issues make sense and are resonable for the platform, but if I wanted any accuracy I had to attach the encoders.
Encoders
Before playing with the encoders I did a little research and found a fantastic page on how they can be used https://www.robotshop.com/letsmakerobots/node/38636. The approach used is very good and will allow the arduino to do other tasks while also allowing the manageing of the speed and distance.
First up was the mounting of the encoder wheel, but unfortunately the motors on the shieldbot have short drive shafts have have no room to attach the encoders. This meant that I had to attach them directly to axle after the gearbox. This provided pretty low resolution (can only detect when the wheel has at least turned 1/8 which is roughly 17.8 mm) but it is better than nothing. Convenitently the axle and one of my phillips head screwdrivers had the same diameter so I just "drilled" away the hardened rubber with the screwdriver and pushed it on the axle.
Once they were attached I attached the sensors for them with tape and attached them to the ports D1 and D2 (which are tied to the external interupts on the Arduino uno).
Now that the hardware was in place it was time for the software.
The Arduino IDE allows for libraries but they can't be opened in the IDE alongside the "sketch" (else it gets saved in the same folder as the sketch), so I grabbed notepad++ and just used the Arduino IDE for the main "loop", compilation and upload.
Based on the lets make robots page I made a new encoder class with 2 methods for the interupts, 2 flags to identify that the interrupts have been triggered, 2 to return the milliseconds passed since the last change and 1 to clear out the variables. Note that I am using miliseconds instead of seconds for timing as the resoltiuon of the encoders is so low and it also means that I don't have to worry about the clock returning to zero for 50 days (much longer than I intend to run to robot for in any single session).
As suggested by the lets make robots page and the Arduino reference page, I made the interrupt methods very small. They just to measure the time since they were last called, store it in a variable and set the flag. The flag remains set until someone gets the time for that particular encoder or clears the variables. Also I made one class for both encoders as it was easier for debugging withing the class (as the Arduino IDE has no true debugging but allows debuging from the serial out).
The code is attached.
Using the encoders for forward movement
Now that I have encoders and I have them recording the time it takes for 1/8 of the wheel to turn I need to turn that into something useful. For my purposes I'm going to have the robot move forward a particular distance and stop. This will mean that when I start using the Raspberry PI I will just need to say how far I wish it to go instead of trying to manage the movement. The Arduino has much quicker access to the sensors and has very little overheads during running, which means it should be much better at keeping track of the relative speed of the wheels and proximity to the destination.
For forward movement a specific distance in a straight line I need three things:
- To know how far to go;
- To know how far I have currently gone; and
- To know I am going in a straight line;
For the coding I just inherited from the sample driver that was supplied by seed studio. During this I realised I could not determine how to access or create a make file in the Arduino IDE, so to prevent problems during compliation I put both this class and the encoder class in the same folder as the sample driver. Once all the libraries were in one place I had no more issues.
My first modifications to the class were to override the forward and stop methods (mostly for setting and clearing variables); add a flag for when we are driving; and add a method to check the driving.
The basic flow of events during the loop in the main "sketch" is expected to be:
- go foward X distance and set driving flag = true
- if driving flag = true then check driving
- check driving stops when have reached the correct distance and driving flag = false
This gives the option of an event to be raised during driving if we have to stop short for some reason (i.e. if the PI says stop)
First on my agenda was to try and drive in a straight line. For this I needed to know the relative speeds of each of the motors and use the check driving method to adjust them when required. The speed of each motor is based on the power allocated to drive each motor, but as there is no fix ratio for power to speed we need to use the encoders to determine how fast we are going.
To determine the speed I check to see if one or both of the encoder flags have been tripped then get the time from the encoder. This time tells me how long it took to turn 1/8 of a wheel turn and therefore we have a speed. To keep things in integers I recorded the speed as micrometers per millisecond (Obviously the measurements are not remotely this accurate but integer math is much faster than floating point math).
Once I have the speed of each motor I can compare and adjust the power to each motor. For each engine I convert the difference in speed for each wheel to a ratio of the speed of that wheel and then multiply by the power to get a power difference that is comparable to the difference in speed.
Roughly the formlua is:
(speed difference to other wheel / speed of wheel) * power to that wheel = difference in power to adjust for speed diffence
Now we could just remove that power from the fastest wheel but eventually the robot would slow down to zero if the differences switch back and forth enough. And we can't just add it to the slowest wheel as there is a maximum power that can be applied to either wheel. So we check the speed and the relative power for each wheel and balance the power between the minimum desired power and the maximum power.
The code is attached
Unit Testing
Although my initial tests were quite promising I wasn't fully confident that my logic was correct and that I wouldn't break the code when I modified it. So I downloaded Unitest++ and started wrapping up my libraries.
Unit testing the libraries were firaly straight forward once I wrote a basic mock for the Arduino and added a neat hack for making private and protected members available for unit testing by using a #define to redefine private and protected(http://stackoverflow.com/questions/2085501/how-to-access-private-class-fields-from-a-test-using-unittest). For the Mock Arduino i just created a separate Arduino.h and cpp files set the functions in there that were required and added additional functions to set timers, trigger interrupts, etc.
Mock Arduino.h and Unit Tests attached.
- CPU: arduino uno, Raspberry Pi
- Programming language: C++
This is a companion discussion topic for the original entry at https://community.robotshop.com/robots/show/low-cost-mapping-robot