Batch Iteration Testing¶
quad_perf_tests ships a headless batch runner for executing the full sim → plan → control stack many times in a row — useful for regression and robustness testing across randomized runs. Each iteration brings everything up, drives the robot to a goal, records bags, tears the stack down, and the runner moves on to the next.
Repurposed from quad_training
This tooling was previously the standalone quad_training package. It now lives in quad_perf_tests and is meant for iteration / regression testing, not for generating learned-policy training data.
What one iteration does¶
launch/run_iteration.py orchestrates a single headless run with event-driven sequencing:
- Gazebo (headless) + robot spawn in a sitting pose.
wait_for_robot_nodeblocks until the robot is up, controllers are active, and it has settled upright.- Stand command (
control/mode = 1). - Planning stack (
quad_plan.py: global planner, local planner / NMPC, body force estimator). time_sync_node+ bag recording (iteration_logging.py).episode_monitor_nodeauto-terminates the run on goal reached, planner failure, or a body collision, then shuts the whole launch down cleanly.
Run a single iteration directly:
ros2 launch quad_perf_tests run_iteration.py
ros2 launch quad_perf_tests run_iteration.py robot_type:=spirit world:=rough.sdf
| Arg | Default | Purpose |
|---|---|---|
robot_type |
go2 |
Robot config (go2, spirit, a1, …) |
world |
flat.sdf |
Gazebo world |
plan_delay |
5.0 |
Seconds after stand before launching the planners |
output_dir |
./iteration_bags |
Where bags are written |
Running many iterations¶
scripts/run_iterations.py drives run_iteration.py N times, fully isolating each run — it kills lingering processes and resets the ROS 2 daemon between iterations, and retries failed runs.
# 10 iterations of the default robot
ros2 run quad_perf_tests run_iterations --num_episodes 10
# Custom robot/world with a 5-minute per-iteration safety timeout
ros2 run quad_perf_tests run_iterations --num_episodes 5 \
--robot_type spirit --world rough.sdf --timeout 300 --output_dir /data/bags
| Flag | Default | Purpose |
|---|---|---|
--num_episodes |
(required) | Number of iterations to run |
--timeout |
300 |
Per-iteration safety timeout (s) |
--robot_type |
go2 |
Robot config |
--world |
flat.sdf |
Gazebo world |
--plan_delay |
5.0 |
Seconds after stand before planning |
--output_dir |
./iteration_bags |
Bag output directory |
--max_retries |
3 |
Retries per iteration before skipping |
What gets recorded¶
Each iteration produces two bags under output_dir:
*_synced— time-aligned bundles fromtime_sync_node(state/ground_truth,control/joint_command,control/grfs,local_plan,state/estimate).*_raw— all full-rate topics (states, plans, foot plans, body-force,cmd_vel, terrain map) for richer post-hoc inspection.
Post-process with the Quad Logger tools.
To record against an already-running stack without the full orchestration:
Driving with the cmd_vel publisher¶
For tracking/stress tests that don't need the goal-seeking planner, the cmd_vel_publisher_node feeds deterministic or randomized velocity commands instead. See the quad_perf_tests reference.
Troubleshooting¶
Iterations hang on startup or controllers never activate
Stale Gazebo transport state from a previous run. The runner clears /tmp/gz-* and restarts the ROS 2 daemon between iterations — if you launched run_iteration.py directly, kill leftover gz sim processes manually first.
Every iteration times out
The robot isn't reaching the goal. Check plan_delay is long enough for the planners to come up, and confirm the world/robot combination is feasible by running it once interactively with a GUI.
run_iterations counts a failed run as done
After --max_retries attempts an iteration is counted anyway to avoid an infinite loop. Inspect that run's logs/bags to see why it failed.