Modular Design of Embedded Software for Distributed Robot Control Nicolas Champagne-Williamson, Computer Science, Cornell University Jason Cortell, Lab Manager, Mechanical and Aerospace Engineering, Cornell University Prof. Andy Ruina, Mechanical and Aerospace Engineering, Cornell University Dec. 16, 2009. Funded by Semiconductor Research Corporation with Support from Intel Foundation
Abstract Cornell s bipedal robot, Ranger, is going through brain surgery. In an effort to increase power efficiency and walking distance, all of its electronics and software have been redesigned and implemented in a modular way to allow for greater adaptability. We have created a simple operating system for our boards, and numerous modules which add extra functionality as needed. The data nexus provides the linkage between all of these modules, and allows the modules themselves to be more abstract. The projects for each microcontroller on the robot now take only a couple of hours to program and test, and future robots will take advantage of this distributed computer system for quick and easy design. Introduction The previous iteration of Cornell s bipedal robot, Ranger (figure 1), had one main microcontroller which controlled the entire robot. However, this caused many problems when updating or adding features to the robot, because of the number of wires that had to be routed to the main board and the limited number of ports available. Therefore a new distributed control system was designed which employs a number of smaller satellite microcontrollers to manage many lower level aspects of the robot, such as motor control and reading input from sensors. These controllers all communicate with each other and the high level controller main brain over a Controller Area Network (CAN) bus. However, each of these boards requires a separate software project yet share similar functionality, so it was decided that software should be implemented as modular and independent of the specific board as possible. figure 1. Cornell s Ranger Robot The result is a multitude of modules, each encapsulating some functionality of the robot, such as controlling current to a motor or scheduling tasks for the microprocessor. These modules can be grouped into system (the core of the software) versus module (extra functionality) code, and board independent (same on every board) versus board specific (setup required on each board) code. These modules allow us to create new projects quickly, and are both adaptable and powerful enough to implement additional functionality when and where we need it. Materials and Methods The current electronics on Ranger are built around generic boards, which themselves have hardware modules. The microcontroller used on each board is an LPC2194/01 ARM7, chosen because of its low power consumption. All the programming is done in C using the Keil uvision IDE, and programs are flashed to each board using Keil s ULINK2. Every board has a project unique to it (because no board has exactly the same function) and these projects all link to the same modules, to limit copying code and updating files in multiple places. The module files are kept in a Subversion repository on the Ruina Lab server. The software for each board is split up into modules, each encompassing some small, but not necessarily simple, functionality. For example, the motor controller module
uses proportional-integrative-derivative (PID) control of the current going to the motor, which itself uses an external analog to digital converter (ADC) module to read in the current. In our architecture, there are two distinctions in the types of modules: board specific/board independent, and module code/system code. The board specific modules are low level programs that have to be configured for each specific project. One of the problems we ran into was how to link the modules together. For example, the motor controller needs to get the motor current in order to work properly, but it doesn t care from where; on one board it might be the external ADC and on another it might be the internal ADC. Our solution was the Data Nexus, a board specific module whose purpose was to create the linking function between modules, as well as perform unit conversions. In our example, the data nexus would define a get_motor_current() method that uses whatever module has access to the motor current on that specific board, converts this value into amps, and passes this function to the motor controller to be called as needed. The board independent modules are the same on every board. All of the configurations for these modules are done through the board specific modules. The board independent modules are designed to be as abstracted as possible, and to be reusable both in different boards and with different microprocessors. However, the latter was mostly undoable because so much of the algorithms design was based around how the board worked. For example, many of the modules depended on hardware communication protocols, such as Serial Peripheral Interface (SPI) or Universal Asynchronous Receive Transmit (UART) to talk to needed peripherals, but the registers used had very specific orders and methods of doing things, that to try and abstract it all out would create unnecessarily complex code. The system code is included with every project and forms the core of the software. It includes both specific and independent files, and provides basic functionality such as error handling, function scheduling, hardware and software setup, and the data nexus. The module code adds extra functionality as needed to each board s project, based on the desired job of the board. Results The following is an overview of the files that were implemented to satisfy the design constraints. See figure 2 for a graphical look at how they all fit together. 1. System Code: The Core code on the microcontroller 1.1. Board Independent: Same code on every board Scheduler: This program uses a schedule defined in software setup to decide what tasks should be run when. It has the ability to be synced from the main brain, but will continue to run in error mode if it becomes unsynchronized with the main brain. Includes: This file contains links to all necessary header files of all possible modules, regardless of whether they are used or not within the project. It is included with every module and allows linking to any other modules. Error: Code from this module is called from modules when any sort of error occurs. It has different levels of priority, and relays the error messages over CAN to the main brain. 1.2. Board Specific: Different versions on each board
Hardware Setup: This code sets up all the necessary hardware for the project, including configuring communication protocols, input/output pins, and timers. Software Setup: This code sets up all the necessary software for the project, including passing specific parameters to the software modules such as which encoders to read from, or how often to blink LEDs. Interrupts: Configures the vectored interrupts and fast interrupts. Data Nexus: This board specific system code ties all of the board independent modules together, allowing the modules themselves to be abstract. For example, it defines functions to read in sensor input, and passes that to modules which need that sensor info, converting units or data types if necessary. It also defines CAN wrapper methods for transmitting data to the main brain. Local Headers: This board specific system code provides board specific access to some modules, such as modules that are being debugged or that are in progress, and whose inclusion in the standard includes.h file would break other projects. 2. Module Code: Adds additional functionality to a board 2.1. Board Independent: Same code on every board Absolute Encoder: Reads input from the magnetic absolute encoder over SPI. Internal ADC: Reads and stores input from the internal analog to digital converters (4 of them). External ADC: Reads and stores input from the external analog to digital chip, communicating over the Synchronous Serial Port (SSP). Button: Finds the state of buttons. Buzzer: Controls the frequency and amplitude of a buzzer. CAN: Receives and transmits packets over the CAN busses. Song: Controls for playing songs using the buzzer. Purely for fun, it can play the Mario theme song and the Cornell Alma Mater. Heartbeat: Some sort of heartbeat for the board to let us know it s on and working properly, usually accomplished by blinking an LED. LCD: Controls for displaying text using the 16 character LCD display. Limit Switch: Simulates a limit switch, which is on if the value on some pin has occurred for a given number of time counts. Microstrain IMU: Communicates with the Microstrain Inertial Measurement Unit (IMU) to get information about acceleration and angles. Motor Controller: Controls the DC motors using PWM and PID current control. Also has support for PID position control. Quadrature Encoder: Analyzes input from the quadrature encoder on motors to find position and velocity. Radio Control: Finds the frequency of the radio control signal. UART: Prints characters of Universal Asynchronous Receive Transmit.
UI LED: Code to specifically control the tricolor LEDs on the user interface board. Hardware Software Sensors Registers System Code Setup and Configuration Data Nexus Modules Figure 2. Overview of the different parts of a board. All of the modules are referenced through the data nexus. Setup and configuration on the software level give modules access to hardware, including sensors and microcontroller registers. Discussion The current software for the robot is nearing completion. Left to be done are a few more modules, such as Bluetooth communication, and the high-level control code, which is currently being designed around hierarchical finite state machines. Additionally, all of the boards projects need to be assembled using the modules described above. The ease of configuration means that this will take a fraction of the time it would otherwise have taken to design separate code for each microcontroller. However, there are a few problems I d like to address with our current code, and some possible solutions. 1) We ran into problems when anyone updated or changed the header to a module. Because we began testing boards and complete projects in parallel with the modules, a change in a module often resulted in the breaking of most other projects due to compilation errors. These were often caused by mismatched function signatures or type definitions. Our quick solution to this was the local headers file, which allowed in progress modules or debugging modules to only be used by the one board it was being debugged on. This was only a temporary fix, because when the module was complete and working, it would still cause the same issues with projects that were previously working fine when the main file was finally updated. A better solution would be having working versions of the code, so that projects that other people use aren t affected by updates and fixes unless they want to update to a later version. 2) The purpose of our modules was to make it much easier to design new robots because we simply add boards as we need them. The boards and software were made to be flexible and versatile, so that they could work in a variety of situations. However, I believe we could abstract the code even more, so that some modules could work regardless of the microcontroller or hardware. Code like the PID algorithm used in the motor controller and the method for finding velocity using the Quadrature Encoder can, and probably should, be separate from commands and functions which
access the hardware directly. Instead we could design and use drivers for each aspect of the hardware we need to interface with, so instead of the motor controller setting the pulse width modulation (PWM) directly in its code, it would defer to some function passed to it by the data nexus, which would have direct access to the PWM registers on the board. These drivers would be unique to the microcontroller, but independent of the board, and would form the interface between the hardware and the software. Additionally, this would allow software to be run on a wider range of hardware without always having to be rewritten, although the drivers themselves would still need to be implemented. 3) Thought has been given to open-sourcing this project and allowing others who develop robots to have access to our modular boards and software, but currently putting a project together requires too much high-level knowledge of the whole process. To make this more accessible, some user interface must be designed that simplifies the whole process, allowing users to drag and drop modules into place and connecting them as they need. The automation of configuration needed for this process will also enforce conditions of the code that are often missed by handbuilding projects, such as certain settings and orderings that aren t explicit in the code. Conclusion We set out to create a system for quickly building future robots using modular systems for both software and hardware, allowing us to add what we need to an arbitrarily complex robot. We found that for the most part, the modules were able to be the same on every board, with only parameters and hardware setup needed to make them work. Additionally we designed a simple operating system that works with the modules, and a data nexus that ties all of the modules together, allowing the modules themselves to be abstract. Future robots are now able to be constructed much more quickly, and more freedom can be given to their mechanical design. As more robots are developed, the software itself will also change, updating current modules, adding new modules, and further abstracting and automating the process to make implementing electronics and programming robots easy enough for any person.