A PDDL-based managing system for SUAVE.
This repo contains the experimental setup used in the paper "Plan your Self-Adaptation! – Efficient Task and Architecture Co-adaptation Planning for Robots".
The remainder of this README explains how to reproduce the experiments.
The first step is to build the suave_planta docker image, if you want to have it locally:
docker build -t suave_planta .Run docker image:
Run docker image without web interface (with nvidia)(don't forget to install the docker-nvidia-toolkit)
docker run -it --rm --gpus all --runtime=nvidia --name suave_planta -e DISPLAY=$DISPLAY -e QT_X11_NO_MITSHM=1 -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=all -v $HOME/suave/results:/home/ubuntu-user/suave/results -v /dev/dri:/dev/dri -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro ghcr.io/kas-lab/suave_planta:mainWithout a nvidia GPU:
docker run -it --rm --name suave_planta -e DISPLAY=$DISPLAY -e QT_X11_NO_MITSHM=1 -v $HOME/suave/results:/home/ubuntu-user/suave/results -v /dev/dri:/dev/dri -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro ghcr.io/kas-lab/suave_planta:mainNote: If you want to use the host machine display, run the following command before running the docker container:
xhost +To develop suave_planta with docker, you will need to fetch the repos you want to develop into your host machine and mount the relevant directories inside the container.
For example:
Mount $HOME/suave/results, $HOME/suave_ws/src/suave, $HOME/suave_ws/src/suave_planta, and $HOME/suave_ws/src/ros2_planning_system
docker run -it --rm --gpus all --runtime=nvidia --name suave_planta -e DISPLAY=$DISPLAY -e QT_X11_NO_MITSHM=1 -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=all -v /dev/dri:/dev/dri -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro -v $HOME/suave/results:/home/ubuntu-user/suave/results -v $HOME/suave_ws/src/suave_planta:/home/ubuntu-user/suave_ws/src/suave_planta -v $HOME/suave_ws/src/ros2_planning_system:/home/ubuntu-user/suave_ws/src/plansys2 -v $HOME/suave_ws/src/suave:/home/ubuntu-user/suave_ws/src/suave suave_plantadocker run -it --rm --gpus all --runtime=nvidia --name suave_planta -e DISPLAY=$DISPLAY -e QT_X11_NO_MITSHM=1 -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=all -v /dev/dri:/dev/dri -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro -v $HOME/suave/results:/home/ubuntu-user/suave/results -v $PWD/src/suave_planta:/home/ubuntu-user/suave_ws/src/suave_planta -v $PWD/src/ros2_planning_system:/home/ubuntu-user/suave_ws/src/plansys2 -v $PWD/src/suave:/home/ubuntu-user/suave_ws/src/suave suave_plantadocker run -it --rm --gpus all --runtime=nvidia --name suave_planta -e DISPLAY=$DISPLAY -e QT_X11_NO_MITSHM=1 -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=all -v /dev/dri:/dev/dri -v /tmp/.X11-unix:/tmp/.X11-unix -v /etc/localtime:/etc/localtime:ro -v $HOME/suave/results:/home/ubuntu-user/suave/results -v $PWD:/home/ubuntu-user/suave_ws/src/suave_planta suave_plantaBefore requesting its first plan, PLANTA waits for PlanSys2 to become active and for the first water-visibility observation to be applied to the planning problem. The SUAVE monitor publishes the initial visibility during vehicle startup and holds the visibility schedule at time zero until GUIDED. This prevents planning with unknown visibility while preserving the timing of visibility changes. If applying the initial observation fails, the controller reports a failure instead of planning with missing visibility.
To run all six experiment campaigns (exp1-exp3 and extended_exp1-extended_exp3) sequentially in one go, use the generic run_batch node from suave_runner, configured through batch_campaigns.yml:
ros2 launch suave_planta run_batch.launch.pyNote: running all six campaigns back-to-back takes several hours to a few days, depending on the machine and num_runs per experiment — plan to leave it running unattended (e.g. in screen/tmux) rather than waiting on it.
This creates a timestamped batch directory under ~/suave/results/batches/ (e.g. batch_20260913_104200/) containing:
state.json— the batch checkpoint, tracking each campaign's status (pending,running,completed,incomplete,failed, orinterrupted)run_batch.log— the overall orchestration log<campaign_name>.log— combined stdout/stderr for each campaign (e.g.exp1.log)campaigns/<campaign_name>/— each campaign's own results directory, in the same layoutsuave_runnerproduces for a single campaign
Checking whether a batch needs to be resumed: a batch finished cleanly if run_batch.log ends with All campaigns completed successfully.. If it stops early — Ctrl+C, a crash, a failed campaign — the log ends instead with a Batch interrupted; ... or Batch finished with N incomplete, ... line followed by the exact resume command to use. You can also check campaign-by-campaign at any time by inspecting state.json:
grep '"status"' ~/suave/results/batches/<batch_dir>/state.jsonAny campaign not marked "status": "completed" still needs to (re)run.
Resuming an interrupted batch: pass the batch's state.json as the resume_state_file parameter; campaigns already marked completed are skipped, and an incomplete campaign picks up from its last saved run instead of starting over:
ros2 run suave_runner run_batch --ros-args -p resume_state_file:=~/suave/results/batches/<batch_dir>/state.jsonNote: to run a custom subset or order of campaigns, edit batch_campaigns.yml (or point run_batch at your own copy via --params-file) before starting a new batch. -p fail_fast:=true stops the batch as soon as a campaign fails instead of continuing to the next one, and -p dry_run:=true prints the ros2 run command for each campaign without executing anything.
You can run it with the launchfile:
Experiment 1:
ros2 launch suave_planta exp1_suave_runner.launch.pyExperiment 2:
ros2 launch suave_planta exp2_suave_runner.launch.pyExperiment 3:
ros2 launch suave_planta exp3_suave_runner.launch.pyNote: with the default configuration and launch files above, the results will be save in the directory ~/suave/results
Experiment 1:
ros2 launch suave_planta extended_exp1_suave_runner.launch.pyExperiment 2:
ros2 launch suave_planta extended_exp2_suave_runner.launch.pyExperiment 3:
ros2 launch suave_planta extended_exp3_suave_runner.launch.pyNote: with the default configuration and launch files above, the results will be save in the directory ~/suave/results
Changing experiments config: Simply create a new configuration file for the runner, for example by modifying the exp3_runner_config.yml file. Then, pass it to the suave_runnernode, for example, by modifying the exp3_suave_runner.launch.py launch file.
Alternatively, you can run the suave_runner node directly with the parameters you want. Check some examples below:
PLANTA:
ros2 run suave_runner suave_runner \
--ros-args \
-p gui:=False \
-p experiment_logging:=True \
-p experiments:='[
"{\"experiment_launch\": \"ros2 launch suave_planta suave_planta.launch.py\", \
\"num_runs\": 3, \
\"adaptation_manager\": \"planta\", \
\"mission_name\": \"suave\"}"
]'SUAVE extended with PLANTA:
ros2 run suave_runner suave_runner \
--ros-args \
-p gui:=False \
-p experiment_logging:=True \
-p experiments:='[
"{\"experiment_launch\": \"ros2 launch suave_planta suave_planta_extended.launch.py\", \
\"num_runs\": 20, \
\"adaptation_manager\": \"planta\", \
\"mission_name\": \"suave_extended\"}"
]'SUAVE with no managing subsystem:
ros2 run suave_runner suave_runner \
--ros-args \
-p gui:=False \
-p experiment_logging:=True \
-p experiments:='[
"{\"experiment_launch\": \"ros2 launch suave_none suave_none.launch.py\", \
\"num_runs\": 1, \
\"adaptation_manager\": \"none\", \
\"mission_name\": \"suave\"}"
]'Run ardusub:
sim_vehicle.py -L RATBeach -v ArduSub --model=JSON --consoleRun the simulation:
ros2 launch suave simulation.launch.py x:=-17.0 y:=2.0Run PLANTA:
ros2 launch suave_planta suave_planta.launch.pyThe launches below analyze the bundled, run-matched *_sorted.csv files
using the paired Wilcoxon signed-rank test with Holm correction. Standard
SUAVE inputs are in results/suave/exp1, exp2, and exp3; extended inputs
are in the corresponding results/suave_extended/ folders. These launches
replace the old Mann-Whitney configurations that referenced raw CSVs no
longer present in results/.
Paths are resolved from the installed suave_planta package rather than a
particular user's home or workspace path. With a symlink installation,
the input files point back to the source package's bundled results. Rebuild
after updating the launch/config files and result files:
colcon build --symlink-install --packages-select suave_runner suave_planta
source install/setup.bashExperiment 1:
ros2 launch suave_planta exp1_analysis.launch.pyExperiment 2:
ros2 launch suave_planta exp2_analysis.launch.pyExperiment 3:
ros2 launch suave_planta exp3_analysis.launch.pyOutputs are written beside the inputs by default, using the prefix
expN_wilcoxon (or extended_expN_wilcoxon). To keep the bundled outputs
intact, supply a separate output root, for example:
ros2 launch suave_planta exp1_analysis.launch.py output_root:=/tmp/planta_analysisThis writes under /tmp/planta_analysis/suave/exp1/. The extended launch
uses suave_extended/expN/ under the selected root.
Experiment 1:
ros2 launch suave_planta extended_exp1_analysis.launch.pyExperiment 2:
ros2 launch suave_planta extended_exp2_analysis.launch.pyExperiment 3:
ros2 launch suave_planta extended_exp3_analysis.launch.pyFor example, save extended experiment 1 outputs to another directory:
ros2 launch suave_planta extended_exp1_analysis.launch.py \
output_root:=/tmp/planta_analysisEdit the analysis configs to choose the sorted method
CSV files. The YAML node namespace is /wilcoxon_analysis. File paths use
$(var results_root) and $(var output_root) substitutions expanded by the
launch files; these configs are intended to be used through the launches.
Each single-experiment launch accepts:
| Argument | Default | Purpose |
|---|---|---|
results_root |
Installed package's results/ |
Root containing suave/ and suave_extended/ |
output_root |
Same as results_root |
Root for generated CSVs in the same experiment layout |
correction |
none |
none or explicit holm |
Both metrics exclude a pair when either method did not find the pipeline.
Search time uses the less alternative and distance uses greater for
row method minus column method. Holm correction pools the computed tests
across both metrics within that experiment. Use p_adjusted in
*_wilcoxon_results.csv for corrected comparisons; the two secondary
matrices and the node's console significance labels use raw p-values.
run_batch.launch.py writes a batch root containing state.json and
campaigns/<experiment>/. By default it creates
~/suave/results/batches/batch_YYYYMMDD_HHMMSS and prints the exact batch
directory. Pass that same root to the new analysis launch:
ros2 launch suave_planta run_batch.launch.py
# After the run finishes, use the batch directory printed by the runner:
ros2 launch suave_planta batch_analysis.launch.py \
batch_dir:=/home/ubuntu-user/suave/results/batches/batch_YYYYMMDD_HHMMSSAlternatively, choose an explicit directory for both commands. Run analysis after the batch completes:
ros2 launch suave_planta run_batch.launch.py \
batch_dir:=/home/ubuntu-user/suave/results/batches/my_batch
ros2 launch suave_planta batch_analysis.launch.py \
batch_dir:=/home/ubuntu-user/suave/results/batches/my_batchThe runner requires a new directory for a fresh batch. To resume an existing
batch, leave batch_dir empty and supply
resume_state_file:=/path/to/batch/state.json instead.
The analysis launch accepts the batch root, not its campaigns/ child
and not the bundled suave_planta/results/ tree. It automatically sorts raw
CSV files using their original completion markers, then runs paired Wilcoxon
analysis. The input directory must come from a completed matching campaign;
incomplete runs and unsafe reconstruction are reported as errors.
| Batch analysis argument | Default | Purpose |
|---|---|---|
batch_dir |
Required | Exact output root of the batch runner |
output_root |
<batch_dir>/campaings_results |
Root for analysis outputs |
config_dir |
Config paths recorded in state.json |
Override with original configs if those paths moved |
correction |
none |
Correction separately within each experiment |
Results go to <batch_dir>/campaings_results/<experiment>/wilcoxon_analysis/.
For the older batch discussed in this project, the same launch works with
batch_dir:=/home/ubuntu-user/suave/results/batches/all_experiments_20260904_093305.
If necessary, add config_dir:=/path/to/original/campaign/configs; do not
substitute configs from a different campaign.
Q-Q plots and LaTeX tables remain available through the batch scripts:
python3 src/suave/suave_runner/suave_runner/analysis/qq_plot_batch.py /path/to/batch
python3 src/suave/suave_runner/suave_runner/latex/wilcoxon_latex_tables_batch.py /path/to/batchQ-Q plots use campaings_results/<experiment>/q-q-plots/. Tables read the
existing Wilcoxon analysis and use
campaigns_latex_tables/wilcoxon_analysis/, with Holm-adjusted p-values by
default when the analysis used Holm correction. The standalone analysis
command is still available in suave_runner/suave_runner/analysis/.
See suave_runner's guide for details.
If you want to try the OWL to PDDL conversion separately, check the examples below.
With ROS:
ros2 run owl_to_pddl owl_to_pddl.py --ros-args -p owl_file:=owl/suave.owl -p in_domain_file:=pddl/suave_domain.pddl -p out_domain_file:=pddl/suave_domain_created.pddl -p in_problem_file:=pddl/suave_problem.pddl -p out_problem_file:=pddl/suave_problem_created.pddl -p add_numbers:=true -p replace_output:=trueSUAVE extended
ros2 run owl_to_pddl owl_to_pddl.py --ros-args -p owl_file:=owl/suave_extended.owl -p in_domain_file:=pddl/suave_domain_extended.pddl -p out_domain_file:=pddl/suave_domain_extended_created.pddl -p in_problem_file:=pddl/suave_problem_extended.pddl -p out_problem_file:=pddl/suave_problem_extended_created.pddl -p add_numbers:=true -p replace_output:=trueWithout ROS:
OWLToPDDL.sh --owl=owl/suave.owl --tBox --inDomain=pddl/suave_domain.pddl --outDomain=pddl/suave_domain_created.pddl --aBox --inProblem=pddl/suave_problem.pddl --outProblem=pddl/suave_problem_created.pddl --add-num-comparisons --replace-outputSUAVE extended
OWLToPDDL.sh --owl=owl/suave_extended.owl --tBox --inDomain=pddl/suave_domain_extended.pddl --outDomain=pddl/suave_domain_extended_created.pddl --aBox --inProblem=pddl/suave_problem_extended.pddl --outProblem=pddl/suave_problem_extended_created.pddl --add-num-comparisons --replace-outputIf you want to run the planner individually, check the examples below.
ros2 run downward_ros fast-downward.py pddl/suave_domain_created.pddl pddl/suave_problem_created.pddl --search 'astar(blind())'SUAVE extended:
ros2 run downward_ros fast-downward.py pddl/suave_domain_extended_created.pddl pddl/suave_problem_extended_created.pddl --search 'astar(blind())'ros2 run owl_to_pddl count_pddl_complexity.py pddl/suave_domain_created.pddl pddl/suave_problem_created.pddlros2 run owl_to_pddl count_pddl_complexity.py pddl/suave_domain_extended_created.pddl pddl/suave_problem_extended_created.pddl