How to run Ubuntu on VOXL using Docker

Table of contents

  1. Overview
  2. Requirements
  3. Configuration
  4. Download & Push a Docker Image to VOXL
  5. Running a Docker Image
  6. Running a Docker Image with the voxl-docker tool
  7. optional: give the image your own tag
  8. Making Changes and Archive the image
  9. Configuring a program to run automatically inside a docker on boot
    1. hello world
    2. mavros
  10. Building Your Own Docker Image for VOXL
  11. Troubleshooting
  12. Access to the GPU
  13. Access to MIPI cameras
  14. Access to the DSPs

Overview

This document provides an overview of how to run docker containers on your VOXL developer board!

Alongside a copy of docker itself, the VOXL Software Bundle includes voxl-docker-support which provides a hello-world Docker image along with tools to configure and enable Docker on VOXL.

Dependencies to run Docker on VOXL are contained in VOXL Factory Bundle as of version 0.0.2

Requirements

Follow the VOXL Quickstarts and make sure the following items are installed on VOXL:

Configuration

After installing the required system image and bundles, run the configuration script:

yocto:/# voxl-configure-docker-support.sh
Stopping original docker service
Enabling our own services docker-start & docker-prepare
starting docker-start.service
loading hello-world docker image
successfully loaded hello-world
starting docker-prepare service

done configuring voxl-docker-support

Now you can list the available images.

yocto:/# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED                  VIRTUAL SIZE
hello-world         latest              efc161607398        Less than a second ago   2.088 kB

Try running the hello-world image!

yocto:/# docker run hello-world

Hello from Docker on arm64!
This message shows that your installation appears to be working correctly.

Download & Push a Docker Image to VOXL

Download the pre-built docker image roskinetic-xenial_v1_0.tgz from Modal-AI Downloads. To build this image yourself from docker files, follow the instructions here. For the remainder of these instructions, it’s easier to use the pre-built image for now.

VOXL’s flash memory is broken up into partitions, the largest of which is /data/ which is 16GB. voxl-docker-support already configures docker to use this partition for storage. When pushing a large docker image to VOXL you should also push to this directory.

The following steps push an image and load it on VOXL. This will be slow so we suggest using a USB3.0 cable if you have one.

me@mylaptop:~$ adb push roskinetic-xenial_v1_0.tgz /data/
me@mylaptop:~$ adb shell
/ # bash
yocto:/# cd /data/
yocto:/data# docker load -i roskinetic-xenial_v1_0.tgz

Now look to make sure the image is loaded

yocto:/data# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
hello-world         latest              efc161607398        3 years ago          2.088 kB
roskinetic-xenial   v1.0                ef8f2f502423        About a minute ago   1.633 GB

Running a Docker Image

When running an image, we suggest telling docker to mount the home directory in Yocto to a directory inside the docker image to facilitate transferring data in and out. Here, we mount /home/root/ in Yocto to /root/yoctohome/ in docker. We also suggest using the –net=host option so processes such as ROS running inside Docker can transparently communicate with processes outside of the Docker. With this method ROS nodes can run and communicate both inside and outside of a Docker image seamlessly.

docker run -it --rm --privileged --net=host --name roskinetic -v /home/root:/root/yoctohome/:rw -w /root/ roskinetic-xenial:v1.0 /bin/bash

For a complete list of arguments, see the Official Docker Documentation. However, for the purposes of running docker images on VOXL, here are the arguments you will likely wish to use:

Argument Description
-it Interactive mode, usually used when you wish to open a bash shell inside the docker.
–rm Automatically remove the container when it exits to prevent containers stacking up.
–privileged Gives the docker container access to /dev/
–net=host Lets network interfaces appear the same inside and outside of the docker container.
–name {name} Give a name to the running instance so you can identify and attach to it by name.
-v {outside}:{inside}:rw Mounts a directory outside the container to a directory inside the container.
-w {working_dir} Set the working directory inside the container.

Running a Docker Image with the voxl-docker tool

If you are using voxl-docker-support >= v1.1 as included with voxl-software-bundle 3.0 you can launch the docker image with voxl-docker which is faster to type and will mount your Yocto home directory inside the docker image at ~/yoctohome/

yocto:/home/root# voxl-docker -i roskinetic-xenial:v1.0
roskinetic:~$ ls
ros_catkin_ws  yoctohome
roskinetic:~$

optional: give the image your own tag

To save time, you can tag the docker image you just loaded with a shorter name.

yocto:/home/root# docker tag roskinetic-xenial:v1.0 kinetic
yocto:/home/root# voxl-docker -i kinetic
roskinetic:~$ ls
ros_catkin_ws  yoctohome
roskinetic:~$

Making Changes and Archive the image

Most of the time, you should be working in the shared directory /root/yoctohome/ inside the docker image so that changes you make in this directory are saved when you close the docker instance. However. if you wish you make changes to the docker image itself such as installing a new package, you need to commit these changes.

In one terminal, start the docker image and make the changes you want.

yocto:/home/root# voxl-docker -i roskinetic-xenial:v1.0
roskinetic:~$ sudo apt install htop

Now, leave this session running and in a second terminal use docker ps to see the running container instance.

yocto:/# docker ps
CONTAINER ID        IMAGE                    COMMAND             CREATED
616362e8cdb1        roskinetic-xenial:v1.0   "/bin/bash"         About a minute ago

Then copy the container id of the instance you want to commit and use docker commit to save the changes. You can see a new image has been made right away.

yocto:/# docker commit 616362e8cdb1 roskinetic-xenial:htop
40869821b17f1b49196b354a3bbdf9fab70bdb4310b0b20d8470a361fa19ac59
yocto:/# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
roskinetic-xenial   htop                40869821b17f        29 seconds ago      1.635 GB
roskinetic-xenial   v1.0                0e12749bdc00        2 hours ago         1.633 GB

To back up your newly modified image and transfer to other devices you can save it in an archive. The output of the following will match the prebuilt image we distribute at downloads.modalai.com

yocto:/# docker save roskinetic-xenial:v1.0 | gzip > /data/roskinetic-xenial_v1_0.tgz

Configuring a program to run automatically inside a docker on boot

The voxl-docker-support package includes a systemd service called docker-autorun which, once enabled, will execute the /etc/modalai/docker-autorun.sh script. Put whatever you like in that autorun script, but it’s intended for things running inside docker containers.

The docker_autorun.sh script contains two examples:

hello world

This example just starts the hello-world docker image which does nothing except print to the screen and exit. Note that we run in non-interactive mode with the -n argument and set the entrypoint as an empty string with the -e argument as we don’t want it executing anything

voxl-docker -n -i hello-world -e ""

mavros

Assuming you have following the mavros instructions, this example runs mavros automatically on boot. Note we use the -w argument to set the working directory to where the run_mavros.sh script is located.

voxl-docker -n -i roskinetic-xenial:v1.0 -w /root/yoctohome/mavros_test/ -e "/bin/bash run_mavros.sh"

Building Your Own Docker Image for VOXL

To build this image from docker files, follow the instructions here.

Troubleshooting

If you have issues, check the two systemd services provided by voxl-docker-support. Most likely you just need to reboot.

yocto:/# systemctl status docker-start
● docker-start.service
   Loaded: loaded (/etc/systemd/system/docker-start.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 1970-01-01 00:00:06 UTC; 11min ago

yocto:/# systemctl status docker-prepare
● docker-prepare.service
   Loaded: loaded (/etc/systemd/system/docker-prepare.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Thu 1970-01-01 00:00:36 UTC; 11min ago
  Process: 3691 ExecStart=/usr/bin/docker-prepare.sh (code=exited, status=0/SUCCESS)
 Main PID: 3691 (code=exited, status=0/SUCCESS)

Access to the GPU

OpenCL is used to access the GPU. For example, OpenCV can be built with OpenCL support to accelerate certain operations using the GPU hardware. To get OpenCL working in Docker use the example project located here to get started.

Access to MIPI cameras

The MIPI cameras are accessed using Qualcomm proprietary 32-bit libraries and services that are not available in the Docker environment. There are a few workarounds to this.

  1. Use a ROS node outside of the Docker to publish camera frames and put ROS node(s) inside of the Docker that subscribe to the camera frames.
  2. Place frames into a named FIFO outside of the Docker and then pull the frames from the FIFO inside the Docker. An example of this can be seen in the hellocamera application in apps-proc-examples. The hello camera application has a -l option to specify the name of a pipe to use.
    hello_hal3_camera -l /dev/frame0
    

    Once this is running then you can map the frame FIFO into a docker container.

    docker run -it --rm --privileged -v /dev/frame0:/opt/frame0 <docker image name>
    

    Applications like FFmpeg running in the Docker can now access the frame FIFO.

    ffmpeg -an -sn -f rawvideo -pix_fmt nv21 -s:v 640x480 -i /opt/frame0 <output options>
    

Access to the DSPs

The sensors DSP (sDSP) and applications DSP (aDSP) communicate to the CPUs via an RPC mechanism that has not yet been ported to the Docker environment.