0
$\begingroup$

Rosanswers logo

Is there a way to remotely launch nodes in ROS2? This could be done in ROS1 using the <machine> attribute. It's listed in the ROS2 design document but I can't figure out how to use it. I'm using the python launch and launch_ros libraries.


Originally posted by smorad on ROS Answers with karma: 71 on 2020-10-26

Post score: 7


Original comments

Comment by resnault on 2021-04-01:
Hi. Same need. I didn't find into the documentation...

$\endgroup$

4 Answers 4

1
$\begingroup$

Rosanswers logo

As far as I can tell, this is still a work in progress, but has been on the backlog to get merged into the documentation. Lots of discussion here, and it seems the change to the documentation is awaiting approval to get merged in.

The tentative document can be found here, but it doesn't look like the proposed changes have made their way into code yet.

I'd imagine it is pretty low priority because ssh is possible on all the tier 1 platforms (ever since Windows joined the club).


Originally posted by shonigmann with karma: 1567 on 2021-06-07

This answer was NOT ACCEPTED on the original site

Post score: 2


Original comments

Comment by alikureishy on 2022-02-17:
It has been quite some time since this was posted. Could anyone comment on when support for remote-launching nodes via roslaunch can be expected to make it into ROS2?

$\endgroup$
1
$\begingroup$

Rosanswers logo

I've just found a way to perform a remote launch from a python launch file using the ExecuteProcess launch action. There are several problems you're going to face if you go this way, so here are the workarounds I used to solve them (on Ubuntu 20.04).

First, let's state what we are trying to achieve:

  1. We have a main machine A and a machine B reachable via ssh [email protected] from machine A (without a password, i.e. use ssh-keygen -t rsa -b 2048 && ssh-copy-id [email protected] first)
  2. We have a launch file drinks_hmi_launch.py in drinks_hmi_bringup package on machine B (we can launch it from machine B via ros2 launch drinks_hmi_bringup drinks_hmi_launch.py)
  3. We have a launch file drinks_bringup_launch.py in drinks_bringup package on machine A which is supposed (among other actions) to launch drinks_hmi_launch.py on machine B

Now the following command should work from machine A, i.e. it launches the nodes, prints out output and can be terminated via SIGINT (Ctrl+C):

ssh -t [email protected] 'bash -i -c "ros2 launch drinks_hmi_bringup drinks_hmi_launch.py"'

However, after putting it directly into ExecuteProcess action in drinks_bringup_launch.py, the process complains about not having a proper tty console and the nodes on machine B are not terminated after terminating the ros2 launch drinks_bringup drinks_bringup_launch.py process.

A workaround is to call the above command inside a detached screen (use sudo apt install screen if not present). This, however, raises two further problems:

  • The screen does not pass SIGINT from calling process to inner processes, so it does not terminate the nodes when Ctrl+C is pressed. However it can be terminated explicitly using a remote_launch_terminator node which calls screen -S hmi -X quit (see the complete solution below)
  • The screen does not pass output from inner processes to calling process, so this needs to be done explicitly by redirecting inner output to outer

The complete solution is as follows:

/home/machine_A/drink_ws/src/launch_utils/launch_utils/remote_launch_terminator.py:

import rclpy
from rclpy.node import Node
import os

def main(args=None):
    rclpy.init(args=args)

    node = Node('remote_launch_terminator')
    node.declare_parameter('screen_pid', 'remote')
    screen_pid = node.get_parameter('screen_pid').get_parameter_value().string_value

    try:
        rclpy.spin(node)
    except:
        pass

    print('Terminating ' + screen_pid)
    os.system('screen -S ' + screen_pid + ' -X quit')

if __name__ == '__main__':
    main()

/home/machine_A/drink_ws/src/drinks_bringup/launch/drinks_bringup_launch.py:

import os

from ament_index_python.packages import get_package_share_directory

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions.execute_process import ExecuteProcess

def generate_launch_description():
    return LaunchDescription([
        
        # other actions
        
        # remote launch
        ExecuteProcess(
            name='hmi',
            cmd=['{ outer_stdout=$(readlink -f /proc/self/fd/3); } 3>&1 && screen -DmS hmi bash -i -c "ssh -t [email protected] \'bash -i -c \\"ros2 launch drinks_hmi_bringup drinks_hmi_launch.py\\"\' > $outer_stdout"'],
            output="screen",
            shell=True,
            emulate_tty=True
        ),
        Node(
            package='launch_utils',
            executable='remote_launch_terminator',
            name='remote_launch_terminator',
            output='screen',
            parameters=[{'screen_pid': 'hmi'}]
        ),
    ])

Hope this helps, cheers! ;)

Note: ExecuteProcess action does not work inside OnShutdown event, that's why the remote_launch_terminator must be used


Originally posted by afrixs with karma: 61 on 2022-04-26

This answer was NOT ACCEPTED on the original site

Post score: 3

$\endgroup$
1
  • $\begingroup$ ROS2 can do everything - you just have to write the code yourself! $\endgroup$ Commented Apr 28 at 3:57
0
$\begingroup$

Rosanswers logo

My answer to this is to use the existing linux system to do this: systemd. Basically do the following:

On the target machine, the machine you want to run remote nodes on, do the following:

  1. Make sure the nodes you want to run are on the remote machine already and it's already built on that machine.
  2. Make a bash script that executes ros2 launch and sources the right install directory. We'll call this remote_ros.sh. This is a script because you might want to do other things with this.
  3. Make a remote_ros.service file that runs the remote_ros.sh script. This can be setup to start on boot or not.

On the local machine, the machine you want to launch remote nodes on, do the following:

  1. Make a launch script that SSHes into the remote machine and executes systemctl stop remote_ros and/or systemctl start remote_ros. This can be done with the ExecuteProcess launch action.

Stop will kill the nodes and start will bring them up. If you don't want to stop the nodes, I believe start is a NOOP if they are already running.

I think this solution is more straight forward than the others presented and ideally the service just runs all the time, but you can always kill it with the systemctl stop command.


Originally posted by gerok with karma: 11 on 2022-09-19

This answer was NOT ACCEPTED on the original site

Post score: 1

$\endgroup$
0
$\begingroup$

I am late to the party, but I found a reasonable way of executing ros2 launch files when a system boots, or when initiated remotely using ssh.

I have an autostart folder for my 'dobbo' robot containing 3 files:

dobbo.sh - This sets up the ROS2 environment and runs the launch file(s)

#!/bin/bash

#This file starts all the necessary ROS2 nodes for dobbo on the rpi.

export HOME=/home/paul

source /opt/ros/humble/setup.bash
source /home/paul/ros2_ws/install/setup.bash

ros2 run camera_ros camera_node --ros-args -p width:=800 -p height:=600

dobbo.service - This defines the service for systemd

[Unit]
Description=Dobbo systemd service.

[Service]
Type=simple
ExecStart=/bin/bash /usr/bin/dobbo.sh

[Install]
WantedBy=multi-user.target

install_dobbo.sh - A convenience script that permissions the files and puts them in the right folders. Run this script from 'autostart' folder to install, or reinstall the files.

#!/bin/bash

sudo cp dobbo.sh /usr/bin
sudo chmod +x /usr/bin/dobbo.sh

sudo cp dobbo.service /etc/systemd/system
sudo chmod 644 /etc/systemd/system/dobbo.service

After installation the service can be started, stopped and show its status:

sudo systemctl start dobbo
sudo systemctl stop dobbo
sudo systemctl status dobbo

It can be 'enabled' causing it to run immediately on boot:

sudo systemctl enable dobbo

In order to start and stop the service remotely, using ssh, you need to provide a password to 'sudo'. This can be done with the -S option (however it is insecure as 'ps' would enable a user to see the password):

ssh <user>@<remote_machine> sudo -S systemctl start dobbo

or from a script:

ssh <user>@<remote_machine> 'echo password | sudo -S systemctl stop dobbo'
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.