Chief Delphi

Chief Delphi (http://www.chiefdelphi.com/forums/index.php)
-   Programming (http://www.chiefdelphi.com/forums/forumdisplay.php?f=51)
-   -   Share you Autonomous (http://www.chiefdelphi.com/forums/showthread.php?t=129256)

_untitled_ 03-05-2014 14:56

Re: Share you Autonomous
 
We used to hardcode our autonomous routine in C++, but we found that compilation and deploy times added too much overhead and made it hard to quickly change routines on the fly.

To address this issue, we tried a scripted approach this year to autonomous. The first iteration of our "scripting" approach was a simple list of commands that the C++ codebase interpreted step-by-step:
Code:

# sample one ball auto
drive(72.0)
collector_move(false) # collector down
fire()

For parallel routines to run (e.g. parallelize loading and turning), we had a parallel command:
Code:

parallel(load(), turn(30.0))
This worked out pretty well for us; it provided the speed and convenience that we wanted for fast, on-the-fly editing. However, when we tackled a hot-goal routine, we realized that the lack of control flow structures or loops resulted in the software team having to go back to the C++ codebase to add new routines in C++, negating the speed and convenience benefits that we achieved.

We looked at a lot of possibilities, and we decided on Lua, a scripting language popularly used in video games where processing has to finish in 20-30 millisecond time steps. As a plus, an open-source ANSI-C interpreter was available. We put together a quick prototype, exposing C++ functionality to Lua using its aliasing feature.

However, we quickly realized that an issue inherent to using Lua (or any other general-purpose scripting language, for that matter) was that we lost the step-by-step and parallelization functionalities. At the same time, we didn't want to have to pause execution of the main robot loop to run our autonomous routine or have to coordinate multi-threaded execution. It turns out that Lua's coroutine functionality is perfect for achieving this functionality. When the Lua code calls a C++ function, the function pauses execution of the script and keeps a tab on the requested function. On each update of the script interpreter, the function is checked for completion, and upon completion, the script is resumed and is run until the next pause or the end of the script. This is great because from the script's perspective, everything is clean and nice and you don't have to deal with waiting for routines to complete and whatnot.

The next issue we wanted to address was running parallel routines. To do this, we created two functions, beginActionGroup(name:string) and endActionGroup(). If calls to C++ functions are made between these two functions, the script delays execution and instead adds the routine to a list instead of running the routine immediately (and yielding execution of the script). When the action group is run, each of the sub-routines are run in parallel. To run action groups in parallel, the function runParallel(group1:string, group2:string, group3:string, ...) is called and behaves like any other routine (e.g. script execution is paused until completion).

With these issues solved, we were able to write the following for our double ball hot routine:

Code:

--[[
DoubleHot.lua

Autonomous routine for firing a two hot goals.
The robot starts in the center and drives into
the colored zone for mobility points, turns
towards the first hot goal, fires, reloads, then
turns to the other hot goal and fires.
--]]

require "common"

-- == CONFIGURABLE CONSTANTS == --
GOAL_SIDE_PADDING = 48.0 -- inches, distance from center of goals
SHOOTING_RANGE = 138.0 -- inches, distance to shoot


-- == Routine == --
routineStartTime = GetTimeMillis()

TURN_ANGLE = math.asin(GOAL_SIDE_PADDING / SHOOTING_RANGE) * 180.0 / math.pi -- calculate turn angle from params
DRIVE_DISTANCE = 216.0 - math.sqrt(SHOOTING_RANGE * SHOOTING_RANGE - GOAL_SIDE_PADDING * GOAL_SIDE_PADDING)

-- -1: Left
--  0: Center
--  1: Right
heading = 0 -- neutral position

function defaultTwoBall()
        turn(TURN_ANGLE)
        fire(true)
        collect()
        loadLauncher()
        collectorMove(true)
        pause(0.5)
        fire()
end

function shootHotGoal()
        local side = getHotGoalSide()
        local holdDown = true

        if(heading == 0) then
                if side == 0 then
                        return false
                end
        elseif heading == -1 then
                side = 1
                holdDown = false
        elseif heading == 1 then
                side = -1
                holdDown = false
        end

        local turnAngle = side * TURN_ANGLE * -1

        turn(turnAngle)
        fire(holdDown)

        heading = side

        return true
end

function centerRobot()
        local turnAngle = (0 - heading) * TURN_ANGLE * -1

        turn(turnAngle)
end

turn(0.0)
dribbleDrive(-1 * DRIVE_DISTANCE, 1.0)
collectorMove(true)

if shootHotGoal() then
        collectorMove(true)
        centerRobot() -- back to neutral pos to pick up ball
        collect()
        loadLauncher()
        collectorMove(true)
        pause(0.5)
        shootHotGoal()
else
        defaultTwoBall()
end

BufferedPrint("Routine completed in "..((GetTimeMillis() - routineStartTime) / 1000.0).."s.")

The above example doesn't show off parallelization, so here's an example of our ball dribbling routine in Lua:

Code:

function dribbleDrive(distance, waitTime)
        beginActionGroup("drive")
                pause(waitTime)
                drive(distance)
        endActionGroup()

        beginActionGroup("dribble")
                dribble()
        endActionGroup()

        runParallel("drive", "dribble")
end

Some post-season projects we're considering include a LabView-esque drag-and-drop editor for Lua, and we're also pursuing the possibility of using Lua to code some of our subsystems next year to lower the difficulty barrier for new members. :)

SoftwareBug2.0 03-05-2014 20:04

Re: Share you Autonomous
 
Seeing all the examples here makes me realize that there are major differences not only in coding styles or technologies used but also in the logic implemented. I wonder how much what teams do in autonomous is influenced by the style in which it's written.

At each step in our autonomous program we had multiple things that could happen. Basically, there was:
1) What if autonomous mode ends?
2) What if you're running out of time in autonomous mode?
3) What if the normal action of the step has completed?
4) What if none of the above have happened?

This seems like it would be horribly awkward to code in the styles that most people appear to be using but it was no big deal for us since we had an explicit state machine. It seems weird to me that even teams that care enough to embed a Lua interpreter or write their own scripting language to run their autonomous modes don't seem to be doing anything like this.

_untitled_ 03-05-2014 23:02

Re: Share you Autonomous
 
My answers are in bold red:

Quote:

Originally Posted by SoftwareBug2.0 (Post 1382381)
1) What if autonomous mode ends?
The Lua interpreter is just like any other routine in our C++ codebase, and our routine system allows for graceful abortion of a routine mid-execution. When auto ends, the Lua routine is aborted, and the system restores the robot to a clean state.
2) What if you're running out of time in autonomous mode?
The above routine I posted does not implement this (although the defaultTwoBall logic is similar), but for our single hot routine, we keep track of the start time, and if we detect that the time elapsed has exceeded some value before a side has been detected, the routine defaults and shoots.
3) What if the normal action of the step has completed?
If the normal action of the step completes, the script resumes and runs the next instruction.
4) What if none of the above have happened?
This is a pretty generic question. If you mean to ask if a step doesn't successfully complete, you can add a time-out condition to abort the routine and forcefully continue the script. The logical control structures that Lua provides make it very easy to address these situations.


SoftwareBug2.0 04-05-2014 00:52

Re: Share you Autonomous
 
Quote:

Originally Posted by _untitled_ (Post 1382421)
My answers are in bold red:

What I was trying to get at with the questions was that our next state logic looked like this:
Code:

State next_state(State current_state){
        switch(current_state){
                case A:
                        if(!autonomous_mode) return W; //#1
                        if(almost_out_of_time) return X; //#2
                        if(ready) return Y; //#3
                        return Z; //#4
                case B:
                        ...
        }
}

I hope that clears things up.

Does your robot always do the same thing on abortion of the Lua script? It seems like it should be possible to make it do different things since you could call some C++ function to set a variable to remember what mode you should go to after the script finishes.

_untitled_ 04-05-2014 01:16

Re: Share you Autonomous
 
Quote:

Originally Posted by SoftwareBug2.0 (Post 1382441)
Does your robot always do the same thing on abortion of the Lua script? It seems like it should be possible to make it do different things since you could call some C++ function to set a variable to remember what mode you should go to after the script finishes.

Does our codebase do it? No; we have an event-driven routine system, and when the autonomous-end event is fired, the autonomous routine (and the underlying Lua scripting service) is aborted if it is still running. We have three modes: disabled, autonomous, and teleop. When autonomous ends, it releases its handles on the robot's resources, allowing other routines to run as well as driver input.

Is it possible? Sure! Lua is really flexible in that you can call into C++, so yes, you would be able to call a C++ function to set a flag, and after autonomous your code could read that flag and act upon it.

JohnGilb 05-05-2014 19:02

Re: Share you Autonomous
 
We implemented a simple state-machine framework this season. It was used in both teleop and autonomous.

It worked as such:

States (which were a lot like commands) could return SUCCESS, FAILURE, or NOT_DONE.

StateMachines would add a number of states into them, then map the state transitions. It would look something like

Code:

int one = new DetectHotState(); //returns SUCCESS for left, FAILURE for right
int two = new DriveLeftState();
int three = new DriveRightState();
int four = new FireCatapultState();

machine.addSuccess(one, two);
machine.addFailure(one, three);
machine.addSuccess(two, four);
machine.addSucecss(three, four);

At runtime, the state machine framework would execute the relevant states. This let us write autonomous (and teleop) programs with a high degree of reuse.

Arhowk 06-05-2014 17:44

Re: Share you Autonomous
 
Here's our 2-ball script. Works off of a custom JavaFX text editor on the developer laptop than is FTP'd and saved on the crio in raw binary format.
Might open source it before 2015, not sure.


Code:

Auton: 2 MoveKickTargeting

DS{
    2 Kick And Move
    Kicks loaded
    Drives
    Uses Camera
    Without PID Drive
}



Procedure{

        shiftLow
    snapshot
    upperStructureManual at -0.2 for 0.25
    kickerBackward at 0.17 for 0.25
   
    after 1
            intakeDown
        kickerForward at 0.1 for 0.25
       
    if startpointCenter
            at 2
            driveForward at 0.75 for 1.5
            if toTheLeft
                    at 5
                    driveTurnRightPID at 45 for 2
          else
                    at 5
                    driveTurnLeftPID at 45 for 2
            endif
            at 8
                    upperStructureManual at -0.2 for 2
                kickerForward at 1 for 0.5
    else
            if inTheCenter
                at 2
                        driveForward at 0.75 for 1.5
                after 1.5
                        upperStructureManual at -0.2 for 2
                    kickerForward at 0.84 for 0.5
        else
            at 2
                    driveForward at 0.75 for 1.5
            after 3
                    upperStructureManual at -0.2 for 2
                kickerForward at 0.84 for 0.5
        endif
    endif

}

not going to explain how the flow is layed out as iits a grammatical language. Some of the indenting and spaces got messed up when copying the code, I need to redo the text editor used thats attached to the deploy program.

Indenting doesnt actually matter, but linebreaks do. The way the commands work is as follows

Code:

public class RequestPeriodic
    public static void processRequest(String processId, double arg)
          if(processId.equalsIgnoreCase("driveForward"){
              drive.arcadeDrive(1,0,false);
          }
    }
}

note that thats psuedocode and not actually how its implemented, but its very close. (its not a string, its an enum) Conditionals are the same, except the function returns a boolean. (One of the things also on my agenda is cacheing conditions to prevent erratic behavior)


All times are GMT -5. The time now is 23:57.

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