You're reading the documentation for an older, but still supported, version of ROS 2. For information on the latest version, please have a look at Iron.

Example 10: Industrial robot with GPIO interfaces

This demo shows how to interact with GPIO interfaces.

The RRBot URDF files can be found in the description/urdf folder.

  1. To check that RRBot descriptions are working properly use following launch commands

    ros2 launch ros2_control_demo_example_10 view_robot.launch.py
    
  2. To start RRBot example open a terminal, source your ROS2-workspace and execute its launch file with

    ros2 launch ros2_control_demo_example_10 rrbot.launch.py
    

    The launch file loads and starts the robot hardware and controllers.

  3. Check if the hardware interface loaded properly, by opening another terminal and executing

    ros2 control list_hardware_interfaces
    
    command interfaces
        flange_analog_IOs/analog_output1 [available] [claimed]
        flange_vacuum/vacuum [available] [claimed]
        joint1/position [available] [claimed]
        joint2/position [available] [claimed]
    state interfaces
        flange_analog_IOs/analog_input1
        flange_analog_IOs/analog_input2
        flange_analog_IOs/analog_output1
        flange_vacuum/vacuum
        joint1/position
        joint2/position
    

    In contrast to the RRBot of example_1, you see in addition to the joints now also GPIO interfaces.

  4. Check if controllers are running by

    ros2 control list_controllers
    
    joint_state_broadcaster[joint_state_broadcaster/JointStateBroadcaster] active
    gpio_controller     [ros2_control_demo_example_10/GPIOController] active
    forward_position_controller[forward_command_controller/ForwardCommandController] active
    
  5. If you get output from above you can subscribe to the /gpio_controller/inputs topic published by the GPIO Controller using ROS 2 CLI interface:

    ros2 topic echo /gpio_controller/inputs
    
    interface_names:
    - flange_analog_IOs/analog_output1
    - flange_analog_IOs/analog_input1
    - flange_analog_IOs/analog_input2
    - flange_vacuum/vacuum
    values:
    - 0.0
    - 1199574016.0
    - 1676318848.0
    - 0.0
    
  6. Now you can send commands to the GPIO Controller using ROS 2 CLI interface:

    ros2 topic pub /gpio_controller/commands std_msgs/msg/Float64MultiArray "{data: [0.5,0.7]}"
    

    You should see a change in the /gpio_controller/inputs topic and a different output in the terminal where launch file is started, e.g.

    [RRBotSystemWithGPIOHardware]: Got command 0.5 for GP output 0!
    [RRBotSystemWithGPIOHardware]: Got command 0.7 for GP output 1!
    
  7. Let’s introspect the ros2_control hardware component. Calling

ros2 control list_hardware_components

should give you

Hardware Component 1
    name: RRBot
    type: system
    plugin name: ros2_control_demo_example_10/RRBotSystemWithGPIOHardware
    state: id=3 label=active
    command interfaces
            joint1/position [available] [claimed]
            joint2/position [available] [claimed]
            flange_analog_IOs/analog_output1 [available] [claimed]
            flange_vacuum/vacuum [available] [claimed]

This shows that the custom hardware interface plugin is loaded and running. If you work on a real robot and don’t have a simulator running, it is often faster to use the mock_components/GenericSystem hardware component instead of writing a custom one. Stop the launch file and start it again with an additional parameter

ros2 launch ros2_control_demo_example_10 rrbot.launch.py use_mock_hardware:=True

Calling list_hardware_components with the -v option

ros2 control list_hardware_components -v

now should give you

Hardware Component 1
    name: RRBot
    type: system
    plugin name: mock_components/GenericSystem
    state: id=3 label=active
    command interfaces
            joint1/position [available] [claimed]
            joint2/position [available] [claimed]
            flange_analog_IOs/analog_output1 [available] [claimed]
            flange_vacuum/vacuum [available] [claimed]
    state interfaces
            joint1/position [available]
            joint2/position [available]
            flange_analog_IOs/analog_output1 [available]
            flange_analog_IOs/analog_input1 [available]
            flange_analog_IOs/analog_input2 [available]
            flange_vacuum/vacuum [available]

One can see that the plugin mock_components/GenericSystem was now loaded instead: It will mirror the command interfaces to state interfaces with identical name. Call

ros2 topic echo /gpio_controller/inputs

again and you should see that - unless commands are received - the values of the state interfaces are now nan except for the vacuum interface.

interface_names:
- flange_analog_IOs/analog_output1
- flange_analog_IOs/analog_input1
- flange_analog_IOs/analog_input2
- flange_vacuum/vacuum
values:
- .nan
- .nan
- .nan
- 1.0

This is, because for the vacuum interface an initial value of 1.0 is set in the URDF file.

<gpio name="flange_vacuum">
  <command_interface name="vacuum"/>
  <state_interface name="vacuum">
    <param name="initial_value">1.0</param>
  </state_interface>
</gpio>

Call again

ros2 topic pub /gpio_controller/commands std_msgs/msg/Float64MultiArray "{data: [0.5,0.7]}"

and you will see that the GPIO command interfaces will be mirrored to their respective state interfaces.

Files used for this demos

Controllers from this demo