After our successful but not stress free demonstration of ARPool at the Ontario Centers of Excellence Discovery Show I took it upon myself to make a rather large structural change to the ARPool program in an effort to avoid similar problems in the future. I wanted to change ARPool from an event driven program to what I refer to as a state machine, a technique I learned through my involvement in First Robotics.
If you read my last blog post on our experience at OCE you’ll know that one of our main problems with the system was difficulty understanding the order in which code was called and our lack of control over changing this process. The ARPool system relied on several timer events as well as signals and slots in an interrupt fashion to get things done in the right order. If things are not executed in the right order then the system simply has no chance – for example if the projector doesn’t illuminate the table before we try to detect the pool balls then it won’t be able to find them. We had many problems with the system becoming out of sync and the whole experience felt rather unstable even after the system was working.
Now don’t get me wrong it’s not that I think event driven programs are bad – they aren’t and in many applications it is the only possible way to make something work, it’s just that in the case of ARPool it doesn’t make any sense. ARPool is a program that only needs to do one thing at a time and it all needs to happen in the right order – so why bother with all the event driven stuff? The final point is that being a University lab we don’t have the resources to maintain a system that complicated and many people who have and will work on the system don’t have the required background to effectively work on such a system. ARPool needed an overhaul.
Introducing the state machine: It is really quite a simple idea - there is a main loop and inside this loop there is a single switch case block. The switch is executed on a “state counter” variable which for extra points should be made an enum with relevant state names. Inside each state (a case block) the program does its thing and then if appropriate changes the state counter to the next state or leaves the state variable as it is. Here is a simple example:
enum State
{
detectBalls,
trackCue
};
State state = detectBalls;
while(true)
{
Switch(state)
{
Case(detectBalls):
detectBalls();
state = trackCue;
break;
Case(trackCue):
// code
break;
// and so on…
}
}
It’s probably not the first you thing you would think of but after you try it out it’s a pretty nice way to do things. I picked up on this approach earlier this year while working on programming the CRio device for First Robotics – see First Robotics has these built in safety functions around your robot class if your robot’s main function doesn’t finish executing at least several times a second it assumes that you’ve either lost communication or wrote an infinite loop. You’re basically forced into this state machine idea if you want to do anything complicated.
Anyways First’s API and safety functions are a topic of a different rant how did all of this turn out for ARPool? Well surprisingly well it only took me about 2.5 hours of straight coding to completely pull out the Qt gui, remove all the event driven code and re-write the main program to utilize the state machine concept. It compiled and the code was way cleaner, shorter and I could finally see where and in what order my vision functions were called. All in all a great victory – however it was time to return to my other projects and leave my shiny new ARPool program sitting there waiting to be tested on the real system.
*edit* 09/01/2012 I finally got my shot to try the new ARPool code, it actually worked! Every programmer has a few moments they are proud of and this was definitely one of mine, re-structuring an entire program (like major major stuff here) not being able to test it for months and it actually just worked! Not every day gets to be like this.