Creating Flexible, Script-Controlled
Autonomous Software
My Name: Chris Hibner
Mentor FRC 51 - Wings of Fire
Control
Systems Engineer
Involved with FIRST for a LONG time
Plenty of industry experience
Cool Degrees (Go Blue!)
chiefdelphi.com: “Chris Hibner”
Why should I care about this?
1. Flexibility: do you really know what the
optimal autonomous routine is?
2. Fast changes: no need to compile and
deploy code.
3. “Building Block” method: easy to break the
problem into small, manageable chunks.
4. Expandability: easy to add more features if
you deem necessary.
It
is not a magic bullet to solve all of your
autonomous problems.
It is simply a method of organizing your software.
The
method presented here is not the only
way to accomplish scripted autonomous
Most likely, it is not the best way.
It is a fairly simple way (and highly
effective).
The
“Primitive”, or building block.
It is a single, simple move that you program your
robot to do – usually a driving maneuver.
Think of it as a toolbox or a bag of tricks
What
are some primitives that you would
want in your autonomous software?
Ex:
1.
2.
3.
4.
5.
Delay / Do Nothing
Drive straight
Turn in place
Go to X,Y coordinate
etc.
How
to transition from one primitive to the
next?
Exit conditions must be defined for each primitive
What would be the exit conditions for the
primitives we decided on?
CAVEAT: What if you can’t get to the exit condition?
What
parameters are needed by the primitives?
Operating parameters (heading, distance, power,
etc.)
Exit Conditions
Each
primitive is like a function or class.
Parameters are passed to the primitive like
arguments to a function
Ex: straight(heading, distance, power)
The
primitives are selected and the
parameters are passed to the primitive via a
simple script.
Therefore: no need to change your software: just
change the script!
Each
primitive is assigned a unique ID number
(enumeration).
Each primitive has numeric parameters.
Since we only need numeric data, a matrix (2-D
Array) can be used to represent the script.
Each column of the script is one primitive
1st entry: primitive ID
2nd entry: parameter 1
3rd entry: parameter 2
Etc.
Number
of entries in each column depends on
the maximum number of parameters.
The
script is a matrix (2-D Array)
Each column represents one primitive
Example:
1st primitive
1st column:
Starting position
ID
param1
param2
.
.
.
.
0
261
203
0
0
2
0
1
0
15
0.5
0
0
0.4
0
0
250
0.5
0
0
0.4
2
10
500
1
0
0
0.4
2
10
1500
0.5
0
1
0.4
Example
script means the following:
Column 1: Start at Hdg = 0; X = 261; Y = 203
Column 2: Drive Straight (ID 1), Hdg = 0, for
15 inches, power = 0.5, Roller at 0.4.
ID
Hdg
dist/time
power
kick
roller
0
261
203
0
0
2
0
1
0
15
0.5
0
0
0.4
0
0
250
0.5
0
0
0.4
2
10
500
1
0
0
0.4
2
10
1500
0.5
0
1
0.4
Column
3: Do Nothing (ID 0) for 250 msec.
Column 4: Hold Position (ID 2), Hdg = 10 deg,
for 500 msec, Max power = 1.0.
Column 5: Hold Position, Hdg = 10 deg, for
1500 msec, kick ball
ID
Hdg
dist/time
power
kick
roller
0
261
203
0
0
2
0
1
0
15
0.5
0
0
0.4
0
0
250
0.5
0
0
0.4
2
10
500
1
0
0
0.4
2
10
1500
0.5
0
1
0.4
The
script can be as long or as short as you
want. Each script can be different.
0
1
0
2
261 0
0 10
203 15 250 500
0 0.5 0.5 1
0
0
0
0
2
0
0
0
0 0.4 0.4 0.4
The
file.
2
2
10 0
1500 1000
0.5 1
0
0
1
0
0.4 0.4
1
7
5
6
2
2
2
0 391 391 0.3 -77 -77 -77
65 174 174 500 1000 1000 500
0.5 0.75 0.65 0.2 2
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
-0.3 0
0
0
0
0
0
script is stored on the cRIO as a .CSV
First column is used to tell the autonomous program where the robot is starting (Heading and X and Y Coordinates). See be
use as many primitives as you wish
Primitive ID:
Desired X:
Desired Y:
Desired Heading:
Power Level or Time:
Desired Elevator:
Desired Arm:
Upper Claw PWM:
Lower Claw PWM:
Reserved:
0
410
222
0
0
0
0
0
0
0
1
470
200
0
0.5
90
70
0
0
0
Primitive ID Quick Key:
0 Delay/Coast
1 Drive to XY full speed
2 Drive to XY gradual approach
3 Hold XY Position
4 Turn to heading
5 Drive Timed Open Loop
6 Line Follow
2
480
200
0
0.5
90
70
0
0
0
2
485
200
0
0.5
90
70
0
0
0
3
485
200
0
1000
90
70
-0.6
-0.6
0
1
480
200
0
0.3
90
70
-0.6
-0.6
0
2
425
175
0
0.5
0
-100
0
0
0
3
420
175
0
1000
0
-100
0.6
0.6
0
3
425
175
0
50
0
60
0.5
-0.5
0
2
480
150
0
0.5
90
70
0
0
0
2
485
150
0
0.5
90
70
0
0
0
Column
index is the key! – this selects the
primitive and parameters from the matrix.
0
261
203
0
0
2
0
1
0
15
0.5
0
0
0.4
0
0
250
0.5
0
0
0.4
2
10
500
1
0
0
0.4
2
10
1500
0.5
0
1
0.4
Use
a “Case Structure” or “switch/case”
Each “Case” <==> one primitive
Each case is numbered: set each case
number to the primitive ID you selected.
In each case, put your code for that primitive
(or call a function for the primitive) – don’t
forget the exit condition logic in your code.
In each primitive, when you reach the exit
condition, increment the array column index.
After incrementing the index, the next primitive
happens automatically – like magic
ColumnIndex
1
0
15
0.5
0
0
0.4
= 1:
ColumnIndex = 2:
0
0
250
0.5
0
0
0.4
In
your software, when you reach the exit
condition of one primitive:
Increment ColumnIndex (ColumnIndex++)
Next
primitive will start “automagically”
Cycling
0
261
203
0
0
2
0
ColumIndex:
through the primitives:
1
0
15
0.5
0
0
0.4
1
0
0
250
0.5
0
0
0.4
2
10
500
1
0
0
0.4
2
3
2
10
1500
0.5
0
1
0.4
4
// atnArr is the autonArray
// ind is the column index
1
0
15
0.5
0
0
0.4
switch (atnArr[0][ind])
{
case 0:
ind = delay(atnArr[1][ind],atnArr[2][ind]);
break;
case 1:
ind = straight(atnArr[1][ind],atnArr[2][ind]);
break;
Etc...
uint8 ind = straight(float desiredHdg, float dist)
{
const float speed = 0.7;
float turn = PID(desiredHdg, Hdg);
rightDrive = (speed + turn);
leftDrive = (speed – turn);
// exit condition:
if (encoderDist >= dist)
{
ind++;
}
return ind;
}
Separate
2-D array into each row:
Index
into each parameter via the Index:
Use
Case Structure (C++: switch/case)
Each case is a primitive.
Number each case with the primitive ID.
In each case, put your code for that primitive
– don’t forget the exit condition logic.
Start autonomous with array index = 0 or 1
(depending if you use the first column for
starting position).
In each primitive, when you reach the exit
condition, increment the array index.
The next primitive starts automatically.
cRIO
file structure:
How
Use
to read a file in the cRIO:
Read From Spreadsheet File.vi
AutonArray is our 2-D array
Getting
the scripts to the cRIO: FTP
FileZilla is free
Let’s
make a script for our autonomous code
and run it in simulation.
Shameless
here.
plug for tomorrow’s presentation
1.
2.
3.
4.
Create your autonomous code in a sub-vi.
Put your sub-vi in
AutonomousIndependent.vi
Hook up inputs (sensor readings) and
outputs (motor commands) to your sub-vi.
Wrap a “while loop” around everything.
(That’s all there is to it)
Why:
Add flexibility / fast autonomous changes
How: Determine your primitives
Determine primitive parameters – don’t forget
exit conditions.
Create Case Structure – one case for each
primitive.
Use 2-D array as a script to control the
autonomous software
Primitive is determined by the column index.
When you exit one primitive, increment the
column index and the next primitive starts
automatically.
Spreadsheet
program, such as Excel or
OpenOffice Calc
FTP software (such as FileZilla)
Simulation program is nice to have.
2nd shot at shameless plug
Presentation
material and sample software
will be posted on chiefdelphi.com in CDMedia/Papers area.
Feel free to send me a PM on
chiefdelphi.com : “Chris Hibner”
© Copyright 2026 Paperzz