Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to launch multiple arm with single description file? #825

Closed
catmulti7 opened this issue Sep 27, 2023 · 3 comments
Closed

How to launch multiple arm with single description file? #825

catmulti7 opened this issue Sep 27, 2023 · 3 comments

Comments

@catmulti7
Copy link

Hi,
I've got a dual-arm system which has 2 UR5 arms fixed on a base. My plan is to run 2 driver in different namespace and control these arms separately with MoveIt2.
Since it is possible that two arm collide with each other or base, I want to wrote a description file include base and both arm so MoveIt can plan a safe path for me. I have done this with ROS1, but I'm not sure if I can do same thing with ROS2.
I followed UniversalRobots/Universal_Robots_ROS2_Description#70 to write the description file, but I'm not so sure about how to launch driver in different namespace. I tried to run ur_control.launch.py twice with different namespace, but it seems only one arm is launched successfully. Here is my launch file.

import os
from ament_index_python.packages import get_package_share_directory
from launch.actions import IncludeLaunchDescription
from launch import LaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument, LogInfo
from launch.substitutions import LaunchConfiguration

from launch.actions import GroupAction
from launch_ros.actions import PushRosNamespace


def generate_launch_description():

    ur_type = LaunchConfiguration('ur_type') 
    arm_0_robot_ip = LaunchConfiguration('arm_0_robot_ip') 
    arm_0_controller_file = LaunchConfiguration('arm_0_controller_file') 
    arm_0_tf_prefix = LaunchConfiguration('arm_0_tf_prefix') 
    arm_0_script_command_port = LaunchConfiguration('arm_0_script_command_port')

    arm_1_robot_ip = LaunchConfiguration('arm_1_robot_ip') 
    arm_1_controller_file = LaunchConfiguration('arm_1_controller_file') 
    arm_1_tf_prefix = LaunchConfiguration('arm_1_tf_prefix') 
    arm_1_script_command_port = LaunchConfiguration('arm_1_script_command_port')

    # # UR specific arguments
    ur_type_arg = DeclareLaunchArgument(
            "ur_type",
            default_value='ur5',
            description="Type/series of used UR robot.",
            choices=["ur3", "ur3e", "ur5", "ur5e", "ur10", "ur10e", "ur16e", "ur20"],
    )
    arm_0_robot_ip_arg = DeclareLaunchArgument(
            "arm_0_robot_ip",
            default_value='192.168.56.3',
            description="IP address by which the robot can be reached.",
    )
    arm_0_controller_file_arg = DeclareLaunchArgument(
            "arm_0_controller_file",
            default_value="arm_0_ur_controllers.yaml",
            description="YAML file with the controllers configuration.",
    )
    arm_0_tf_prefix_arg = DeclareLaunchArgument(
            "arm_0_tf_prefix",
            default_value="arm_0_",
            description="tf_prefix of the joint names, useful for \
            multi-robot setup. If changed, also joint names in the controllers' configuration \
            have to be updated.",
    )

    arm_0_script_command_port_arg =  DeclareLaunchArgument(
            "arm_0_script_command_port",
            default_value="50001",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )


    arm_1_robot_ip_arg = DeclareLaunchArgument(
            "arm_1_robot_ip",
            default_value='192.168.56.101',
            description="IP address by which the robot can be reached.",
    )
    arm_1_controller_file_arg = DeclareLaunchArgument(
            "arm_1_controller_file",
            default_value="arm_1_ur_controllers.yaml",
            description="YAML file with the controllers configuration.",
    )
    arm_1_tf_prefix_arg = DeclareLaunchArgument(
            "arm_1_tf_prefix",
            default_value="arm_1_",
            description="tf_prefix of the joint names, useful for \
            multi-robot setup. If changed, also joint names in the controllers' configuration \
            have to be updated.",
    )
    arm_1_script_command_port_arg =  DeclareLaunchArgument(
            "arm_1_script_command_port",
            default_value="50006",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )

    

    ur_launch_dir = get_package_share_directory('ur_robot_driver')

    arm_0 = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(os.path.join(ur_launch_dir, 'launch', 'ur_control.launch.py')),
        launch_arguments={'ur_type': ur_type,
                          'robot_ip': arm_0_robot_ip,
                          'controllers_file': arm_0_controller_file,
                          'tf_prefix': arm_0_tf_prefix,
                          'script_command_port': arm_0_script_command_port}.items())
    
    arm_0_with_namespace = GroupAction(
     actions=[
         PushRosNamespace('arm_0'),
         arm_0
      ]
    )

    arm_1 = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(os.path.join(ur_launch_dir, 'launch', 'ur_control.launch.py')),
        launch_arguments={'ur_type': ur_type,
                          'robot_ip': arm_1_robot_ip,
                          'controllers_file': arm_1_controller_file,
                          'tf_prefix': arm_1_tf_prefix,
                          'script_command_port': arm_1_script_command_port
                          }.items())
    
    arm_1_with_namespace = GroupAction(
     actions=[
         PushRosNamespace('arm_1'),
         arm_1
      ]
    )

    
    return LaunchDescription([
        ur_type_arg,
        arm_0_robot_ip_arg,
        arm_0_controller_file_arg,
        arm_0_tf_prefix_arg,
        arm_0_script_command_port_arg,
        arm_1_robot_ip_arg,
        arm_1_controller_file_arg,
        arm_1_tf_prefix_arg,
        arm_1_script_command_port_arg,
        arm_0_with_namespace,
        arm_1_with_namespace
    ])

and I got

[ur_ros2_control_node-12] terminate called after throwing an instance of 'std::system_error'
[ur_ros2_control_node-12]   what():  Failed to bind socket for port 50001 to address. Reason: Address already in use: Address already in use
[ur_ros2_control_node-1] [WARN] [1695810663.355767038] [UR_Client_Library:arm_0_]: Connection attempt on port 50001 while maximum number of clients (1) is already connected. Closing connection.
[ur_ros2_control_node-1] [WARN] [1695810663.363975935] [UR_Client_Library:arm_0_]: Connection attempt on port 50004 while maximum number of clients (1) is already connected. Closing connection.
[ur_ros2_control_node-1] [WARN] [1695810663.372521821] [UR_Client_Library:arm_0_]: Connection attempt on port 50005 while maximum number of clients (1) is already connected. Closing connection.

I already set different ports for both arms in description file so this error really confuse me. My description file is here
I hope somebody can give me a instruction about how to fix my launch file or a example of launch multiple arm.
Thank you for reading my issue and help me.

@fmauch
Copy link
Contributor

fmauch commented Sep 29, 2023

As far as I can see you only defined different ports for the script_command_port. Apart from that you'll also have to redefine the reverse_port, script_sender_port and trajectory_port. We seem to be missing those inside our current launch file, so thank you for the hint.

Please see the description for the ports.

@catmulti7
Copy link
Author

@fmauch Sorry for my late reply, I fix launch file as you said and it seems working well now. Thank you a lot.
This is my launch file, in case someone else need it.

import os
from ament_index_python.packages import get_package_share_directory
from launch.actions import IncludeLaunchDescription
from launch import LaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument, LogInfo
from launch.substitutions import LaunchConfiguration

from launch.actions import GroupAction
from launch_ros.actions import PushRosNamespace


def generate_launch_description():

    ur_type = LaunchConfiguration('ur_type') 
    arm_0_robot_ip = LaunchConfiguration('arm_0_robot_ip') 
    arm_0_controller_file = LaunchConfiguration('arm_0_controller_file') 
    arm_0_tf_prefix = LaunchConfiguration('arm_0_tf_prefix') 
    arm_0_script_command_port = LaunchConfiguration('arm_0_script_command_port')
    arm_0_trajectory_port = LaunchConfiguration('arm_0_trajectory_port')
    arm_0_reverse_port = LaunchConfiguration('arm_0_reverse_port')
    arm_0_script_sender_port = LaunchConfiguration('arm_0_script_sender_port')

    arm_1_robot_ip = LaunchConfiguration('arm_1_robot_ip') 
    arm_1_controller_file = LaunchConfiguration('arm_1_controller_file') 
    arm_1_tf_prefix = LaunchConfiguration('arm_1_tf_prefix') 
    arm_1_script_command_port = LaunchConfiguration('arm_1_script_command_port')
    arm_1_trajectory_port = LaunchConfiguration('arm_1_trajectory_port')
    arm_1_reverse_port = LaunchConfiguration('arm_1_reverse_port')
    arm_1_script_sender_port = LaunchConfiguration('arm_1_script_sender_port')

    # # UR specific arguments
    ur_type_arg = DeclareLaunchArgument(
            "ur_type",
            default_value='ur5',
            description="Type/series of used UR robot.",
            choices=["ur3", "ur3e", "ur5", "ur5e", "ur10", "ur10e", "ur16e", "ur20"],
    )
    arm_0_robot_ip_arg = DeclareLaunchArgument(
            "arm_0_robot_ip",
            default_value='192.168.56.3',
            description="IP address by which the robot can be reached.",
    )
    arm_0_controller_file_arg = DeclareLaunchArgument(
            "arm_0_controller_file",
            default_value="arm_0_ur_controllers.yaml",
            description="YAML file with the controllers configuration.",
    )
    arm_0_tf_prefix_arg = DeclareLaunchArgument(
            "arm_0_tf_prefix",
            default_value="arm_0_",
            description="tf_prefix of the joint names, useful for \
            multi-robot setup. If changed, also joint names in the controllers' configuration \
            have to be updated.",
    )

    arm_0_script_command_port_arg =  DeclareLaunchArgument(
            "arm_0_script_command_port",
            default_value="50002",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )
    arm_0_trajectory_port_arg = DeclareLaunchArgument(
            "arm_0_trajectory_port",
            default_value="50003",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )
    arm_0_reverse_port_arg = DeclareLaunchArgument(
            "arm_0_reverse_port",
            default_value="50001",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )
    arm_0_script_sender_port_arg = DeclareLaunchArgument(
            "arm_0_script_sender_port",
            default_value="50005",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )


    arm_1_robot_ip_arg = DeclareLaunchArgument(
            "arm_1_robot_ip",
            default_value='192.168.56.101',
            description="IP address by which the robot can be reached.",
    )
    arm_1_controller_file_arg = DeclareLaunchArgument(
            "arm_1_controller_file",
            default_value="arm_1_ur_controllers.yaml",
            description="YAML file with the controllers configuration.",
    )
    arm_1_tf_prefix_arg = DeclareLaunchArgument(
            "arm_1_tf_prefix",
            default_value="arm_1_",
            description="tf_prefix of the joint names, useful for \
            multi-robot setup. If changed, also joint names in the controllers' configuration \
            have to be updated.",
    )
    arm_1_script_command_port_arg =  DeclareLaunchArgument(
            "arm_1_script_command_port",
            default_value="50010",
            description="Port that will be opened to forward script commands from the driver to the robot",
    )

    arm_1_trajectory_port_arg = DeclareLaunchArgument(
            "arm_1_trajectory_port",
            default_value="50009",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )
    arm_1_reverse_port_arg = DeclareLaunchArgument(
            "arm_1_reverse_port",
            default_value="50006",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )
    arm_1_script_sender_port_arg = DeclareLaunchArgument(
            "arm_1_script_sender_port",
            default_value="50007",
            description="Port that will be opened to forward script commands from the driver to the robot",
        )


    

    ur_launch_dir = get_package_share_directory('ur_robot_driver')

    arm_0 = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(os.path.join(ur_launch_dir, 'launch', 'ur_control.launch.py')),
        launch_arguments={'ur_type': ur_type,
                          'robot_ip': arm_0_robot_ip,
                          'controllers_file': arm_0_controller_file,
                          'tf_prefix': arm_0_tf_prefix,
                          'script_command_port': arm_0_script_command_port,
                          'trajectory_port': arm_0_trajectory_port,
                          'reverse_port': arm_0_reverse_port,
                          'script_sender_port': arm_0_script_sender_port,
                          }.items())
    
    arm_0_with_namespace = GroupAction(
     actions=[
         PushRosNamespace('arm_0'),
         arm_0
      ]
    )

    arm_1 = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(os.path.join(ur_launch_dir, 'launch', 'ur_control.launch.py')),
        launch_arguments={'ur_type': ur_type,
                          'robot_ip': arm_1_robot_ip,
                          'controllers_file': arm_1_controller_file,
                          'tf_prefix': arm_1_tf_prefix,
                          'script_command_port': arm_1_script_command_port,
                          'trajectory_port': arm_1_trajectory_port,
                          'reverse_port': arm_1_reverse_port,
                          'script_sender_port': arm_1_script_sender_port,
                          }.items())
    
    arm_1_with_namespace = GroupAction(
     actions=[
         PushRosNamespace('arm_1'),
         arm_1
      ]
    )

    
    return LaunchDescription([
        ur_type_arg,
        arm_0_robot_ip_arg,
        arm_0_controller_file_arg,
        arm_0_tf_prefix_arg,
        arm_0_script_command_port_arg,
        arm_0_trajectory_port_arg,
        arm_0_reverse_port_arg,
        arm_0_script_sender_port_arg,
        arm_1_robot_ip_arg,
        arm_1_controller_file_arg,
        arm_1_tf_prefix_arg,
        arm_1_script_command_port_arg,
        arm_1_trajectory_port_arg,
        arm_1_reverse_port_arg,
        arm_1_script_sender_port_arg,

        arm_0_with_namespace,
        arm_1_with_namespace
    ])

@samjo1234
Copy link

Hi, I am working on the same project, and do you mind if I asked what your ur_control.launch.py looks like? Thank you in advance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants