Autoware implementation of DoppelTest for multi-vehicle scenario generation and execution.
Paper: https://dl.acm.org/doi/10.1109/ICSE48619.2023.00216
autoware_controller/: receiver API, sender, rosbag control, violation analysisautoware_launch/: Docker image and container launch helpersautoware_map/: Lanelet2 map directories used by Autoware experimentsscenario_runner/: scenario generation and experiment orchestrationstart.sh: starts containers and receivers for the default multi-vehicle workflow
- Linux
- Docker
rocker- NVIDIA GPU, drivers, and X11 support for the provided launch scripts
Install uv first. Official docs: https://docs.astral.sh/uv/getting-started/installation/
curl -LsSf https://astral.sh/uv/install.sh | shFrom the repo root, sync the project environment:
uv syncThis repo already vendors rocker under autoware_launch/rocker/. uv sync installs it from the local path, so do not clone it separately.
If you want to run on maps other than the default BorregasAve, install ROS 2 first using the official Humble installation guide:
https://docs.ros.org/en/humble/Installation.html
Then source ROS 2 and install Lanelet2:
source /opt/ros/humble/setup.bash
sudo apt install ros-humble-lanelet2lanelet2 is not managed by uv here. It comes from the ROS environment and is used for reliable Lanelet2 routing/regulatory parsing and by the violation analyzer.
bash autoware_launch/build/build_docker.sh --multi_containerbash start.shThis starts or reuses autoware_1 to autoware_5, assigns ROS_DOMAIN_ID=1..5, launches autoware_controller/receiver.py in the background, waits for /health, and prints the receiver URLs.
uv run --script scenario_runner/test_main.py --num-vehicles 5 --generations 1Meaning:
--num-vehicles 5: each generated scenario uses 5 active Autoware vehicles--generations 1: run one GA generation
After experiments, remove the Autoware containers:
bash autoware_launch/scripts/kill_all.shBy default, test_main.py auto-discovers running containers named autoware_1, autoware_2, ... through Docker and uses their current container IPs.
Default map:
autoware_map/BorregasAve/lanelet2_map.osm
To run on sample-map-planning, add --map:
uv run --script scenario_runner/test_main.py \
--num-vehicles 5 \
--generations 1 \
--map autoware_map/sample-map-planning/lanelet2_map.osmMain entrypoint:
uv run --script scenario_runner/test_main.pyIt orchestrates:
- Autoware startup and recovery
- sender restart with peer receiver URLs
- localization and route initialization
- autonomous mode switching
- pedestrian and traffic signal publishing
- rosbag control
- violation, decision, and fitness collection
Common examples:
Fixed vehicle count:
uv run --script scenario_runner/test_main.py \
--num-vehicles 3 \
--generations 10Mixed-size scenarios:
uv run --script scenario_runner/test_main.py \
--min-vehicles 2 \
--max-vehicles 5 \
--duration-hours 1Explicit receiver URLs:
uv run --script scenario_runner/test_main.py \
--url http://<vehicle1-ip>:5002 \
--url http://<vehicle2-ip>:5002 \
--url http://<vehicle3-ip>:5002 \
--num-vehicles 3Useful flags:
--log-dir: output directory for scenario JSON files and GA logs--restart-wait: wait time after Autoware restart, default60--max-recovery-retries: per-scenario recovery attempts before marking failure--conflict-only: generate only conflict scenarios--no-conflict-only: disable conflict-only generation
The receiver listens on 0.0.0.0:5002 inside each container.
Health check:
curl http://127.0.0.1:5002/healthStart Autoware:
curl -X POST http://127.0.0.1:5002/autoware/start \
-H 'Content-Type: application/json' \
-d '{}'Start sender:
curl -X POST http://127.0.0.1:5002/sender/start \
-H 'Content-Type: application/json' \
-d '{}'Start sender with explicit peer URLs:
curl -X POST http://127.0.0.1:5002/sender/start \
-H 'Content-Type: application/json' \
-d '{
"receiver_urls": [
"http://<peer1-ip>:5002/perception",
"http://<peer2-ip>:5002/perception"
]
}'Main parameters still relevant in the current code:
ROS_DOMAIN_ID: per-container ROS 2 isolation; assigned automatically bystart.sh,dev_start.sh, anddev_into.shCONTAINER_COUNT: number of containers started bystart.sh, default5AUTO_START_RECEIVER: set0to makestart.shskip receiver startupAUTOWARE_RECEIVER_URLS: explicit receiver base URLs fortest_main.pyAUTOWARE_RECEIVER_CONTAINER_PREFIX: Docker auto-discovery prefix fortest_main.py, defaultautoware_AUTOWARE_RECEIVER_PORT: receiver port for Docker auto-discovery, default5002RECEIVER_LOG_ROOT: override receiver log directory
Removed:
RECEIVER_INSTANCE
test_main.py writes to the selected --log-dir:
test_main.logGeneration_XXXXX_Scenario_XXXXX.jsonGA_selection_gen_XXXXX.json
Receiver logs default to:
container_<ROS_DOMAIN_ID>/log/
Start one container:
bash autoware_launch/scripts/dev_start.sh --use_multi_container --container_name autoware_1Enter a container shell:
bash autoware_launch/scripts/dev_into.sh --container_name autoware_1Enter and start the receiver:
bash autoware_launch/scripts/dev_into.sh --container_name autoware_1 --start-receiverFor custom container names without numeric suffixes:
bash autoware_launch/scripts/dev_start.sh --use_multi_container --container_name my_av --ros-domain-id 7
bash autoware_launch/scripts/dev_into.sh --container_name my_av --ros-domain-id 7 --start-receiver- Passing
/perceptionURLs totest_main.pyinstead of receiver base URLs - Running
test_main.pyfrom an environment that cannot access Docker when relying on auto-discovery - Using a
--mappath that exists on the host but not inside the Autoware containers
If you use this repository in research, cite the DoppelTest paper and document any local changes to Autoware, maps, or analysis logic.