Hi Dirk,
So I'll give a runthrough of the code, and if there's any extra info you want on any areas, please let me know.
There's a high level overview in the
blog post I wrote on programming the robot using Python, which also has a diagram that might make things a bit clearer.
The software is organised in a number of levels, so working from bottom and going up we have
Mini Driver FirmwareThe Mini Driver is an Arduino compatible board that drives the motors and provides a good place to attach sensors. On bootup the Pi will try to connect with the Mini Driver to check what version of firmware it's running. If it doesn't find the correct version then it will upload the correct version using a Python library called Ino. The main Mini Driver firmware file is an Arduino sketch called
mini_driver_firmware.ino, The code is rather dense because I'm trying to fit a fair bit into the limited memory of the Mini Driver 8kb. This would have been epic space for veteran coders, but for someone like me used to GB of RAM it feels tight.
The main part to look at here is the 'loop' routine. This runs continuously
- Receiving and processing messages that come over the USB serial connection
- Adjusting the frequency of the PWM signals used to drive the motors
- Writing out servo motor commands
- Reading from sensors and sending the data back, this happens at a rate of 100Hz
So the firmware is fairly simple really. It just sits there, taking commands from the Pi and pumping sensor readings back up to the Pi. The communication routines are probably a bit over complicated. Data is sent back and forth in binary to save space, and a simple checksum is used to detect if data gets corrupted as it is sent over serial.
MiniDriver Python ClassOne level up from the Mini Driver Firmware is the MiniDriver Python class that lives in
mini_driver.py. Again, this is probably a bit more complicated than it needs to be but the basic idea is that this is the Python class that establishes and manages the serial connection with the Mini Driver. It also takes care of checking that the firmware is correct, and uploading a new version if it's not there. The Mini Driver class uses a Connection class to manage the serial connection and a SerialReadProcess (which for some reason seems to be a thread) so that we can read data coming from the USB serial port without interrupting the rest of the program. Whenever you call a routine like getUltrasonicReading on the MiniDriver class, it will go and ask the Connection class to see what data it has got. Similarly, when you call the routine setOutputs, this gets sent to the Connection class which then sends a message over the USB serial connection.
You can actually use the MiniDriver class in your own programs without using the rest of oor software. Simply shut down the robot web server by running
- Code: Select all
sudo service robot_web_server stop
and then look at either
robot_control_test.py or
robot_sensor_test.py for examples of how to do this.
Robot Web ServerNow, we could have just stopped at the MiniDriver Python class. That's what a lot of other companies that produce robot kits do. However we wanted to produce a more complete experience for people getting started with their robot. So, making use of the MiniDriver Python class we've implemented a Python web server using the Tornado library. This is started automatically as a service when the Pi starts up (see the bottom of the installation instructions
here for how we do this). The main file of the web server is
robot_web_server.py. This will serve all of the web interface files that are found in the www directory, and also exposes a websocket interface. In robot_web_server.py, websocket communication is handles by the ConnectionHandler which uses the sockjs.tornado library (sockjs is an implementation of websockets). Commands that come over websockets are simply text strings, things like GetRobotStatus and 'SetMotorSpeeds 50.0 50.0'. The ConnectionHandler class communicates with a RobotController class in
robot_controller.py. The RobotController class is the 'robot' that the web server sees and controls. The reason for not using the MiniDriver class directly is because I wanted to make it possible for people to use their own robots with their software. So for example, if you have your own robot control board and created a Python class that could talk to it, you could modify robot_controller.py to use your Python class rather than the MiniDriver class. You would simply modify all parts of robot_controller.py where it uses self.miniDriver. In the future, I hope to be able to swap between robot hardware using something like a configuration file.
Camera codeCamera streaming is handled by a separate C/C++ progam called
raspbery_pi_camera_streamer. This program is started up whenever someone sends 'StartStreaming' to the web server, and it must be sent every 2 seconds or so, otherwise the camera streamer program will be shut down. For example if you shut down the web interface for example you should see the red led on the camera go off after a bit.
The camera streamer is a separate web server which sends out JPEG images from the camera, 640x480 for normal viewing 160x120 for computer vision work and it also sends out motion vectors. The camera streaming could probably have been written in Python using the picamera library but I wanted to learn how to use mmal.
Web Interface
As discussed above, the web interface is sent out by the web server and is everything in the
www directory. This is fairly basic HTML5 code and Javascript that I've cobbled together from a number of examples on the internet. The web interface communicates with the web server by using web sockets in order to get a low latency interface. The reason for using a HTML5 web interface is so that the interface of the robot works on as many devices as possible. With limited time and resources I didn't want to have to create an Android app, an iphone app etc. There are lots of ways I'd like to improve the web interface in the future.
The py_websockets_bot libraryThe
py_websockets_bot library is a Python library that lets you write control scripts for the robot. I did think initially about getting people to shut down the web server before writing scripts that used the MiniDriver Python library, but thought that this would be a bit naff. It would be better without that complication. Therefore I decided to take advantage of the fact that the robot was running a web server that spoke websockets and made use of that.
This actually gives a number of advantages. Firstly, people can keep using the web interface to observe what the robot is doing, whilst their control scripts are running. Secondly, it's really easy to write control scripts that can run on another more powerful computer, and control the robot over the network, which is great if you need more power for computer vision etc. Thirdly, if you do want to run a script on the Pi, it's really easy to move it over to the Pi and it should still work, you just tell the script that the address of the webserver is localhost rather than a remote IP address.
Ideally what I'd like to do is let people put scripts that use py_websockets_bot on the Pi, and then activate them using the web interface. So you could write a 'dance script' for example, and then activate it by pressing a button in the web interface. This shouldn't be difficult to do, but finding the time for it is tough.
So...That was all a bit of a brain dump, so thanks if you stuck with it to the end. Hopefully that's given you a good insight into most of the code base. Let me know if you'd like more detail on anything specific.
Cheers
Alan