Running ROS Nodes on Boot

Preamble

This is a reworking of a blog post that used to be on the Rover Robotics blog, which they have since taken down - sad face.

We often want our robots to be ready to use at the point of power on. We don’t want to be having to call every ROS launch in our stack every time we want to run our robot. Even if you combine all your ROS launch files into one ROS launch to rule them all the following guide will be useful to get you to a more polished robotic product, instead of a development tool.

How To

If you want to run through this yourself then you can follow along here. However, if you’d rather shortcut this I have an accompanying GitHub repository that can get you up and running also.

There are two main parts to this:

  1. Running ROS Core on start up

  2. Calling a ROS launch file on start up

From here it is possible to call any further ROS launch files required just repeating the second part as often as required.

Pointing to the ROS Master

It is useful to make a separate file that sets your ROS_MASTER_URI that we can source when launching ROS Core or any ROS launch. In the example here we’ll just set it to be localhost but if we wanted to connect to a remote master we could edit this file appropriately and then just use the ROS launch part of this guide.

We’ll make a folder for our file then create the file.

sudo mkdir /etc/ros
sudo nano /etc/ros/env.sh

Put the following lines into the /etc/ros/env.sh file.

#!/bin/sh
export ROS_MASTER_URI=http://localhost:11311

Exit and save with Ctrl+x followed by y and hit enter.

ROS Core

In order to run ROS Core on start up we are going to create a system service to source all the appropriate environment variables and run ROS core.

sudo nano /etc/systemd/system/roscore.service

Inside this file add the following content, changing your_robots_user to the default user name used by your embedded system on start up. You should also change your_ros_distro to be the distribution of ROS you are using, eg. noetic.

[Unit]
Description=ROScore service
After=network-online.target

[Service]
Type=forking
User=your_robots_user
ExecStart=/bin/sh -c ". /opt/ros/your_ros_distro/setup.sh; . /etc/ros/env.sh; roscore & while ! echo exit | nc localhost 11311 > /dev/null; do sleep 1; done"

[Install]
WantedBy=multi-user.target

Exit and save with Ctrl+x followed by y and hit enter.

So what is going on in this service? This is the explanation from the original Rover Robotics blog post.

“Source all the environment variables, start roscore in a fork. Since the service type is forking, systemd doesn’t mark it as ‘started’ until the original process exits, so we have the non-forked shell wait until it can connect to the TCP opened by roscore, and then exit, preventing conflicts with dependent services.”

To have the service called on start up we need to make the system aware of the new service file and then enable it.

sudo systemctl daemon-reload
sudo systemctl enable roscore.service

Now it will run after the next reboot but if we want to run it during the current session we can issue the systemctl start command and check it is operational with the systemctl status command

sudo systemctl start roscore.service
sudo systemctl status roscore.service

Looks like I’ve almost been up and running for six months.

● roscore.service
     Loaded: loaded (/etc/systemd/system/roscore.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2021-09-28 22:30:50 UTC; 5 months 17 days ago
   Main PID: 83757 (roscore)
      Tasks: 29 (limit: 2100)
     CGroup: /system.slice/roscore.service
             ├─83757 /usr/bin/python3 /opt/ros/noetic/bin/roscore
             ├─83774 /usr/bin/python3 /opt/ros/noetic/bin/rosmaster --core -p 11311 -w 3 __log:=/home/matthew/.ros/log/ba331818-20ab-11ec-a101-81da22434a69/master.log
             └─83784 /opt/ros/noetic/lib/rosout/rosout __name:=rosout __log:=/home/matthew/.ros/log/ba331818-20ab-11ec-a101-81da22434a69/rosout-1.log

ROS Launch

With ROS core we could just call the roscore command directly inside the service, but with ROS launch we’ll need to create a separate shell script and execute that inside the service.

Let’s do that now, note that you may want to change the file name from ros_package_auto_launch to suit the ROS package you are going to launch.

sudo nano /usr/sbin/ros_package_auto_launch

Put the text below into this file, but there are a few things you’ll need to change to suit your use case. /path/to/your/catkin_ws/devel/setup.bash should be replaced with the actual path to your catkin workspace’s setup.bash. Change your_robots_user to the default user name used by your robot on start up. Finally edit the launch command, roslaunch ros_package init.launch –wait to suit your ROS package and launch file. I’d recommend keeping the –wait, as this will wait for a ROS master to be available before trying to execute the launch file.

#!/bin/bash
source /path/to/your/catkin_ws/devel/setup.bash

source /etc/ros/env.sh

export ROS_HOME=$(echo ~your_robots_user)/.ros

roslaunch ros_package init.launch --wait

# Operation Canceled
exit 125

Exit and save with Ctrl+x followed by y and hit enter.

Make the file executable.

sudo chmod +x /usr/sbin/ros_package_auto_launch

Now we can create a system process to call the shell script above.

sudo nano /etc/systemd/system/ros_package.service

This service is a bit easier to understand that the one for ROS Core. It is simply calling the above shell script and it requires that the roscore.service has been called. This requirement can be removed if connecting to a ROS master over a network. Be sure to change your_robots_user for the user name used on your embedded device. Be sure that the /usr/sbin/ros_package_auto_launch matches the file we that we created with our roslaunch command inside.

[Unit]
Requires=roscore.service
After=network-online.target roscore.service

[Service]
Type=simple
User=your_robots_user
ExecStart=/usr/sbin/ros_package_auto_launch

[Install]
WantedBy=multi-user.target

Similarly to what I showed for ROS Core, in order to have the service called on start up we need to make the system aware of the new service file and then enable it.

sudo systemctl daemon-reload
sudo systemctl enable ros_package.service

Now it will run after the next reboot but if we want to run it during the current session we can issue the systemctl start command and check it is operational with the systemctl status command

sudo systemctl start ros_package.service
sudo systemctl status ros_package.service

Here is the status return, active and running.

● ros_package.service
     Loaded: loaded (/etc/systemd/system/ros_package.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2021-10-05 13:21:59 UTC; 5 months 11 days ago
   Main PID: 1295877 (ros_package)
      Tasks: 33 (limit: 2100)
     CGroup: /system.slice/ros_package.service
             ├─1295877 /bin/bash /usr/sbin/ros_package_auto_launch
             ├─1295899 /usr/bin/python3 /opt/ros/noetic/bin/roslaunch ros_package init.launch --wait

Hey you!

Found this useful or interesting?

Consider donating to support.

Any question, comments, corrections or suggestions?

Reach out on the social links below through the buttons.