Project 4: Finite State Machines and Control

All components of the project are due by Thursday, April 24th at 9:00pm.

Project Goals

At the end of this project, you should be able to: Your group may choose:
Option 1 (height sensing and control), or
Option 2 (mission sequencing)

Hardware Overview

The heli hardware is the same as the previous projects. Remember that we have a total of two test stations and three heli stations.

Option 1

Each test station is equipped with a Devantech sonar sensor unit and a compass. Each sonar unit is equipped with a sonic transmitter which will emit a small burst of clicks. The receiver will sense the signal that has reflected off of some surface. By measuring the return time of the signal, we can infer the distance to the surface.

For the heli stations, the sensors will be placed underneath the helicopter (facing upwards). This way, we can measure the height of the heli at a given time. We will then use this information to hover the heli at some specified height.

The interface to the sonar unit is as follows:

  • White wire: sonar command.
    • Bring this line high for at least 10 usec.
    • Once it is returned to a low state, the sonar will produce a brief sonic burst.

  • Green wire: signal return.
    • Once the sonic burst is complete, this line will be brought high by the sonar itself.
    • When the reflected signal is sensed, this line will be brought low (between 100 usec and 18 msec later).
    • If no reflected signal is observed, the sonar will timeout and bring this line low at about 36 msec.

Note: After the reflected signal has been detected, you must wait for at least 10 msec to command the next sonic pulse.

Hint: consider using a counter that tells the ISR "what time it is" (e.g., when to emit the burst, when it is measuring the pulse, and when it is waiting before it emits the next pulse).


Project Overview: Option 1

For this project, you will modify your microcontroller circuit to provide an interface to the sonar unit. In addition, you will be writing the software that is necessary to control the sonar and interpret the signals that you receive from it.

Note: in the next project, you will be integrating the current work into the helicopter control problem (and doing height and yaw control simultaneously). So: do not completely dismantle your project 2 implementation.

In brief, your code must:

  • Continuously sense the distance to an object
  • Indicate the distance using your "velocity" LEDs
  • Maintain a specified height


Project Components

All components are required to receive full credit for the project.

Part 1: Microcontroller Circuit

Designate one additional input pin and one output pin. Wire these into the sonar connector.

Part 2: Sensing Distance

Implement an interrupt service routine (e.g., one that is attached to the timer0 overflow interrupt) that does the following:
  • Produces the appropriate sonar control signal.
  • Measures the duration of the pulse from the sonar.
  • Once the pulse is measured, places the resulting value into the global variable:

    int16_t global_duration;

    If there is an error, this variable should be set to a value of -1.

Part 3: Height Display

  • Implement a function:

    int16_t get_height(void)

    That will safely read the value from global_duration and return the height of the craft (i.e., measured distance) in millimeters.

    By safely, we mean that this variable should be accessed without being interrupted by the ISR.

  • Display the height using the "velocity" LEDs.

Part 4: Height Control

Given a height goal, alter the throttle command until the current height matches the height goal.

Because your throttle command is not an acceleration (due to the dangling wire), an appropriate implementation is to use an integration controller.

Your main loop will look something like this:

void main_loop(void) {
       float throttle_command = 60.0;

       while(1) {
           heading_last = heading_current;
           heading_current = get_heading();
           heading_error = compute_error(heading_current, heading_goal);
           heading_derivative = compute_derivative(heading_current, heading_last);
           height_current = get_height();

           if(#COMMAND INPUT LINE#) {
                display_orient(heading_current);
           }else{
                display_orient(heading_error);
           }
           display_derivative(heading_derivative);
           yaw_command = yaw_center +
                     Kp * heading_error - Kv * heading_derivative;
           set_yaw(yaw_command);

           
           throttle_command += Ki * (height_goal - height_current);

           #Insert code to ensure that throttle_command remains in the valid limits#

           set_throttle((uint8_t) throttle_command);

           delay_ms(100);
       }
}

Project Overview: Option 2

For this project, you will modify your microcontroller circuit to extend your external command interface (right now, you have 1 switch to control the orientation display). We will use this command interface to trigger a sequence of height and orientation subgoals.

In brief, your code must:

  • command the heli to maintain the designated target orientation and target throttle,
  • respond to new commands by changing the orientation and throttle targets in the specified manner (using a finite state machine),
  • indicate the current state of your FSM using a set of 3 LEDs (you may use your existing LEDs for this).
Note: I am explicitly using the term "target" to mean something different than your "goal". Here, we are using "goal" to mean the value that the PD or I loops are maintaining, and we are using "target" to mean the value that we would like these goals to be at. Targets may be changed arbitrarily by your FSM code. Goals will be changed slowly to eventually reach the targets. This difference will allow us to make smooth transitions from one orientation to another.

Project Components

Part 1: Microcontroller Circuit

  • Designate two digital input pins (you have one already). Call these C1 and C0 (you may choose any 2 pins).
  • Connected these pins to +5V through a 10K ohm resistor (one resistor for each pin).
  • Connect a switch from each of these pins to ground. The pin will read "0" or "1" depending on whether the switch has been pressed.

Part 2: Finite State Machine

Implement in software the following finite state machine:

  • Assign each state a unique integer between 0 and 5
  • Events:
    • Numbers indicating events represent the state of the input command lines C1 and C0 (and "xx" means any value for each)
    • th:goal -> throttle_goal is equal to throttle_target (within some tolerance)
    • heading:goal -> heading_goal is equal to heading_target (within some tolerance)
  • Actions:
    • th:off -> set the throttle_target to off
    • th:low -> set the throttle_target to a low level (high enough to barely hover)
    • th:med -> set the throttle_target to a medium level (~50cm)
    • th:high -> set the throttle_target to a high level (~100cm)
    • heading:N -> set the heading_target to North
    • heading:cur -> set the heading_target to the current heading
    • heading +=90 -> change the heading_target by 90 degrees
  • Display the state of the FSM using a set of 3 LEDs (you may re-use your LEDs)

Note: Develop and test FSM before you move on to the next part (you will have to fake the orientation and throttle goal events)

Part 3: Finite State Machines for Control

Control the craft using this FSM.

Here is a starting point for your main loop:

void main_loop(void) {
       int16_t heading_target = get_heading();
       int16_t heading_goal = heading_target;
       float throttle_target = 50.0;              // Throttle off
       float throttle_goal = throttle_target;

       while(1) {
           
           // Finite state machine control
           switch(state)
           {
               case STATE_WAIT:
                     :
                     :
                  break;
               case STATE_LAND:
                     :
                     :
                  break;
               :
               :
           }
           // Move the throttle goal a little closer to the target
           throttle_goal += K_throttle * clip(throttle_target - throttle_goal, max_throttle_step);

           // Move the heading goal a little closer to the target
           diff = heading_target - heading_goal;
           if(diff > 1800)
               diff -= 3600;
           if(diff < -1799)
               diff += 3600;
           heading_goal += K_heading *  clip(diff, max_heading_step);

           

           // Get current heading and compute PID control signals

           heading_last = heading_current;
           heading_current = get_heading();
           heading_error = compute_error(heading_current, heading_goal);
           heading_derivative = compute_derivative(heading_current, heading_last);

           if(#COMMAND INPUT LINE#) {
                display_orient(heading_current);
           }else{
                display_orient(heading_error);
           }
           display_derivative(heading_derivative);
           yaw_command = yaw_center +
                     Kp * heading_error - Kv * heading_derivative;
           set_yaw(yaw_command);
           
           set_throttle((uint8_t) throttle_goal);

           delay_ms(100);
       }
}


/*
  int16_t clip(int16_t error, int16_t max_error)

  Clip the error to a value that is between -max_error and max_error

*/

int16_t clip(int16_t error, int16_t max_error)
{
       if(error > max_error)
          return(max_error)
       if(error < -max_error)
          return(-max_error)
       return(error);

}


References


What to Hand In

All components of the project are due by Wednesday, April 18th at 5:00pm.

Grading

Group grade distribution:

Grades for individuals will be based on the group grade, but weighted by the assessed contributions of the group members.


fagg [[at]] ou.edu

Last modified: Tue Apr 15 09:58:57 2008