Installing ROS in LXD Containers
sidfaber
on 29 June 2020
It’s the season for updates. The last few weeks have ushered in ROS 1 Noetic and ROS 2 Foxy, both of which target the recently released Ubuntu 20.04 Focal Fossa. As always, new releases come with trepidation: how can I install new software and test compatibility, yet keep my own environment stable until I know I’m ready to upgrade? This is one of the many good reasons to dive into containers
In this blog post we’ll create a base LXD profile with the ROS software repositories and full graphical capabilities enabled. Launch containers to meet your robotics needs: everything from software development and system testing through robot operations can be covered within containers.
Check out our youtube video to see these instructions in action. Also see the full installation instructions for both ROS 1 and ROS 2 available at ros.org
Getting started
All you need to get started is a Linux workstation with LXD installed. The installation of LXD is not covered here as there are a number of great instructions online. See the getting started guide at linuxcontainers.org for more information.
We’ll cover hereafter the three basic steps to getting set up:
- Creating a LXD container profile
- Launching and connecting to the container
- Installing ROS
Create a profile
All LXD containers have a defined profile. A default profile was created when LXD was first set installed, but we will create a ROS specific profile. This will support running ROS (either version 1 or 2) on an Ubuntu image.
The profile contains four specific configuration features:
- ROS software repositories
- Run X apps within the container
- Networking
- A disk storage device
Gather data
In order to set up the profile properly, we must first collect your workstation’s network adapter, and the user/group ID for your account. Find your network adapter using the ip addr show
command:
~:$ ip addr show
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: wlp2s0: mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether a8:00:0b:c0:88:e7 brd ff:ff:ff:ff:ff:ff
3: enx001a98a552d4: mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:1a:98:a5:52:d4 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 brd 192.168.1.255 scope global noprefixroute enx001a98a552d4
valid_lft forever preferred_lft forever
The above example shows three network adapters: loopback, wireless and ethernet (respectively). Select the adapter to use for the container; for this case we will use the wired adapter enx001a98a552d4
To find the id of your non-root account, use the id
command:
~:$ id
uid=1001(sid) gid=1001(sid) groups=1001(sid),4(adm),24(cdrom),27(sudo), ...
In the example above my user id “sid” has a uid and gid of 1001.
Create the ROS Profile
Armed with these two key facts, we can create and edit the LXD profile for our ROS containers:
lxc profile create ros
lxc profile edit ros
This brings up a default profile template for editing within vi. Update the file as follows, then save and exit vi:
config:
environment.DISPLAY: :0
raw.idmap: both [your group id] 1000
user.user-data: |
#cloud-config
runcmd:
- "apt-key adv --fetch-keys 'https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc'"
- "apt-add-repository 'http://packages.ros.org/ros/ubuntu'"
- "apt-add-repository 'http://packages.ros.org/ros2/ubuntu'"
description: ROS
devices:
X0:
path: /tmp/.X11-unix/X0
source: /tmp/.X11-unix/X0
type: disk
eth0:
name: eth0
nictype: macvlan
parent: [your network adapter]
type: nic
root:
path: /
pool: default
type: disk
name: ros
Let’s break down the configuration file and look at each part individually.
Configure the environment
The following line sets the DISPLAY environment variable required by X. The display within the container is always mapped to ”’:0”’.
environment.DISPLAY: :0
Our linux containers will run under the security context of the current user, but all work within the Ubuntu containers will be done under the default ubuntu
user account. The raw.imap
setting maps our workstation’s user and group ID (1001 in this example) into the container’s user and group ID (always 1000 for the default user):
raw.idmap: both 1001 1000
Configure ROS repositories
The ROS software repositories can be added to the container every time a new container is launched. The simplest way to achieve this is to use cloud-init, and add a runcmd
to the user-data section of the profile. Each of these commands will be executed whenever a new container is initialized using this profile.
The apt-key command pulls the ROS distribution signing key from github, while the two add-repository commands add the ROS 1 and ROS 2 software repositories.
user.user-data: |
#cloud-config
runcmd:
- "apt-key adv --fetch-keys 'https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc'"
- "apt-add-repository 'http://packages.ros.org/ros/ubuntu'"
- "apt-add-repository 'http://packages.ros.org/ros2/ubuntu'"
Configure devices
Adding the X0 device to the profile allows X data to flow between the container (“path”) and the host (“source”). If you have multiple graphics cards check the contents of /tmp/.X11-unix
to make sure you’re mapping to the correct source; the source should mirror the $DISPLAY
environment variable from your host.
devices:
X0:
path: /tmp/.X11-unix/X0
source: /tmp/.X11-unix/X0
type: disk
If you have a separate graphics card with a discrete GPU, you may also find it necessary to add the GPU as a device:
mygpu:
type: gpu
name: gui
To learn more about running graphical apps in LXD containers, including some caveats when working with an NVidia GPU, take a look at this blog post by Simos Xenitellis.
Multiple options exist for networking. Although a container can use LXD’s built in address translation or a bridge device, our profile will use the macvlan network driver. Macvlan creates a simple bridge between the parent network adapter and the container so the container receives its own IP address on the host network. With this setup no additional configuration (e.g., port forwarding) is required for either ROS 1 or ROS 2.
The network interface (nic) device configured here uses macvlan to connect the parent network adapter enx001a98a552d4
to the container’s eth0 interface:
eth0:
name: eth0
nictype: macvlan
parent: enx001a98a552d4
type: nic
Take a look at this post if you need more information about LXD networking with macvlan.
A disk image must be connected to the container. This disk device simply uses the lxd default data pool:
root:
path: /
pool: default
type: disk
Launch a container
The lxc launch
command is used to launch a container for the first time. Use the following command to create and run an Ubuntu 20.04 container named “ros-foxy”:
lxc launch -p ros ubuntu:20.04 rosfoxy
Once the container is running, logging in is achieved by simply executing a shell within the container:
lxc exec rosfoxy -- bash
This connects to a shell on the container under the root userid; however, our configuration uses the ubuntu user account. In order to connect to a shell as the ubuntu user, execute the su command within the container:
lxc exec ros -- su --login ubuntu
This tends to be a bit cumbersome to type for every connection to the container. LXD aliases provide an easy way to simplify the command, and also make it more generally applicable. Shorten the command and make it more robust by creating an LXD alias using this command:
lxc alias add ubuntu 'exec @ARGS@ --user 1000 --group 1000 --env HOME=/home/ubuntu/ -- /bin/bash --login'
For more information about the options used with this alias, see this blog post from Simos Xenitellis.
Now connecting to an Ubuntu container with the proper context is as simple as the following command:
lxc ubuntu rosfoxy
Install ROS
Since the ROS repositories have been set up, installation is as simple as an apt-install command for the correct software bundle:
sudo apt install ros-foxy-ros-desktop
Since this container will always run ROS, we can source the ROS environment upon every login by adding it to our .bashrc startup script.
echo "source /opt/ros/foxy/setup.bash" >> ~/.bashrc
source ~/.bashrc
Most ROS commands support context-sensitive tab completion. Although not required, installing python3-argcomplete
will make typing commands much easier:
sudo apt install python3-argcomplete
Now that the installation is complete, try it out by running rqt
or a similar graphical application.
Conclusion
Using this ROS profile, building environments to work with different releases becomes trivial:
lxc launch -p ros ubuntu:20.04 rosnoetic
lxc ubuntu rosnoetic
sudo apt install ros-noetic-desktop-full
LXD provides a number of handy commands for working with containers. For instance we can clone a container by simply using the lxc copy command:
lxc copy rosfoxy rosfoxy-2
When work with the container is complete, simply remove it:
lxc delete rosfoxy-2
Share files between the host and the container, map USB devices–think robotics hardware–to the container with additional LXD configurations.
The possibilities for containers are only limited by your imagination. For some ideas on how to use LXD containers for ROS development, check out this blog post by Ted Kern. We’d appreciate any comments you have on your experiences using LXD containers with ROS!
Ubuntu cloud
Ubuntu offers all the training, software infrastructure, tools, services and support you need for your public and private clouds.
Newsletter signup
Are you building a robot on top of Ubuntu and looking for a partner? Talk to us!
Related posts
Deploying scalable AI and real-time robots at ROSCon 24
Another year, another ROSCon! This year we’re setting off to Odense, Denmark. At Canonical, we are excited to once again sponsor this event for a community...
TurtleBot3 OpenCR firmware update from a snap
The TurtleBot3 robot is a standard platform robot in the ROS community, and it’s a reference that Canonical knows well, since we’ve used it in our tutorials....
Canonical at India Mobile Congress 2024 – a retrospective
With an ambition to become Asia’s technology hub for telecommunications in the 5G/6G era, India hosts the annual India Mobile Congress (IMC) in Pragati...