ROS 2 on Jetson Nano using Docker

Why ROS 2 and Docker

One of my primary objectives is to acquire knowledge on ROS 2, and I strongly believe that this project would be an excellent opportunity to utilize ROS 2. Moreover, I am convinced that Docker could play a crucial role in this project, providing multiple benefits as follows:

Docker can facilitate cross-compiling and building applications that are compatible with both desktop machine (X86/amd64) and Jetson Nano (arm64) devices. This is especially advantageous as it allows for fast compilation on powerful desktop machines, which can then be used on the low-power Jetson Nano processor.

Finally, with Docker, I can conveniently package all the application files along with their dependencies, simplifying deployment across multiple DonkeyJets.

Multiplatform Docker Build

Being able to create a docker image that works on multiple platforms is crucial. One question that arises is why it’s necessary to support a X86 machine if the intention is to use the application on Jetson Nano. I have three reasons for this. First, sometimes the computational demands of certain algorithms are too high for Jetson Nano, so some of the ROS nodes will run on a desktop machine on the same network. Therefore, we require the same ROS 2 environment on the desktop. Second, the robot lacks a screen, and for debugging purposes, it’s necessary to visualize certain data, sensors, or messages. In such cases, I’ll use the ROS environment on the desktop. Third, it is cool 🙂

In this blog I would like to cover building our base docker images that has both CUDA and ROS 2 for both arm64 and amd64 machines.

 I am using two different docker image as the base. One that runs on amd64 and one that run on arm64. For the amd64 I am using nvcr.io/nvidia/cudagl:11.4.2-devel-ubuntu20.04 and for the arm64 I am using nvcr.io/nvidia/l4t-jetpack:r35.2.1 . There are clever ways to fit these two base images into a single docker file. 

I am using docker buildx to build images for multiple platforms.

Here is complete docker file and we are discussing about it below. I saved this file as ros2_base.Dockerfile

It’s worth noting that the initial three lines of the Dockerfile are crucial in ensuring compatibility with both amd64 and arm64 architectures. Additionally, it’s important to remember that this Dockerfile will be used to create two distinct images, not just one. Throughout the build process, docker buildx defines the ${TARGETARCH} variable based on the specified platforms in the build command, and selects the appropriate container accordingly based on the naming convention.

We are building ROS 2 from the source in this Dockerfile based on Ubuntu 18.04. It is possible to start with Ubuntu 20.04 as base and directly use the binary packages to install ROS 2 but we would lose the use of CUDA on Jetson Nano since there is no docker image for Jetson Nano with Ubuntu 20.04 and CUDA support (At least I couldn’t make it work). The CUDA version inside the docker image would be higher than the CUDA version in the host Jetson and then none of the CUDA API calls would work properly inside the container (learned the hard way). To test that I used CUDA Samples git repository, specifically queryDevice example to make sure it returns as expected inside the container on the Jetson Nano.

Before building and pushing the images, it's important to determine where they will be pushed. While Docker Hub is commonly used, private Docker registries are also a viable option. Here are the commands to build the docker images and push them to the registry.
# define a registry to push the images to
export REGISTRY=<your dockerhub username>
# create new buildx that support multiple platforms
docker buildx create --use  --driver-opt network=host --name MultiPlatform

# build the image for two different platforms and push the images
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -f ros2_base.Dockerfile \
  -t ${REGISTRY}/ros2_base:latest \
  --push .

Keep in mind that the execution of this command may require a considerable amount of time to complete. This is due to the fact that it’s building two distinct images, and using an emulator to build for the arm64 platform on an amd64 machine. Additionally, certain commands take some time to execute since we are compiling ROS 2 foxy from source in the docker image.

We can run this image either on the desktop or the Jetson Nano with the following command.
docker run \
  --rm \
  -it \
  --runtime nvidia \
  --network host \
  --gpus all \
  -e DISPLAY \
  ${REGISTRY}/ros2_base:latest \
  bash

Conclusion

At this point, we should have a foundational Docker image that meets our requirements – it includes CUDA and ROS 2, and can be executed on both amd64 and arm64 platforms. Moving forward, we’ll utilize this image to run ROS 2 nodes, and I’ll also create additional docker images that build on top of it, such as those that support RealSense sensors. It’s an exciting time!

Leave a Comment

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