Run Output and Analysis#
What a run writes and how to read it: the run directory, its YAML files, the columns of performance_log.csv, the
Python loader and the figure tools. The loader’s full API is in the
open_apero_analysis README, the
plotting tools in the
open_apero_ros/scripts README.
Run Directory#
run_experiment_sim.launch writes one directory per run,
<data_root>/<scene_dataset>/<scene_id>/<planner>/<YYYYmmdd_HHMMSS>/ (run_experiments.sh with TASK set prefixes
<task>/; paper_sweep.py writes below <--data-root>/<task>/, by default <data root>/paper_sweep/). The data root
is $OPEN_APERO_DATA_ROOT, else ~/open_apero_data; the launch files and tools read it, so the runs, the benchmark
results and the figures share one tree. mission_coordinator_node creates the directory and sets the
planner’s ~run_dir to it. All files but rosparams.yaml are written by open_apero_node (rviz_screenshot.png by
RViz, at its request):
File |
When |
|---|---|
|
At the first |
|
Every planning cycle, with |
|
When the episode ends, and when the platform stops the planner ( |
|
|
|
With |
|
When the budget is spent or |
run_manifest.yaml#
What produced the run: format_version, schema_fingerprint (a hash over the ordered columns), the build provenance
(build_git_sha, build_git_dirty, build_components), start_utc, the agent’s cost_budget (absent when unbounded)
and random_seed, layers (instance name → layer kind) and columns, one {name, type, unit, parent} per CSV column
in CSV order, where parent names the timer a sub-timer nests in.
episode_summary.yaml#
How the episode ended and its totals: format_version, end_reason (budget_exhausted, goal_reached, no_step or
stopped; see Ending an Episode), cycles (the number of CSV rows), total_cost,
cost_budget, total_path_length [m], total_path_duration [s], num_visited_nodes (executed edges),
elapsed_wall_time_s and real_accumulated_gain (layer instance name → gain: m³ explored, m² of surface or the summed
gain of the collected points, by task), the run’s result against total_cost. The last CSV row precedes the last
executed edge, so the totals are only here.
performance_log.csv#
One row per planning cycle; TotalCost against planning.RealAccumulatedGain is the gain-versus-cost curve. The
agent’s columns come first (RosAgent adds ReplanTimeRos, RosCallbacks and RosTimeSec), then every layer’s,
prefixed with its instance name in alphabetical order (backtracking., planning. in the shipped configs). Timers are
steady-clock wall time in seconds per cycle and run only with log_performance; Total* and TimesSelected are
cumulative.
Column |
Unit |
Meaning |
|---|---|---|
|
count |
Executed edges (steps), recovery steps included |
|
m, s |
Executed trajectory length and duration |
|
cost |
Executed edge cost, in the cost computer’s unit (s for |
|
s |
|
|
s |
Marker publishing; time in |
|
s |
Wall and ROS time since the previous cycle |
|
none |
Zero-based cycle index |
|
s |
Steady-clock time since the episode started; ROS clock at this cycle |
|
count |
Commands published and frames mapped since the previous cycle |
|
s |
Graph expansion; graph and gain update |
|
s |
This layer’s |
|
count |
Cycles this layer won |
|
s |
Node generation, edge generation, gain of new nodes |
|
s |
Generator update, gain re-evaluation |
|
s |
Moving the start node to the reached node |
|
none |
Value of the selected candidate |
|
task unit |
The evaluator’s accumulated gain when the cycle starts; 0 in the first row |
|
count |
Expansion rounds this cycle |
|
count |
Graph size after the start-node update, after expansion, at selection |
GenerateNode, GenerateEdge and EvaluateNode run inside <layer>.ExpandTime, GeneratorUpdate and
EvaluatorUpdate inside <layer>.UpdateTime, and every <layer>.PlanPathTime inside PlanPathTotal; the sum
PlanPathTotal + Visualization + RosCallbacks + Σ(<layer>.ExpandTime + <layer>.UpdateTime + <layer>.StartNodeUpdate),
the last term for planning layers only, is a per-cycle total without overlap. To add columns, a layer installs a
LayerMetrics subclass with its own declareFields(); a backtracker overrides Backtracker::declareMetrics().
Reading Runs in Python#
open_apero_analysis is a ROS-free Python package that loads run directories: load_run(run_dir) reads one into a
RunData, load_runs(roots) every run below one or more roots, and aggregate_report(runs) summarizes how many
loaded cleanly. A run is PARSED, DEGRADED (columns missing, or no valid manifest) or SKIPPED (no usable
data); the loader never raises for a bad CSV.
from pathlib import Path
from open_apero_analysis import aggregate_report, load_run, load_runs
scene = Path.home() / "open_apero_data/HSSD/102816852"
run = load_run(max(p for p in (scene / "nbs").iterdir() if p.is_dir())) # the newest nbs run
print(run.status.value, run.num_rows, run.columns["planning.RealAccumulatedGain"][-1])
print(aggregate_report(load_runs(scene)))
The plotting tools build on the package (Getting Started).
Figures#
The figure tools write below <data root>/figures/, at the path of the input each was drawn from (a figure drawn from
no run, such as plot_normalized_gain.py’s, at its top): the curves of the runs in paper_full/explore/HSSD/102816852/
go to figures/paper_full/explore/HSSD/102816852/. They share --out-dir DIR (the root of the tree), --output FILE
(one exact file; not in evaluate_experiment.py) and --open (the system viewer). Each runs without a display, prints
Wrote <path> and writes a .yaml record of the same name beside the figure: the command, the input directories, the
time and the code version.