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.bash

The 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/Contacts

3. 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.py

Using 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.bash

Watch 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.117

For 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.sh

4. Notes for agents

  • No display needed. world.launch.py headless:=true and the coverage regression both set software GL for you. If you drive Gazebo manually, export LIBGL_ALWAYS_SOFTWARE=1 and GALLIUM_DRIVER=llvmpipe first.
  • 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 is oomwoo_one.
  • Bumpers publish ros_gz_interfaces/msg/Contacts on /bumper_left/contact and /bumper_right/contact when 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_unstable and exit code 2 (measurement invalid) rather than reporting a bogus coverage number.

Leave a Reply

Your email address will not be published. Required fields are marked *