After volunteering at the local mini-Maker Faire, and getting a chance to play with the Intel Galileo Gen 2 Arduino board, I was motivated to find a maker project for myself. After a quick survey of the options and my interests, I ended up building a simple, self-balancing robot. The cost of materials was roughly $150-200, and it took me a few weeks to complete.
The robot is built around the Intel Galileo Gen 2 Arduino board. In addition it uses an Arduino motor shield to control two 12V geared motors (120RPM) for robot positioning. An inexpensive accelerometer/gyroscope provides orientation feedback, and a Bluetooth module connects the robot to an Android tablet for control. Two 6V RC car batteries allow the robot to be completely untethered, and a proto shield was used to assemble some of the misc. connections and components. For the Android control app, I used the MIT AI2 development environment.
The key robot components include:
· Arduino board : Intel Galileo Gen 2 Arduino board
· Motor control board : Arduino Motor Shield R3
· Bluetooth Module : KEDSOM Arduino Wireless Bluetooth Transceiver Module
· Accelerometer/Gyro module : Kootek Arduino GY-521 MPU-6050 3-Axis Accelerometer/Gyro
· 2x 12V 120RPM geared DC motors : HOSSEN 12V 120RPM Powerful High Torque DC Gear Box Motor
· 2x 1600mAh NiMH Batteries : Duratrax NiMH 6.0V 1600mHa Flat Rx Univ Connector, ONYX110 Battery Charger
There are some great resources out there on pretty much any topic, and I used many of them, but I wanted to acknowledge a few I relied on heavily for this project:
· Jeff Rowberg's I2CDev libraries for the MPU-6050
· Brett Beauregard's tutorial on PID controller implementation
· Luis Rodenas's sketch to calibrate the MPU-6050
· Whatakuai's MIT AI Bluetooth App starter and Pura Vida Apps BT chat.
Thanks guys for sharing some quality material!
Step 1: Chassis
My intent with the robot chassis was to keep it simple, and use material that was readily available for me. That led to a tiered design, using 1/4" plywood from my wood scrap box and aluminum standoff's. I played with a number of methods of attaching a round motor to a flat piece of plywood, but ended at using 2 hose clamps per motor, threaded through slots in the plywood.
I started the assembly with the motor tier, using the dimensions shown in the diagram. I used a 1/4" bit in my router, with an edge guide to cut the 8 slots to mount the DC motors. I drilled the mounting holes in each corner, counter sinking them on the bottom so that the mounting screw heads were flush. I drilled a larger hole in one corner for routing the motor wires. To attach the motors, I opened the hose clamps all the way to allow the band to be passed through the slots, and then re-connected the clamps to hold the motor in place. I mounted the MPU-6050 module on the motor tier, aligning the center of the main control chip on the module with the drive axel. I used glue to attach an 8-pin female connector to the plywood, and just plugged the module into the connector.
Note : I used only a single hole in each tier for wire management. This results in the DC motor and MPU-6050 wires running parallel for the entire height of the robot. In the end, I needed to switch to a shielded cable for the MPU-6050 connection due to coupling with the DC motor power wires. You can likely avoid the need for a shielded cable here by separating the DC motor and MPU-6050 on opposite sides of the chassis, but it would require drilling a second wire management hole.
Preparing the Battery tier was more straight forward, with only 2 slots for the battery Velcro strap, and the same corner mounting and wire management holes. I again counter sunk the mounting holes on the bottom, as the threaded end of the standoff's were not long enough to reach through the plywood otherwise. I zip-tied the two batteries together, and fastened them to the plywood with a handy Velcro strap found at my local hobby store.
The Electronics tier preparation involved drilling the same corner and wire management holes, as well as mounting holes for the Galileo board and Bluetooth module. I chose to mount the Bluetooth module underneath the Galileo board using shorter standoffs. As the Bluetooth module did not have mounting holes, I made do by cutting notches in the side of plastic standoffs - I am sure there is a more elegant solution, it just evaded me. The top tier was an even simpler construction, as it contains only the 4 corner mounting holes.
Assembly of the chassis was then just the connection of the 4 tiers using the standoff's, starting at one end, and progressing to the other.
For the final step of construction, I used RC truck tires on the robot. I was able to purchase a set from my local hobby store, but they are similar to these online. I also found a motor-shaft-to-wheel adapter at the local hobby shop, but an online search found something similar here.
Step 2: Wiring
The wiring connections between the robot electrical subsystems are shown in the wiring schematic diagram.
To avoid voltage droop for the Galileo board, I ran separate power cables from the battery to the Galileo board and the Arduino motor shield. I cut the end off of an old wall-wart-power-supply to reuse the 2.1mm power plug for connection to Galileo. The motor shield will end up drawing some pretty healthy current, so I used stranded 20 gauge wire for that connection to the battery. I used 3-pin male connectors for the batter connections, which I found at the same local hobby shop - but I had some difficulty finding the equivalent item online.
I used 24 gauge stranded wire, female pin connector terminals and heat shrink to make the cabling between the Galileo board, the Bluetooth module and a shielded, 4 conductor, cable between Galileo and the accelerometer/gyro MPU-6050 modules - with the shield grounded at both ends.
Between the motor control shield and the DC motors, I used solid 22 gauge wires. Notice that the connections are reversed on one of the motors so that the direction control from the Galileo to the motor shield is the same for forward and reverse on both motors. Also note that "forward" is a relative term, and can be changed just through the connection between the motor shield and the DC motors. At final robot testing, you will need to make sure there is correlation between "forward" and the positive output of the PID controller - either through the motor connections or the polarity of the PID terms.
I used a Seeedstudio Arduino protoshield I picked up at RadioShack to provide a common/flexible connection point as well as to mount the misc. resistors and LED's. The component placement on the proto shield kind of evolved over time as I built up the robot, so I am sure there is a more optimal layout. The Galileo Gen 2 supply voltage spec is rated 7-12V, and I found some concerns online about damaging the board if you run it at a supply voltage lower than 7V. As a result, I added the voltage divider network and connection to an analog input so the sketch can monitor the battery voltage as a precaution.
The electrical connections between Galileo and the motor shield are accomplished by attaching the motor shield to the Galileo Arduino connector, with the proto shield stacked on top of the motor shield.
Note : As the DC motor supply voltage is being connected directly to the motor shield, I did cut the Vin trace on the motor control shield using a X-acto knife to isolate the Galileo and motor shield supply voltage rails.
I did have to place some electrical tape between the motor shield and the proto shield to avoid shorts between the two (probably just an indication of my primitive soldering skills).
Step 3: Robot Software
I choose to implement the robot sketch in a single file, which makes it a bit long, but puts everything in one place. Rather than walking through the SW line-by-line, I will just comment on how it is organized, and what each section is focused on.
The sketch starts with the standard comment header and library include files. I did document the shield pin usage in the header comment to help keep myself straight on SW use of the pins. I also defined a data structure which is used by the PID function, with the thought this would make it easier to add a second cascaded speed PID later.
Next are the #define statements for constants and commonly used values that can be conveniently changed. For example, once you determine what the optimal P, I and D values are for your robot, they can be permanently recoded here. Next come the variable definitions, which I tried to group in a logical fashion - you can be the judge of how well I did.
The next section contains the functions that are used later in the sketch. The first function implements the balancing PID. This is the SW I leveraged heavily from Brett Beauregard's tutorial. Included in the PID function is a section to record the PID information to SD card if data logging is enabled. While tuning the PID values, I found it instructional, and entertaining, to see the individual PID term behavior. This saves the data to a file in CSV format, which I later imported into Excel and generated graphs for visualization. This all obviously works only if you have an SD card in the Galileo board.
This is followed by a few motor control functions which map motor control information to direction, speed and brake controls for the motor shield
Next is a function to retrieve the position data from the MPU-6050 module. I stole this with pride from Jeff Rowberg's reference code. I am using a regular-interval-data-pull method for data transfer, rather than using interrupts like Jeff did, as I had some difficulty getting the interrupts to work. The time interval is implemented in the main program loop, and I found that a 3ms interval was adequate in ensuring there were no data overflows.
We then move on to a set of functions to handle the Bluetooth communication. These start with a few functions to send the more complex commands - these were broken out into functions primarily for code readability. Then comes the primary Bluetooth command parser, which implements the receive communication protocol with the Android app. This function accumulates characters from the Bluetooth serial port until the end-of-command character is detected, and then it decodes the command. As expected, the decode ends up just being a big case statement.
The final function is the high level robot control state machine. I implemented it in the fashion of a finite-state-machine (FSM) - probably overkill, but I envisioned expanding this as capabilities are added to the robot.
Next is the setup function required by all Arduino sketches. The comments in the code should make this section pretty self-explanatory. There is a section that attempts to initialize the SD file for data logging, and should error out if an SD card is not present, but I didn't spend much time validating this.
Finally, there is the main sketch loop. The first section implements a timer for data retrieval from the MPU-6050. If data is retrieved, there are some calculations to detect if the robot is upright, or off balance.
Next the serial port connected to the Bluetooth module is checked for a received character, and if one is waiting, the Bluetooth function is called.
This is followed by the PID and motor control timer. The "if" statement ensures that the PID/motor control only happens when the robot is actively trying to balance. This is where the MPU_6050 data is fed into the PID, and the PID output is passed onto the motor control. This is also where the steering is implemented, by adding the Bluetooth derived direction input to one motor, and subtracting it from the other. Because we are doing additional math on the PID output, we have to again constraint the PID Max/Min limits to keep within the motor PWM values.
Finally the robot control state machine is called, and a timer is used to implement the robot heart beat function. I started with a separate heart beat Bluetooth command and battery voltage command, but then merged them into a single function, with the voltage being sent as a heartbeat indication.
And that is it - a bit long winded, but nothing too complex.
Step 4: Android App
From the beginning of this project, one of my objectives was to develop a companion Android app to allow control of the robot. As my SW development skills are "still in development", I was delighted to stumble across the MIT AI2 Android app development environment - it turned out to be perfect for what I wanted to do.
I spent some time with a number of the tutorials to get a feel for how things were setup, and with Whatakuai's and Pura Vida Apps on how to leverage the BT interface. But rather than spending time explaining MIT AI2, I will describe how the app is meant to be used and how the blocks are structure.
The app is a simple collection of buttons and sliders to allow the user to configure and control the robot. Below is a description of the various app controls.
· BT Module : Every time the app is started, or the robot power cycled, the user will need to connect to the robot BT module. This is the button that facilitates that action.
· Standby : Once robot is connected to BT, and the Galileo board has booted, the Robot starts in the standby state. This button is used to instruct the robot to enter the active, balancing state. Please note that the robot will only attempt to balance if it is within 2deg of vertical, so even if this button reports "Active", you will need to position the robot in a near vertical position for it to begin balancing. This button can be used to toggle between "Standby" and "Active" as desired.
· PID : This button is used to allow the user to change the "P", "I" and "D" values for the PID controller in the robot while the robot is operating. When pressed, it will retrieve the PID values from the robot and display them below the respective "+" and "-" buttons. The increment and decrement value for each parameter are hard coded into the robot sketch.
· FW Ver : This is the robot sketch Firmware version. I went through many versions of sketches, and multiple SD cards, so I found it very helpful to know which version was running on the robot. This value is hard coded into the sketch using a #define statement, and is sent to the Android App when the robot goes out of Standby.
· Vbat : The battery voltage from the robot, sent every second
· HB : Heartbeat indicator. The text changes color once per second to indicate the sketch is still running on the robot.
· Roll Calibrate : based on the module you have, and how it is mounted on the robot, vertical may not read as 0deg. This allows you to "calibrate" what the MPU reads when the robot is vertical. The "+" and "-" buttons are shared between the Roll Calibrate and Motor Slack, so only one can be changed at any given time. The current Roll Calibrate value is retrieved from the robot when the radio button is selected.
· Motor Slack : The geared DC motors I used had a zone in the lower PWM values which do not result in any motor shaft movement. This results in a non-linear region of control for the PID output. I found that by "jumping" over these low PWM value (motor slack) in the PID output, you could better balance the robot. This button allow you to retrieve the current value from the robot, and adjust the size of the "jump"
· Speed : this allows the user to control the robot speed. Due to a limitation in the MIT AI compile environment, the app does not allow multi-touch, so you cannot change the speed and direction sliders simultaneously. An internet search on the topic shows there is a work around to allow multi-touch operation, but I have not yet pursued it.
· Direction : this allows the user to control the robot direction.
· Quit : exits the Android App
I found the visual method used by MIT AI2 to develop the app details on the "Blocks" section to be a bit cumbersome - but I will let you come to your own conclusions. I was not successful in taking a screen shot of the Blocks layout, but they are arranged in three columns. You should be able in import the .iai file into MIT AI2 and view the details for yourself.
The first column implements the Quit, BT Module, Standby, Data Log, Speed, Direction, Roll Error and Motor Slack buttons.
The second column implements the PID enable button, and the P, I and D "+" and "-" buttons. It also implements the Roll Error and Motor Slack "+" and "-" buttons.
The third column implements the Bluetooth timer, used to check the interface for received information, and blocks to extract and implement commands from the robot. It took some trial and error to get the Bluetooth data reception and command extraction working, and the result is a bit brute force-ish, but in the end, it worked fairly well.
Step 5: Setup and Tuning
In building the robot, I developed a temporary, dedicated sketch for each subsystem (MPU-6050, BT Module, Motor Shield), to break the problem down into sizes I could handle. But even before that, it all started with getting the heart of the system, the Galileo Gen 2 board, up and running.
There is a good "getting started" guide online in which you install the Android IDE and Galileo device drivers, and update the FW on the Galileo board.
Note : As both the Bluetooth and MPU-6050 modules use 3.3V signaling, be sure to move the I/O voltage jumper on the Galileo board to the 3.3V setting to avoid damaging the modules.
I also suggest you take the time to load a bootable image onto an SD card (make sure to get the latest image). It allows your Arduino sketch to be retained on Galileo through board power cycles. On the down side, it does increase the boot time of the Galileo board (it takes on the order of 1min to boot), but it is a worthwhile tradeoff in my eyes.
Note : I delayed taking the step of booting from SD (yes, because I am lazy). As a result, I had to tether the robot to my PC through the USB cable so I could download the sketch on each power cycle. This was an inconvenience which turned to disaster, when the robot fell on the USB cable, breaking the micro USB connector on the Galileo board. I now have to resort to loading new sketches onto the SD card on the host PC, and then booting Galileo from SD, and moving the sketch to /sketch/sketch.elf through the Linux serial console, and then reboot Galileo … all because the host connection through the USB is no longer functional. So take heed from my sad tale, and consider yourself warned if you decide to operate your robot with the USB cable attached.
I also played with several mounting locations for the MPU-6050. I started with it mounted on the Electronics tier, under the Galileo board, but found moving it to the motor tier allowed for better control.
By far the least deterministic phase I encountered was determining the right P, I and D values for the PID controller. I have heard this is a black art, but now have a much healthier respect for the process having spent some time in it. It was in this phase of the development that I added the PID data logging to the SD card, so I could figure out each term's contribution to the output. There are a number of treatments of manual PID tuning online, but I found the best approach to be as follows:
· Set the I and D terms to 0
· Increase the P term until the robot is nearly able to stay vertical. With the motors I chose, that was the robot racing across the floor leaning in one direction
· Reduce the P term somewhat, and increased the I term until the robot can balance, but did so with oscillations
· Increased the D term to reduce the oscillation.
I still am not happy with the results I have obtained, as they seem to be either optimized for good balance, or good driving. The search continues …
As for the Roll Calibration, I went back and calibrated the MPU-6050 partway through the development, after I added the Roll Calibration capability. To my surprise, after I calibrated the MPU-6050 module, I didn't find the Roll Calibration changes to have a significant effect.
I did find that adjusting the Motor Slack made the robot more responsive - up to a point, beyond which it began to oscillate.
Step 6: Conclusion
I think that some of the best advice I have heard about life applies equally well to the maker process - be sure to enjoy the journey. While I completely enjoyed every moment of this project, let's face it, the result is just a simple, self-balancing robot.
Some future improvements that I think would be fun to pursue include:
· Add speed/displacement indication for the robot (adding encoders or moving to stepper motors)
· Adding obstacle/collision avoidance sensors and capability
· Add audio input for voice command, music/beat detection
· Add machine vision for navigation/robot control
· Add robot auto navigation/environment mapping
That about covers it for my simple robot. Now it is time to make your own. Modify. Improvise. Improve. Enjoy.