CHAPTER 8

BrickPi Python Robot


IN THIS CHAPTER, I WILL BE showing you how to build a robotic car out of Lego building blocks with control provided by a RasPi. Using a Lego EV3 Mindstorms kit is normally the way such a project would be done with a control module nicknamed the “brick.”

BrickPi

Figure 8-1 shows a current EV3 control module, which is powered by six AA batteries contained within the case.

Images


Figure 8-1 EV3 brick.

Instead of the EV3 brick, I will be using a BrickPi controller, which is a RasPi coupled with a special I/O board to control the Lego robotic car. I will also refer to the robotic car as the CastorBot . I made the controller substitution for the obvious reason that this project is in a book concerning RasPi control. However, another important reason was to demonstrate how an object-oriented language such as Python can be used to program this robot instead of the drag-and-drop graphical programming environment normally used with Mindstorms. I do wish to point out the drag-and-drop programming is perfectly fine for most users, but the Python approach allows for much more flexibility and utility in creating control programs for the CasterBot.

Figure 8-2 shows a BrickPi with a RasPi already mounted in its enclosure.

Images


Figure 8-2 BrickPi.

It is necessary to delve into how Mindstorms sensors and motors are controlled in order to understand how the BrickPi functions. I will first examine the sensor ports.

Chapter 8 Parts List

Images

Sensor Ports

All Mindstorms sensors are controlled by using commands sent over the I2C bus. This bus was first introduced in Chapter 3 , where it was used to establish a data communications link between the Arduino coprocessor and the Lidar module. In this project, the I2C Protocol will be used to create direct communications links between the RasPi and the Mindstorms sensors. You might want to go back to Chapter 3 and refresh yourself on how the I2C functions, but it is not really essential to build this project because the single sensor used in this project is pluggable and will play after the software is loaded. Table 8-1 , which is courtesy of Wikipedia, shows the makeup of the connector used to interconnect all Mindstorms sensors with the BrickPi I/O board. All the electrical contacts are clearly identified in Figure 8-2 for your reference.


TABLE 8-1 Mindstorms Sensor Connector-Pin Layout

Images

You may have noticed that the I2C signal lines also have a corresponding RS-485 designation shown in the “Function” column. The reason for this is that an RS-485 serial link can communicate over much longer distances, up to 1 km, while an I2C is restricted to much closer distances, typically on the order of tens of meters. The RS-485 and I2C are very different from one another. The I2C is synchronous , meaning that it requires a clock signal to function, while RS-485 is asynchronous , relying instead on start and stop bits to form a data frame or packet. As far as I can determine, all the Mindstorms kits use an I2C for communications, but it is nice to know that the Mindstorms brick designers planned for optional longer-range communications.

You should also note that while the sensor/motor connector looks somewhat similar to an ordinary RJ11 telephone jack, it is not the same. For instance, the latching device is offset to the left as you look at the Mindstorms connector, with the pins facing down. The RJ11 has its latching device in the center top. Additionally, the pin spacing is slightly different, with the RJ11 spaced at 0.60 mm between pins and the Mindstorms spacing at 0.70 mm between pins. The bottom line in all this is that you should not try to jam a RJ11 connector into a Mindstorms jack; it will not work and likely will cause serious damage if you attempt to apply power. Lego Mindstorms, as well as a few other suppliers, have these connectors available for purchase if you want to build your own.

I will next discuss the Mindstorms motor ports, now that you have had a good introduction to the sensor ports.

Motor Ports

Mindstorms motor ports use the same connector as used with the sensor ports, but the pin designations differ significantly, and they do not use I2C as a communication protocol.

CAUTION

It is important to ensure that only motors are plugged into the motor jacks and only sensors into the sensor jacks. There is a potential for sensor electrical damage if you inadvertently plug a sensor into a motor jack because motor supply voltage may be present on pins 1 and 2, which might harm the sensor.

Table 8-2 shows the makeup of the connector used to interconnect Mindstorms motors with the BrickPi I/O board.


TABLE 8-2 Mindstorms Motor Connector-Pin Layout

Images

The MOTOR 1 and MOTOR 2 lines refer to the power supply for a single motor. Placing the positive power supply lead on MOTOR 1 and the negative lead on MOTOR 2 will cause the motor to rotate in a certain direction. Reversing the polarity will cause the motor to rotate in the opposite direction. The motor supply lines are connected to the AtMega pulse-width modulation (PWM) lines with an inline driver chip, which I further explain in the next section.

It is now time to examine the BrickPi’s specialized I/O board in more detail because it is the reason why a RasPi can function as a substitute controller for the Lego brick.

BrickPi Specialized I/O Board

The block diagram in Figure 8-3 shows how the specialized I/O board functions. Incidentally, from now on I will refer to this board simply as the I/O board because you already know what I am talking about.

Images


Figure 8-3 Block diagram for the I/O board.

This block diagram is based on a schematic provided by the BrickPi manufacturer, Dexter Industries. One AtMega328 processer is shown in the figure, and it provides the I2C control signals for sensors S1, S2, and S5. The control signals for motors A and B are also provided by this chip in conjunction with a PWM driver chip. The I/O board also contains another identical AtMega328 processor that controls sensor ports S3 and S4 as well as motor ports C and D. These processors may be considered as coprocessors, as discussed in Chapter 2 , because their primary functions are to process and manipulate data that otherwise would require the RasPi to handle. Without these coprocessors, it is highly likely that the RasPi would not be capable of processing all the data constantly flowing from the sensors and motors and thus be unable to control the robot as required. The data generated from the motors are encoder pulses, which come from quadrature encoders mounted in each of the motors. Hundreds of pulses are generated for every revolution of the motor shaft, and every pulse must be counted to track the motor’s operation accurately.

Each AtMega328 controls the speed and rotation direction of two motors by sending low-level digital signals to a TI SN754410NE driver chip, which, in turn, converts these signals into high-level PWM pulses capable of driving the motors directly.

The sensor ports are configured in a parallel multidrop arrangement with external pull-up resistors to ensure that the I2C bus functions properly. The sensor input lines also are connected to the AtMega328 analog-to-converter (ADC) lines so that any compatible sensors with analog voltage outputs, such as a light-intensity sensor, also may be handled.

Of course, all the uncommitted RasPi GPIO pins are also available via the 26-pin header extension on the BrickPi board, as shown in Figure 8-4 .

Images


Figure 8-4 GPIO extension pins.

Another Python library named python-rpi.gpio enables I/O for these GPIO pins. I will demonstrate how to use one of the RasPi GPIO pins to control one of the BrickPi’s LEDs in a later section.

At this point, I would recommend that you label all the sensor and motor ports, as shown in Figure 8-5 . This will help you to identify the proper jacks to use when connecting the motors and sensors.

Images


Figure 8-5 I/O board with labeled ports.

This concludes my introduction to the BrickPi. It is now time to introduce the robotic car and show you how to build it.

The CasterBot

The CasterBot shown in Figure 8-6 was built from the components contained in Lego EV3 Kit Number 31313. It is entirely possible to customize and build the CasterBot from other Lego kits, and I would urge you to do so, especially if you already have some components from other kits. The build plan and bill of material (BOM) are based on the 31313 kit, and you should use it as a template to follow and modify as needed to suit your situation. Of course, you will need motors and a sensor compatible with the Mindstorms connectors, which are either the NXT or EV3 models.

Images


Figure 8-6 The CasterBot.

The build diagram, BOM, and build plan for the CasterBot are all on this book’s companion website. All the instructions are quite detailed, and they should allow you to build the CasterBot without any problems. The only parts missing from the BOM are the BrickPi and four connector pegs used to mount the BrickPi to the two 13M Lego beams mounted on top of the CasterBot. In addition, you will need three small flexible cables to connect the two motors and the ultrasonic sensor to the BrickPi. The cable connections are as follows facing the CasterBot:

Images Left motor connects to motor port B.

Images Right motor connects to motor port C.

Images Ultrasonic sensor connects to sensor port S1.

The CasterBot battery supply now warrants some additional discussion in the next section.

CasterBot Power Supply

The CasterBot has a substantial current draw when operating with two motors. I measured the average current drain to be approximately 0.5 A, which would quickly drain a six-pack of AA batteries, which typically powers the Lego brick. I chose instead to use a three-cell (3S) lithium ion polymer (LiPo) battery, which provides approximately 12 V when fully charged. Figure 8-7 shows the LiPo battery used in this project.

Images


Figure 8-7 3S LiPo battery.

Also shown in the figure is a battery condition monitor attached to the LiPo’s balancer connection. This device is very inexpensive and is well worth purchasing because it can indicate when the LiPo is nearing its discharged state. You do not want to discharge this particular LiPo battery below 10.2 V because it becomes quite difficult for the automatic charger to function correctly with a battery in this state.

This battery is also rated at 2200 mAh, which means that it should provide an operational time of about 2 hours for the CasterBot before it reaches its minimum voltage, as I described earlier, and needs a recharge. The operational time also may be shortened somewhat if the CasterBot is operated at maximum speed for an extended period of time.

CAUTION

This battery must be recharged using an approved LiPo charger. Attempting to use a charger designed only for nickel-cadmium (NiCad) or lead-acid batteries potentially could cause a mishap that might involve an exploding battery or even one that catches fire.

Some readers also may know that Mindstorms motors are usually operated with 9 V and may wonder if 12 V would possibly overload them. You may be assured that 12 V will not harm them but simply cause them to rotate about 25 percent faster than with a 9-V supply. The BrickPi board also has a voltage regulator, which can easily handle the 12 V to supply regulated 5 V to the RasPi as well as to its own processors.

Figure 8-8 shows a bottom-side view of a CasterBot with the battery installed in a cradle that was designed especially to hold a battery of this particular size.

Images


Figure 8-8 LiPo battery installed in a CasterBot.

I used gold-plated pins and sockets to connect the battery to the wires leading to the BrickPi’s power connector. This power connector comes with the BrickPi and has a 9-V battery clip attached. I cut this clip off and soldered the loose ends to two wires with the pin connectors already attached. The battery side likewise had two pin jacks soldered onto its wires. Be sure that you maintain the battery polarity, that is, positive to positive, or red, leads and negative to negative, or black, leads. Figure 8-9 shows a close-up of the battery connection configuration.

Images


Figure 8-9 Battery connections.

The battery pins and jacks I used are readily available from most hobby stores because they are commonly used in radio-controlled (R/C) aircraft and cars. I also chose not to include a battery switch; I simply unplug the positive lead when I need to turn the CasterBot off.

WiFi Dongle

You also will need to install a WiFi dongle to enable the CasterBot’s untethered movement. While it is possible to use a long USB cable to control the CasterBot from a laptop, it is not an optimal solution. A small WiFi dongle, as shown in Figure 8-10 , will allow the bot to be completely portable while still maintaining a wireless connection to the laptop using SSH.

Images


Figure 8-10 WiFi dongle.

The WiFi dongle has a maximum range of 100 m, which should be more than adequate for all CasterBot movements.

You should also realize that you will need to do an initial setup on the BrickPi’s RasPi using a normal workstation configuration, as described in Chapter 1 . However, you first need to download and install a specially modified Wheezy Linux distribution, which is discussed in the next section.

Software Installation and Configuration

The following instructions are based on an introductory tutorial available from the Dexter Industries website (www.dexterindustries.com ). Please follow these next steps in order to successfully install the software required to program and control the CasterBot:

     1. Download the Wheezy distribution created for the BrickPi at http://sourceforge.net/projects/dexterindustriesraspbianflavor/ .

     2. Unzip the download into an image file using WinZip or 7Zip.

     3. Write the image to an SD card using the Win32DiskImager program.

     4. Plug the SD card into the RasPi, which is set up in a stand-alone configuration with the WiFi dongle plugged in.

     5. Power-on the RasPi, and enter the regular user name/password.

     6. Run the raspi-config program by entering sudo rasp-config .

     7. Expand the file system.

     8. Set the appropriate location and time zone.

     9. Check that SSH is enabled in the Advanced Options.

10. Close out of the rasp-config app, and start the GUI desktop by entering startx .

11. Click on the WiFi config icon, and scan for your access point.

12. Select your access point, enter the appropriate passphrase, and then click on Connect. Close the app.

13. Click on Accessories, and click on LX Terminal.

14. Enter ifconfig , and write down the IP address that was assigned by the access point. Close the Terminal window.

15. Close the desktop by clicking on the Reboot selection.

Next, connect to the RasPi using an SSH connection from your laptop with the IP address from step 10 and using an appropriate program suitable for the laptop, that is, PuTTY for a Windows machine and Terminal for a Mac. Figure 8-11 shows the Terminal screen for my initial login to the RasPi from my MacBook.

Images


Figure 8-11 Initial RasPi SSH login on a MacBook Pro.

Now you are ready to run the following program, which will allow you to remote control the CasterBot. The program is named simplebot_speed.py and is located in the following directory:

Images

You should change into this directory and enter the following to run the program:

Images

Following are the single-letter commands that you may enter to instruct the CasterBot to perform various movements:

Images

You will also need to press the ENTER key after typing a command to send it to the CasterBot. The source code for this program is listed next with annotations to assist you in understanding the various functions.

Images

Images

Images

I will use this program as a base for additional functions, which will include an obstacle-avoidance algorithm. However, I will briefly discuss the ultrasonic sensor in the next section before adding the obstacle-avoidance functionality.

Ultrasonic Sensor

Figure 8-12 is a close-up of the ultrasonic sensor that is used to detect obstacles in the path of the CasterBot.

Images


Figure 8-12 Ultrasonic sensor.

This particular sensor comes from an NXT Lego kit, but it is completely compatible with the BrickPi. The EV3 series also has a similar sensor, which may be used without any modifications to the software.

The ultrasonic sensor contains an embedded microprocessor as part of the encapsulated sensor hardware. This processor controls the ultrasonic transmitter and receiver transducers that physically measure distance by bouncing discrete sound wave pulses off objects and timing how long the sound takes to transit. The distance is easily calculated because the speed of sound in air is relatively constant. This is very similar to how bats navigate in caves and attics. Figure 8-13 is the sensor’s block diagram, which shows how it functions.

Images


Figure 8-13 Ultrasonic sensor block diagram.

The sensor uses an embedded processor, which off-loads any additional computational tasks from a RasPi in a similar fashion to the way the AtMega coprocessors function on the BrickPi. The sensor’s processor measures sound pulse times to a resolution as fine as 1 ms, which a RasPi could not handle without introducing some timing errors.

The ultrasonic sensor measures distances from 3 to 250 cm with an accuracy of approximately ±2 cm. Distance measurements also depend on the size and texture of the object that reflects the sound pulses. A wall provides excellent reflections, while a stuffed teddy bear would be more problematic.

The ultrasonic sensor requires a few statements to integrate into a Python program. The first one associates the sensor with a specified port:

Images

The next statement commands the BrickPi to update all sensor and motor data:

Images

The final statement retrieves the most current ultrasonic distance measurement and assigns it to the variable dest :

Images

The issue now is to create software, which will be activated if an obstacle is detected within a preset threshold in the Casterbot’s path. I discuss this software in the next section.

Obstacle-Avoidance Algorithm

Creating obstacle-avoidance software is an interesting exercise that involves thinking through all the various scenarios that the CasterBot may encounter in its travels from a start to a finish point. I had to severely constrain the types of obstacles to use because it would be literally impossible to account for all possible obstacle types. I chose to use a small cardboard box because it would be easy to place in the CasterBot’s intended path, and if it were struck, it should not cause any damage to the robot. The obstacle-clearance software should be reasonably modifiable to accommodate other types of objects once this initial program is shown to be effective.

I don’t believe that there is a formal procedure that will create an optimal obstacle-avoidance algorithm. I have found that using a two-dimensional playing field with the start and stopping points as well as the obstacle in place helped me to visualize how the robot might maneuver between the start and stopping points while avoiding the obstacle. Figure 8-14 is a diagram of an example playing field with start point A and finish point B.

Images


Figure 8-14 Obstacle playing field diagram.

An obstacle detection point is shown as a numbered triangle. The CasterBot path is shown as a dotted line with the X predefined incremental path length. Table 8-3 is a pseudocode listing that describes the CasterBot behavior as it starts on a straight-line path from A to B and encounters the obstacle placed near point number 1. Note that I deliberately used only one obstacle in the direct path between A and B to simplify the example. Even so, the code to implement the avoidance behavior rapidly becomes complex.


TABLE 8-3 Pseudocode for a CasterBot Obstacle-Avoidance Algorithm

Images

All the steps to avoid an obstacle are contained in the method named obstacleAvoid() . This method is called when the ultrasonic sensor senses an obstacle within a threshold distance, which in this case is set at 25 cm. The whole process of tracking the bot is called dead reckoning and presumes that all 90° turns are perfect and that all transversed incremental distances are the same. In reality, this can never be the case because the bot tires will slip and motors do not instantly start and stop, meaning that the incremental distances traveled will not quite match the values commanded. The net effect of all these issues is that the bot should arrive at a finish circle, where the circle diameter is proportional to the cumulative error in the bot’s path.

It also would be convenient to have an indicator on the BrickPi to show when the obstacle-avoidance algorithm is activated. This is easily accomplished because the BrickPi has two LEDs installed on the board edge that can be individually controlled by using a Python GPIO library named python-rpi.gpio. You will need to install the library on the RasPi by using this command:

Images

You will need to import the library by using this command:

Images

The mode must next be set, followed by setting the GPIO that is connected to the LED as an output:

Images

The 12 used in the setup method actually refers to RasPi GPIO pin 18 . The other BrickPi LED is designated as 13 but really refers to RasPi GPIO pin 27 . The pin numbering complies with the WiringPi designations that I discussed in Chapter 6 . The python-rpi.gpio library is based, in large part, on the WiringPi library. I recommend that you review Figure 6-19 , which clearly illustrates the relationships between the WiringPi and RasPi GPIO pin labels.

Turning on the LED is accomplished using this statement:

Images

Of course, all you need to do is change True to False to turn off the LED.

I added the ultrasonic sensor, obstacle-avoidance algorithm, and indicator control to the preceding program and renamed it obstacleAvoid.py . I also modified the original program to remove the movement commands because they were unnecessary for this particular situation. The program is listed next and is also available on this book’s companion website.

Images

Images

Images

Images

In the next section, I discuss how the obstacle-avoidance algorithm worked, pointing out the successes and where some improvements should be made.

Obstacle-Avoidance Demonstration

Figure 8-15 shows the CasterBot setup on the real obstacle course. The total distance between the start and stop points is 120 cm, or approximately 5 ft, so you need a moderate-sized area to set up the course.

Images


Figure 8-15 Obstacle course.

I also set up an SSH session between the CastorBot and my MacBook Pro to initiate the program and start the robot on its path. Run the program by entering:

Images

You will be prompted to enter the direct path length between the start and finish points in centimeters. This path length is divided by 10 to create the number of X increments that the bot uses to transverse the path. The program will display the number of path increments left to be completed and will halt when it completes the last increment.

I initially observed that the 90° turns were more like 120° turns because the timing was off a bit. I subsequently reduced the time spent in a turn to about 0.8 second, which corrected the overturning problem. I also noticed that the playing surface on which I ran the CasterBot seriously affected its performance. Running on a carpet would slow the bot down, which then would cause the turns to be much less than 90° and subsequently cause the obstacle avoidance to fail. In addition, the bot would not complete the commanded path length because completing the total path depends on time duration, not actual distance traveled. I tried to use the motor encoder pulses to overcome this timing problem, but that led to a whole series of programming issues with the BrickPi library. After much struggling with the library, I decided that it was just more practical to select a proper playing surface, such as a wood floor, than trying to adapt the library software.

This last demonstration concludes this chapter on how to build and operate a BrickPi-controlled robotic car.

Summary

I began this chapter with a brief introduction to the BrickPi, which is a substitute controller for an EV3 Lego Mindstorms brick control module. Using a BrickPi in place of the regular Mindstorms brick allows you to program in whatever Raspian-compatible language you choose instead of being restricted to the Mindstorms drag-and-drop environment. I chose to use Python as the language to control a robotic car, named the CasterBot.

I next discussed how you could build this car out of Lego components using a complete set of referenced instructions. A LiPo battery also was recommended as a power source because the BrickPi and Mindstorms motors consume quite a bit of power.

A simple Python program was next shown that allowed a user to remotely control the car using an SSH connection on a laptop. The car could be commanded to go forward, backward, turn right or left, and stop. It also could be sped up or slowed down, all by using single-character commands from the laptop.

An ultrasonic sensor was discussed next in regard to its internal operation and how it would be used to avoid obstacles when the car was operated autonomously. I developed a program that drove the car in a straight line between two points but could maneuver around an obstacle it detected in its path.

The chapter concluded with my observations on how well the bot performed on the obstacle course and where improvements could be made.