---
title: 'Your first robot: Sharing with others [5/5]'
description: This is the fifth (and final) blog post in this series about creating
  your first robot with ROS and Ubuntu Core. In the previous post we discussed methods
  of control, did a little math, and wrote the ROS driver for our robot. But it still
  required several nodes to be running at once, and sharing  […]
url: https://canonical.com/blog/your-first-robot-sharing-with-others-5-5?format=md
---

1. [Blog](https://canonical.com/blog)
2. Article

---

[Kyle Fazzari](https://canonical.com/blog/author/kyrofa "More about Kyle Fazzari")

16 March 2018

# Your first robot: Sharing with others [5/5]

[Edukit](https://canonical.com/blog/tag/edukit)
[Raspberry Pi](https://canonical.com/blog/tag/raspberry-pi)
[robotics](https://canonical.com/blog/tag/robotics)
[Robots](https://canonical.com/blog/tag/robots)
[ROS](https://canonical.com/blog/tag/ros)
[sc:series:your-first-robot](https://canonical.com/blog/tag/scseriesyour-first-robot)
[snapcraft.io](https://canonical.com/blog/tag/snapcraft-io)
[Ubuntu Core](https://canonical.com/blog/tag/ubuntu-core)

---

Share the article
> Please note that this blog post has out technical information that may no longer be correct. For latest updated documentation about robotics in Canonical please visit <https://ubuntu.com/robotics/docs>.

This is the fifth (and final) blog post in [this series about creating your first robot with ROS and Ubuntu Core](https://canonical.com/blog/your-first-robot-a-beginners-guide-to-ros-and-ubuntu-core-1-5). In the [previous post](https://canonical.com/blog/your-first-robot-the-driver-4-5) we discussed methods of control, did a little math, and wrote the ROS driver for our robot. But it still required several nodes to be running at once, and sharing it with the world involved uploading your source code somewhere and convincing people to install ROS, build your package, and use it. Today we’re going to simplify both of those issues by discussing ROS launch files, and packaging everything we’ve written as a snap that can be installed by your friends with a few keystrokes, even without knowing anything about ROS.

Alright, let’s get started! Remember that this is also a video series, feel free to watch the video version of this post:

&amp;lt;span style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" data-mce-type="bookmark" class="mce\_SELRES\_start"&amp;gt;﻿&amp;lt;/span&amp;gt;

### Prerequisites

This post will assume the following:

* You know what snaps are, and have completed the “[create your first snap](https://tutorials.ubuntu.com/tutorial/create-your-first-snap#0)” tutorial.
* You have a store account at [dashboard.snapcraft.io](https://dashboard.snapcraft.io/)

### Step 1: Create launch files

In parts [3](https://canonical.com/blog/your-first-robot-the-controller-3-5) and [4](https://canonical.com/blog/your-first-robot-the-driver-4-5), we needed to open several terminals because we needed to run several different ROS nodes. It’s difficult to remember every component of our system, and it gets exponentially harder as we start adding parameters, etc. Running individual nodes is handy for testing things out, but we’re done with that step. It’s time to make our system a little more of a product, something you can just run and use to control your robot. As you learned in the [eighth ROS tutorial](http://wiki.ros.org/ROS/Tutorials/UsingRqtconsoleRoslaunch), ROS has a tool for dealing with this: launch files. These give us a simple language we can use to specify what nodes need to launch at the same time, and with what parameters.

Let’s get started writing launch files for the ROS system that controls our robot. We’ll put these launch files in the **edukit\_bot** package we’ve been working on throughout this series, in a new **launch** folder. Make that folder now:

```
$ mkdir ~/edukit_bot_ws/src/edukit_bot/launch
```

In order for our robot (and launch files) to be reusable, we’re not actually going to create a single launch file. We want a single entry point (i.e. we want to be able to launch a single launch file and have it bring up the whole system), but we also want to keep things as modular as possible. So we’ll write one launch file per subsystem, and then one super launch file in charge of bringing up the entire system. We have two subsystems for our robot: low-level control, and teleoperation. Let’s write our “super” launch file first.

#### Step 1.1: Write robot launch file

Create a new file **~/edukit\_bot\_ws/src/edukit\_bot/launch/edukit\_bot.launch** and open it up in your editor. Make it look like this:

```
<launch>
  <include file="$(find edukit_bot)/launch/includes/teleop.launch.xml" />
  <include file="$(find edukit_bot)/launch/includes/control.launch.xml" />
</launch>
```

That’s it. All this launch file does is “include” the launch files for our two subsystems (which we have yet to write). When this launch file is run, it runs those. Alright, let’s get started on our subsystems’ launch files.

#### Step 1.2: Write teleop launch file

Why are we splitting these up, exactly? Let’s say in the future you want to work toward making this robot autonomous. In that case, you may no longer want the teleoperation nodes to be launched. Do you think you’ll remember which nodes relate to teleoperation and which don’t? I certainly won’t! It’s easier just to keep related pieces together, and remove the “include” line that launches the teleoperation nodes if you want to remove that functionality.

First, you need to create the **includes** directory into which we’ll place our subsystem launch files:

```
$ mkdir ~/edukit_bot_ws/src/edukit_bot/launch/includes
```

Create a new file **~/edukit\_bot\_ws/src/edukit\_bot/launch/includes/teleop.launch.xml**. Why do we end this one with **.xml** and not just **.launch** like the other one? Because **roslaunch** (the command that runs launch files) will tab-complete files that end in **.launch**. Since these subsystem launch files don’t do much by themselves, we don’t want them to be exposed to end users. Ending them with **.xml** essentially hides them from **roslaunch**, but still lets us use them with includes. Anyway, open that file in your editor. Make it look like this:

```
<launch>
  <node pkg="joy" type="joy_node" name="joystick">
    <param name="autorepeat_rate" value="1" />
  </node>

  <node pkg="teleop_twist_joy" type="teleop_node" name="joystick_to_twist">
    <param name="scale_angular" value="4" />
  </node>
</launch>
```

This file launches the two nodes that make up our teleoperation (i.e. what we discussed in [part 3](https://canonical.com/blog/your-first-robot-the-controller-3-5)). Note that we’re using the **autorepeat\_rate** parameter of the joy node. This parameter tells joy to rebroadcast joystick values, even if they haven’t changed, at a rate of at least 1 Hz (if the joystick moves, the rate will be much greater than 1 Hz, we just want to make sure we don’t run into our driver’s timeout functionality if the joystick simply hasn’t moved).

We’re also using the **scale\_angular** parameter of the **teleop\_node** so the robot actually turns (as we discussed in [part 4](https://canonical.com/blog/your-first-robot-the-driver-4-5)).

#### Step 1.3: Update dependencies

We’ve just finished writing a launch file in *our* package that assumes the presence of *other* ROS packages (and their nodes). However, we’re not depending on those ROS nodes at all. Let’s add both **joy** and **teleop\_twist\_joy** to our list of dependencies in our **edukit\_bot**‘s **package.xml**, making it look something like this in the end:

```
<?xml version="1.0"?>
  <package format="2">
    <name>edukit_bot</name>
    <version>0.1.0</version>
    <description>The edukit_bot package</description>

    <maintainer email="you@you.com">You</maintainer>

    <license>GPLv3</license>

    <buildtool_depend>catkin</buildtool_depend>
    <build_depend>rospy</build_depend>

    <exec_depend>rospy</exec_depend>
    <exec_depend>geometry_msgs</exec_depend>
    <exec_depend>python-rpi.gpio</exec_depend>
    <exec_depend>joy</exec_depend>
    <exec_depend>teleop_twist_joy</exec_depend>
</package>
```

#### Step 1.4: Write the low-level control launch file

Create a new file **~/edukit\_bot\_ws/src/edukit\_bot/launch/includes/control.launch.xml**. Make it look like this:

```
<launch>
  <node name="driver" pkg="edukit_bot" type="driver_node" />
</launch>
```

This one is tremendously simple, as it’s just launching the driver we wrote in [part 4](https://canonical.com/blog/your-first-robot-the-driver-4-5). No parameters to tweak, since the default values are suitable (at least for my robot, perhaps yours is different).

We’re done making changes to our package, so let’s rebuild it:

```
$ cd ~/edukit_bot_ws
$ catkin_make
```

#### Step 1.5: Test launch system

Alright, let’s take our new launch files for a spin. This time, you only need a single terminal! Make sure it’s in your classic shell. First, activate your workspace:

```
$ cd ~/edukit_bot_ws
$ source devel/setup.sh
```

Now launch the super launch file, which will launch everything else:

```
$ roslaunch edukit_bot edukit_bot.launch
```

You’ll see all the nodes that make up our system come up, and you should be able to control your robot. So much easier than running them all individually!

### Step 2: Package this workspace as a snap

At this point, we have our entire ROS system launchable by a single command. Wouldn’t it be neat to have this system running as soon as we boot our Raspberry Pi? Also, it would be cool to ask your friends to take your version of this series for a spin, and be able to test theirs out as well! Both of these are easily accomplished by packaging our ROS system as a snap. Note that the workspace (and **snapcraft.yaml**) used here is [available for reference](https://github.com/kyrofa/edukit_bot).

#### Step 2.1: Add install rules

The first step toward packaging anything is typically to ensure that we’re installing the components of our package correctly. With its use of workspaces, ROS makes this dangerously easy to ignore, but it’s still important. We have two components of our **edukit\_bot** package: the driver, and the launch files. Open up **edukit\_bot**‘s **CMakeLists.txt**, and add the following two install rules to the bottom:

```
# ... <snip>

# Install driver node
install(PROGRAMS
  src/driver_node
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

# Install launch files
install(DIRECTORY launch
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
```

Now when we install our ROS package, it will install both our driver node as well as the launch files we’ve written.

#### Step 2.2: Install Snapcraft

In order to build a snap, we need to install a tool called **snapcraft**. From your classic shell:

```
$ sudo apt install snapcraft
```

#### Step 2.3: Write the snapcraft.yaml

We tell **snapcraft** how to create our snap by writing a **snapcraft.yaml**. We can get an initial version with **snapcraft init**:

```
$ cd ~/edukit_bot_ws
$ snapcraft init
Created snap/snapcraft.yaml.
Edit the file to your liking or run `snapcraft` to get started
```

Do as it asks, and open up **snap/snapcraft.yaml** in your editor. Make it look something like this:

```
name: edukit-bot-kyrofa
version: '1.0'
summary: ROS system for teleoperating edukit bot
description: |
  A ROS system for teleoperating the EduKit #3, the robotics kit.
  This accompanies the "Your First Robot" series written by Kyle
  Fazzari.

  Note that the `joystick` interface is needed for access to the
  gamepad, and the `physical-memory-control` interface is needed
  for access to GPIO. Both of these interfaces must be connected
  before this snap will function:

    $ sudo snap connect edukit-bot-kyrofa:joystick
    $ sudo snap connect edukit-bot-kyrofa:physical-memory-control

grade: stable
confinement: strict

parts:
  workspace:
    plugin: catkin
    rosdistro: kinetic
    source: .
    catkin-packages: [edukit_bot]

    # Required to build RPi.GPIO
    build-packages: [python-dev, python-setuptools]

apps:
  launch:
    command: roslaunch edukit_bot edukit_bot.launch
    daemon: simple
    plugs:
      # Basic networking
      - network
      - network-bind

      # Needed for access to gamepad
      - joystick

      # Needed for GPIO memory-mapping
      - physical-memory-control
```

Let’s break this down piece by piece.

```
name: edukit-bot-kyrofa
version: '1.0'
summary: ROS system for teleoperating edukit bot
description: |
  A ROS system for teleoperating the EduKit #3, the robotics kit.
  This accompanies the "Your First Robot" series written by Kyle
  Fazzari.

  Note that the `joystick` interface is needed for access to the
  gamepad, and the `physical-memory-control` interface is needed
  for access to GPIO. Both of these interfaces must be connected
  before this snap will function:

    $ sudo snap connect edukit-bot-kyrofa:joystick
    $ sudo snap connect edukit-bot-kyrofa:physical-memory-control
```

This is just useful metadata for our snap. The name must be unique (that’s why I put my nickname at the end), but you’re pretty much free to put whatever you want for the rest. The description here hints at confinement, which we’ll cover in a moment.

```
grade: stable
confinement: strict
```

**grade** can be either **stable** or **devel**. If it’s **devel**, the store will prevent you from releasing into stable channels– think of it as a safety net to prevent accidental releases. If it’s **stable**, you can release it anywhere.

**confinement** can be **strict**, **devmode**, or **classic**. **strict** enforces confinement, whereas **devmode** allows all accesses, even those that would be disallowed under **strict** confinement (and logs accesses that would otherwise be disallowed for your reference). **classic** is even less confined than **devmode**, essentially running exactly like something you’d install from a deb. There is [more extensive documentation on confinement available](https://docs.snapcraft.io/snap-confinement).

I personally always use **strict** confinement unless I know for sure that the thing I’m snapping won’t run successfully under confinement, in which case I’ll use **devmode**. I typically avoid **classic** unless I never intend for the app to run confined (e.g. if I was snapping vim, I know it would always need access to everything). In this case, this snap can run quite well under strict confinement. We’ll discuss this some more momentarily.

```
parts:
  workspace:
    plugin: catkin
    rosdistro: kinetic
    source: .
    catkin-packages: [edukit_bot]

    # Required to build RPi.GPIO
    build-packages: [python-dev, python-setuptools]
```

This is where we tell Snapcraft how to build our Catkin workspace. Every snap is made up of a number of parts. In our case, there’s only one, called **workspace**. We say that it’s built using the **catkin** plugin, using **kinetic** (as opposed to Lunar or Indigo, as we discussed in part 2). We give it a list of Catkin packages to build, in this case just **edukit\_bot**. Finally, we give it a list of packages that need to be installed in order to build our ROS package, or more specifically, our **RPi.GPIO** dependency.

```
apps:
  launch:
    command: roslaunch edukit_bot edukit_bot.launch
    daemon: simple
    plugs:
      # Basic networking
      - network
      - network-bind

      # Needed for access to gamepad
      - joystick

      # Needed for GPIO memory-mapping
      - physical-memory-control
```

Here we expose our single launch file that brings up the whole system. We create a new app called “launch” that runs the **roslaunch** command we used in step 1.5. We’re saying that this should be running at boot with **daemon: simple**. Finally, we provide a list of **plugs**, which is where confinement comes into play.

Since this snap is strictly confined, by default it doesn’t really have access to anything. No network, no GPIO, no controller. So we add plugs for each of those things: ROS needs the network for its nodes to function, we need access to the controller, and we need access to GPIO. [Read more about interfaces](https://docs.snapcraft.io/interface-management).

That’s it! Let’s build it.

#### Step 2.4: Build the snap

This part is pretty easy:

```
$ cd ~/edukit_bot_ws
$ snapcraft
```

Note that the Raspberry Pi is not as blazingly fast as we’d all like it to be. This will take some time. You’ll see Snapcraft fetch **rosdep**, which is then used to determine the dependencies of the ROS packages in the workspace. This is only **edukit\_bot** in our case, which we know depends upon **rospy**, **geometry\_msgs**, **python-rpi.gpio**, **joy**, and **teleop\_twist\_joy**. It then pulls those down and puts them into the snap along with **roscore**. Finally, it builds the requested packages in the workspace, and installs them into the snap as well. At the end, you’ll have your snap.

#### Step 2.5: Test your snap

Now that we have our snap built, let’s test it out! First, install it:

```
$ cd ~/edukit_bot_ws
$ sudo snap install edukit-bot-kyrofa_1.0_armhf.snap --dangerous
edukit-bot-kyrofa 0.1 installed
```

Now that the snap is installed, since we made our ROS system a daemon it’s already up and running. Unfortunately, it has already probably died because it can’t access the joystick or GPIO. We need to give it permission to do that by connecting the **joystick** and **physical-memory-control** interfaces:

```
$ sudo snap connect edukit-bot-kyrofa:joystick
$ sudo snap connect edukit-bot-kyrofa:physical-memory-control
```

We only need to do that once. Now that access has been granted, we just need to restart the service so it tries again:

```
$ sudo systemctl restart snap.edukit-bot-kyrofa.launch.service
```

Now you should be able to drive your robot around as before, but everything is running out of that single snap.

### Step 3: Share the snap with others

All you need to do to share your project with others is release the snap in the store!

#### Step 3.1: Tell Snapcraft who you are

We’re about to use Snapcraft to register and upload a snap using the store account you created when satisfying the prerequisites. For that to work, you need to sign in with Snapcraft. From your classic shell:

```
$ snapcraft login
```

#### Step 3.2: Register the snap name

As I mentioned before, snap names are globally unique, so only one developer can register and publish a snap with a given name. Before you can publish this snap you need to make sure that snap name is registered to you (note that this corresponds to the **name** field in the **snapcraft.yaml** we created moments ago). Again from your classic shell:

```
$ snapcraft register <my snap name>
```

Assuming that name is available, you can proceed to upload it. Otherwise you may need to rename/rebuild your snap to whatever name you managed to register (i.e. change the **name** field in the YAML and run **snapcraft** again, it won’t take long).

#### Step 3.3: Release the snap

There are four release channels available by default. In order of increasing stability, these channels are **edge**, **beta**, **candidate**, and **stable**. We’ve tested this snap and it’s strictly confined, so we can really release it wherever we want. Just remember that if you put it in the **stable** channel, anyone can find and install it. If you’re not ready for that kind of exposure (perhaps you want your friend to test it first), you can release into the **candidate** channel:

```
$ snapcraft push path/to/my.snap --release=candidate
```

Once the upload and automated reviews finish successfully, anyone in the world can install your snap on the Raspberry Pi controlling their EduKit bot by running:

```
$ sudo snap install --candidate <my snap name>
```

Remember they also need to connect those interfaces so the snap can use the gamepad and GPIO. Mine ([see the code](https://github.com/kyrofa/edukit_bot)) is in the stable channel, so you can take it for a spin with:

```
$ sudo snap install edukit-bot-kyrofa
```

### Conclusion

That’s all, folks! I hope you enjoyed this series, and I hope that this brief introduction to professional robotics will serve you well in the years to come. If this is a field that you’d like to pursue, I can’t recommend internships enough, especially if you’re at the university level. At this point, you can put experience with ROS and Ubuntu Core on your resume, which will help you stand out from the pack!

If you’re looking for ways to continue hacking on this robot using ROS, I suggest doing some research into ways to obtain the wheel speeds. Once you have that, you can start investigating morphing your driver node into something you can use with the [diff\_drive\_controller](https://wiki.ros.org/diff_drive_controller), which goes back to [ros\_control](https://wiki.ros.org/ros_control), which we discussed in [part 4](https://canonical.com/blog/your-first-robot-the-driver-4-5). Who knows, maybe I’ll write another series about that someday.

*This article originally appeared on [Kyle Fazzari’s blog](https://kyrofa.com/posts/your-first-robot-sharing-with-others-5-5)*.

[Get in touch

Interested in running Ubuntu in your organization?](https://ubuntu.com/about/contact-us/form)

## Sign up for our newsletter

Get the latest Canonical news and updates in your inbox.

Work email:

\*I agree to receive information about Canonical's
products and services.

By submitting this form, I confirm that I have read and agree to [Canonical's Privacy Policy](https://canonical.com/legal/dataprivacy).

Sign up

## Share on

---

## Related posts

[### Extending ROS Noetic Support with ESM-Enabled Content Snaps](https://canonical.com/blog/extending-ros-noetic-support-with-esm-enabled-content-snaps)

Canonical has now extended its ESM (Expanded Security Maintenance) for ROS coverage to ROS Noetic content-sharing snaps. With ESM for ROS now available in both deb and snap...

[Florencia Cabral Berenfus](https://canonical.com/blog/author/flor-cabral)

17 December 2025

[### Discover your fully open source robotics observability at ROSCon 2025](https://canonical.com/blog/roscon-2025)

Another year, another ROSCon! This year we’re heading to Singapore, and Canonical is once again thrilled to sponsor this important community event. Just like last year in...

[Gabriel Aguiar Noury](https://canonical.com/blog/author/g-aguiar-noury)

22 October 2025

[### Canonical is now a platinum member in the Open Source Robotics Alliance](https://canonical.com/blog/osra-membership)

Ubuntu is the home of ROS. The very first ROS distribution, Box Turtle, launched on Ubuntu 8.04 LTS, Hardy Heron, and since then, Ubuntu and ROS have grown hand in hand. With...

[Gabriel Aguiar Noury](https://canonical.com/blog/author/g-aguiar-noury)

20 August 2025

[### So you need to add microcontrollers to your fleet: now what?](https://canonical.com/blog/microcontrollers-ubuntu-core-golioth)

Your Ubuntu Core fleet is running beautifully. OTA updates roll out in minutes. Every device is strictly confined, cryptographically attested, and carrying a 10 to 15 year long...

[Jonathan Beri](https://canonical.com/blog/author/jmberi)

18 June 2026
