Docker

The UniROS stack targets ROS Noetic on Ubuntu 20.04. Many modern hosts can’t install Noetic natively — Ubuntu 22.04 / 24.04 OEM laptops, machines whose GPUs need a newer-than-Noetic-supported driver toolchain, Windows-with-WSL2 dev boxes. Docker is the supported path for those users.

The image bakes the full installer output into an Ubuntu 20.04 + ROS Noetic container: framework packages (UniROS, MultiROS, RealROS, sb3_ros_support), application packages (rl_environments, rl_training_validation), the four robots’ description-extras helpers, robot vendor packages (Interbotix RX200 + VX300S, Niryo Ned2, UR5e + Robotiq), and rl_envs_cube_tracker. Same install logic as the bootstrap script — there’s a single source of truth.

When to use Docker

  • Your host runs Ubuntu 22.04 / 24.04 and you can’t downgrade.

  • Your GPU’s driver stack doesn’t have first-class Ubuntu 20.04 support, or your modern PyTorch / CUDA combination needs a Python newer than the 3.8 that ships with Ubuntu 20.04 / Noetic.

  • You’re on Windows with WSL2.

  • You want a reproducible, throw-away environment for CI or for reproducing the paper’s results without committing your dev machine to Noetic.

A native Ubuntu 20.04 install (see Installation Option A or B) is still the fastest path if your host already runs that distribution.

What works today

  • Image build with the full UniROS stack baked in (single ./build.sh).

  • Headless run — drops you into a bash shell with the workspace sourced. Suitable for training scripts, roscore, rospy, and headless Gazebo.

  • GUI run — Gazebo and RViz windows render on the host display via rocker. Auto-detects NVIDIA for hardware-accelerated rendering; falls back to software.

  • Hardware passthrough — USB-attached arms via --device, network-attached arms via --network=host.

  • Bind-mount a host workspace for active development — edit source files in your IDE on the host, the container sees changes immediately.

  • Non-root user matching your host UID/GID by default, so catkin build results in the container don’t end up root-owned on the host.

Note

Already on Ubuntu 20.04? Skip Docker and run install_uniros_stack.sh directly on the host. The native path is faster, smaller, and avoids GL-passthrough / nvidia-container-toolkit compatibility quirks that have surfaced with newer NVIDIA driver branches on 20.04 hosts. Docker is meant for hosts that can’t install 20.04 natively.

Two image variants

Variant

Tag

Base image

Size

When to pick

Default

uniros:noetic

nvidia/cuda:12.9.2-runtime-ubuntu20.04

~16 GB

NVIDIA GPU host AND you want CUDA-backed PyTorch / TF running inside the container.

Slim

uniros:noetic-slim

osrf/ros:noetic-desktop-full-focal

~12 GB

No GPU, or training runs on the host while only the env runs in the container, or you just want a smaller download. Gazebo / RViz still get hardware-accelerated GL via rocker --nvidia.

Both variants ship identical application code (same UniROS framework, same rl_environments, same training scripts). The only difference is whether CUDA runtime libraries are baked in.

Quick start

git clone -b gymnasium https://github.com/ncbdrck/UniROS.git
cd UniROS/docker

# Default image (CUDA-runtime base, ~16 GB)
./build.sh
./run_gui.sh

# — or — slim image (no CUDA baked in, ~12 GB)
./build.sh --slim
./run_gui.sh -t uniros:noetic-slim

First build takes 30–60 minutes (clones every robot vendor repo, pip-installs SB3 / PyTorch, builds the catkin workspace).

The same docker/ tree (canonical in UniROS, byte-identical copies in MultiROS, RealROS, sb3_ros_support, rl_environments, rl_training_validation) ships in every ecosystem repo so you can build the image from whichever repo you cloned first.

Hardware passthrough

Network-attached robots (Niryo Ned2, UR5e) work out of the box. run.sh enables --network=host by default, so any ROS_MASTER_URI you set inside the container reaches the robot’s onboard rosmaster:

./run.sh
# then inside the container:
export ROS_MASTER_URI=http://ned2.local:11311
rostopic list

USB-attached arms (Interbotix RX200 / VX300S over U2D2) need two host-side steps:

  1. Install the Interbotix udev rules on the host. The installer detects UNIROS_INSTALL_IN_DOCKER=1 and skips udev inside the container (no udevd to talk to). From the host workspace:

    sudo cp ~/uniros_ws/src/interbotix_ros_core/interbotix_ros_xseries/interbotix_xs_sdk/99-interbotix-udev.rules \
            /etc/udev/rules.d/
    sudo udevadm control --reload-rules && sudo udevadm trigger
    
  2. Edit docker/run.sh (or run_gui.sh) and uncomment the --device=/dev/ttyDXL:/dev/ttyDXL line.

GPU access for Gazebo hardware rendering or for in-container ML uses NVIDIA’s Container Toolkit. Once installed, uncomment --gpus all in run.sh, or run_gui.sh will detect and use NVIDIA automatically via rocker’s --nvidia flag.

Active development

To edit source files on the host with your IDE while the container runs builds and tests, bind-mount your host workspace at the container’s workspace path:

./run.sh -w ~/uniros_ws
./run_gui.sh -w ~/uniros_ws

The host workspace must be a fully bootstrapped catkin workspace — either built by install_uniros_stack.sh on a native Ubuntu 20.04 host, or by a prior turn-key container build whose /home/uniros/uniros_ws was copied out. catkin build inside the container writes build/ and devel/ to the host workspace with your host UID, because build.sh matched UID/GID at image build time.

Roadmap

The image currently runs both the ROS env and the learning algorithm inside the same container. That’s fine for sb3_ros_support and for any algorithm that can tolerate Python 3.8 (the version Noetic pins). It’s the wrong shape if you want to run a modern learning stack — for example, current PyTorch requires Python 3.9 or newer; the latest CUDA / Blackwell wheels are only published for newer Python interpreters; many recent JAX / RL frameworks have similar floors.

We’re working on a remote env mode that splits the stack across the container boundary:

  • The container holds the ROS env (rospy, Gazebo, MoveIt, the four robots’ drivers) on the pinned Python 3.8 / Noetic side.

  • The host holds the learner (any Python version, any CUDA, any PyTorch / JAX / Tianshou / CleanRL / hand-written loop).

  • They communicate over a TCP socket that speaks a small Gymnasium RPC: reset, step, close, plus the observation and action space metadata.

From the algorithm’s perspective it stays a Gymnasium env — uniros.make("RX200ReacherSim-v0", remote="localhost:5555") returns something with .reset() and .step() that you can hand to any training loop. From the env’s perspective, uniros-env-server inside the container hosts the env class and serves the RPC.

The pieces this needs — a versioned wire protocol, a codec that covers Box / Dict / Discrete spaces, vectorised-env support for SB3, structured error envelopes, an in-container process-lifecycle equivalent of the existing in-process GymProxy.close() — make it a small subsystem rather than a single edit, so it will land in a future release rather than the current one.

Until that lands, the supported pattern is to run the whole stack inside the container (training and env both) using sb3_ros_support or any other Gymnasium-compatible framework that runs on Python 3.8.

Troubleshooting

X11 forwarding doesn’t work and the GUI run script errors out. Make sure rocker is installed on the host (sudo apt install python3-rocker), that your $DISPLAY is set, and that xhost +local: (or a more restrictive equivalent) has allowed the container’s X11 connections. On Windows / WSL2, make sure WSLg is enabled (Windows 11 has it built in; Windows 10 needs the WSLg preview).

``rosrun`` can’t find a node after sourcing inside the container. The container’s entrypoint is bash with /home/uniros/uniros_ws/devel/setup.bash sourced via ~/.bashrc. If you bind-mounted a host workspace, make sure catkin build has run inside that workspace at least once, and re-source devel/setup.bash.

``catkin build`` outputs end up root-owned on the host. You probably built the image with a UID that doesn’t match your host UID. Rebuild with ./build.sh -u $(id -u) -g $(id -g), or run via ./run_gui.sh which uses rocker’s --user flag to layer your host UID at runtime.

Network-attached robot isn’t reachable from inside the container. run.sh uses --network=host so the container shares your host’s network stack. If you’re on macOS / Windows where host-mode networking isn’t fully supported, you’ll need to publish the right ports (-p flags) or run the rosmaster inside the container and have the robot point to it.

See also

  • The repo-local quick reference: docker/README.md in any ecosystem repo.

  • Installation Option C — the install-page summary of Docker.

  • The bootstrap installer for native installs: Installation Option A.