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. Delay / Do Nothing 2. Drive straight 3. Turn in place 4. Go to X,Y coordinate 5. 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 1 st entry: primitive ID 2 nd entry: parameter 1 3 rd 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: ID 1 st column: Starting position 1 st primitive param1 param
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 ID Hdg dist/time power roller kick
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 roller kick
The script can be as long or as short as you want. Each script can be different. The script is stored on the cRIO as a.CSV file
Column index is the key! – this selects the primitive and parameters from the matrix
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: ColumnIndex = 2: In your software, when you reach the exit condition of one primitive: Increment ColumnIndex (ColumnIndex++) Next primitive will start “automagically”
Cycling through the primitives: ColumIndex:
// atnArr is the autonArray // ind is the column index 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 to read a file in the cRIO: Use 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 plug for tomorrow’s presentation here.
1. Create your autonomous code in a sub-vi. 2. Put your sub-vi in AutonomousIndependent.vi 3. Hook up inputs (sensor readings) and outputs (motor commands) to your sub-vi. 4. 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. 2 nd shot at shameless plug
Presentation material and sample software will be posted on chiefdelphi.com in CD- Media/Papers area. Feel free to send me a PM on chiefdelphi.com : “Chris Hibner”