Docs menu
Command reference
One copy-paste block for every mode of the car, with the output that shows it is working.
Each section gives the terminals a mode needs, the commands for each, and the output that shows the mode is working. Run the commands on the car’s Jetson, over SSH or at its desktop.
Set up every terminal
Run these two lines in every new terminal before any ros2 command. The first loads ROS 2 Jazzy, and the second loads the workspace’s packages.
source /opt/ros/jazzy/setup.bash
source ~/racerbot-ws/install/setup.bashBoth commands print nothing when they succeed. If a later ros2 command reports Package ... not found, that terminal skipped this step.
Safety rules for every mode
- Hold LB on the F710 (XInput mode) whenever the car should move. Releasing LB commands zero speed in every mode, including the autonomous ones.
- Run anything new or changed with the wheels off the ground first.
- Never run
teleop_launch.pyalongside an autonomous mode. Its commands win at the mux, so the autonomous mode cannot drive. - Keep one person beside the battery switch whenever the car drives itself.
Check the hardware
Run the doctor before a session. It checks each device from USB up to its ROS topic, reads the battery voltage from the VESC, and never publishes or opens a device.
ros2 run racerbot_doctor doctorThe last line reads 0 FAIL when the hardware is ready. Each failure names the first layer that is broken, for example a missing /dev/sensors/vesc link or a LiDAR that publishes too slowly.
Drive by hand
Terminal 1 starts the hardware. Leave it running for every mode below unless a section says otherwise. Bringup never moves the car by itself.
ros2 launch f1tenth_stack bringup_launch.pyTerminal 2 starts manual control:
ros2 launch f1tenth_stack teleop_launch.pyHold LB, then use the left stick for speed and the right stick for steering. Full stick commands 5 m/s.
| Car 1 | Car 2 | |
|---|---|---|
| Full-stick steering | 0.38 rad | 0.436 rad |
Follow the gap
gap_follow drives without a map. See Follow the gap for how it works.
Terminal 2, with bringup running in Terminal 1:
ros2 launch gap_follow gap_follow_launch.pyThe terminal prints a decision line every second, such as DRIVE [gap_follow] selected preferred depth 2.00m gap .... The car drives while LB is held.
Add an option to the end of the command when needed:
| Option | Effect |
|---|---|
max_speed:=1.0 |
Sets the top speed for this run. Must be positive and at most 5.0 m/s. |
opponent_awareness:=false |
Turns off tracking and following other cars, which is on by default. Tested only in the simulator. |
steering_damping:=false |
Turns off the gyro steering term, which is on by default. |
imu:=false |
Starts without the IMU stack. Refused if any IMU feature is on. |
fused_speed:=true |
Uses the IMU-fused speed on /odom/fused. Safety checks use the more cautious of it and the wheel speed. |
imu_understeer:=true |
Caps speed when the car turns much less than commanded |
imu_traction:=true |
Holds the speed command while the wheels spin. Only ever lowers speed. |
imu_impact_stop:=true |
Latches speed 0 after a large acceleration spike. Release and press LB to resume. Threshold provisional. |
imu_all:=true |
Turns on the four features above. An explicit false on one of them wins. |
The IMU features have been run only in the simulator, and their thresholds were measured on car 1. Keep them off on car 2 until its BNO085 mount yaw is measured. See IMU features before trying one on the car.
Map a course
map_course drives laps with gap_follow while slam_toolbox builds a map. It then saves the map and a racing line, stops the car, shuts itself down and prints the commands to race on that map. It requires bringup to be running and refuses to start without it.
Put the car on the track, pointing in the direction it will lap.
Terminal 2, with bringup running in Terminal 1:
ros2 run racerbot_launch map_coursemap_course maps on the gyro by default, so keep the car still for the first 3 seconds while the IMU measures its gyro bias. Then hold LB for the whole run. Releasing LB pauses mapping, and holding it again continues.
The run is working when the terminal shows Closed mapping lap 1/3, 2/3 and 3/3. The car then stops past the start line, Saved occupancy map successfully. appears, and the terminal ends with a block that starts MAPPING FINISHED. and contains the race commands. Before them, map_course checks the map against the run’s scans and prints GO: or NO-GO:. Do not race a NO-GO map.
Leave the car where it stopped. That spot is the start pose for racing on this map. To race the map on another day, mark the spot and direction on the floor before moving the car.
| Option | Effect |
|---|---|
mapping_max_speed:=1.5 |
Lower speed for a course nobody has driven yet. Default 3.5 m/s. |
laps:=4 |
A different number of laps, at least 2 |
record_bag:=false |
Skips the rosbag of the run in ~/racerbot-ws/data/<id>/racerbot_runs/. A bag lets the run be replayed and analyzed. Default true. |
analysis:=true |
Runs the summary, IMU report and SLAM report after mapping. They take minutes. |
gyro_odom:=false imu_lap_turns:=false |
Maps on the steering model instead of the gyro |
Every option is on Map a course. If the terminal reports the hardware is not running, start bringup in Terminal 1 first. Ctrl+C stops the run, keeps what it mapped and still runs the map check. If the last lap fails the quality check, the run drives up to two more laps.
Race on a saved map
Racing localizes the car on the saved map with the particle filter, then follows the racing line with pure_pursuit. The car must be where mapping left it, or back on the marked spot facing the same way. The particle filter starts from that pose and searches only about 0.5 m and 20° around it. This mode has been tested only in the simulator.
Terminal 2, with bringup running in Terminal 1. Replace <id> with rb1 or rb2, and <run> with the folder name from the end of mapping. The printout at the end of mapping already contains these commands with the folder filled in.
ros2 launch racerbot_launch race_launch.py run_dir:=$HOME/racerbot-ws/data/<id>/racerbot_auto/<run>The default steering law is pure pursuit. Choose another with steering_law:=:
ros2 launch racerbot_launch race_launch.py run_dir:=$HOME/racerbot-ws/data/<id>/racerbot_auto/<run> steering_law:=stanley
ros2 launch racerbot_launch race_launch.py run_dir:=$HOME/racerbot-ws/data/<id>/racerbot_auto/<run> steering_law:=lqr
ros2 launch racerbot_launch race_launch.py run_dir:=$HOME/racerbot-ws/data/<id>/racerbot_auto/<run> steering_law:=mpcThe mode is working when Particle filter is at the start pose; racing can begin. appears. The race controller starts only after that line, and the car then drives the racing line while LB is held. If the filter never reaches the start pose, the car is not on the mapping spot or the map is wrong. Move the car and run the command again.
To find the newest run folders and print a run’s commands again:
ls -t ~/racerbot-ws/data/<id>/racerbot_auto | head -3
cat ~/racerbot-ws/data/<id>/racerbot_auto/<run>/commands.txtMap and race in one run
This launch maps the course and hands control to pure pursuit as soon as lap 2 passes the quality check, with no restart. It starts the hardware too, so run it instead of bringup.
Terminal 1:
ros2 launch racerbot_launch auto_map_race_launch.pyThe terminal shows lap messages, a handover summary, then Transition complete: pure pursuit now has drive control.
Dashboard
The dashboard watches and tunes the car from a browser at dashboard.sfuracerbot.ca, behind a sign-in with a team email. It publishes to no ROS topic, so it can run alongside any mode, in any spare terminal.
ros2 launch racerbot_launch dashboard_launch.pyThe log reports that the server is listening on port 8080, and the site lists the car as CAR ONLINE.
To add the camera feed, start the car’s camera, which camera_launch.py chooses from the car profile:
ros2 launch racerbot_launch camera_launch.py| Car 1 | Car 2 | |
|---|---|---|
| Camera it starts | USB webcam, through webcam_launch.py | RealSense D435i, through realsense_camera_launch.py |
| Dashboard site ID | rb1 | rb2 |
Record a run
Recording saves the logs, decisions and stop reasons of a run for analysis afterwards. Bringup records every autonomy run by itself: when a driving node starts, the bringup terminal prints recording ... -> ~/racerbot-ws/data/<id>/racerbot_runs/<ts>, and when it stops, saved run .... <id> is rb1 or rb2. Manual driving is not recorded, and record:=false on bringup turns automatic recording off.
To record a manual drive, start the recorder in its own terminal first. Bringup’s recorder then stays out of the way.
ros2 launch race_diagnostics record_run.pyAfter the run, summarize and analyze it in any terminal:
ros2 run race_diagnostics summarize_run ~/racerbot-ws/data/<id>/racerbot_runs/<ts>
python3 ~/racerbot-ws/tools/run_analysis/analyze_run.py ~/racerbot-ws/data/<id>/racerbot_runs/<ts>The analysis writes analysis/report.md in the run folder, with a reason for every stop.
Simulator
The simulator runs the stack without the car. Never run it on the car while the drivers are running: it fakes LB and a second /scan. It refuses to start while it sees the real drivers on the ROS graph.
ros2 launch racerbot_sim sim_auto_map_race_launch.pyThis runs the full map-then-race flow in simulation. Two faster checks run gap_follow’s logic without ROS:
python3 ~/racerbot-ws/tools/f1tenth_sim/sweep_gap.py --track indoor --seeds 6
python3 ~/racerbot-ws/tools/f1tenth_sim/eval_gap_multi.py --track indoor_wide --cars 3 --speeds 1.8,3.5,3.5 --set enable_opponent_awareness=trueEach line of output reads pass N/N when every run finished, with contact 0 for one car or wall 0 car-car 0 for several.
Stop everything
Press Ctrl+C in each terminal, control layer first and bringup last, and turn the battery off after every process has exited. If a process does not exit, stop it by name:
pkill -f "joy_node|joy_teleop|vesc_driver_node|urg_node_driver|ackermann_mux|ackermann_to_vesc_node|vesc_to_odom_node|static_transform_publisher|gap_follow_node|slam_toolbox|particle_filter|pure_pursuit_node|waypoint_recorder_node"Saved files
| Contents | Location |
|---|---|
Maps, racing lines and commands.txt from mapping runs (<id> is rb1 or rb2) |
~/racerbot-ws/data/<id>/racerbot_auto/<YYYYMMDD-HHMMSS>/ |
| The same, from the simulator | ~/racerbot-ws/data/<id>/racerbot_sim/auto/<YYYYMMDD-HHMMSS>/ |
| Run recordings: logs, events and an optional bag | ~/racerbot-ws/data/<id>/racerbot_runs/<ts>/ |
gap_follow speeds and limits |
src/gap_follow/config/gap_follow.yaml |
| Racing-line limits for mapping runs | src/pure_pursuit/config/auto_map_race.yaml |
Files behind these commands
| Command | File |
|---|---|
bringup_launch.py, teleop_launch.py |
f1tenth_stack/launch/GitHub |
gap_follow_launch.py |
gap_follow_launch.pyGitHub |
map_course |
racerbot_launch/scripts/map_courseGitHub |
race_launch.py, auto_map_race_launch.py, dashboard_launch.py, camera_launch.py |
racerbot_launch/launch/GitHub |
record_run.py |
record_run.pyGitHub |
sim_auto_map_race_launch.py |
sim_auto_map_race_launch.pyGitHub |
| The daily command sheet | docs/launchstart.mdGitHub |
Checked against
car 1 commit 8a88dcc
and
car 2 commit 80da6f5, before the two workspaces merged.