Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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 runtime container

### Bringup

Launch complete simulation:
Expand Down
117 changes: 90 additions & 27 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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"]
157 changes: 157 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# 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.

## 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.
- 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 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:

```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
```

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
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.
31 changes: 0 additions & 31 deletions docker/bootstrap-workspace.sh

This file was deleted.

Loading