CSE 466 - Wireless Blimp Control

Altitude Control

IR Sensors

The BlimpBot PCB comes with five infra-red photo-diodes: one facing down for elevation controls, and four horizontal facing (front/back, left/right) for proximity sensing and collision avoidance. There is a single IR photo-detector on the bottom of the PCB that senses the reflectance from each of these devices. In order to read each direction separately the appropriate LED must be selected and illuminated while the detector is sampled by the analog to digital converter (ADC) on the MSP430 micro-controller.

An auxiliary MUX is used to select between IR diodes (see part 74HC238 in the schematics). The appropriate LEDs are selected on MUX output pins Y0-4 for the Back, Front, Left, Right, and Down IR LEDs respectively. MUX input pins A2, A1, A0 are set to the binary address of the output selected (A2 is MSB, A0 is LSB). These pins are controlled with the MSP430 output pins PJ.1, PJ.0, and P1.6 respectively. MSP430 output pin P2.0 is toggled to enable the MUX, sending current to the selected IR LED to illuminate it. See Table 1 below for the exact addressing scheme:

Tigure 1. IR LEDs with decoder select values and emitter direction.

Each pass through the main control loop the sensors may be read. If the PC side GUI sends a “Query IR” command, then all 5 sensors are read. If the alt_hold global flag is set to true then only the down facing sensor is read for use by the height controller.

A call to the read_sensor(int sensor_ID) function initiates the reading with a call the select_sensor(int sensor_ID) function. This sets the correct address bits on the MUX. The read_sensor function then enables the ADC10 peripheral on the MSP430 and runs a loop to collect a predetermined number of samples. The purpose of collecting multiple samples is to find the mean of the noisy sensor readings. We experimented with numbers from 64 down to 8 samples. The former made our sensing loop fairly slow, and the latter resulted in too much variance in the reading. We finally settled on 32 samples, which seemed a good compromise. This is a #define value that can be easily modified.

We chose values that are powers of two so we could normalize with a right bit shift of the log2 of this number. This is the fastest technique for doing integer division in an embedded context. In the end we did not normalize these readings, preferring instead to use the larger accumulated value for increased resolution. Note that this normalization step is added later in the control algorithm when we divide the final speed calculation for the down motor.

Each pass through this sensor loop we sample two value with the ADC10: first with the LED off to read the ambient IR illumination, second with the IR LED on to measure reflectance (a proxy for proximity). The difference of these two sampled values is accumulated to give our final reading. Before the function exits the ADC10 is disabled. Note that this method uses explicit initialization of conversions by setting the ADC10SC bit and waiting for !ADC10BUSY, rather enabling ADC10 interrupts and using the ISR.

The method of averaging or accumulating samples gave us good results. We would have liked to implement the Kalman Filter as well, but this proved difficult to do in embedded software on the blimp, without a floating point unit. We would perhaps attempt this in future work.

Height Level Control

We maintain altitude level with a PID Controller that we implemented on the blimp-side. This was a design decision we chose over the more popular PC-side controller. This seemed like a more natural choice given that this is an embedded programming class, and it allows the blimp to be more autonomous. This choice also reduces the number of control packets that must be sent over the air. A check box in our GUI toggles the alt_hold global flag in the blimp code. A text box and “Set Altitude” button in the GUI allows us to send the set-point value to the blimp. A call to the alt_hold_PID(int ir_down) function from the main control loop makes the PID calculations and sets the down motor speed to maintain height. We calculate the three values as follows:

P_error: This is the instantaneous position error, the difference ir_down - alt_set_point.

I_error: We experimented with several different techniques. Initially we used an IIR filter on the P_error, using 75% old value and 25% new. This works because it gives us a time moving average of the position error, which is somewhat proportional to a time integral. By scaling this result with a coefficient we can get a functional I_error term.

This technique, however, is not correct. It actually produces a normalized exponential moving average, which is not the same as a time integral. The largest this value can grow to is the current P_error, no greater.

Next we tried an infinite accumulation of the P_error. This suffered from the problem of over-correction and oscillation. To solve this we went to a windowed moving average, collecting a finite history of position error values in an array and shifting them each time step while adding the most recent. The I_error becomes the sum of this array. This technique proved the most effective. We experimented with histories of 10, 5 and 3 time steps and settled on 3. Longer histories created too much delay, and hence more over-correction and oscillation.

D_error: The is calculated as the instantaneous rate of change between position errors on successive time steps. In order to smooth these values from noisy readings we ran the D_error term through an IIR filter with 7/8ths new value and 1/8th old. The D_error term seems to help dampen oscillation and hold a more steady height.

Final PID tuning was done by multiplying each term by integer constants, kP, kI, and kD respectively. Lastly we right bit-shifted the result to scale it smaller. Many experimental iterations we used to arrive at the final tuning, which gave good results depending on floor surface and proximity to walls (which increased reflectance and caused the blimp to rise).