From 178fab2fdb9e2c454725c7996119ce0736160b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Gal=C3=A1n=20Avil=C3=A9s?= Date: Thu, 28 May 2026 13:01:32 +0200 Subject: [PATCH 1/7] Delete old docker version --- docker/bootstrap-workspace.sh | 31 ----- docker/devel-entrypoint.sh | 94 ------------- docker/docker.md | 242 ---------------------------------- 3 files changed, 367 deletions(-) delete mode 100644 docker/bootstrap-workspace.sh delete mode 100644 docker/devel-entrypoint.sh delete mode 100644 docker/docker.md diff --git a/docker/bootstrap-workspace.sh b/docker/bootstrap-workspace.sh deleted file mode 100644 index 23ad4b7..0000000 --- a/docker/bootstrap-workspace.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -SRC_DIR="${1:?source workspace path is required}" -MANIFEST_FILE="${2:?external repos manifest path is required}" - -required_repos=( - "robotnik/robotnik_description" - "robotnik/robotnik_sensors" - "robotnik/robotnik_common" - "robotnik/robotnik_interfaces" - "robotnik/robotnik_moveit_configs" - "robotnik/robotnik_teleop_panel" -) - -missing_repo=false - -for repo_path in "${required_repos[@]}"; do - if [ ! -d "${SRC_DIR}/${repo_path}" ]; then - missing_repo=true - break - fi -done - -if [ "${missing_repo}" = true ]; then - echo "[bootstrap-workspace] Importing missing external repositories into ${SRC_DIR}" - vcs import --skip-existing "${SRC_DIR}" < "${MANIFEST_FILE}" -else - echo "[bootstrap-workspace] External repositories already present, skipping import." -fi diff --git a/docker/devel-entrypoint.sh b/docker/devel-entrypoint.sh deleted file mode 100644 index b4c0df2..0000000 --- a/docker/devel-entrypoint.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -ROS_DISTRO="${ROS_DISTRO:-jazzy}" -CONTAINER_USERNAME="${CONTAINER_USERNAME:-robot}" -WORKSPACE_DIR="${WORKSPACE_DIR:-${HOME}/ros2_ws}" -SRC_DIR="${SRC_DIR:-${WORKSPACE_DIR}/src}" -REPO_ROOT="${REPO_ROOT:-${SRC_DIR}/robotnik/robotnik_simulation}" -EXTERNAL_REPOS_FILE="${EXTERNAL_REPOS_FILE:-${REPO_ROOT}/dependencies/repos/robotnik_simulation.${ROS_DISTRO}-devel.repos}" -CONTAINER_GROUP="${CONTAINER_GROUP:-$(id -gn "${CONTAINER_USERNAME}")}" - -log() { - echo "[devel-entrypoint] $*" -} - -source_setup_file() { - local setup_file="$1" - if [ -f "${setup_file}" ]; then - # ROS setup scripts are not always compatible with nounset semantics. - set +u - # shellcheck disable=SC1090 - source "${setup_file}" - set -u - fi -} - -prepare_workspace_permissions() { - mkdir -p "${WORKSPACE_DIR}" "${SRC_DIR}" "${WORKSPACE_DIR}/build" "${WORKSPACE_DIR}/install" "${WORKSPACE_DIR}/log" - chown -R "${CONTAINER_USERNAME}:${CONTAINER_GROUP}" "${WORKSPACE_DIR}" -} - -install_local_debs() { - local deb_dir="${REPO_ROOT}/debs" - local deb_packages=( - ros-jazzy-robotnik-common-msgs - ros-jazzy-robotnik-controllers-msgs - ros-jazzy-robotnik-controllers - ) - - if ! compgen -G "${deb_dir}/*.deb" > /dev/null; then - log "No local Robotnik debs found, skipping deb installation." - return 0 - fi - - if ! dpkg-query -W "${deb_packages[@]}" >/dev/null 2>&1; then - log "Installing local Robotnik debs from ${deb_dir}" - apt-get update - apt-get install -y "${deb_dir}"/*.deb - else - log "Local Robotnik debs already installed." - fi -} - -bootstrap_workspace() { - mkdir -p "${SRC_DIR}/robotnik" - gosu "${CONTAINER_USERNAME}:${CONTAINER_GROUP}" /usr/local/bin/bootstrap-workspace.sh "${SRC_DIR}" "${EXTERNAL_REPOS_FILE}" -} - -install_rosdeps() { - if [ ! -d "${SRC_DIR}" ]; then - log "Workspace source directory ${SRC_DIR} not found, skipping rosdep install." - return 0 - fi - - log "Refreshing apt package indexes before rosdep install" - apt-get update - log "Installing remaining rosdep dependencies from ${SRC_DIR}" - rosdep install \ - --from-paths "${SRC_DIR}" \ - --ignore-src \ - -r \ - -y \ - --rosdistro "${ROS_DISTRO}" \ - --skip-keys "warehouse_ros_mongo" -} - -main() { - source_setup_file "/opt/ros/${ROS_DISTRO}/setup.bash" - source_setup_file "${WORKSPACE_DIR}/install/setup.bash" - - prepare_workspace_permissions - bootstrap_workspace - install_local_debs - install_rosdeps - - if [ "$#" -eq 0 ]; then - exec gosu "${CONTAINER_USERNAME}:${CONTAINER_GROUP}" sleep infinity - fi - - exec gosu "${CONTAINER_USERNAME}:${CONTAINER_GROUP}" "$@" -} - -main "$@" diff --git a/docker/docker.md b/docker/docker.md deleted file mode 100644 index 7d539ce..0000000 --- a/docker/docker.md +++ /dev/null @@ -1,242 +0,0 @@ -# Docker development guide - -This document describes the development-oriented Docker workflow for `robotnik_simulation` on `jazzy-devel`. - -The Docker environment prepares the workspace automatically inside the container by: - -- importing the external repositories required by `robotnik_simulation` -- installing the Robotnik-specific `.deb` packages shipped in this repository -- resolving the remaining dependencies with `rosdep` - -The workspace build remains a manual step inside the container. - -The container is intentionally configured to re-check and re-apply the local `.deb` installation and `rosdep install` steps every time it starts. This is done on purpose to keep the development environment flexible instead of assuming a fixed preconfigured runtime state. - -## Prerequisites - -Before using the Docker workflow, make sure you have: - -- Docker installed -- permission to run Docker commands with `sudo` -- access to an X11 session if you want to launch Gazebo or RViz with GUI - -Start from the repository root: - -```bash -cd ~/ros2_ws/src/robotnik/robotnik_simulation -``` - -Run every `LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) sudo docker compose ...` command from this repository root. - -## Installation and first setup - -Use this section the first time you create the Docker environment, or whenever you need to rebuild the image from source. - -### 1. Build and start the development container - -Use the Docker Compose file under `docker/` to build the image and start the container: - -```bash -LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) sudo docker compose -f docker/docker-compose.yaml up --build -``` - -This command: - -- builds the `robotnik_simulation:jazzy-devel` image -- creates the persistent workspace volumes -- starts the `robotnik_simulation_devel` container -- runs the development entrypoint, which prepares the workspace before leaving the container ready for interactive use - -> **Important**: the container is not considered ready until the entrypoint finishes importing repositories, installing local `.deb` packages and running `rosdep`. - -> **Terminal behaviour**: this command stays attached to the container output. Once the startup sequence has finished, press `Ctrl+C` if you want to recover that host terminal and continue working from other terminals with `docker exec`. - -### 2. Open a shell inside the container - -Once the container is running, open an interactive shell as the `robot` user: - -```bash -sudo docker exec -it -u robot robotnik_simulation_devel bash -``` - -Use the `robot` user for development and builds to avoid permission issues in the workspace. - -### 3. Build the workspace inside the container - -The Docker entrypoint prepares the environment, but the workspace build is still manual: - -```bash -cd ~/ros2_ws -colcon build --symlink-install -source install/setup.bash -``` - -## Daily usage - -Once the image has already been built and the workspace has been compiled at least once, normal development sessions do not require `up --build` again. - -### 1. Start the existing container again if it was stopped - -Use this command only if the container already exists and was previously stopped: - -```bash -sudo docker start -a robotnik_simulation_devel -``` - -This command keeps the current terminal attached to the container output while the development entrypoint runs again. - -If the container is already running, you do not need to call `docker start`. - -Once the startup sequence is complete, press `Ctrl+C` if you want to reuse that host terminal. The container will keep running in the background. - -### 2. Open terminal 1 inside the container - -Open a new terminal on the host and enter the running container: - -```bash -sudo docker exec -it -u robot robotnik_simulation_devel bash -``` - -When you want to leave a shell inside the container and return to the host terminal, run: - -```bash -exit -``` - -### 3. Launch Gazebo world in terminal 1 - -After the workspace is built and sourced, you can launch the simulation normally: - -```bash -cd ~/ros2_ws -source install/setup.bash -ros2 launch robotnik_gazebo_ignition spawn_world.launch.py world:=empty -``` - -If the host machine has NVIDIA graphics and you want to force Gazebo to use it from inside the Docker container, launch the world with: - -```bash -cd ~/ros2_ws -source install/setup.bash -__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia \ - ros2 launch robotnik_gazebo_ignition spawn_world.launch.py world:=empty -``` - -### 4. Open terminal 2 and spawn the robot - -`spawn_robot.launch.py` requires Gazebo to already be running, so launch it from a second shell inside the same container. - -Open another terminal on the host and enter the container again: - -```bash -sudo docker exec -it -u robot robotnik_simulation_devel bash -``` - -Then run: - -```bash -cd ~/ros2_ws -source install/setup.bash -ros2 launch robotnik_gazebo_ignition spawn_robot.launch.py robot:=rbwatcher -``` - -### 5. Optional: use the integrated bringup flow instead - -Instead of using terminal 1 for `spawn_world` and terminal 2 for `spawn_robot`, you can launch the integrated bringup flow from a single shell inside the container: - -```bash -cd ~/ros2_ws -source install/setup.bash -ros2 launch robotnik_simulation_bringup bringup_complete.launch.py robot_model:=rbsummit use_gui:=true use_rviz:=false -``` - -For more bringup examples and parameters, see [`../common/robotnik_simulation_bringup/README.md`](../common/robotnik_simulation_bringup/README.md). - -### 6. Stop the container but keep it for later reuse - -Use `stop` when you want to end the current session but keep the container available so it can be started again later with `docker start`: - -```bash -sudo docker stop robotnik_simulation_devel -``` - -### 7. Remove the Compose container - -Use `down` when you want Docker Compose to stop and remove the current container instance while keeping the image available: - -```bash -LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) sudo docker compose -f docker/docker-compose.yaml down -``` - -After `down`, the next session should start again with `docker compose ... up`, not with `docker start`. - -## Optional: GPU mode - -If the host has NVIDIA support configured for Docker, you can enable GPU access by adding the GPU override file: - -```bash -LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) sudo docker compose \ - -f docker/docker-compose.yaml \ - -f docker/docker-compose.gpu.yaml \ - up --build -``` - -When using the GPU-enabled Docker mode on hybrid Intel/NVIDIA systems, Gazebo may still need the NVIDIA offload prefixes at launch time: - -```bash -cd ~/ros2_ws -source install/setup.bash -__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia \ - ros2 launch robotnik_gazebo_ignition spawn_world.launch.py world:=empty -``` - -For a complete validation flow of driver detection, Docker GPU passthrough and OpenGL renderer selection, see [`../robotnik_gazebo_ignition/README.md#gpu-verification-guide`](../robotnik_gazebo_ignition/README.md#gpu-verification-guide). - -To stop and remove that GPU-enabled Compose container, use the same pair of files: - -```bash -LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) sudo docker compose \ - -f docker/docker-compose.yaml \ - -f docker/docker-compose.gpu.yaml \ - down -``` - -## Cleanup and rebuild - -Use this section only when you want to remove the current Docker state and start again from a clean setup. - -### 1. Remove the container manually - -If you need to force removal of the existing container: - -```bash -sudo docker rm -f robotnik_simulation_devel -``` - -### 2. Remove the generated image - -If you need to rebuild the image from scratch, remove it first: - -```bash -sudo docker rmi robotnik_simulation:jazzy-devel -``` - -### 3. Remove the persistent workspace volumes - -If you want a completely clean Docker workspace state, remove the persistent volumes: - -```bash -sudo docker volume rm \ - robotnik_simulation_ws_workspace_src \ - robotnik_simulation_ws_workspace_build \ - robotnik_simulation_ws_workspace_install \ - robotnik_simulation_ws_workspace_log -``` - -## Notes - -- If you modify `docker/Dockerfile`, `docker/devel-entrypoint.sh` or `docker/bootstrap-workspace.sh`, rebuild the image with `docker compose ... up --build`. -- If the container starts again after `docker stop`, the development entrypoint will re-check the workspace, local `.deb` installation and `rosdep` state before leaving the container ready. -- If you only modify the repository source code, the changes are visible inside the container through the bind mount and no image rebuild is required. -- The Docker workflow is intended for development on `jazzy-devel`; it is not a release image flow. -- Work is still in progress on a separate release-oriented Docker image that can be distributed through Docker Hub and used as a direct simulation launcher, configured through environment variables without requiring the development workflow described above. From 9b3b1d6777b6fc111566729a5be72318fe99f4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Gal=C3=A1n=20Avil=C3=A9s?= Date: Thu, 28 May 2026 13:03:21 +0200 Subject: [PATCH 2/7] Add docker version for launch with image prebuild --- docker/Dockerfile | 117 +++++++++++++++++++++++++-------- docker/docker-compose.gpu.yaml | 2 +- docker/docker-compose.yaml | 29 ++++---- docker/runtime-entrypoint.sh | 100 ++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 46 deletions(-) create mode 100644 docker/runtime-entrypoint.sh diff --git a/docker/Dockerfile b/docker/Dockerfile index b0192dc..2701678 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,14 +1,18 @@ -FROM osrf/ros:jazzy-desktop +FROM osrf/ros:jazzy-desktop AS builder SHELL ["/bin/bash", "-c"] -ENV DEBIAN_FRONTEND=noninteractive -ENV ROS_DISTRO=jazzy - ARG USERNAME=robot ARG USER_UID=1000 ARG USER_GID=1000 +ENV DEBIAN_FRONTEND=noninteractive +ENV ROS_DISTRO=jazzy +ENV WORKSPACE_DIR=/opt/robotnik_ws +ENV SRC_DIR=/opt/robotnik_ws/src +ENV REPO_ROOT=/opt/robotnik_ws/src/robotnik/robotnik_simulation +ENV EXTERNAL_REPOS_FILE=/opt/robotnik_ws/src/robotnik/robotnik_simulation/dependencies/repos/robotnik_simulation.jazzy.repos + COPY dependencies/requirements/builder/packages.txt /tmp/requirements/builder-packages.txt COPY dependencies/requirements/base/packages.txt /tmp/requirements/base-packages.txt @@ -42,35 +46,94 @@ RUN if ! getent group "${USER_GID}" >/dev/null; then \ else \ useradd --uid "${USER_UID}" --gid "${USER_GID}" --create-home --shell /bin/bash "${USERNAME}"; \ fi \ + && mkdir -p "/home/${USERNAME}" \ + && chown -R "${USER_UID}:${USER_GID}" "/home/${USERNAME}" /etc/ros/rosdep + +RUN gosu "${USERNAME}:${USER_GID}" bash -lc '\ + for attempt in 1 2 3; do \ + rosdep update && exit 0; \ + echo "rosdep update failed on attempt ${attempt}, retrying..."; \ + sleep 5; \ + done; \ + exit 1' + +WORKDIR ${WORKSPACE_DIR} + +RUN mkdir -p "${SRC_DIR}/robotnik" + +COPY . ${REPO_ROOT} + +RUN awk ' \ + BEGIN { skip = 0 } \ + /^ robotnik\/robotnik_simulation:/ { skip = 1; next } \ + skip && /^ [^[:space:]]/ { skip = 0 } \ + !skip { print } \ + ' "${EXTERNAL_REPOS_FILE}" > /tmp/robotnik_external.repos \ + && vcs import --skip-existing "${SRC_DIR}" < /tmp/robotnik_external.repos + +RUN if compgen -G "${REPO_ROOT}/debs/*.deb" > /dev/null; then \ + apt-get update \ + && apt-get install -y "${REPO_ROOT}"/debs/*.deb \ + && rm -rf /var/lib/apt/lists/*; \ + else \ + echo "No local Robotnik debs found, skipping deb installation."; \ + fi + +RUN source /opt/ros/${ROS_DISTRO}/setup.bash \ + && apt-get update \ + && rosdep install \ + --from-paths "${SRC_DIR}" \ + --ignore-src \ + -r \ + -y \ + --rosdistro "${ROS_DISTRO}" \ + && colcon build \ + --merge-install \ + --base-paths "${SRC_DIR}" \ + && rm -rf /var/lib/apt/lists/* + +FROM builder AS runtime + +ARG USERNAME=robot +ARG USER_UID=1000 +ARG USER_GID=1000 + +ENV HOME=/home/${USERNAME} +ENV WORKSPACE_DIR=/opt/robotnik_ws + +COPY docker/runtime-entrypoint.sh /usr/local/bin/runtime-entrypoint.sh +COPY env /opt/robotnik/config/env + +RUN chmod +x /usr/local/bin/runtime-entrypoint.sh \ + && rm -rf "${WORKSPACE_DIR}/build" "${WORKSPACE_DIR}/log" "${WORKSPACE_DIR}/src" \ + && if ! getent group "${USER_GID}" >/dev/null; then \ + groupadd --gid "${USER_GID}" "${USERNAME}"; \ + fi \ + && if id -u "${USERNAME}" >/dev/null 2>&1; then \ + usermod --uid "${USER_UID}" --gid "${USER_GID}" --home "/home/${USERNAME}" --shell /bin/bash "${USERNAME}"; \ + elif getent passwd "${USER_UID}" >/dev/null; then \ + existing_user="$(getent passwd "${USER_UID}" | cut -d: -f1)"; \ + usermod --login "${USERNAME}" --gid "${USER_GID}" --home "/home/${USERNAME}" --move-home --shell /bin/bash "${existing_user}"; \ + else \ + useradd --uid "${USER_UID}" --gid "${USER_GID}" --create-home --shell /bin/bash "${USERNAME}"; \ + fi \ && usermod -aG sudo,video,dialout "${USERNAME}" \ && if getent group render >/dev/null; then usermod -aG render "${USERNAME}"; fi \ && echo "${USERNAME} ALL=(root) NOPASSWD:ALL" > "/etc/sudoers.d/${USERNAME}" \ && chmod 0440 "/etc/sudoers.d/${USERNAME}" \ - && mkdir -p "/home/${USERNAME}/ros2_ws/src/robotnik" \ - && chown -R "${USER_UID}:${USER_GID}" "/home/${USERNAME}" - -COPY docker/devel-entrypoint.sh /usr/local/bin/devel-entrypoint.sh -COPY docker/bootstrap-workspace.sh /usr/local/bin/bootstrap-workspace.sh - -RUN chmod +x /usr/local/bin/devel-entrypoint.sh /usr/local/bin/bootstrap-workspace.sh - -RUN printf '%s\n' \ - 'if [ -f /opt/ros/${ROS_DISTRO}/setup.bash ]; then source /opt/ros/${ROS_DISTRO}/setup.bash; fi' \ - 'if [ -f /home/robot/ros2_ws/install/setup.bash ]; then source /home/robot/ros2_ws/install/setup.bash; fi' \ - > /etc/profile.d/robotnik_ros_setup.sh \ + && printf '%s\n' \ + 'if [ -f /opt/ros/${ROS_DISTRO}/setup.bash ]; then source /opt/ros/${ROS_DISTRO}/setup.bash; fi' \ + 'if [ -f /opt/robotnik_ws/install/setup.bash ]; then source /opt/robotnik_ws/install/setup.bash; fi' \ + > /etc/profile.d/robotnik_ros_setup.sh \ && chmod 0644 /etc/profile.d/robotnik_ros_setup.sh \ + && chown -R "${USER_UID}:${USER_GID}" /opt/robotnik \ && printf '%s\n' \ - 'source /etc/profile.d/robotnik_ros_setup.sh' \ - >> /root/.bashrc + 'source /etc/profile.d/robotnik_ros_setup.sh' \ + >> /root/.bashrc \ + && echo 'source /etc/profile.d/robotnik_ros_setup.sh' >> "/home/${USERNAME}/.bashrc" \ + && chown -R "${USER_UID}:${USER_GID}" "/home/${USERNAME}" USER ${USERNAME} -WORKDIR /home/${USERNAME}/ros2_ws - -RUN echo "source /opt/ros/${ROS_DISTRO}/setup.bash" >> "/home/${USERNAME}/.bashrc" \ - && echo 'if [ -f ~/ros2_ws/install/setup.bash ]; then source ~/ros2_ws/install/setup.bash; fi' >> "/home/${USERNAME}/.bashrc" \ - && rosdep update - -USER root +WORKDIR /home/${USERNAME} -ENTRYPOINT ["/usr/local/bin/devel-entrypoint.sh"] -CMD ["sleep", "infinity"] +ENTRYPOINT ["/usr/local/bin/runtime-entrypoint.sh"] diff --git a/docker/docker-compose.gpu.yaml b/docker/docker-compose.gpu.yaml index 3ea9eb6..83b8acc 100644 --- a/docker/docker-compose.gpu.yaml +++ b/docker/docker-compose.gpu.yaml @@ -1,5 +1,5 @@ services: - robotnik_simulation_devel: + robotnik_simulation: gpus: all environment: NVIDIA_VISIBLE_DEVICES: ${NVIDIA_VISIBLE_DEVICES:-all} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d111dde..c476b0d 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -1,9 +1,9 @@ -name: robotnik_simulation_ws +name: robotnik_simulation services: - robotnik_simulation_devel: - container_name: robotnik_simulation_devel - image: robotnik_simulation:jazzy-devel + robotnik_simulation: + container_name: robotnik_simulation + image: ${ROBOTNIK_SIMULATION_IMAGE:-robotnik_simulation:jazzy} build: context: .. dockerfile: docker/Dockerfile @@ -18,21 +18,14 @@ services: DISPLAY: ${DISPLAY} ROS_DISTRO: jazzy ROS_DOMAIN_ID: ${ROS_DOMAIN_ID:-0} - WORKSPACE_DIR: /home/robot/ros2_ws - SRC_DIR: /home/robot/ros2_ws/src + WORKSPACE_DIR: /opt/robotnik_ws + RUNTIME_AUTOSTART: ${RUNTIME_AUTOSTART:-true} + ROBOTNIK_ENV_FILE: /opt/robotnik/config/env/robot.env + ROBOTNIK_LAUNCH_PACKAGE: ${ROBOTNIK_LAUNCH_PACKAGE:-robotnik_simulation_bringup} + ROBOTNIK_LAUNCH_FILE: ${ROBOTNIK_LAUNCH_FILE:-bringup_complete.launch.py} + ROBOTNIK_LAUNCH_ARGS: ${ROBOTNIK_LAUNCH_ARGS:-} devices: - /dev/dri:/dev/dri volumes: - - workspace_src:/home/robot/ros2_ws/src - - workspace_build:/home/robot/ros2_ws/build - - workspace_install:/home/robot/ros2_ws/install - - workspace_log:/home/robot/ros2_ws/log - - ../:/home/robot/ros2_ws/src/robotnik/robotnik_simulation + - ../env/robot.env:/opt/robotnik/config/env/robot.env:ro - /tmp/.X11-unix:/tmp/.X11-unix - command: ["sleep", "infinity"] - -volumes: - workspace_src: - workspace_build: - workspace_install: - workspace_log: diff --git a/docker/runtime-entrypoint.sh b/docker/runtime-entrypoint.sh new file mode 100644 index 0000000..61a2743 --- /dev/null +++ b/docker/runtime-entrypoint.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +set -eo pipefail + +ROS_DISTRO="${ROS_DISTRO:-jazzy}" +CONTAINER_USERNAME="${CONTAINER_USERNAME:-robot}" +WORKSPACE_DIR="${WORKSPACE_DIR:-/opt/robotnik_ws}" +RUNTIME_AUTOSTART="${RUNTIME_AUTOSTART:-true}" +ROBOTNIK_ENV_FILE="${ROBOTNIK_ENV_FILE:-/opt/robotnik/config/env/robot.env}" +ROBOTNIK_LAUNCH_PACKAGE="${ROBOTNIK_LAUNCH_PACKAGE:-robotnik_simulation_bringup}" +ROBOTNIK_LAUNCH_FILE="${ROBOTNIK_LAUNCH_FILE:-bringup_complete.launch.py}" +ROBOTNIK_LAUNCH_ARGS="${ROBOTNIK_LAUNCH_ARGS:-}" + +log() { + echo "[runtime-entrypoint] $*" +} + +source_setup_file() { + local setup_file="$1" + if [ -f "${setup_file}" ]; then + set +u + # shellcheck disable=SC1090 + source "${setup_file}" + set -u + fi +} + +load_runtime_env_file() { + if [ ! -f "${ROBOTNIK_ENV_FILE}" ]; then + return 0 + fi + + set -a + # shellcheck disable=SC1090 + source "${ROBOTNIK_ENV_FILE}" + set +a +} + +append_launch_arg() { + local arg_name="$1" + local arg_value="$2" + + if [ -n "${arg_value}" ]; then + launch_args+=("${arg_name}:=${arg_value}") + fi +} + +resolve_world_path() { + if [ -n "${WORLD_PATH:-}" ]; then + printf '%s\n' "${WORLD_PATH}" + return 0 + fi + + if [ -n "${WORLD:-}" ]; then + local pkg_prefix + pkg_prefix="$(ros2 pkg prefix robotnik_gazebo_ignition)" + printf '%s/share/robotnik_gazebo_ignition/worlds/%s.world\n' "${pkg_prefix}" "${WORLD}" + return 0 + fi + + printf '\n' +} + +main() { + source_setup_file "/opt/ros/${ROS_DISTRO}/setup.bash" + source_setup_file "${WORKSPACE_DIR}/install/setup.bash" + load_runtime_env_file + + if [ "$#" -gt 0 ]; then + exec "$@" + fi + + if [ "${RUNTIME_AUTOSTART}" = "true" ]; then + log "Launching ${ROBOTNIK_LAUNCH_PACKAGE} ${ROBOTNIK_LAUNCH_FILE}" + if [ -n "${ROBOTNIK_LAUNCH_ARGS}" ]; then + read -r -a launch_args <<< "${ROBOTNIK_LAUNCH_ARGS}" + exec ros2 launch "${ROBOTNIK_LAUNCH_PACKAGE}" "${ROBOTNIK_LAUNCH_FILE}" "${launch_args[@]}" + fi + + launch_args=() + append_launch_arg "robot_id" "${ROBOT_ID:-}" + append_launch_arg "robot" "${ROBOT:-}" + append_launch_arg "robot_model" "${ROBOT_MODEL:-}" + append_launch_arg "robot_xacro_path" "${ROBOT_XACRO_PATH:-}" + append_launch_arg "use_gui" "${USE_GUI:-}" + append_launch_arg "low_performance_simulation" "${LOW_PERFORMANCE_SIMULATION:-}" + append_launch_arg "use_rviz" "${USE_RVIZ:-}" + append_launch_arg "frame_prefix" "${FRAME_PREFIX:-}" + append_launch_arg "run_moveit" "${RUN_MOVEIT:-}" + append_launch_arg "arm_type" "${ARM_TYPE:-}" + append_launch_arg "world_path" "$(resolve_world_path)" + + exec ros2 launch "${ROBOTNIK_LAUNCH_PACKAGE}" "${ROBOTNIK_LAUNCH_FILE}" "${launch_args[@]}" + fi + + log "Runtime autostart disabled, keeping container alive." + exec sleep infinity +} + +main "$@" From 0f6961afb8cba7b29c2f4239aec4600e74888015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Gal=C3=A1n=20Avil=C3=A9s?= Date: Thu, 28 May 2026 13:04:38 +0200 Subject: [PATCH 3/7] Fix robot enviroment for launch --- env/robot.env | 138 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 115 insertions(+), 23 deletions(-) diff --git a/env/robot.env b/env/robot.env index f08c6a0..eae4e7e 100644 --- a/env/robot.env +++ b/env/robot.env @@ -1,25 +1,117 @@ -# This file aims to set the environment variables for the robot. -# Uncomment the robot you are using to set the variables accordingly. -# Make sure to comment the other variables to avoid overriding them. +# Runtime configuration for: +# docker compose -f runtime/docker-compose.yaml up +# +# Edit the variables below. Detailed help is at the end of this file. + +ROBOT_ID=robot +ROBOT=rbwatcher +ROBOT_MODEL=rbwatcher +ROBOT_XACRO_PATH= + +USE_GUI=true +USE_RVIZ=true +LOW_PERFORMANCE_SIMULATION=true +RUN_MOVEIT=false +ARM_TYPE=ur10e -# Environment variables: -# ROBOT: Base robot platform -# Available options: -# rbkairos, rbrobout, rbsummit, rbtheron, rbvogui, rbvogui_xl -# -# ROBOT_MODEL: detailed variant/configuration. Must match ROBOT -# Available options: -# rbkairos, rbkairos_plus, -# rbrobout, rbrobout_plus, -# rbsummit, -# rbtheron, -# rbvogui, -# rbvogui_xl -# -# HAS_ARM: Indicates if the robot has a robotic arm. -# This enables arm controllers and related functions. -# Options: true, false +WORLD=demo +WORLD_PATH= -ROBOT=rbkairos -ROBOT_MODEL=rbkairos -HAS_ARM=false +FRAME_PREFIX= + + +# ============================================================================ +# Help +# ============================================================================ +# +# This file is read by `runtime/runtime-entrypoint.sh` and translated into the +# launch arguments for: +# ros2 launch robotnik_simulation_bringup bringup_complete.launch.py +# +# Notes: +# - Empty values mean "use the launch file default". +# - `WORLD_PATH` has priority over `WORLD`. +# - `ROBOT_XACRO_PATH` has priority over the xacro auto-resolved from +# `ROBOT` + `ROBOT_MODEL`. +# - Rebuild is not required when you only change this file. +# +# ROBOT_ID +# Robot instance name / namespace. +# Affects topics, TF prefixes and node namespaces. +# Example: robot, robot1, summit_a +# +# ROBOT +# Base Robotnik platform family. +# Supported values in this repo: +# rb1, rbfiqus, rbkairos, rbrobout, rbsummit, rbsummit_steel, +# rbtheron, rbvogui, rbvogui_xl, rbwatcher +# +# ROBOT_MODEL +# Specific variant for the selected robot family. +# Supported values in this repo: +# rb1, rbfiqus, rbkairos, rbkairos_plus, rbrobout, rbrobout_plus, +# rbsummit, rbsummit_steel, rbtheron, rbtheron_plus, +# rbvogui, rbvogui_plus, rbvogui_xl, rbwatcher +# +# Typical pairs: +# ROBOT=rbkairos + ROBOT_MODEL=rbkairos +# ROBOT=rbkairos + ROBOT_MODEL=rbkairos_plus +# ROBOT=rbrobout + ROBOT_MODEL=rbrobout_plus +# ROBOT=rbvogui + ROBOT_MODEL=rbvogui_plus +# +# ROBOT_XACRO_PATH +# Optional full path to a custom URDF/XACRO file. +# Leave empty to use: +# robotnik_description/robots//.urdf.xacro +# +# USE_GUI +# true: launch Gazebo GUI +# false: headless Gazebo +# +# USE_RVIZ +# true: launch RViz in the integrated bringup +# false: do not launch RViz +# +# LOW_PERFORMANCE_SIMULATION +# true: lighter simulation settings +# false: normal settings +# +# RUN_MOVEIT +# true: launch MoveIt after bringup +# false: simulation/navigation only +# +# ARM_TYPE +# Arm model for robots with manipulator. +# Common values: +# ur3e, ur5e, ur10e +# +# WORLD +# Built-in world name from `robotnik_gazebo_ignition/worlds`. +# Supported values: +# demo, empty, ionic, lightweight_scene +# +# WORLD_PATH +# Optional absolute path to a custom `.world` file. +# If set, it overrides WORLD. +# +# FRAME_PREFIX +# Optional TF prefix. +# If empty, the default is `_`. +# +# Example presets +# +# Local simulation with GUI + RViz: +# ROBOT=rbwatcher +# ROBOT_MODEL=rbwatcher +# USE_GUI=true +# USE_RVIZ=true +# +# Headless simulation: +# USE_GUI=false +# USE_RVIZ=false +# +# Kairos with arm and MoveIt: +# ROBOT=rbkairos +# ROBOT_MODEL=rbkairos_plus +# ARM_TYPE=ur10e +# RUN_MOVEIT=true From 49d34a5e2199e5a9b47c3c3328412ff712b4628f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Gal=C3=A1n=20Avil=C3=A9s?= Date: Thu, 28 May 2026 14:30:48 +0200 Subject: [PATCH 4/7] Fix docker image tag --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index c476b0d..d64997b 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -3,7 +3,7 @@ name: robotnik_simulation services: robotnik_simulation: container_name: robotnik_simulation - image: ${ROBOTNIK_SIMULATION_IMAGE:-robotnik_simulation:jazzy} + image: ${ROBOTNIK_SIMULATION_IMAGE:-robotnik/simulation-gz:jazzy} build: context: .. dockerfile: docker/Dockerfile From 7b5bf25f01ec9f16a17d28cf82f5e7c0e3dc3980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Gal=C3=A1n=20Avil=C3=A9s?= Date: Thu, 28 May 2026 15:44:00 +0200 Subject: [PATCH 5/7] Add readme for docker launch --- docker/README.md | 136 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 docker/README.md diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..baeeea5 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,136 @@ +# Docker guide + +This directory contains the Docker files used to run `robotnik_simulation`. + +The simulation configuration is defined in: + +```bash +../env/robot.env +``` + +Edit that file to choose the robot, world, GUI, RViz and other bringup options before launching the container. + +## Prerequisites + +- Docker must be installed. +- Your user should have permission to use Docker. +- If your user is not in the Docker group, run the commands below with `sudo`. + +## 1. Run the published image + +Use this option when the image is already published in Docker Hub or another registry. + +From the repository root: + +```bash +cd ~/ros2_ws/src/robotnik/robotnik_simulation +``` + +Pull the image: + +```bash +docker pull robotnik/simulation-gz:jazzy +``` + +Launch the simulation: + +```bash +ROBOTNIK_SIMULATION_IMAGE=robotnik/simulation-gz:jazzy \ +docker compose -f docker/docker-compose.yaml up +``` + +If you prefer, you can also export `ROBOTNIK_SIMULATION_IMAGE` in the shell before launching. + +To stop the published-image run started with `docker compose ... up`, press `Ctrl+C` once to stop the simulation gracefully. If the container does not exit cleanly, press `Ctrl+C` again to force it to stop. + +## 2. Build a new image locally + +Use this option when you want to generate the image from source with the current contents of the repository. + +From the repository root: + +```bash +cd ~/ros2_ws/src/robotnik/robotnik_simulation +``` + +Build the image: + +```bash +LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) docker compose -f docker/docker-compose.yaml build +``` + +Launch the simulation with the locally built image: + +```bash +docker compose -f docker/docker-compose.yaml up +``` + +You can also build and launch in a single step: + +```bash +LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) docker compose -f docker/docker-compose.yaml up --build +``` + +## 3. Daily workflow + +Once the container is running, keep the first terminal attached to: + +```bash +docker compose -f docker/docker-compose.yaml up +``` + +That terminal shows the bringup logs and lets you stop the session with `Ctrl+C`. + +If you want to work inside the running container, open a second terminal and enter it as the `robot` user: + +```bash +docker exec -it -u robot robotnik_simulation bash +``` + +Inside that shell you can inspect topics, launch extra nodes or interact with the simulated robot using normal ROS 2 commands such as: + +```bash +ros2 topic list +ros2 node list +ros2 topic echo /clock +``` + +If you prefer to leave the simulation running in the background, start it detached: + +```bash +docker compose -f docker/docker-compose.yaml up -d +``` + +Then attach a shell with `docker exec` whenever you need to work inside the container. + +To stop the running container without removing it: + +```bash +docker stop robotnik_simulation +``` + +To start that same stopped container again and reattach to its logs: + +```bash +docker start -a robotnik_simulation +``` + +To stop and remove the Compose container completely: + +```bash +docker compose -f docker/docker-compose.yaml down +``` + +To remove the local image: + +```bash +docker rmi robotnik/simulation-gz:jazzy +``` + +If you are using a published image name instead of the default local tag, remove that specific image tag instead. + +## Notes + +- Changing `../env/robot.env` does not require rebuilding the image. +- Changing source code, dependencies, manifests or `docker/Dockerfile` does require rebuilding the image. +- For NVIDIA systems, use `docker/docker-compose.gpu.yaml` together with the main compose file. From 0b9685ec32e4a5ae43d04165584c63eaf6452770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Gal=C3=A1n=20Avil=C3=A9s?= Date: Thu, 28 May 2026 15:44:55 +0200 Subject: [PATCH 6/7] Update docker information --- README.md | 11 +++++++++++ robotnik_gazebo_ignition/README.md | 9 +++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b8ac6d2..eaaf15b 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,17 @@ Simulation-based environments and launch assets for Robotnik platforms on ROS 2. Before launching the simulation, ensure that the installation steps for one of the available simulators listed above have been completed. +### Docker + +If you want to run the integrated simulation in Docker, the current workflow is documented in [`docker/README.md`](docker/README.md). + +That guide covers: + +- running the published image from Docker Hub +- building a local image from the current workspace +- editing [`env/robot.env`](env/robot.env) to choose robot, world, GUI and RViz options +- opening a second terminal with `docker exec` to interact with the running simulation from inside the container + ### Bringup Launch complete simulation: diff --git a/robotnik_gazebo_ignition/README.md b/robotnik_gazebo_ignition/README.md index 2d4d017..4dd4565 100644 --- a/robotnik_gazebo_ignition/README.md +++ b/robotnik_gazebo_ignition/README.md @@ -6,7 +6,7 @@ This package provides the Gazebo-based simulation layer for Robotnik robots on R > **Branch-specific guide**: `robotnik_gazebo_ignition` is maintained across ROS 2 distro branches, but the Gazebo version changes with each branch. This README documents only the validated workflow for `jazzy-devel`: ROS 2 Jazzy + Gazebo Harmonic. For conceptual background about architecture, compatibility and versioning, see [`../docs/ros2-gazebo-compatibility.md`](../docs/ros2-gazebo-compatibility.md). > -> **Docker guide**: the full development Docker workflow is documented separately in [`../docker/docker.md`](../docker/docker.md). +> **Docker guide**: the full development Docker workflow is documented separately in [`../docker/README.md`](../docker/README.md). ## What this package includes @@ -367,12 +367,13 @@ The package includes control profiles under `robotnik_gazebo_ignition/config/pro ## Docker -The full development Docker workflow is documented in [`../docker/docker.md`](../docker/docker.md). +The full development Docker workflow is documented in [`../docker/README.md`](../docker/README.md). Use that guide for: -- container creation and rebuilds -- daily development usage +- running the published image or building a local one +- editing the runtime configuration in [`../env/robot.env`](../env/robot.env) +- opening a second terminal with `docker exec` to interact with the running simulation - GPU-enabled Docker sessions - cleanup and reset commands - notes about the future release-oriented Docker image workflow From b1a55d140daf8569754ba9c24d9fb0843f7d3c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Gal=C3=A1n=20Avil=C3=A9s?= Date: Thu, 28 May 2026 16:03:07 +0200 Subject: [PATCH 7/7] Fix the description of docker building --- README.md | 4 ++-- docker/README.md | 23 ++++++++++++++++++++++- robotnik_gazebo_ignition/README.md | 4 ++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index eaaf15b..8262275 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ If you want to run the integrated simulation in Docker, the current workflow is That guide covers: - running the published image from Docker Hub -- building a local image from the current workspace +- building an image that copies only this repository from the local machine and pulls any extra required repositories from `dependencies/repos/robotnik_simulation.jazzy.repos` - editing [`env/robot.env`](env/robot.env) to choose robot, world, GUI and RViz options -- opening a second terminal with `docker exec` to interact with the running simulation from inside the container +- opening a second terminal with `docker exec` to interact with the running simulation from inside the runtime container ### Bringup diff --git a/docker/README.md b/docker/README.md index baeeea5..abbd78f 100644 --- a/docker/README.md +++ b/docker/README.md @@ -10,6 +10,25 @@ The simulation configuration is defined in: Edit that file to choose the robot, world, GUI, RViz and other bringup options before launching the container. +## Workspace model + +This Docker setup does not run directly from your host ROS 2 workspace as a bind-mounted development tree. + +When the image is built, it creates an internal workspace at `/opt/robotnik_ws` and: + +- copies only this `robotnik_simulation` repository from your local machine into that workspace +- imports the additional Robotnik repositories declared in `dependencies/repos/robotnik_simulation.jazzy.repos` +- resolves dependencies and builds the workspace inside the image + +The runtime container uses the installed workspace from `/opt/robotnik_ws/install`. It does not keep the `src` tree from the build stage. + +That means: + +- changes in your local repository are not reflected in the container until you rebuild the image +- other local packages from your host workspace are not included automatically +- if the image needs additional repositories, they must be added to `dependencies/repos/robotnik_simulation.jazzy.repos` +- `docker exec` gives you access to the runtime environment and installed packages, not to a live bind-mounted source workspace + ## Prerequisites - Docker must be installed. @@ -45,7 +64,7 @@ To stop the published-image run started with `docker compose ... up`, press `Ctr ## 2. Build a new image locally -Use this option when you want to generate the image from source with the current contents of the repository. +Use this option when you want to generate the image from the current contents of this repository. Any additional repositories that must be part of the image need to be declared in `dependencies/repos/robotnik_simulation.jazzy.repos`. From the repository root: @@ -95,6 +114,8 @@ ros2 node list ros2 topic echo /clock ``` +This shell is attached to the runtime container built from the installed workspace in `/opt/robotnik_ws/install`. + If you prefer to leave the simulation running in the background, start it detached: ```bash diff --git a/robotnik_gazebo_ignition/README.md b/robotnik_gazebo_ignition/README.md index 4dd4565..434c73a 100644 --- a/robotnik_gazebo_ignition/README.md +++ b/robotnik_gazebo_ignition/README.md @@ -371,9 +371,9 @@ The full development Docker workflow is documented in [`../docker/README.md`](.. Use that guide for: -- running the published image or building a local one +- running the published image or building an image that copies only this repository from the local machine and pulls any extra required repositories from [`../dependencies/repos/robotnik_simulation.jazzy.repos`](../dependencies/repos/robotnik_simulation.jazzy.repos) - editing the runtime configuration in [`../env/robot.env`](../env/robot.env) -- opening a second terminal with `docker exec` to interact with the running simulation +- opening a second terminal with `docker exec` to interact with the running simulation inside the runtime container - GPU-enabled Docker sessions - cleanup and reset commands - notes about the future release-oriented Docker image workflow