Go to Post When everyone is exactly even, who do you look up to for ideas and inspiration? - artdutra04 [more]
Home
Go Back   Chief Delphi > Technical > Programming > Java
CD-Media   CD-Spy  
portal register members calendar search Today's Posts Mark Forums Read FAQ rules

 
Reply
Thread Tools Rate Thread Display Modes
  #1   Spotlight this post!  
Unread 03-30-2011, 04:16 PM
Ben_R_R's Avatar
Ben_R_R Ben_R_R is offline
Assistant Programing Mentor
FRC #2194 (Fondy Fire)
Team Role: Mentor
 
Join Date: Jan 2008
Rookie Year: 2007
Location: WI
Posts: 21
Ben_R_R is an unknown quantity at this point
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):

PHP Code:
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?
__________________
2194 Alumni and Mentor

Last edited by Ben_R_R : 03-30-2011 at 05:03 PM.
Reply With Quote
  #2   Spotlight this post!  
Unread 03-30-2011, 04:58 PM
Jared Russell's Avatar
Jared Russell Jared Russell is offline
Taking a year (mostly) off
FRC #0254 (The Cheesy Poofs), FRC #0341 (Miss Daisy)
Team Role: Engineer
 
Join Date: Nov 2002
Rookie Year: 2001
Location: San Francisco, CA
Posts: 3,069
Jared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond reputeJared Russell has a reputation beyond repute
Re: Autonomous State Machine: Abstract Inner Classes?

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:

Code:
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:

Code:
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.
Reply With Quote
  #3   Spotlight this post!  
Unread 03-30-2011, 06:03 PM
Ben_R_R's Avatar
Ben_R_R Ben_R_R is offline
Assistant Programing Mentor
FRC #2194 (Fondy Fire)
Team Role: Mentor
 
Join Date: Jan 2008
Rookie Year: 2007
Location: WI
Posts: 21
Ben_R_R is an unknown quantity at this point
Re: Autonomous State Machine: Abstract Inner Classes?

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.

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.
__________________
2194 Alumni and Mentor
Reply With Quote
Reply


Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump


All times are GMT -5. The time now is 08:54 AM.

The Chief Delphi Forums are sponsored by Innovation First International, Inc.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi