# ( Program Design
)
|
|
LightRover
has two major modes of operation: calibration, and follow. These modes are
controlled by the main state machine. This organization, combined with the
needfor timed motor, sensor, and LED control naturally lead to a timed based
scheduler program. Only one ISR was necessary-Timer0 Clear on Compare Match,
which interrupts every 1 ms and updates the timers used in the program. Our
full feature set had four modes: Calibration, Follow, Menu, Program. However,
we decided that working for a stable Follow Mode was more important than
additional features, so Program, and Menu Modes were not included in the final
design. |
# Program Design
Philosophy |
Before writing any code, we
sat down and discussed features we wanted to see and how they should work.
Since the different features would use the same functions (such as sensing and
motor control), our focus was to develop those functions to be as general as
possible. For example, we have one function motorControl that takes in a
desired direction (foward, left, right, stop) as a parameter and outputs the
appropriate control signals. This allows the caller to specify its own timing. |
#
Initialization |
The code will
initialize various values, state variables, as well as Timer0 and the ADC upon
booting. All these initializations are wrapped in the function initialize. When the
initializations are complete, the code falls into the main while loop, where it
stays for the remainder of the execution. LightRover's first action is to begin
in calibration mode, where it will attempt to adjust to its surroundings. |
# Calibration Mode |
As previously
mentioned, when the boot process concludes it enters Calibration Mode. This
mode has three states: CALIB_INIT, CALIB_CHECK, and CALIB_DONE, and the state
changes at 1.5 second intervals. Typically we begin in state CALIB_INIT to take
an initial sensor reading and change to state CALIB_CHECK. 1.5 seconds later,
in CALIB_CHECK, we take another sensor reading and compare it with the value
previously obtained in CALIB_INIT. If the two results are deemed equivalent, the threshold value is calculated. We calculate the threshold
light intensity to be 7% greater than the measured light. This requires a
floating point multiply, which we are able to do because this mode is not time
critical, and the states have a long 1.5 second interval. The read and
comparison cycle is done first for the left sensor, then for the right; with
LED's indicating the sensor. If the comparison fails (if there is a flickering
light in the area, for example) then a retry counter is incremented and the
process begins over again. However, if there are more than three consecutive
calibration failures, the default threshold values are loaded and the program
falls into Follow Mode. |
This mode is
in function calibrate.
We discussed how the robot should handle different light environments and
decided that it would be best for the robot itself to set itself. Calibration
works very well, but one issue it does not address is what to do when there is
too much light and we need to decrease the threshold value. It is very
difficult to discern a very bright environment from a constant light signal. We
were unable to come to a solution without making judgements on the usage of the
robot (What if the user wished to have the robot follow the
sun?). |
|
Fig
SW-1: Calibration Mode state diagram. Interval 1.5 sec |
# Menu Mode (not featured in final design) |
A working Program Mode
required some method for the user to select either Program or Follow modes. To
address this, LightRover was supposed to fall into Menu Mode after
initialization and calibration. Shining a light at the left sensor would
correspond to selecting Follow Mode, and the right sensor would select Program
Mode. Although this was fully operation, the decision to remove Program Mode
meant the removal of Menu Mode. |
# Follow Mode |
Follow Mode
is the major operating mode and is our original vision: to have the robot detect
and follow light. The sensors are handled by the function sensors which outputs a result
(DETECTED_BOTH, DETECTED_LEFT, DETECTED_RIGHT, DETECTED_NONE). Function motorControl will take in the
result and send the control to the appropriate motor. |
We use two ADC channels
(channel 0 for left, and channel 1 for right). Because it is not possible to
perform two ADC conversions simultaneously, we take a reading and compare it
with the previous reading of the opposing sensor. For example, if we were to
read the left sensor, we would compare it with the right sensor's value taken
from the previous cycle. The actual comparison is done by helper function compare. compare will check if the signal
is above the threshold level then check to see if one reading is significantly
greater than the opposite sensor's reading. Because it is unlikely for two sensors
to receive the exact same sensor reading, we defined an acceptable range. The ADC
provides 8-bit precision so the sensor reading will range from 0-255. We measured
the difference in voltage between the two sensors using the multimeter. Using the
formula (V/5)*(256) we calculated the ADC value. We found that the sensored differed
about 0.02 volts or 1. This value was too strict to be usable, so we instead found a value through
experimentation (which we found to be 10). |
Function motorControl has gone through
several revisions. We began by using a chain of if else statements (as seen in
litestep.c), but since the implementation required that we be able to control
each motor independently, we moved to using an array. Array steps[] holds the control
signals and are used for both motors. By default the control signals are for
the right motor, but since all the motor signals are output to a single PORTB, a
simple left shift by 4 controls the left motor. The resulting code is very compact since we use the
same single array to control both motors and use two step counters to enable
independent motor control. |
We found using the same timer
for both the sensors and motor control resulted in very jerky movements. In the
final design, each has its own timer so the MCU can send more stable control
signals to the motor for a longer period of time. With the sensors originally
reading at very fast pace, the motors would respond too quickly, resulting in
robot that could do little more than tremble in the same place unless the light
source was very close. |
# Program Mode (not featured in final design) |
Program Mode was an
additional feature where the user could program the robot by light. This mode
was desirable because it reused a lot of the functions used by Follow Mode, and
it would have been interesting to see a robot programmed by unconventional
means. Program Mode consisted of two states: PGM_PGM and PGM_PLAYBACK. PGM_PGM
was the program state, where shining a light at a sensor corresponded to an "instruction"
(see the table below) |
Sensor |
Instruction |
Left sensor |
Left turn |
Right sensor |
Right turn |
Both sensors |
Forward |
No light detected |
No instruction | | |
Fig
SW-2: Program Mode "instruction set". Unfortunately the
complexity proved to be too much for LightRover. |
This mode was designed to be
very simple, in fact, the distances were preset to be the turning radius of the
robot. These instructions were to be stored in a five instruction array that
would be played back in the PGM_PLAYBACK state when the array index reached
maximum capacity. |
PGM_PLAYBACK mode worked
correctly if we hardcoded an array of instructions, but we did not have
sufficient time to determine why the PGM_PGM mode did not work. Since the
programming part of the Program Mode failed to function correctly, there was
very little reason to keep around this mode. |