Search This Blog

Showing posts with label FSM. Show all posts
Showing posts with label FSM. Show all posts

Tuesday, 22 April 2014

Introducing an Arduino Finite State Machine library to PenguinBot

First off... PenguinBot was featured on Hackaday last Friday!  Yay!




I spent a little time yesterday reworking my code for PenguinBot to replace the homemade state machine of  If/then/else and switch statements, with a proper State Machine Library.

I specifically chose the SMLib  State Machine Library because of it's simplicity and lack of overhead.   I know there are other full featured State Machine Libraries out there, but this one serves the purpose well. 

After all..  it's just a penguin that avoids obstacles...  most of the time... more or less... 

This library allows you to have a Head and Body for each state.  
The "Head" is for initializing variables each time you transition into the state.  The "Body" contains the actions that are to take place each time you loop through the state,such as incrementing a counter, or checking a sensor.



Regardless of whether you are in "Manual Mode", "Object Avoidance Mode", or the yet to be implemented "Light Following Mode",  State Machine "m1" controls motion. 
The states are as follows:
1 = Stopped
2 = Forward
3 = Avoiding Obstacle
4 = Turn Left
5 = Turn Right
6 = Reverse
The Global Variable "MotionStop" dictates how much time is spend in any state.


Current (imperfect) code here:



Setup and Initialization of the State Machine :
/************************************************************************/
#include <SM.h>
SM m1(m1s1h, m1s1b);  //Initialize state machine m1 with head and body

setup(){
// There is nothing in setup related to SMLib
}

//  Typical events to manage an Autonomous Robot in the main loop
void loop() {
  get_sensors(); // Read all sensors, like proximity, etc...
  EXEC(m1); // Execute the State Machine
  provide_feedback(); // Send status updates via Serial
  get_serial(); // Grab commands from Serial
}
/**************************************************************************/



This is the FSM.ino module:
/**********************************************************************/
State machine m1 is for motion control
the states are as follows:
1 = Stopped
2 = Forward
3 = Avoiding Obstacle
4 = Turn Left
5 = Turn Right
6 = Reverse
MotionStop dictates how much time is spend in any state.
*/

State m1s1h(){ // m1s1 is --- Motion:Stopped
  Serial.println("Motion:Stopped (State 1)");
  halt();
}//m1s1h()

State m1s1b(){
  if(m1.Timeout(5000)){ // Maximum 5 seconds idle time
    m1.Set(m1s2h, m1s2b);
    Serial.println("changing to Motion:Forward (State 2)");
  };
}//m1s1b()

State m1s2h(){ // m1s1 is --- Moving Forward
  Serial.println("Motion: Forward (State 2)");
}//m1s2h()

State m1s2b(){
  if(m1.Timeout(MotionStop)){ // Maximum 5 seconds forward motion
    m1.Set(m1s1h, m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
  
  if(Distance > MINDIST){
   BlinkM_fadeToRGB( blinkm_addr, 0,255,0 ); // Display GREEN Status
   forward();
  } else {
    Serial.println("changing to Motion:Avoid Obstacle (State 3)");
    m1.Set(m1s3h, m1s3b);
  }
}//m1s2b()

State m1s3h(){ // m1s3 is --- Avoid Obstacle
  Serial.println("Motion: Avoid Obstacle (State 3)");
  MotionStop = 200; // Set turn time for Obstacle Avoidance
  BlinkM_fadeToRGB( blinkm_addr, 255,0,0 ); // Display Warning RED

}//m1s3h()

State m1s3b(){
  if(DistLeft > DistRight){
    m1.Set(m1s4h, m1s4b); // Change State to Left Turn
  } else if(DistRight > DistLeft){
    m1.Set(m1s5h, m1s5b); // Change State to Right Turn
  } else {
    Serial.println("changing back to Motion:Stopped (State 1)");
    m1.Set(m1s1h, m1s1b);
  }
}//m1s3b()


State m1s4h(){ // m1s1 is --- Turning Left
  Serial.println("Motion: Left Turn (State 4)");
}//m1s4h()

State m1s4b(){
  if(m1.Timeout(MotionStop)){ // Maximum 5 seconds forward motion
    m1.Set(m1s1h, m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
   BlinkM_fadeToRGB( blinkm_addr, 0,255,0 ); // Display GREEN Status
   left();
}//m1s4b()


State m1s5h(){ // m1s1 is --- Turning Right
  Serial.println("Motion: Right Turn (State 5)");
}//m1s5h()

State m1s5b(){
  if(m1.Timeout(MotionStop)){ // Maximum 5 seconds forward motion
    m1.Set(m1s1h, m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
   BlinkM_fadeToRGB( blinkm_addr, 0,255,0 ); // Display GREEN Status
   right();
}//m1s5b()


State m1s6h(){ // m1s1 is --- Reverse
  Serial.println("Motion: Reversing (State 6)");
}//m1s6h()

State m1s6b(){
  if(m1.Timeout(MotionStop)){ // Maximum 5 seconds forward motion
    m1.Set(m1s1h, m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
   BlinkM_fadeToRGB( blinkm_addr, 0,255,0 ); // Display GREEN Status
   reverse();
}//m1s6b()

/************************************************************************/


MotionStop duration is typically set either as a Serial Command parameter when in Manual Mode, or predetermined time lapse for "In Motion" or "Idle"  when in Autonomous Mode (Either Obstacle Avoidance or Light Following).

The exception to this, is in State 3 --- Avoid Obstacle.  In this state, it has been determined that an obstacle blocks the way ahead, and an assessment is done as to whether there is more room to the left or to the right.   "MotionStop" duration is set up to allow just enough time to turn the bot roughly 90 degrees. "MotionStop = 200; // Set turn time for Obstacle Avoidance"


Over the next few days, I will convert the rest of the code to use this library, and show the "Light Following Mode"



References:
Arduino-Pi: Of Finite State Machines and Robotics
https://github.com/michaeljball/PenguinBot

Arduino Playground: A novel and relaxed view on finite state machines
http://playground.arduino.cc/Code/FiniteStateMachine
Robot Virtual Worlds – Maze Crawler
Embedded Micro:  basic FSM to control a very simple robot.
http://www.mathertel.de/Arduino/FiniteStateMachine.aspx
http://hacking.majenko.co.uk/finite-state-machine


Wednesday, 26 February 2014

Of Finite State Machines and Robotics...

My friend Jon Hylands had asked a simple question the other day as to whether I used Finite State Machines in my code.

 I quipped back "Of course! Is there any other way?" 

But that got me to thinking... Was this always the case?  And where did trial and error coding end and planned FSM begin?  In discussions with a few other Hobby Roboticists, it became evident that the concept of a "Finite State Machine" was not fully clear.

The Academics in the group (who probably wouldn't read my tripe anyway) would tell you that a Finite State machine is " a mathematical model of computation used to design both computer programs and sequential logic circuits. It is conce......"   blah blah blah......


 Flow chart and a set of tables!    A finite state machine is simply a flow chart that follows a table of possible states that something can be in, provided the appropriate inputs.

I know many of us, myself included start off small projects just stringing pieces of code together to "get a result".  But very soon into the project you realize that you need to define the parameters of operation. 

For instance, in a very simple "Object Avoidance Robot"  that just wanders around and makes sure it doesn't hit things, there is still a State Machine to follow. 


Following the above routine, you should be able to wander until your battery dies...  Not very smart, not very useful, but predictable.

Even if you have a "Fully Manual Control" Robot, ie: you have some means of providing motion commands and expect it to react in kind,  you likely still have some kind of logic flow (State Machine) running to manage this motion.

For  instance something as "Simple" as a "Move Forward to position X,Y" command might *really* look like this:


When you start drawing out the various "states" of your Robot:
  • Stopped
  • moving forward - forward path clear - left side clear - right side clear
  • moving forward - obstacle in distance - left side clear - right side clear
  • moving forward - obstacle in near - left side clear - right side clear
  • moving forward - forward path clear - left side blocked - right side clear
  • moving forward - obstacle in distance - left side blocked - right side clear
  • moving forward - obstacle in near - left side blocked - right side clear
  • etc...
You will realize just how convoluted this can get... This is where a "State Table" comes in handy.

Turn each question into a Yes or No, True or False.  Don't ask "how far is it to the obstacle?"  but rather "Is there an obstacle within 12cm?" 


In this table, we are simply looking forward, not side to side.  Is our path clear, or is there an object present, distant or near, and is our compass heading on target or drifting Clockwise or Counter Clockwise.
The answers to these Boolean states will drive the left and right wheel Direction and Speed.

Creating State Tables for your various routines will help you better understand the expected outcomes based on inputs.  You may suddenly understand why your Robot keeps getting trapped in that corner!


References:
http://playground.arduino.cc/Code/FiniteStateMachine
http://en.wikipedia.org/wiki/Finite-state_machine