Tuesday 13 August 2024

ROS2 with Docker Compose on a Raspberry Pi 5

Continuing our learning of ROS2 running in Docker containers on top of Raspberry Pi OS (see previous post in this blog). The management of the Docker containers can be further simplified with the Docker Compose tool. If you followed the Docker installation instructions in my previous post then you already have Docker Compose installed. So you can get straight on with using it.
Docker Compose allows us to manage multiple containers with a single configuration file. A Docker network is automatically created so the containers can communicate with each other. The tool works from a yaml file named docker-compose.yml which contains the complete configuration of each container.

In the previous post, we created our container with a command line containing several parameters. These can be put into the docker-compose.yml file to simplify the process.
Starting with an empty directory, create a new file named docker-compose.yml with the following contents:

services:

    sim:

      image: ros_docker:latest

      command: ros2 run turtlesim turtlesim_node

      environment:

        DISPLAY:

        QT_X11_NO_MITSHM: 1

      volumes:

        - /tmp/.X11-unix:/tmp/.X11-unix:rw


I put this file in a folder named 'jazzy'. Docker Compose will use the folder name to generate the container names. This docker-compose.yml file contains a single container definition 'sim'.
From a terminal in the jazzy folder, run the command:

docker compose up -d


This will create a new container, start it up and run the command to launch the turtlesim_node in the turtlesim package. The '-d' option runs the commands in the background, returning the terminal prompt to you (otherwise the terminal window is tied up until the node is terminated).

You should see the TurtleSim window open. Run the command docker ps to see what containers are running. You should see a new container was created named 'jazzy-sim-1'. This time instead of being randomly generated the container name has been taken from the folder name, and service name used. As it is the first instance, it is appended with '-1'.

If you close the TurtleSim window, the container will exit as the node it was running has terminated. If you run the 'docker compose up -d' command again it will restart the existing container, rather than creating a new one each time, compared to the 'docker run' command we used previously.
We can add more services to run additional containers and launch them as a group. Extend the docker-compose.yml file to contain the following:


services:

    sim:

      image: ros_docker:latest

      command: ros2 run turtlesim turtlesim_node

      environment:

        DISPLAY:

        QT_X11_NO_MITSHM: 1

      volumes:

        - /tmp/.X11-unix:/tmp/.X11-unix:rw

    dev-build:

      image: ros_docker:latest

      command: rqt

      environment:

        DISPLAY:

        QT_X11_NO_MITSHM: 1

      volumes:

        - /tmp/.X11-unix:/tmp/.X11-unix:rw


Now when you run 'docker compose up -d' you should see you have 2 containers, one named 'jazzy-sim-1' and a second named 'jazzy-dev-build-1' running the rqt tool. As these are automatically in the same docker network, the rqt tool can control the TurtleSim node as in the ROS Tutorials. If you close one of the tool windows (stopping its container) and run 'docker compose up -d' again, it will detect that one of the containers is already running, and just launch the one which is stopped.


As before, you can open an interactive shell in these containers, using the exec command:

docker exec -it jazzy-dev-build-1 bash


This allows us to set up the shell to source the ROS packages as before, by running the following command from the shell:

echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc


Now exit the shell, and use docker exec to start a new one. You should be able to run ROS2 commands from the shell now. Interestingly, it was not necessary to source the setup.bash script in the docker-compose.yml file to run the ROS2 commands there. I am not clear why it works, but it does so I'm happy with it.

Access to the file system of the Host

Now we can use Docker Compose to manage our containers, we can take a look at mapping the host Pi file system inside our containers. Following the Client Libraries tutorial at: https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Colcon-Tutorial.html we need to create a folder on our host Pi called 'ros2_ws'. I created this folder in my home directory. Next add the path to this folder on the host, to the path '/ros2_ws' in the dev-build container, by changing the container definition in the docker-compose.yml file to the following:

    dev-build:

      image: ros_docker:latest

      command: rqt

      environment:

        DISPLAY:

        QT_X11_NO_MITSHM: 1

      volumes:

        - /tmp/.X11-unix:/tmp/.X11-unix:rw

        - ~/ros2_ws:/ros2_ws


Stop the container (by closing the rqt tool window) and start it again by running the 'docker compose up -d' command. Now if you open a shell in the jazzy-dev-build-1 container, you should see a folder /ros2_ws which will be mapped to the folder ~/ros2_ws on the host. This should enable you to run the package building tutorials with the files being saved outside your container. So if your container is ever destroyed, the files are not lost when a new container is created by Docker Compose. Note that as we have changed our docker-compose.yml file, Docker Compose will detect the change and destroy the old container and recreate it. So once it starts up again, we will need to source our ROS2 packages again (just once if we put the command into the bash.rc file again).

In the next post, we will look at using a game controller in ROS in a Docker container.

No comments:

Post a Comment