分享

Call Services in ROS 2

 Leseo 2024-05-11 发布于湖北

Overview

Duration: 3:00

Services within the Robot Operating System (ROS) are often used to control your robot. After getting your robot built and software installed, the next step is to make it do something. Before a deep dive into writing software, let’s create a few simple service calls to do some basic actions with your robot.

ROS or ROS 2?

ROS 2 is a complete overhaul of ROS, the de facto Robot Operating System for global robotics R&D. ROS 2 builds upon the rich history of ROS with many improved features to meet the needs of commercial robots. Although it is possible to combine components of both ROS and ROS 2 in a single robot, the versions are generally not compatible.

This tutorial is written exclusively for ROS 2.

What you’ll learn

In this tutorial we’ll cover how to discover and call ROS 2 services on an OpenMANIPULATOR-X robot arm from Robotis.

Using a different robot? No problem! The commands are very similar, but you’ll get different results depending on the services made available with your robot. So long as you’re running ROS 2, you should be able to use this tutorial!

What you’ll need

All you need to complete this terminal is a running robot arm assembled and set up for ROS 2 according to the manufacturer’s directions. Alternatively you can also work through this tutorial using the turtlesim demo described in the ROS 2 tutorials. An introductory level knowledge of YAML will also help, since we’ll be using YAML to communicate with ROS services.

How will you use this tutorial?

  • Read through it
  • Read it and complete the exercises with an OpenMANIPULATOR-X robot arm
  • Read it and complete the exercises with another robot
56voters

    What is your current level of experience with ROS?

    • New to ROS
    • Familiar with ROS but new to ROS 2
    • Proficient with ROS and ROS 2
    55voters

      Discover Services

      Duration: 12:00

      Let’s look to see what services the robot publishes.

      Before beginning

      Launch your robot according to the instructions. Here’s how to launch the OpenMANIPULATOR-X robot:

        cd robotis_ws
        source install/setup.bash
        ros2 launch open_manipulator_x_controller open_manipulator_x_controller.launch.py
      

      You should see a successful launch similar to the following:

      [INFO] [launch]: All log files can be found below /home/ubuntu/.ros/log/xxxxxxxx
      [INFO] [launch]: Default logging verbosity is set to INFO
      [INFO] [launch.user]: Execute OpenManipulator-X Controller!!
      [INFO] [open_manipulator_x_controller-1]: process started with pid [24857]
      

      The arm should now stay locked in position and will remain locked so long as this command is running. Leave this running and work through the rest of this tutorial in a new terminal window.

      Find Services

      Use the ros2 service list command to list all the services provided by your robot. Your output should look like the following:

      /launch_ros/describe_parameters
      /launch_ros/get_parameter_types
      /launch_ros/get_parameters
      /launch_ros/list_parameters
      /launch_ros/set_parameters
      /launch_ros/set_parameters_atomically
      /open_manipulator_x/goal_drawing_trajectory
      /open_manipulator_x/goal_joint_space_path
      ...
      

      This is a long list. However, many of these services with the “parameters” name are internal to ROS 2 and not something we’ll be using. Use grep to filter out those services with this command:

      ros2 service list|grep -v parameter
      

      This results in a much smaller list and can group them by function (spacing added for readability):

      /open_manipulator_x/goal_drawing_trajectory
      
      /open_manipulator_x/goal_joint_space_path
      /open_manipulator_x/goal_joint_space_path_from_present
      /open_manipulator_x/goal_joint_space_path_to_kinematics_orientation
      /open_manipulator_x/goal_joint_space_path_to_kinematics_pose
      /open_manipulator_x/goal_joint_space_path_to_kinematics_position
      
      /open_manipulator_x/goal_task_space_path
      /open_manipulator_x/goal_task_space_path_from_present
      /open_manipulator_x/goal_task_space_path_from_present_orientation_only
      /open_manipulator_x/goal_task_space_path_from_present_position_only
      /open_manipulator_x/goal_task_space_path_orientation_only
      /open_manipulator_x/goal_task_space_path_position_only
      
      /open_manipulator_x/goal_tool_control
      
      /open_manipulator_x/set_actuator_state
      

      Although the service names seem pretty self-explanatory, reviewing the documentation for the robot may also help you to better understand the purpose of these services.

      In the next step we’ll dig into the the set_actuator_state service to lock and unlock the servos on the arm.

      Discover the Service Interface

      Duration: 7:00

      Messages are used to communicate with ROS services. In order to control a service, we need to figure out how to craft a message it understands, and these messages types are defined in the service interface.

      Find the service message type

      An easy way to find the message type for the set_actuator_state service is to use the -t option when listing services:

      ros2 service list -t | grep state
      

      This shows the type of message expected by the service:

      /open_manipulator_x/set_actuator_state [open_manipulator_msgs/srv/SetActuatorState]
      

      Many services can use the same message type, but each service accepts only a single type of message. For our example, the set_actuator_state service has an interface type open_manipulator_msgs/srv/SetActuatorState.

      Explore the message interface

      Armed with the type of message, we now need to how to craft a message of the right type. This requires exploring the actual message structure: the message interface specification. You can find the details for a message interface using the interface verb as follows:

      ros2 interface show open_manipulator_msgs/srv/SetActuatorState
      

      This returns two different message structures separated by dashes. The first structure is the message type sent to the service, while the second structure is the message type returned by the service:

      bool set_actuator_state
      ---
      bool is_planned
      

      This interface is quite simple: send a Boolean true/false value, and receive a Boolean true/false message in response.

      :exclamation: Does ros2 interface generate an error?

      The interface command was introduced with ROS Eloquent, before Eloquent it was the srv command. If you’re using ROS Dashing or an earlier verison of ROS, use the ros2 srv command instead. All the features of ros2 srv have now been rolled into ros2 interface.

      To better understand service interfaces, compare the output of the command ros2 interface list --only-srvs (or ros2 srv list on Dashing) and ros2 service list. The service command shows services that are currently running on your robot, while the interface command shows services that are configured for your robot. Services may be configured but not actually started; for instance, your robot may not have a feature that comes with the code base, or a service may be dynamically started based on external conditions.

      Send a simple message to a service

      Duration: 5:00

      Now we have everything we need to send a message to the robot. It’s time to use the ros2 service call command to call the service. This command takes three arguments:

      • service_name: The name of the service
      • service_type: The type message received by of the service
      • values: The actual message sent to the service

      The message values need are sent in YAML format. Since our message simply requires a single Boolean value named set_actuator_state as shown by the message interface, encoding this YAML is fairly straight forward.

      The final command looks like this:

      ros2 service call   '/open_manipulator_x/set_actuator_state'   'open_manipulator_msgs/srv/SetActuatorState'   '{ set_actuator_state: false }'
      

      You should see the following response:

      waiting for service to become available...
      requester: making request: open_manipulator_msgs.srv.SetActuatorState_Request(set_actuator_state=False)
      
      response:
      open_manipulator_msgs.srv.SetActuatorState_Response(is_planned=True)
      

      The response matches our expectations: a single variable named “is_planned” set to True which means the robot plans to carry out our request.

      Congratulations! You’ve now sent your first service command to your robot. Lock the arm again (hint: send the same message, but set the value to “true”) and then let’s work with something a bit more complex.

      Craft a Complex Message

      Duration: 7:00

      Our next challenge will be to open and close the robot gripper.

      Find the service interface

      A slightly more complex message is to open and close the robot gripper. This is done through the goal_tool_control service. Using the ros2 service and ros2 srv commands, we can map out how to communicate with the service:

      • Service: /open_manipulator_x/goal_tool_control
      • Service Type: open_manipulator_msgs/srv/SetJointPosition
      • Request Msg:
        • string: planning_group
        • JointPosition: joint_position
        • float64: path_time

      Hmm. The arguments planning_group and path_time are primitive field types (string and float64), but the variable joint_position is a message of type JointPosition: this is message type encapsulated within the service request. We need to find out how to craft a JointPosition message within our reqest. Use output from the command ros2 interface list to show the fully qualified message name so we can dig more into the message structure:

      Messages:
          action_msgs/msg/GoalInfo
          action_msgs/msg/GoalStatus
          ...
          open_manipulator_msgs/msg/JointPosition
          open_manipulator_msgs/msg/KinematicsPose
          open_manipulator_msgs/msg/OpenManipulatorState
          ...
      
      

      Many messages such as GoalInfo come installed with ROS, but your robot often will also use custom types, which is the case for our JointPosition message. As you can see from the interface information, this message is part of the open_manipulator_msgs namespace. Use the msg show command to show details about the JointPosition message:

      ros2 interface show open_manipulator_msgs/msg/JointPosition
      

      This command gives us details about the message structure:

        string[]   joint_name
        float64[]  position
        float64    max_accelerations_scaling_factor
        float64    max_velocity_scaling_factor
      

      The brackets after the data type for joint_name and joint_position arguments means these are arrays, not individual values. When we craft the YAML message for the service we need to send an array, not an individual value.

      :exclamation: Get another error?

      If you’re using ROS Dashing or an earlier verison of ROS, use the ros2 msg show command instead of ros2 interface show and everything will work fine. Similar to the ros2 srv, the ros2 msg command has been rolled into the ros2 interface command. Both srv and msg are deprecated and likely to be removed in future releases of ROS.

      We’ve come a long way without referring to vendor documentation, but at some point you may find that you have to actually read the manual. A quick look at the documentation for the robot arm lets us know the joint_name of the servo we’re trying to move is called “gripper”, and its position must be a value between -0.01 (fully closed) and 0.01 (fully open).

      We now have enough information to create a well formed service call which opens the joint:

      ros2 service call   '/open_manipulator_x/goal_tool_control'   'open_manipulator_msgs/srv/SetJointPosition' '{
        planning_group: "",
        path_time: 1,
        joint_position: {
          joint_name: ["gripper"],
          position: [0.01],
          max_accelerations_scaling_factor: 1.0,
          max_velocity_scaling_factor: 1.0
        }
      }'
      

      Another quick look at the documentation (or a bit of trial-and-error) shows that we can take default values for most of the parameters and remove them from the message. This results in a fairly simple command we can use to open the gripper:

        ros2 service call     '/open_manipulator_x/goal_tool_control'     'open_manipulator_msgs/srv/SetJointPosition'     '{ joint_position: { joint_name: ["gripper"], position: [0.01] } }'
      

      Can you create a service call to close the gripper?

      Move to a Point in Space

      Duration: 5:00

      We’re on a roll. Let’s make the arm move!

      Set a pose

      In order to move the arm to a specific position, we’ll use the SetKinematicsPose interface. This service interface contains a number of nested message structures, but the steps you’ve already completed should allow you to create a well-formed YAML request. Here’s a summary of message types and fields used by SetKinematicsPose. Can you reproduce this list on your own using the commands we’ve already covered?

      Message Type Field Type Field Name
      open_manipulator_msgs/srv/SetKinematicsPose string planning_group
      string end_effector_name
      KinematicsPose kinematics_pose
      float64 path_time
      open_manipulator_msgs/msg/KinematicsPose geometry_msgs/Pose pose
      float64 max_accelerations_scaling_factor
      float64 max_velocity_scaling_factor
      float64 tolerance
      geometry_msgs/msgs/Pose Point position
      Quaternion orientation
      geometry_msgs/msgs/Point float64 x
      float64 y
      float64 z
      geometry_msgs/msgs/Quaternion float64 x
      float64 y
      float64 z
      float64 w

      Now we can call the SetKinematicsPose service. Fill in the YAML structure with the information gathered from exploring the message structure to move the robot arm to position (x=0, y=0, z=0):

        ros2 service call     '/open_manipulator_x/goal_task_space_path'     'open_manipulator_msgs/srv/SetKinematicsPose'     '{
            planning_group: "abc", 
            end_effector_name: "gripper", 
            kinematics_pose: { 
              pose: {
                position: {
                  x: 0.0,
                  y: 0.0,
                  z: 0.0
                },
                orientation: {
                  x: 0.0,
                  y: 0.0,
                  z: 0.0,
                  w: 0.0
                },
              },
              max_accelerations_scaling_factor: 1.0,
              max_velocity_scaling_factor: 1.0,
              tolerance: 0.005
              },
            path_time: 3.0
            }'
      

      Did your robot arm move? If not, it’s probably because moving this robot moving to (0, 0, 0) may be unreasonable. Instead try moving to (0.15, 0.00, 0.20) and see how that works!

      Keep in mind that ROS has standardized on the International System of Units for all measurements, so distances are all in meters.

      Once again we can remove some unnecessary arguments (for now) to simplify:

        ros2 service call     '/open_manipulator_x/goal_task_space_path'     'open_manipulator_msgs/srv/SetKinematicsPose'     '{end_effector_name: "gripper", path_time: 3.0,
            kinematics_pose: {pose: {position: {x: 0.15, y: 0.0, z: 0.2 }}}}'
      

      Conclusion

      Duration: 2:00

      Congratulations! You’re now able to control your robot to run some straight forward tasks without having to compile and run code.

      Next Steps

      Continue exploring the wealth of documentation on how to create and use services in ROS 2 89.

      You can also roll a few of these utility commands into a simple shell script to quickly do some work with your robot. Here’s an example sample script to get you started:

      #! /bin/bash
      usage() {
          echo "Usage: $0 verb
      Verbs:
        close    Close the gripper
        help     This message
        home     Go to home position
        lock     Locks the arm servos
        open     Open the gripper
        unlock   Unlocks the arm servos
      "
      }
      
      if [ -z "$1" ]
      then
          usage
          exit 0
      fi
      case "$1" in
        "close" )
            ros2 service call           '/open_manipulator_x/goal_tool_control'           'open_manipulator_msgs/srv/SetJointPosition'           '{ joint_position: { joint_name: ["gripper"], position: [-0.01] } }'
        ;;
        "help" )
            usage
        ;;
        "home" )
            ros2 service call           '/open_manipulator_x/goal_task_space_path'           'open_manipulator_msgs/srv/SetKinematicsPose'           '{end_effector_name: "gripper", path_time: 3.0,
                kinematics_pose: {pose: {position: {x: 0.15, y: 0.0, z: 0.2}}}}'
        ;;
        "lock" )
            ros2 service call           '/open_manipulator_x/set_actuator_state'           'open_manipulator_msgs/srv/SetActuatorState'           "{ set_actuator_state: true }"
        ;;
        "open" )
            ros2 service call           '/open_manipulator_x/goal_tool_control'           'open_manipulator_msgs/srv/SetJointPosition'           '{ joint_position: { joint_name: ["gripper"], position: [0.01] } }'
        ;;
        "unlock" )
            ros2 service call           '/open_manipulator_x/set_actuator_state'           'open_manipulator_msgs/srv/SetActuatorState'           "{ set_actuator_state: false }"
        ;;
        * )
            echo "ERROR: '$1' not recognized.\n"
            usage
      esac
      

      Futher Reading

        本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
        转藏 分享 献花(0

        0条评论

        发表

        请遵守用户 评论公约

        类似文章 更多