Project 3: Pulse-Width Modulation and Proportional-Derivative Control
- All components of the project are due by Wednesday, April 20th
at 5:00pm.
- Discussion within groups is fine.
- Discussion across groups may not be about the specifics of the
solution (general programming/circuit issues are fine to
discuss).
Project Goals
At the end of this project, you should be able to:
- control the speed of DC motors through an H-bridge circuit,
- implement and tune a proportional-derivative control law that
maintains the hovercraft's heading at some desired orientation,
- implement an obstacle avoidance control law that keeps the hovercraft
pointing away from nearby obstacles, and
- implement a high-level control law that decides which low-level
control law to employ at a given time (PD or obstacle).
Project Components
All components are required to receive full credit for the project.
Part 1: Microcontroller Circuit
The current amplifier board is composed of two full H-Bridge
circuits. We will be using one full H-Bridge to control the middle
fan (allowing us to control rotation speed and direction). We will
split the other H-Bridge into two "Half Bridges": one for each of the
left and right fans. This will allow us to control speed of these two
fans, but not direction.
The board and connections to your circuit are documented on the
Lab
Hardware page: see the Motor Control Board section.
Connect the current amplifier board to your three fans and to motor power:
- Add wires to connect the motor control board to the fans and to the batteries
- Connect GND and VIN directly to the battery (not to your
5V regulated power supply)
- Connect the middle fan to port 2 (OUT 2A and 2B)
- Connect the right fan: red wire to OUT 1A and black wire to GND
- Connect the left fan: red wire to OUT 1B and black wire to GND
Connect the current amplifier board to your Atmel chip (the 15 pin
connector on one side of the board):
- Connect 1PWM and +5V(IN) to your Atmel's +5V power supply
- Connect GND and GND to your Atmel's ground
- Choose one of: timer1, 3, 4 or 5. For the chosen timer, you must have
all three PWM pins available (they are labeled OCXA, OCXB and
OCXC on the Arduino circuit, where X = 1, 3, 4 or 5).
- Connect the following to your Atmel (you will need to select
pins that are not shared with the programmer):
- 1INA: Pulse-Width Modulated (PWM) input for the right
fan. Connect to OCXA
- 1INB: PWM input for the left fan. Connect to OCXB
- 2PWM: PWM input for the middle fan. Connect to OCXC
- 2INB/2INA: direction control for the middle fan (0/1:
one direction; 1/0: the other direction). Connect to
free digital output pins.
Part 2: Fan Control Interface
Note: this part will count for a total of one personal
programming credit
Create the function interface that will generate the PWM signals
for each of the three PWM inputs to the motor control board.
Define four macros at the top of your C file (or in a private header file):
#define BRAKE 0
#define HOVER 1
#define LEFT 1
#define RIGHT 0
These strings can be used in place of the integers and make your code
much more readable.
Implement the following functions:
-
void middle_thrust_dir(uint8_t dir) that sets the
direction bits for the middle fan.
If dir = HOVER, then the craft should hover (assuming an
appropriate level of thrust).
If dir = BRAKE, then the craft should be pulled to the ground.
- void middle_thrust_magnitude(int16_t mag) that sets
the thrust magnitude for the middle fan. This function must
ensure that mag falls within the range of 0... 1023
(which correspond to 0% ... 100% duty cycle). If it does not,
then the value should be clipped to this range.
- void lateral_thrust_magnitude(int16_t mag_left, int16_t
mag_right) that sets the thrust magnitude for the left
and right fans. This function must
ensure that mag_left and mag_right fall
within the range of 0... 1023 (which correspond to 0% ... 100%
duty cycle). If either does not, then the offending value
should be clipped to this range.
Note:
- Initialization of the three PWM channels must happen within
your main() function (see the lecture notes on timers).
Testing
Implement the following functions:
Modify your main function such that it executes one of the two
above functions, depending on the initial state of switch 0.
Part 3: Proportional Derivative Control
Note: this part will count for one personal programming credit
Create a function that will implement a proportional-derivative
controller:
void pd_control(int16_t error, int16_t rotation_rate, uint16_t
forward_thrust) where error is the heading error, rotation_rate
is the rate of craft rotation, and forward_thrust is the total forward
thrust that should be generated by the left/right fans combined (this
latter value will be between 0 ... 1023)
This function will:
- compute a left/right differential control signal for the fans
using the proportional-derivative control law and the heading
error and current rotation rate (note: you should include an
error deadband and maximum value),
- add this differential control signal to the forward_thrust
input,
- set mag_left and mag_right (note: make
sure that these never fall outside the range of 0 ... 1023).
Note: you are strongly encouraged to use integer math instead of
floating point math.
Notes on tuning the PD control parameters:
- Start with Kp = 0, and slowly bring Kv up to a reasonable
level. Your craft should resist rotation. If it accelerates
instead, then you probably have the sign of Kv wrong.
- Next, set Kv = 0, and slowly bring Kp up to a reasonable
level. You will see oscillations (this is ok for the
instant). If the craft turns away from the goal, they you have the sign
wrong on Kp.
- Now use your selected Kp and Kv. When you have oscillations,
your choices are to: increase Kv or reduce Kp. Make adjustments slowly.
- There is such a thing as Kp being too high. This comes from the
fact that from one control decision to the next there is some
amount of delay. The larger the delay, the larger the
oscillations. In this case, your only choice is to drop Kp
(and likely Kv, too).
Part 4: Obstacle Avoidance Control
Note: this part will count for one half of a personal programming credit
Create a function that will implement an obstacle avoidance
controller:
int8_t obstacle_control(uint16_t distance_left, unt16_t distance_right, int16_t rotation_rate, uint16_t
forward_thrust) distance_left and distance_right are the
distances (in mm) to the nearest obstacle for the left and right
sensors, rotation_rate is the rate of craft rotation, and forward_thrust is the total forward
thrust that should be generated by the left/right fans combined (this
latter value will be between 0 ... 1023)
This function will do the following:
- If one distance sensor reads far (e.g., > 50 cm) and the other reads near (e.g., < 40 cm),
then the left/right thrust should be set such that the craft turns away from
the near object and the function should return 0.
"Turning away" should be implemented by
driving the left/right thrust with forward_thrust +
delta and forward_thrust - delta, where
delta is either positive or negative, depending
on the direction to turn.
Note: you will probably also need to mix in a damping term that is
proportional to rotation_rate. You will probably
also need to adjust the 40 and 50 cm parameters.
- Otherwise, the function should not adjust the left/right thrust and should return -1.
Part 5: High-Level Control
Note: this part will count for one personal programming credit
For this part, you will implement a high-level control loop that will
cycle once every 50 ms. For each of these control steps, the
hovercraft will first steer away from obstacles (while moving
forward). If there are no obstacles, then the hovercraft will steer
toward the goal heading (also, moving forward).
-
Modify the your program such that it is structured as follows
(you will, of course, need to add other code):
// Global flag
volatile uint8_t flag_timing = 0;
// Interrupt service routine: called every time
// the timer 0 counter transitions from 0xFF to 0.
// Period of flag_timing is 3 * 256 * 1024 / 16000000 = 49.152 ms
ISR(TIMER0_OVF_vect) {
static uint8_t count = 0; // Set to zero at beginning of program only
if(++count == 3){
// Set the flag to indicate that the period has passed
flag_timing = 1;
count = 0;
}
};
int main(void) {
int16_t counter = 0;
int16_t heading, heading_last, heading_goal, heading_error;
int16_t rotation_rate, distance_left, distance_right;
#APPROPRIATE VARIABLE DECLARATIONS HERE#
#APPROPRIATE INITIALIZATIONS HERE#
timer0_config(TIMER0_PRE_1024); // Prescale by 1024
timer0_enable(); // Enable the timer 0 overflow interrupt
sei(); // Enable global interrupts
// Initialize variables
heading_goal = get_orientation();
distance_left = get_distance(LEFT);
distance_right = get_distance(RIGHT);
// Begin to lift off the ground
middle_thrust_dir(HOVER);
#RAMP UP MIDDLE THRUST TO HOVER#
// Loop for ~30 seconds as long as the distances are safe
while(counter < 20*30 &&
distance_left > 150 &&
distance_right > 150) {
heading = get_orientation();
heading_error = compute_error(heading_goal, heading);
rotation_rate = get_rotation_rate();
distance_left = get_distance(LEFT);
distance_right = get_distance(RIGHT);
// Display
if(#SWITCH 0 OPEN#) {
display_distance(distance_left);
}else{
display_rotation_rate(rotation_rate);
}
if(#SWITCH 1 OPEN#) {
display_orient(heading_error);
}else{
display_orient(heading);
}
// Steer away from obstacles
status = obstacle_control(distance_left, distance_right, rotation_rate, #PICK SOME SMALL VALUE#);
if(status == -1) {
// No obstacles: steer to desired heading
pd_control(heading_error, rotation_rate, #PICK THE SAME SMALL VALUE#);
}
// Increment time
++counter;
if(flag_timing) {
// Error condition: your code body is taking too much
// time. Indicate this with an LED display of some form
}
// Wait for the flag to be set (happens once every ~50 ms)
while(flag_timing == 0) {};
// Clear the flag for next time.
flag_timing = 0;
}
// Shut down hovercraft
lateral_thrust_magnitude(0, 0);
#RAMP DOWN MIDDLE THRUST TO ZERO#
while(1){}; // Spin forever
}
Part 6: Hovercraft Layout
Revisit the mounting of your components on the Frisbees:
- Make sure that all components and cables are secure
- Make sure that the compass is far away from the motors and any wires that carry a substantial amount of current
- Check that the Frisbee is balanced (i.e., the center of mass is
at the center of the Frisbee)
Let us know if you need any additional components for mounting.
References
What to Hand In
All components of the project are due by Wednesday, April 20th at 5:00pm.
- Demonstration/Presentation/Code Review: All group
members must be present. You must reserve one hour with the
instructor for this process
- Demonstrate your final product.
- Present your design and implementation (5 minutes).
The group is responsible for assembling a short presentation
consisting of 3-4 slides (on computer or in printed
form). Describe the design including:
- which team members were responsible for what
components (including the personal programming components),
- the circuit,
- your software solution for transforming the
analog value read from the sensors into the returned
integer,
- your software solution for displaying sensor
values,
- DO NOT include low-level code (only give
the general logic and equations)
Notes:
- All team members must be able to speak to the hardware
and software solutions.
- At the end of this meeting, you will have your group
grade (individual grades will be assigned at a later time).
- Code: Check your documented code to your
subversion repository.
- Circuit diagram: check your circuit diagram into your
subversion repository. This diagram must be developed
using EagleCad (see the downloads page).
- Presentation: check a copy of your powerpoint
presentation into the subversion repository.
- Personal report: One submission per person through CATME.
Grading
Group grade distribution:
- 35%: Project implementation
- 30%: Demonstration/presentation of working project (to either
of the TA or the instructor)
- 35%: Code documentation and group report
Grades for individuals will be based on the group grade, but weighted
by the assessed contributions of the group members.
fagg [[at]] cs.ou.edu
Last modified: Sat Apr 23 22:32:54 2011