Autonomous State Machine: Abstract Inner Classes?

Hello all.

The season is over for my team, and though next year is quite a ways off, I’m already looking ahead to next season. One question that has been weighing on my mind is the best way to actually go about implementing the state machine for autonomous in Java.

The most common way, based on the code I’ve seen posted here, seems to be some kind of large if-else if structure, or a switch block. We took this route for the code at our first regional. I personally have always found this method difficult to read and maintain.

The way we did it at our second regional was to create an inner abstract class with one abstract method, getNextState(), then in the body of the method we put the actions for that state. We then put the states into a hashtable with the name of the state as the key. Here is an example (not from our final code, and with the boring repetitive bits removed for brevity and ease of reading):


public class StateTestAutonomousFF implements AutonomousFF {
    
    // snip...
    
    /** Autonomous state, put actions in the getNextState() method. */
    private abstract class State{
        public abstract State getNextState();  
    };
    
    /** String-keyed table of all the states */
    private Hashtable states;
    
    /** Stores the current Autonomous state */
    private State currState;
    
    // snip...
    
    /**
     * The basic autonomous routine: Raise the arm, drive straight to the wall 
     * then hang the tube.
     * 
     * @return The first state in the basic autonomous routine. calling method
     *  should set currState equal to the returned state.
     */
    private State loadBasicSM(){
        states = new Hashtable();

        // snip... Example State:
        states.put("raiseArm", new State(){ public State getNextState() {
            // return next state when the arm is raised
            if(arm.raiseArm(RobotArmFF.TOP)){
                return (State)states.get("driveToWall");
            } 
            return this;
        }});

        // snip...
        
        return (State)states.get("firstState");
    }
    // snip...
    
    /** Call once per autonomous loop */
    public void periodicUpdate(){
        if(currState != null){
            currState = currState.getNextState();
        } else {
            stopEverything();
        }
    }
    
    // snip...

}// End class

To me this code is much more readable and self documenting, and adding a new state or even a whole new state machine is relatively easy. I am curious what the other Java programmers on Chief Delphi think. Do you find this method easier/harder to read? Do you have other criticisms? Has anyone done anything similar? Has anyone done anything better?

1 Like

We have an abstract class called “State” that gets extended by our various states (DriveForDistance, TurnToAngle, GoToArmPosition, ScoreTube, Wait, etc.)

I would suggest making your abstract inner class into its own “outer” class in its own file. Then, rather than anonymous inheritance, create a new concrete class for each distinct state. The advantage of this, IMO, is twofold:

  1. It makes debugging and tracing your code easier.

  2. It facilitates easier/more elegant version control (less likelihood of conficts, and more obvious where the changes were between versions)

My team did something very similar for our solution to autonomous mode this year. Our state machine is built around the IterativeRobot philosophy - writing code that is designed to be executed in periodic loops. Here is what our State class looks like:


public abstract class State
{
    private String mName;

    protected State(String aName)
    {
        mName = aName;
    }

    public String toString()
    {
        return mName;
    }

    public void enter()
    {

    }
    
    public abstract void running();

    public abstract boolean isDone();

    public void exit()
    {

    }
}

And here is the code that calls the various periodic functions of the current State:

public class StateMachine
{
    State] mStates;
    int mCurrentState;
    boolean mStarted;

    public StateMachine(State] aStates)
    {
        mStates = aStates;
        mCurrentState = 0;
        mStarted = false;
    }

    public void run()
    {
        if( mCurrentState < mStates.length )
        {
            if( !mStarted )
            {
                mStates[mCurrentState].enter();
                mStarted = true;
            }
            else if( mStates[mCurrentState].isDone() )
            {
                mStates[mCurrentState].exit();
                System.out.println("Exiting state: " + mStates[mCurrentState]);
                mCurrentState++;
                if( mCurrentState < mStates.length )
                {
                    mStates[mCurrentState].enter();
                    System.out.println("Entering state: " + mStates[mCurrentState]);
                }
                else
                {
                    System.out.println("Finished state machine.");
                }
            }
            else
            {
                mStates[mCurrentState].running();
            }
        }
    }
}

StateMachine.run() is called by the autonomousPeriodic() function that our main class overrides from IterativeRobot.

The last nifty feature is that when autonomous mode begins, our robot looks on its filesystem for a file called “autonomous.txt”. This is a list of states (and their arguments, such as speeds or positions) that we edit over FTP from our driver station (using any standard text editor - we like Notepad++ for the built-in FTP integration). This is a nice feature to have once each state works correctly, but you need to tweak things like distances or angles - we can edit our autonomous code without re-compiling.

I like the idea with your enter() and exit() methods, I was using a boolean flag for initializing states, I like your way better.

One minor critique is that the way you have it set up there is no way to conditionally change a state. This year it wasn’t needed, but I keep hoping for a game where the autonomous routine changes based on field conditions which aren’t determined before the start of the match. :smiley:

As to inner vs. outer classes: The number and the size of the states was why we decided to use inner classes. All told we had about 20 different states, on various machines, most of which consisted of 5 to 10 lines (Most were testing states, our final routine had 5 states). This seems like an awful lot of outer classes.

Ease of use for version control is certainly a strong argument. This year our team didn’t use any formal version control software, but I’m pushing for it for next year. If that happens there is a good chance we’ll go with outer classes.