We’ve recently celebrated the release of ROS 2 Humble Hawksbill with a post detailing how to get started developing for the new release in containers. In addition, we shared an overview of the new features included with this new release, particularly its enhanced security features.
This week we are tackling the logical next step in software development: packaging. Indeed, once we’re done developing our super cool ROS 2 Humble application, we still have to get it out into the hands of our users.
In this post, we are going to see how to package a ROS 2 Humble application as a snap with an ‘hello world’-like example.
A ROS 2 Humble snap you said?
Snaps are app packages for desktop, cloud and IoT that are easy to install, secure, cross‐platform and dependency‐free.
Snaps are the ideal deployment solution for ROS-based applications in that they are a self-contained, sandboxed, cross distribution packaging solution. Not to mention that they natively support ROS to ease your packaging journey. You can find further information about all the features they offer on the dedicated robotics page!
Setting up snapcraft
Firstly, let’s get the tool that allows us to create snaps: snapcraft.
sudo snap install --classic snapcraft
We will also install LXD which snapcraft uses as a backend for clean, confined and reproducible packaging,
sudo snap install lxd
sudo lxd init --minimal
Note that we have initialized LXD with a bunch of default parameters. Head to the LXD documentation if you prefer to specify some of those parameters yourself.
We are all set up to package our first example, so let’s do just that.
A talker-listener example
For this example, we’ll take it easy as we won’t even need to write any code. Instead, we are going to package one of the ROS 2 demo from the GitHub repository. I’ve picked a demo from the ‘demo_nodes_cpp‘ package, more specifically the ‘talker_listener.launch.py‘ demo.
As its name suggests, this demo launches a ‘talker’ which publishes a string message on a ROS 2 topic. together with a ‘listener’ which reads said message. Both print the message sent/received to the console to easily follow along. And of course both are launched at once from a single launch file.
While this example may seem a little too simple, it is an ideal first contact with snaps. We will see how easy it is to package ROS 2 applications with snaps.
The snapcraft.yaml file
Snapcraft relies on the ‘snapcraft.yaml’ configuration file to drive the packaging process. So, let us create one,
mkdir -p ~/ros2_ws/first_snap/
and then populate it as follows,
summary: ROS 2 Humble talker/listener example
This example launches a ROS 2 Humble talker and listener.
command: opt/ros/humble/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py
plugs: [network, network-bind]
Believe it or not, this is all we need to create our snap. But let us inspect this file more closely.
Breaking it all down
We can identify 3 distinct blocks in the aforementioned
At the top of the file, there is some boiler-plate that is common to most snaps. The snap
version etc. Nothing unusual here. Then comes
base: core22 which is a base snap that will provide a runtime environment to our application based on Ubuntu 22.04. The last entry in this block is
confinement: strict which states that our application is strictly confined. In other words, it cannot access any resource on the host machine.
The second block,
apps, specifies the application(s) that’s exposed from the snap. Here, a single application is listed whose
command is very familiar. Furthermore, our application also lists some
interfaces in the
plugs section. Note that Interfaces allow our confined application to access specific resources of the host machine. In this case, our snap will have access to network-related interfaces that allow for the ROS 2 topics to flow. Finally, the
extensions: [ros2-humble] will automatically fill up some other fields which are common to ROS 2 Humble snaps. In case you are curious about what an extension does, note that you can ‘expand’ it. Reveal all of its secrets by issuing the following command,
SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 snapcraft expand-extensions
At last we are taking a look at the third block:
parts tag defines the different pieces that make up our final application. They include a
source entry for the source code such as local files, a tarball, or as in this example, a GitHub repository at a specific branch. What’s more, a ‘part’ can include
build-packages, which are only required at build time unlike
stage-packages which are only needed at runtime. Most importantly, each part is handled by a
plugin. Here we are using the
colcon plugin, which makes use of the familiar build tool in ROS 2. Then again this plugin has its own options, which you can review with the command,
snapcraft help colcon
Now that the
snapcraft.yaml file is defined, it is time to build the snap.
Building the ROS 2 Humble snap
To build the snap, issue the command
snapcraft in a terminal,
$ SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 snapcraft
* metallic sound of software being forged *
Created snap package
Snap, reveal yourself,
Surely, there it is!
How about we install it now? To do so, type,
$ sudo snap install --dangerous ros2-talker-listener_0.1_amd64.snap
ros2-talker-listener 0.1 installed
The use of the
--dangerousflag since we are installing a snap from disk instead of using the store.
Ok, but does it
[INFO] [launch]: All log files can be found below /home/ubuntu/snap/ros2-talker-listener/x6/ros/log/2022-05-24-15-24-50-823207-localhost-24895
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [talker-1]: process started with pid 
[INFO] [listener-2]: process started with pid 
[talker-1] 2022-05-24 15:24:51.341 [RTPS_TRANSPORT_SHM Error] Failed to create segment ac1bbb6c86049e8a: Permission denied -> Function compute_per_allocation_extra_size
[listener-2] 2022-05-24 15:24:51.346 [RTPS_TRANSPORT_SHM Error] Failed to create segment e75bbd4ac39608ab: Permission denied -> Function compute_per_allocation_extra_size
[talker-1] 2022-05-24 15:24:51.348 [RTPS_MSG_OUT Error] Permission denied -> Function init
[listener-2] 2022-05-24 15:24:51.348 [RTPS_MSG_OUT Error] Permission denied -> Function init
[talker-1] [INFO] [1653420292.379305200] [talker]: Publishing: 'Hello World: 1'
[listener-2] [INFO] [1653420292.380423139] [listener]: I heard: [Hello World: 1]
[talker-1] [INFO] [1653420293.379072149] [talker]: Publishing: 'Hello World: 2'
[listener-2] [INFO] [1653420293.379286698] [listener]: I heard: [Hello World: 2]
[talker-1] [INFO] [1653420294.379120962] [talker]: Publishing: 'Hello World: 3'
[listener-2] [INFO] [1653420294.379682258] [listener]: I heard: [Hello World: 3]
Yes it does! How great.
What’s even greater is that you could install and run this snap on another computer even if it doesn’t have ROS 2 installed! Heck, you could install and run this snap on another Linux distribution from 4 years ago!
Note that the error message from FastDDS, the underlying DDS library, does not prevent the application from running. In other words we can safely ignore that for now. If you want to know more about how shared-memory plays along with snap, we covered the topic at length in a dedicated blog: “How to use ROS 2 shared memory in snaps”.
We have seen in this post how to create a snap for a ROS 2 Humble application. While this demo is fairly trivial, packaging a more complex ROS stack isn’t much more complicated. To demonstrate that, have a look at the series “How to set up TurtleBot3 in minutes with snaps” where I detail how to snap the entire Turtlebot3.
Furthermore, we’ve seen how you can effectively and easily package your ROS 2 application. How about distributing it now? Have a look at how to do so with the store here.