ros2_control 硬件组件教程

AgenticCoding·十二月创作之星挑战赛 10w+人浏览 466人参与

 系列文章目录

目录

 系列文章目录

前言

0.1 硬件组件的生命周期

0.2 处理读取()和写入()调用过程中发生的错误

一、ros2_control 硬件接口类型

1.1 概述

1.2 关节

1.3 传感器

1.4 GPIO

1.5 硬件组

1.6 示例

二、编写硬件组件

2.1 有用的外部参考资料

2.1.1 ros2_control:设置机器人的硬件接口包

三、硬件组件的不同更新速率

3.1 示例

四、异步运行硬件组件

4.1 参数

4.2 示例

五、语义组件

六、模拟组件

6.1 通用系统

6.1.1 参数

6.1.1.1 组件参数

6.1.1.2 按接口参数


前言

        硬件组件是ros2_control框架中对物理硬件的抽象表示。硬件分为三类:执行器传感器系统。各类型的详细说明请参阅硬件组件描述。

0.1 硬件组件的生命周期

        方法返回值的类型为 rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn,含义如下:

  • CallbackReturn::SUCCESS 表示方法执行成功。
  • CallbackReturn::FAILURE 表示方法执行失败且生命周期转换未成功。
  • CallbackReturn::ERROR 发生需在 on_error 方法中处理的关键错误。

        每次方法执行后硬件将进入以下状态:

  • UNCONFIGURED (on_init, on_cleanup):硬件仅完成初始化,但通信尚未启动,且未将接口导入 ResourceManager。
  • INACTIVE(on_configure, on_deactivate):

    与硬件建立通信并完成硬件组件配置。可读取状态信息,但命令接口(仅限系统和执行器)不可用。

    当前阶段,硬件组件实现可自行决定是否继续使用从 CommandInterfaces 接收的指令,或完全跳过这些指令。

  • FINALIZED(on_shutdown):硬件接口已准备就绪,可进行卸载/销毁操作。已分配内存完成清理。
  • ACTIVE(on_activate):

    可读取状态信息。

    仅限系统与执行器:

    硬件电源电路处于激活状态,硬件可执行移动操作(例如制动器已解除)。命令接口已启用,应向硬件发送指令。

0.2 处理读取()和写入()调用过程中发生的错误

        当硬件接口类的 read() 或 write() 方法返回 hardware_interface::return_type::ERROR 时,将调用on_error(previous_state)方法处理发生的错误。

        错误处理遵循节点生命周期:若操作成功,则返回CallbackReturn::SUCCESS且硬件恢复至UNCONFIGURED状态;若发生任何ERROR或FAILURE,硬件将终止于FINALIZED状态且无法恢复。唯一解决方案是重新加载整个插件,但控制器管理器当前尚未提供此项服务。


一、ros2_control 硬件接口类型

        ros2_control 框架提供了一组硬件接口类型,可用于为特定机器人或设备实现硬件组件。以下各节将描述不同的硬件接口类型及其用法。

1.1 概述

        ros2_control 中的硬件以 URDF 格式描述,内部解析后封装为 HardwareInfo 对象。相关定义详见 ros2_control 存储库,可查阅其中定义的结构体以了解每个 XML 标签可用的属性。下文提供展示结构的通用示例,更具体的示例请参见下文的示例部分。

<ros2_control name="Name_of_the_hardware" type="system">
  <hardware>
    <plugin>library_name/ClassName</plugin>
    <!-- added to hardware_parameters -->
    <param name="example_param">value</param>
  </hardware>
  <joint name="name_of_the_component">
    <!-- `data_type` argument is optional (defaults to double). -->
    <command_interface name="interface_name" data_type="double">
      <!-- All of them are optional. -->
      <param name="min">-1</param>
      <param name="max">1</param>
      <param name="initial_value">0.0</param>
       <!-- Optional. Added to the key/value storage parameters -->
      <param name="own_param_1">some_value</param>
      <param name="own_param_2">other_value</param>
    </command_interface>
    <!-- Short form to define StateInterface. Can be extended like CommandInterface. -->
    <state_interface name="position"/>
  </joint>
</ros2_control>

1.2 关节

        <joint>标签用于聚合物理机器人及执行器关节相关的接口。这些接口包含命令接口和状态接口,用于设置硬件目标值并读取其当前状态。

        所有定义在<ros2_control>标签中的关节必须存在于控制器管理器接收的URDF中。

        关节状态接口可通过 joint_state_broadcaster 以 ROS 主题形式发布。

1.3 传感器

        <sensor> 标签用于聚合描述硬件内部状态等多重状态接口。

        根据传感器类型,ros2_controllers 随附若干特定语义组件及广播器,详情参见 semantic_components。

1.4 GPIO

        <gpio>标签用于描述无法关联至关节或传感器的机器人设备输入输出端口。其解析逻辑类似于包含命令/状态接口的<joint>标签,必须至少包含一个<command>或<state>子标签。

        选择“gpio”作为关键词体现其通用性。尽管严格用于数字信号,该标签可描述任何模拟电信号、数字信号或物理量值。

        <gpio> 标签可作为三类硬件组件(系统、传感器或执行器)的子节点使用。

        由于以<gpio>标签实现的端口通常具有高度应用特异性,ros2_control框架中不存在通用发布者。每个应用程序都需要实现自定义的gpio控制器。示例请参见演示库中的GPIO控制器示例。

1.5 硬件组

        硬件组件组作为复杂系统中的关键组织机制,能有效促进错误处理与容错机制。通过将相关硬件组件(如机械臂内的执行器)进行分组,用户可建立统一的错误检测与响应框架。

        硬件组件组在互联硬件组件间的错误传播中发挥着至关重要的作用。例如在机械臂系统中,将执行器分组可实现错误传播。当组内某执行器故障时,错误可传播至其他执行器,从而提示系统潜在问题。默认情况下,执行器错误仅限于其自身硬件组件,其余组件可不受影响地继续运行。在提供的ros2_control配置中,每个<ros2_control>块内的<group>标签标志着硬件组件的分组,从而实现系统内的错误传播机制。

1.6 示例

        以下示例展示了如何在ros2_control URDF中使用不同硬件接口类型。这些接口可在各类硬件组件(系统、执行器、传感器)中组合使用(详见文档说明),具体如下:

  1. 配备多GPIO接口的机器人

    RRBot系统

    数字接口:4路输入与2路输出

    模拟接口:2路输入与1路输出

    法兰处真空阀(开/关)

    <ros2_control name="RRBotSystemMutipleGPIOs" type="system">
      <hardware>
        <plugin>ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware</plugin>
        <param name="example_param_hw_start_duration_sec">2.0</param>
        <param name="example_param_hw_stop_duration_sec">3.0</param>
        <param name="example_param_hw_slowdown">2.0</param>
      </hardware>
      <joint name="joint1">
        <command_interface name="position">
          <param name="min">-1</param>
          <param name="max">1</param>
        </command_interface>
        <state_interface name="position"/>
      </joint>
      <joint name="joint2">
        <command_interface name="position">
          <param name="min">-1</param>
          <param name="max">1</param>
        </command_interface>
        <state_interface name="position"/>
      </joint>
      <gpio name="flange_digital_IOs">
        <command_interface name="digital_output1"/>
        <state_interface name="digital_output1"/>    <!-- Needed to know current state of the output -->
        <command_interface name="digital_output2"/>
        <state_interface name="digital_output2"/>
        <state_interface name="digital_input1"/>
        <state_interface name="digital_input2"/>
      </gpio>
      <gpio name="flange_analog_IOs">
        <command_interface name="analog_output1"/>
        <state_interface name="analog_output1">    <!-- Needed to know current state of the output -->
          <param name="initial_value">3.1</param>  <!-- Optional initial value for mock_hardware -->
        </state_interface>
        <state_interface name="analog_input1"/>
        <state_interface name="analog_input2"/>
      </gpio>
      <gpio name="flange_vacuum">
        <command_interface name="vacuum"/>
        <state_interface name="vacuum"/>    <!-- Needed to know current state of the output -->
      </gpio>
    </ros2_control>
  2. 具备电吸附与真空吸附功能的夹爪

    多模态夹爪

    1自由度平行夹爪

    真空吸附开关

    <ros2_control name="MultimodalGripper" type="actuator">
      <hardware>
        <plugin>ros2_control_demo_hardware/MultimodalGripper</plugin>
      </hardware>
      <joint name="parallel_fingers">
        <command_interface name="position">
          <param name="min">0</param>
          <param name="max">100</param>
        </command_interface>
        <state_interface name="position"/>
      </joint>
      <gpio name="suction">
        <command_interface name="suction"/>
        <state_interface name="suction"/>    <!-- Needed to know current state of the output -->
      </gpio>
    </ros2_control>
  3. 带温度反馈和可调校准的力-扭矩传感器

    2D 力-扭矩传感器

    温度反馈(单位:°C)

    提供3种校准矩阵可选,即校准范围

    <ros2_control name="RRBotForceTorqueSensor2D" type="sensor">
      <hardware>
        <plugin>ros2_control_demo_hardware/ForceTorqueSensor2DHardware</plugin>
        <param name="example_param_read_for_sec">0.43</param>
      </hardware>
      <sensor name="tcp_fts_sensor">
        <state_interface name="fx"/>
        <state_interface name="tz"/>
        <param name="frame_id">kuka_tcp</param>
        <param name="fx_range">100</param>
        <param name="tz_range">100</param>
      </sensor>
      <sensor name="temp_feedback">
        <state_interface name="temperature"/>
      </sensor>
      <gpio name="calibration">
        <command_interface name="calibration_matrix_nr"/>
        <state_interface name="calibration_matrix_nr"/>
      </gpio>
    </ros2_control>
  4. 属于同一组的多个硬件组件的机器人:组1

    RRBot系统1和2

    数字:总计4个输入和2个输出

    模拟:总计2个输入和1个输出

    法兰处的真空阀(开/关)

    组:组1

    <ros2_control name="RRBotSystem1" type="system">
      <hardware>
        <plugin>ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware</plugin>
        <group>Group1</group>
        <param name="example_param_hw_start_duration_sec">2.0</param>
        <param name="example_param_hw_stop_duration_sec">3.0</param>
        <param name="example_param_hw_slowdown">2.0</param>
      </hardware>
      <joint name="joint1">
        <command_interface name="position">
          <param name="min">-1</param>
          <param name="max">1</param>
        </command_interface>
        <state_interface name="position"/>
      </joint>
      <gpio name="flange_analog_IOs">
        <command_interface name="analog_output1"/>
        <state_interface name="analog_output1">    <!-- Needed to know current state of the output -->
          <param name="initial_value">3.1</param>  <!-- Optional initial value for mock_hardware -->
        </state_interface>
        <state_interface name="analog_input1"/>
        <state_interface name="analog_input2"/>
      </gpio>
      <gpio name="flange_vacuum">
        <command_interface name="vacuum"/>
        <state_interface name="vacuum"/>    <!-- Needed to know current state of the output -->
      </gpio>
    </ros2_control>
    <ros2_control name="RRBotSystem2" type="system">
      <hardware>
        <plugin>ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware</plugin>
        <group>Group1</group>
        <param name="example_param_hw_start_duration_sec">2.0</param>
        <param name="example_param_hw_stop_duration_sec">3.0</param>
        <param name="example_param_hw_slowdown">2.0</param>
      </hardware>
      <joint name="joint2">
        <command_interface name="position">
          <param name="min">-1</param>
          <param name="max">1</param>
        </command_interface>
        <state_interface name="position"/>
      </joint>
      <gpio name="flange_digital_IOs">
        <command_interface name="digital_output1"/>
        <state_interface name="digital_output1"/>    <!-- Needed to know current state of the output -->
        <command_interface name="digital_output2"/>
        <state_interface name="digital_output2"/>
        <state_interface name="digital_input1"/>
        <state_interface name="digital_input2"/>
      </gpio>
    </ros2_control>

二、编写硬件组件

        在 ros2_control 硬件系统中,组件以库的形式存在,由控制器管理器通过 pluginlib 接口动态加载。以下是创建新硬件接口的分步指南,涵盖源文件、基础测试及编译规则的制定。

  1. 准备软件包
    若硬件接口的软件包尚未存在,请先创建。该软件包应采用 ament_cmake 作为构建类型。最便捷的方式是在线查找最新手册。ros2 pkg create 命令可有效辅助此过程,使用 --help 参数可获取更多操作说明。该命令还提供创建库源文件及编译规则的选项,有助于后续步骤的实施。
  2. 准备源文件
    创建包后,其中至少应包含 CMakeLists.txt 和 package.xml 文件。若尚未存在,还需创建 include/<PACKAGE_NAME>/ 和 src 文件夹。在 include/<PACKAGE_NAME>/ 文件夹中添加 <robot_hardware_interface_name>.hpp,并在 src 文件夹中添加 <robot_hardware_interface_name>.cpp。
  3. 在头文件(.hpp)中添加声明
    1. 注意使用头文件保护机制。ROS2 风格采用 #ifndef 和 #define 预处理指令(更多信息可通过搜索引擎获取)。
    2. 包含 “hardware_interface/$interface_type$_interface.hpp”。$interface_type$根据所用硬件类型可为Actuator、Sensor 或 System。各类型详情请查阅硬件组件说明。
    3. 为硬件接口定义唯一命名空间,通常采用 snake_case 法书写的包名。
    4. 定义硬件接口类,继承自 $InterfaceType$Interface,例如:c++class HardwareInterfaceName : public hardware_interface::$InterfaceType$Interface
    5. 添加无参数构造函数及实现 LifecycleNodeInterface 的公共方法:on_configure、on_cleanup、on_shutdown、on_activate、on_deactivate、on_error;同时重写 $InterfaceType$Interface 定义的方法: on_init、export_state_interfaces、export_command_interfaces、prepare_command_mode_switch(可选)、perform_command_mode_switch(可选)、read、write。
    6. 有关硬件生命周期的详细说明请查阅拉取请求,具体方法定义请参阅“hardware_interface/$interface_type$_interface.hpp” 头文件或执行器、传感器及系统的 Doxygen 文档。
  4. 在源文件(.cpp)中添加定义
    1. 包含硬件接口的头文件,并添加命名空间定义以简化后续开发。
    2. 实现 on_init 方法。在此处应初始化所有成员变量,并处理 info 参数中的参数。首行通常需调用父类的on_init 处理标准值(如名称),使用方式为:hardware_interface::(Actuator|Sensor|System)Interface::on_init(info)。若所有必需参数均已设置且有效,且系统运行正常,则返回 CallbackReturn::SUCCESS;否则返回 CallbackReturn::ERROR。
      1. (可选)添加发布者、服务等

        硬件组件的常见需求是发布状态或诊断信息,同时不干扰实时控制回路。

        这使您能够在不影响实时性能的前提下,向硬件接口添加任何标准ROS 2组件(发布者、订阅者、服务、定时器)。实现此目标主要有三种方式。

        方法 1:使用框架管理的发布者(推荐且最适用于硬件状态消息)

        参见框架管理的发布者

        方法 2:使用框架管理的节点(推荐且最适用于自定义消息)

        框架会为每个硬件组件在内部创建专用的 ROS 2 节点。您的硬件插件可获取该节点的句柄并加以使用。

        访问和使用默认节点:可通过调用 get_node() 方法获取该节点的 shared_ptr,并像使用其他 rclcpp::Node::SharedPtr 一样,用它来创建发布者、定时器等组件。

        // Continuing inside on_configure()
        if (get_node())
        {
           my_publisher_ = get_node()->create_publisher<std_msgs::msg::String>("~/status", 10);
        
           using namespace std::chrono_literals;
           my_timer_ = get_node()->create_wall_timer(1s, [this]() {
              std_msgs::msg::String msg;
              msg.data = "Hardware status update!";
              my_publisher_->publish(msg);
           });
        }

        方法三:使用 `HardwareComponentInterfaceParams` 中的执行器

        对于需要直接控制节点创建的高级用例,可配置 on_init 方法接收 HardwareComponentInterfaceParams 结构体。该结构体包含指向 ControllerManager 执行器的弱引用。

        更新 on_init 签名:首先,硬件接口必须重写接受 HardwareComponentInterfaceParams 的 on_init 版本。

        // In your <robot_hardware_interface_name>.hpp
        hardware_interface::CallbackReturn on_init(
        const hardware_interface::HardwareComponentInterfaceParams & params) override;

        锁定并使用执行器:在 `on_init` 内部,必须安全地“锁定” `weak_ptr` 以获取可用的 `shared_ptr`。随后即可创建自己的节点并将其添加到执行器中。

        // In your <robot_hardware_interface_name>.cpp, inside on_init(params)
        if (auto locked_executor = params.executor.lock())
        {
           my_custom_node_ = std::make_shared<rclcpp::Node>("my_custom_node");
           locked_executor->add_node(my_custom_node_->get_node_base_interface());
           // ... create publishers/timers on my_custom_node_ ...
        }
    3. 编写 on_configure 方法,通常在此处设置与硬件的通信并完成所有配置,以便激活硬件。
    4. 实现 on_cleanup 方法,该方法执行与 on_configure 相反的操作。
    5. Command-/StateInterfaces 现由框架通过 on_export_command_interfaces()  或 on_export_state_interfaces()  方法自动创建并导出,其依据是 ros2_control XML 标签中定义的接口。该标签经解析后,系统将据此创建 InterfaceDescription(详见 hardware_info.hpp)。

      要访问自动创建的命令/状态接口,我们提供了 std::unordered_map<std::string, InterfaceDescription>,其中字符串是接口的完全限定名,InterfaceDescription 是接口的配置。该映射分为type_state_interfaces_ 和 type_command_interfaces_ 两类,其中 type 可取值为:joint(关节)、sensor(传感器)、gpio(GPIO)和 unlisted(未列出)。例如所有关节的命令接口可在joint_command_interfaces_ 映射中找到。unlisted 包含所有未在 ros2_control XML 标签中列出、但通过重写 export_unlisted_command_interface_descriptions() 或 export_unlisted_state_interface_descriptions() 函数创建的自定义命令/状态接口。

      对于传感器类型的硬件接口,不存在 export_command_interfaces 方法。

      需注意,完整接口名称采用 <joint_name>/<interface_type> 的结构。

    6. (可选)若需导出未列在ros2_control XML标签中的命令/状态接口,请按以下步骤操作:
      1. 覆盖 virtual std::vector<hardware_interface::InterfaceDescription> export_unlisted_command_interface_descriptions() 或 virtual std::vector<hardware_interface::InterfaceDescription> export_unlisted_state_interface_descriptions()
      2. 在重写的 export_unlisted_command_interface_descriptions() 或 export_unlisted_state_interface_descriptions() 函数中,为每个需要创建的接口创建 InterfaceDescription 对象,将其添加到向量中并返回该向量:
        std::vector<hardware_interface::InterfaceDescription> my_unlisted_interfaces;
        
        InterfaceInfo unlisted_interface;
        unlisted_interface.name = "some_unlisted_interface";
        unlisted_interface.min = "-5.0";
        unlisted_interface.data_type = "double";
        my_unlisted_interfaces.push_back(InterfaceDescription(info_.name, unlisted_interface));
        
        return my_unlisted_interfaces;
      3. 未列出的接口将根据其创建函数的不同,存储在 unlisted_command_interfaces_ 或 unlisted_state_interfaces_ 映射中。

      4. 可通过 get_state(name)、set_state(name, value)、get_command(name) 或 set_command(name, value) 等方法访问,操作逻辑与常规接口相同。例如:get_state(“some_unlisted_interface”)。

    7. (可选)若默认实现(on_export_command_interfaces() 或 on_export_state_interfaces())无法满足 Command-/StateInterfaces 导出需求,可覆盖这些方法。但需注意以下事项:
      1. 若需提供未列出的接口,必须调用 export_unlisted_command_interface_descriptions() 或 export_unlisted_state_interface_descriptions(),并将它们添加到 unlisted_command_interfaces_ 或 unlisted_state_interfaces_ 中。
      2. 请务必在内部存储创建的 Command-/StateInterfaces 接口,因为您仅返回共享指针,资源管理器不会为硬件提供对这些接口的访问权限。因此必须自行负责存储。
      3. 名称必须唯一!
    8. (可选)对于执行器和系统类型的硬件接口,若您的硬件支持多种控制模式,请实现 prepare_command_mode_switch 和 perform_command_mode_switch 方法。
    9. 在硬件“电源”开启时实现on_activate方法。
    10. 实现 on_deactivate 方法,该方法与 on_activate 方法功能相反。
    11. 实现 on_shutdown 方法,用于实现硬件的优雅关闭。
    12. 实现 on_error 方法,用于处理来自所有状态的不同错误。
    13. 实现 read 方法,从硬件获取状态并将其存储到 export_state_interfaces 中定义的内部变量中。
    14. 实现写入方法,该方法根据export_command_interfaces中定义的内部变量存储的值来控制硬件。
    15. (可选)框架管理发布者
      实现 init_hardware_status_message 和 update_hardware_status_message 方法,通过 control_msgs/msg/HardwareStatus 消息发布框架支持的硬件状态报告:
      1. `init_hardware_status_message`:此非实时方法在初始化期间仅调用一次。必须重写该方法以定义状态消息的静态结构。包括设置 hardware_id、调整 hardware_device_states 向量大小,并为每个设备调整其特定状态向量大小(如 generic_hardware_status、canopen_states),同时填充 device_id 和接口名称等静态字段。在此预分配消息结构对实时安全性至关重要。
        // In your <robot_hardware_interface_name>.hpp
        hardware_interface::CallbackReturn init_hardware_status_message(
        control_msgs::msg::HardwareStatus & msg_template) override;
        
        // In your <robot_hardware_interface_name>.cpp
        hardware_interface::CallbackReturn MyHardware::init_hardware_status_message(
        control_msgs::msg::HardwareStatus & msg)
        {
           msg.hardware_id = get_hardware_info().name;
           msg.hardware_device_states.resize(get_hardware_info().joints.size());
        
           for (size_t i = 0; i < get_hardware_info().joints.size(); ++i)
           {
              msg.hardware_device_states[i].device_id = get_hardware_info().joints[i].name;
              // This example uses one generic status per joint
              msg.hardware_device_states[i].generic_hardware_status.resize(1);
           }
           return hardware_interface::CallbackReturn::SUCCESS;
        }
      2. `update_hardware_status_message`:此实时安全方法由框架的定时器回调调用。您必须重写该方法以填充预结构化消息的动态值。这通常涉及将内部状态变量(在您的`read()`方法中更新)复制到消息字段中。该方法必须快速且不分配内存。

        // In your <robot_hardware_interface_name>.hpp
        hardware_interface::return_type update_hardware_status_message(
        control_msgs::msg::HardwareStatus & msg) override;
        
        // In your <robot_hardware_interface_name>.cpp
        hardware_interface::return_type MyHardware::update_hardware_status_message(
        control_msgs::msg::HardwareStatus & msg)
        {
           for (size_t i = 0; i < get_hardware_info().joints.size(); ++i)
           {
              auto & generic_status = msg.hardware_device_states[i].generic_hardware_status;
              // Example: Map internal state to a standard status field
              if (std::abs(hw_positions_[i]) > joint_limits_[i].max_position)
              {
                 generic_status.health_status = control_msgs::msg::GenericState::HEALTH_ERROR;
              }
              else
              {
                 generic_status.health_status = control_msgs::msg::GenericState::HEALTH_OK;
              }
           }
           return hardware_interface::return_type::OK;
        }
      3. 在URDF中启用:最后,为激活发布器,请在URDF的<hardware>标签中添加status_publish_rate参数。将其设置为0.0可禁用该功能。

        <ros2_control name="MyHardware" type="system">
        <hardware>
           <plugin>my_package/MyHardware</plugin>
           <param name="status_publish_rate">20.0</param>
        </hardware>
        ...
        </ros2_control>

        要查看使用框架管理的节点发布诊断消息的完整可运行实现,请参阅示例 17 中的演示。

    16. 重要提示:在文件末尾关闭命名空间后,添加 PLUGINLIB_EXPORT_CLASS 宏。
      为此需包含头文件“pluginlib/class_list_macros.hpp”。首参数应精确指定硬件接口类,例如<my_hardware_interface_package>::<RobotHardwareInterfaceName>;次参数则为基类,即hardware_interface::(Actuator|Sensor|System)Interface。
  5. 为插件库编写导出定义
    1. 在包中创建 <my_hardware_interface_package>.xml 文件,并添加库及硬件接口类的定义,这些定义需对插件库可见。最简便的方法是参考硬件接口 mock_components 部分中 mock 组件的定义。
    2. 通常,插件名称由包(命名空间)和类名共同定义,例如<my_hardware_interface_package>/<RobotHardwareInterfaceName>。该名称决定了资源管理器搜索硬件接口时的类型标识。其余两个参数必须与 <robot_hardware_interface_name>.cpp 文件末尾宏定义中的内容保持一致。
  6. 编写一个简单测试以检查控制器是否可被找到并加载
    1. 在您的软件包中创建名为 test 的文件夹(若尚未存在),并添加一个名为 test_load_<机器人硬件接口名称>.cpp 的文件。
    2. 您可以复制 test_generic_system.cpp 软件包中定义的 load_generic_system_2dof 内容。
    3. 修改复制的测试文件名,并在最后一行指定硬件接口类型处,替换为 <my_hardware_interface_package>.xml 文件中定义的名称,例如 <my_hardware_interface_package>/<RobotHardwareInterfaceName>。
  7. 在 ``CMakeLists.txt`` 文件中添加编译指令
    1. 在 find_package(ament_cmake REQUIRED) 语句下方添加更多依赖项。至少需包含:hardware_interface、pluginlib、rclcpp 和 rclcpp_lifecycle。
    2. 添加共享库编译指令,提供<robot_hardware_interface_name>.cpp文件作为源代码。
    3. 为库添加目标包含目录。通常仅需添加include目录。
    4. 添加库所需的 ament 依赖项。至少应包含第 1 项下列出的依赖。
    5. 使用以下命令导出 pluginlib 描述文件:
      ..
      code:: cmake
      
      pluginlib_export_plugin_description_file(hardware_interface <my_hardware_interface_package>.xml)
    6. 为目标和包含目录添加安装指令。
    7. 在测试部分添加以下依赖项:ament_cmake_gmock、hardware_interface。
    8. 使用 ament_add_gmock 指令为测试添加编译定义。具体实现可参考 ros2_control 包中模拟硬件的处理方式。
    9. (可选)在 ament_package() 之前将硬件接口库添加至 ament_export_libraries。
  8. 在``package.xml``文件中添加依赖项
    1. 至少在<depend>标签中添加以下包:hardware_interface、pluginlib、rclcpp和rclcpp_lifecycle。
    2. 至少在<test_depend>标签中添加以下包:ament_add_gmock和hardware_interface。
  9. 编译和测试硬件组件
    1. 现在已准备就绪,可使用 colcon build <my_hardware_interface_package> 命令编译硬件组件。执行此命令前请确保进入工作区根目录。
    2. 若编译成功,请在安装目录中执行 setup.bash 文件,并运行 colcon test <my_hardware_interface_package> 命令,以验证新控制器能否通过 pluginlib 库被识别,并由控制器管理器加载。

        就这样!尽情编写出色的控制器吧!

2.1 有用的外部参考资料

2.1.1 ros2_control:设置机器人的硬件接口包

        本用例描述如何使用 ROS 团队工作区(RTW)框架中的脚本,为 ros2_control 框架设置机器人的硬件接口。

        ros2_control_setup-hardware-interface-package 脚本接受机器人硬件接口的文件名,并可选地接受类名。文件名应采用标准 ROS 格式 <my_cool_robot_hardware>。系统将以此名称生成 .cpp 和 .hpp 文件。若未指定类名,则通过驼峰式命名法推断文件名。软件包名称从 'package.xml' 文件中获取。

注意

        建议使用 setup-new-package 脚本设置您的软件包。

警告

        该脚本必须在 ament_cmake 包的根目录下执行,该目录将生成机器人的硬件接口文件。

        脚本会从 templates/ros2_control/hardware 文件夹复制模板文件,重命名文件并替换占位符。脚本还会添加插件描述以及一个简单测试,用于检查插件是否可加载。

ros2_control_setup-hardware-interface-package FILE_NAME [CLASS_NAME]

        在所有文件复制完成并设置占位符后,更改将自动提交到Git暂存区。

三、硬件组件的不同更新速率

        ros2_control 框架允许不同硬件组件以不同更新率运行。当某些硬件组件需要以不同于传统控制回路频率(该频率与 controller_manager 相同)的频率运行时,此功能尤为实用。在配备不同传感器或采用不同通信协议的机器人系统中,存在频率各异的组件实属常见。当某个硬件组件需要以高于其他组件的速率运行时,此特性尤为实用。例如,当 controller_manager 的控制回路频率高于 1000Hz 时,可将某传感器读取频率设为 1000Hz,而其余组件维持 500Hz 运行。只需在硬件组件的 ros2_control 标签中添加 rw_rate 参数,即可轻松定义读写速率。

3.1 示例

        以下示例展示了如何在 ros2_control URDF 中使用不同更新频率的硬件接口类型。这些接口可在不同硬件组件类型(系统、执行器、传感器)中组合使用(详见文档说明),具体如下:

        对于配备多模态机械手和外部传感器的 RRBot,当各组件运行频率不同时:

<ros2_control name="RRBotSystemMutipleGPIOs" type="system" rw_rate="500">
  <hardware>
    <plugin>ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware</plugin>
    <param name="example_param_hw_start_duration_sec">2.0</param>
    <param name="example_param_hw_stop_duration_sec">3.0</param>
    <param name="example_param_hw_slowdown">2.0</param>
  </hardware>
  <joint name="joint1">
    <command_interface name="position">
      <param name="min">-1</param>
      <param name="max">1</param>
    </command_interface>
    <state_interface name="position"/>
  </joint>
  <joint name="joint2">
    <command_interface name="position">
      <param name="min">-1</param>
      <param name="max">1</param>
    </command_interface>
    <state_interface name="position"/>
  </joint>
  <gpio name="flange_digital_IOs">
    <command_interface name="digital_output1"/>
    <state_interface name="digital_output1"/>    <!-- Needed to know current state of the output -->
    <command_interface name="digital_output2"/>
    <state_interface name="digital_output2"/>
    <state_interface name="digital_input1"/>
    <state_interface name="digital_input2"/>
  </gpio>
</ros2_control>
<ros2_control name="MultimodalGripper" type="actuator" rw_rate="200">
  <hardware>
    <plugin>ros2_control_demo_hardware/MultimodalGripper</plugin>
  </hardware>
  <joint name="parallel_fingers">
    <command_interface name="position">
      <param name="min">0</param>
      <param name="max">100</param>
    </command_interface>
    <state_interface name="position"/>
  </joint>
  <gpio name="suction">
    <command_interface name="suction"/>
    <state_interface name="suction"/>    <!-- Needed to know current state of the output -->
  </gpio>
</ros2_control>
<ros2_control name="RRBotForceTorqueSensor2D" type="sensor" rw_rate="250">
  <hardware>
    <plugin>ros2_control_demo_hardware/ForceTorqueSensor2DHardware</plugin>
    <param name="example_param_read_for_sec">0.43</param>
  </hardware>
  <sensor name="tcp_fts_sensor">
    <state_interface name="fx"/>
    <state_interface name="tz"/>
    <param name="frame_id">kuka_tcp</param>
    <param name="fx_range">100</param>
    <param name="tz_range">100</param>
  </sensor>
  <sensor name="temp_feedback">
    <state_interface name="temperature"/>
  </sensor>
  <gpio name="calibration">
    <command_interface name="calibration_matrix_nr"/>
    <state_interface name="calibration_matrix_nr"/>
  </gpio>
</ros2_control>

        在上例中,控制RRBot关节的系统硬件组件运行频率为500 Hz,多模态抓取器运行频率为200 Hz,力矩传感器运行频率为250 Hz。

注意

        在上例中,系统、执行器和传感器硬件组件的 rw_rate 参数分别设置为 500 Hz、200 Hz 和 250 Hz。该参数为可选项,若未设置则采用默认值0,表示硬件组件将以与 controller_manager 相同的速率运行。但若指定速率高于 controller_manager 速率,则硬件组件将以 controller_manager 的速率运行。

四、异步运行硬件组件

        ros2_control 框架支持硬件组件异步运行。当某些硬件组件需要在独立线程或执行器中运行时,此特性尤为实用。例如,当传感器读取数据耗时较长,导致 controller_manager 控制循环周期受影响时,可将传感器置于独立线程或执行器中运行,从而避免阻塞控制循环。

4.1 参数

        可在 ros2_control 硬件配置中设置以下参数以异步运行硬件组件:

  • is_async: (可选) 若设置为 true,硬件组件将异步运行。默认值为 false。

        在 ros2_control 标签下,可添加 properties 标签以指定异步硬件组件的以下参数:

  • thread_priority:(可选)运行硬件组件的线程优先级。优先级为 0 至 99 之间的整数值,默认值为 50。
  • affinity:(可选) 运行硬件组件的线程的CPU亲和性。亲和性是一个CPU核心ID列表。默认值为空列表,表示该线程可在任意CPU核心上运行。
  • print_warnings:(可选)若设置为true,当线程无法满足其时序要求时将输出警告。默认值为true。

注意

        线程优先级仅在硬件组件异步运行时使用。当硬件组件异步运行时,它采用先进先出(FIFO)调度策略。

4.2 示例

        以下示例展示了如何通过 ros2_control URDF 同步和异步地使用不同硬件接口类型。这些接口可在不同硬件组件类型(系统、执行器、传感器)中组合使用(详见文档),具体如下:

        对于配备多模态机械手和外部传感器的 RRBot:

<ros2_control name="RRBotSystemMutipleGPIOs" type="system">
  <hardware>
    <plugin>ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware</plugin>
    <param name="example_param_hw_start_duration_sec">2.0</param>
    <param name="example_param_hw_stop_duration_sec">3.0</param>
    <param name="example_param_hw_slowdown">2.0</param>
  </hardware>
  <joint name="joint1">
    <command_interface name="position">
      <param name="min">-1</param>
      <param name="max">1</param>
    </command_interface>
    <state_interface name="position"/>
  </joint>
  <joint name="joint2">
    <command_interface name="position">
      <param name="min">-1</param>
      <param name="max">1</param>
    </command_interface>
    <state_interface name="position"/>
  </joint>
  <gpio name="flange_digital_IOs">
    <command_interface name="digital_output1"/>
    <state_interface name="digital_output1"/>    <!-- Needed to know current state of the output -->
    <command_interface name="digital_output2"/>
    <state_interface name="digital_output2"/>
    <state_interface name="digital_input1"/>
    <state_interface name="digital_input2"/>
  </gpio>
</ros2_control>
<ros2_control name="MultimodalGripper" type="actuator" is_async="true">
  <properties>
    <async affinity="[2,4]" scheduling_policy="synchronized" print_warnings="true" thread_priority="30"/>
  </properties>
  <hardware>
    <plugin>ros2_control_demo_hardware/MultimodalGripper</plugin>
  </hardware>
  <joint name="parallel_fingers">
    <command_interface name="position">
      <param name="min">0</param>
      <param name="max">100</param>
    </command_interface>
    <state_interface name="position"/>
  </joint>
</ros2_control>
<ros2_control name="RRBotForceTorqueSensor2D" type="sensor" is_async="true">
  <hardware>
    <plugin>ros2_control_demo_hardware/ForceTorqueSensor2DHardware</plugin>
    <param name="example_param_read_for_sec">0.43</param>
  </hardware>
  <sensor name="tcp_fts_sensor">
    <state_interface name="fx"/>
    <state_interface name="tz"/>
    <param name="frame_id">kuka_tcp</param>
    <param name="fx_range">100</param>
    <param name="tz_range">100</param>
  </sensor>
  <sensor name="temp_feedback">
    <state_interface name="temperature"/>
  </sensor>
  <gpio name="calibration">
    <command_interface name="calibration_matrix_nr"/>
    <state_interface name="calibration_matrix_nr"/>
  </gpio>
</ros2_control>

        在上例中,定义了以下组件:

  • 一个名为RRBotSystemMutipleGPIOs的系统硬件组件,包含两个关节和一个同步运行的GPIO组件。
  • 一个名为MultimodalGripper的执行器硬件组件,其关节以30的线程优先级异步运行。
  • 一个名为RRBotForceTorqueSensor2D的传感器硬件组件,包含两个传感器和一个异步运行的GPIO组件,其默认线程优先级为50。

五、语义组件

        为简化常用硬件接口的配置流程,可采用语义组件封装接口的申请/释放机制。基础组件语义组件接口(semantic_components::SemanticComponentInterface)与语义组件命令接口(semantic_components::SemanticComponentCommandInterface)分别用于定义只读设备与只写设备的语义组件。

        现有语义组件接口列表(链接至头文件)及关联广播器(如有):

        现有语义组件命令接口列表(链接至头文件)及其关联控制器(如有):

六、模拟组件

        模拟组件是对硬件组件(即系统、传感器和执行器)的简单“仿真”。它们通过将命令映射到其状态来提供理想行为。在离线测试ros2_control框架时,可使用对应的硬件接口替代真实硬件。其主要优势在于无需接触硬件即可测试框架内部所有“管道”功能,这意味着您能测试控制器、广播器、启动文件,甚至与MoveIt等系统的集成。该方案旨在减少物理硬件调试时间,加速开发进程。

6.1 通用系统

        该组件实现硬件接口层(hardware_interface::SystemInterface),支持命令与状态接口。有关硬件组件的详细信息,请查阅完整文档。

        功能特性:

  • 支持模拟关节功能,通过解析URDF文件实现(详见URDF维基)
  • 支持带偏移量与无偏移量的命令-状态映射
  • 提供伪命令接口,用于从外部节点设置传感器数据(需配合前向控制器使用)
  • 提供伪GPIO接口,用于从外部节点设置传感器数据(需配合前向控制器使用)

6.1.1 参数

        包含所有可选参数(含默认值)的完整示例:

<ros2_control name="MockHardwareSystem" type="system">
  <hardware>
    <plugin>mock_components/GenericSystem</plugin>
    <param name="calculate_dynamics">false</param>
    <param name="custom_interface_with_following_offset"></param>
    <param name="disable_commands">false</param>
    <param name="mock_gpio_commands">false</param>
    <param name="mock_sensor_commands">false</param>
    <param name="position_state_following_offset">0.0</param>
  </hardware>
  <joint name="joint1">
    <command_interface name="position"/>
    <command_interface name="velocity"/>
    <state_interface name="position">
      <param name="initial_value">3.45</param>
    </state_interface>
    <state_interface name="velocity"/>
    <state_interface name="acceleration"/>
  </joint>
  <joint name="joint2">
    <command_interface name="velocity"/>
    <command_interface name="acceleration"/>
    <state_interface name="position">
      <param name="initial_value">2.78</param>
    </state_interface>
    <state_interface name="position"/>
    <state_interface name="velocity"/>
    <state_interface name="acceleration"/>
  </joint>
  <gpio name="flange_vacuum">
    <command_interface name="vacuum"/>
    <state_interface name="vacuum" data_type="double"/>
  </gpio>
</ros2_control>

        请参阅示例_2了解使用 calculate_dynamics 的示例,或参阅示例_10了解与GPIO接口结合使用的示例。

6.1.1.1 组件参数
  • calculate_dynamics(可选;布尔值;默认:false)

        通过欧拉正向积分或有限差分法根据命令计算状态。

  • custom_interface_with_following_offset(可选;字符串;默认:“”)

        将偏移命令映射至自定义接口(参见position_state_following_offset)。

  • disable_commands(可选;布尔值;默认:false)

        禁用命令到状态的镜像映射。当硬件未损坏但突然失去反馈时,此选项可模拟硬件连接错误。也可用于测试硬件在无反馈状态(即开环配置)下的运行情况。

  • mock_gpio_commands(可选;布尔值;默认:false)

        创建用于通过外部命令模拟GPIO状态的伪命令接口。这些接口通常由前向控制器调用,以实现ROS世界中的访问。

  • mock_sensor_commands(可选;布尔值;默认:false)

        创建用于通过外部命令模拟传感器测量的伪命令接口。这些接口通常由前向控制器用于提供ROS世界访问通道。

  • position_state_following_offset(可选;双精度浮点数;默认:0.0)

        在命令映射至状态时添加至状态值的跟踪偏移量。若 custom_interface_with_following_offset 为空,则偏移量应用于位置状态接口;若设置了自定义接口,则位置状态值+偏移量将应用于该接口。

6.1.1.2 按接口参数
  • initial_value(可选;双精度)

        启动后特定状态接口的初始值。示例:

<state_interface name="position">
  <param name="initial_value">3.45</param>
</state_interface>

        若未设置,则在配置生命周期转换中,关节状态接口的初始值将设为0.0。

        注意:此参数与关节接口的 gz_ros2_control 插件共享。对于 Mock 组件,也可为 gpio 或传感器状态接口设置初始值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值