Free worldwide shiping on $150+
OOMWOO Headless Sim for LLM Agents
A concise, copy-paste quickstart for running the OOMWOO robot vacuum entirely headless (no display) — aimed at LLM coding agents, CI, and automation. Every command below is verified against the makerspet/oomwoo:jazzy-dev Docker image. No X server, GPU, or GUI is required; Gazebo renders offscreen with software GL.
1. Start the dev image (headless)
Run the image detached and open a shell in it. This pattern is scriptable and survives across commands, which suits an agent better than an interactive -it --rm session.
docker pull makerspet/oomwoo:jazzy-dev
docker run -d --name oomwoo makerspet/oomwoo:jazzy-dev sleep infinity
docker exec -it oomwoo bash
# inside the container, source ROS 2 and the prebuilt workspace:
source /opt/ros/jazzy/setup.bash
source /ros_ws/install/setup.bashThe image already contains the robot descriptions (oomwoo_one, proscenic_m6pro), the simulation worlds (oomwoo_gazebo), and the bring-up/navigation package (oomwoo_bringup). The default robot model is oomwoo_one (via kaia config robot.model).
2. Run Gazebo headless
Launch the world with the headless:=true switch. This runs Gazebo server-only with offscreen rendering and forces software GL (LIBGL_ALWAYS_SOFTWARE=1, GALLIUM_DRIVER=llvmpipe), so it comes up in Docker/CI with no display.
# spawn the default robot in the living_room world, no GUI
ros2 launch oomwoo_gazebo world.launch.py headless:=true
# (omit headless:=true to get the Gazebo GUI on a machine with a display)In a second shell (docker exec -it oomwoo bash, then source as above) verify the sim and drive the robot:
ros2 topic list
# expect: /scan /odom /cmd_vel /bumper_left/contact /bumper_right/contact ...
# drive forward
ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.2}}'
# watch a bumper fire when it hits a wall/furniture
ros2 topic echo /bumper_left/contact ros_gz_interfaces/msg/Contacts3. Run coverage cleaning
The cleaning behaviour (boustrophedon coverage planner + coverage meter) ships prebuilt in the image as the oomwoo_coverage, oomwoo_nav_localize and oomwoo_sim_support packages. Launch the headless coverage regression (sim + Nav2 + coverage planner + meter) directly:
# headless coverage-cleaning run (no display required)
ros2 launch oomwoo_sim_support coverage_regression.launch.pyUsing an older image that predates the bundled cleaning packages? Clone and build them into the workspace first:
cd /ros_ws
git clone -b main https://github.com/makerspet/oomwoo-ros2-tools src/oomwoo-ros2-tools
colcon build --symlink-install \
--packages-select oomwoo_sim_support oomwoo_coverage oomwoo_nav_localize
source install/setup.bashWatch the floor get covered (second shell). /coverage_meter/ratio rises from 0.0 toward 1.0 as the robot cleans:
ros2 topic echo /coverage_meter/ratio # std_msgs/Float32, 0.0 -> 1.0
ros2 topic echo /coverage_planner/cleaning_active
ros2 topic echo /coverage_meter/efficiency
# example progression on a fresh run:
# ~10s coverage_ratio = 0.019
# ~40s coverage_ratio = 0.058
# ~70s coverage_ratio = 0.084
# ~100s coverage_ratio = 0.117For a single scored pass, use the regression wrapper. It exits 0 only if coverage >= 90% and efficiency >= 80%, and writes a JSON report:
./src/oomwoo-ros2-tools/deploy/run_coverage_regression.sh
cat /root/coverage_report.json
# repeat and report the spread:
RUNS=3 ./src/oomwoo-ros2-tools/deploy/run_coverage_regression.sh4. Notes for agents
- No display needed.
world.launch.py headless:=trueand the coverage regression both set software GL for you. If you drive Gazebo manually, exportLIBGL_ALWAYS_SOFTWARE=1andGALLIUM_DRIVER=llvmpipefirst. - Isolate the ROS graph when running more than one sim:
export ROS_DOMAIN_ID=77 ROS_LOCALHOST_ONLY=1. - Switch robots with
robot_model:=proscenic_m6pro(or any description package) on the launch command; the default isoomwoo_one. - Bumpers publish
ros_gz_interfaces/msg/Contactson/bumper_left/contactand/bumper_right/contactwhen the robot touches a wall or furniture. - Sim stability. Under nested virtualization (WSL2/Docker) the sim clock can occasionally stall; the regression flags this via
/coverage_meter/sim_unstableand exit code 2 (measurement invalid) rather than reporting a bogus coverage number.


