系列文章目录
目录
2.1.1 ros2_control:设置机器人的硬件接口包
前言
硬件组件是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中使用不同硬件接口类型。这些接口可在各类硬件组件(系统、执行器、传感器)中组合使用(详见文档说明),具体如下:
- 配备多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> - 具备电吸附与真空吸附功能的夹爪
多模态夹爪
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> - 带温度反馈和可调校准的力-扭矩传感器
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> - 属于同一组的多个硬件组件的机器人:组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 接口动态加载。以下是创建新硬件接口的分步指南,涵盖源文件、基础测试及编译规则的制定。
- 准备软件包
若硬件接口的软件包尚未存在,请先创建。该软件包应采用 ament_cmake 作为构建类型。最便捷的方式是在线查找最新手册。ros2 pkg create 命令可有效辅助此过程,使用 --help 参数可获取更多操作说明。该命令还提供创建库源文件及编译规则的选项,有助于后续步骤的实施。 - 准备源文件
创建包后,其中至少应包含 CMakeLists.txt 和 package.xml 文件。若尚未存在,还需创建 include/<PACKAGE_NAME>/ 和 src 文件夹。在 include/<PACKAGE_NAME>/ 文件夹中添加 <robot_hardware_interface_name>.hpp,并在 src 文件夹中添加 <robot_hardware_interface_name>.cpp。 - 在头文件(.hpp)中添加声明
- 注意使用头文件保护机制。ROS2 风格采用 #ifndef 和 #define 预处理指令(更多信息可通过搜索引擎获取)。
- 包含 “hardware_interface/$interface_type$_interface.hpp”。$interface_type$根据所用硬件类型可为Actuator、Sensor 或 System。各类型详情请查阅硬件组件说明。
- 为硬件接口定义唯一命名空间,通常采用 snake_case 法书写的包名。
- 定义硬件接口类,继承自 $InterfaceType$Interface,例如:c++class HardwareInterfaceName : public hardware_interface::$InterfaceType$Interface
- 添加无参数构造函数及实现 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。
- 有关硬件生命周期的详细说明请查阅拉取请求,具体方法定义请参阅“hardware_interface/$interface_type$_interface.hpp” 头文件或执行器、传感器及系统的 Doxygen 文档。
- 在源文件(.cpp)中添加定义
- 包含硬件接口的头文件,并添加命名空间定义以简化后续开发。
- 实现 on_init 方法。在此处应初始化所有成员变量,并处理 info 参数中的参数。首行通常需调用父类的on_init 处理标准值(如名称),使用方式为:hardware_interface::(Actuator|Sensor|System)Interface::on_init(info)。若所有必需参数均已设置且有效,且系统运行正常,则返回 CallbackReturn::SUCCESS;否则返回 CallbackReturn::ERROR。
-
(可选)添加发布者、服务等
硬件组件的常见需求是发布状态或诊断信息,同时不干扰实时控制回路。
这使您能够在不影响实时性能的前提下,向硬件接口添加任何标准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_ ... }
-
- 编写 on_configure 方法,通常在此处设置与硬件的通信并完成所有配置,以便激活硬件。
- 实现 on_cleanup 方法,该方法执行与 on_configure 相反的操作。
- 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> 的结构。
- (可选)若需导出未列在ros2_control XML标签中的命令/状态接口,请按以下步骤操作:
- 覆盖 virtual std::vector<hardware_interface::InterfaceDescription> export_unlisted_command_interface_descriptions() 或 virtual std::vector<hardware_interface::InterfaceDescription> export_unlisted_state_interface_descriptions()
- 在重写的 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; -
未列出的接口将根据其创建函数的不同,存储在 unlisted_command_interfaces_ 或 unlisted_state_interfaces_ 映射中。
-
可通过 get_state(name)、set_state(name, value)、get_command(name) 或 set_command(name, value) 等方法访问,操作逻辑与常规接口相同。例如:get_state(“some_unlisted_interface”)。
- (可选)若默认实现(on_export_command_interfaces() 或 on_export_state_interfaces())无法满足 Command-/StateInterfaces 导出需求,可覆盖这些方法。但需注意以下事项:
- 若需提供未列出的接口,必须调用 export_unlisted_command_interface_descriptions() 或 export_unlisted_state_interface_descriptions(),并将它们添加到 unlisted_command_interfaces_ 或 unlisted_state_interfaces_ 中。
- 请务必在内部存储创建的 Command-/StateInterfaces 接口,因为您仅返回共享指针,资源管理器不会为硬件提供对这些接口的访问权限。因此必须自行负责存储。
- 名称必须唯一!
- (可选)对于执行器和系统类型的硬件接口,若您的硬件支持多种控制模式,请实现 prepare_command_mode_switch 和 perform_command_mode_switch 方法。
- 在硬件“电源”开启时实现on_activate方法。
- 实现 on_deactivate 方法,该方法与 on_activate 方法功能相反。
- 实现 on_shutdown 方法,用于实现硬件的优雅关闭。
- 实现 on_error 方法,用于处理来自所有状态的不同错误。
- 实现 read 方法,从硬件获取状态并将其存储到 export_state_interfaces 中定义的内部变量中。
- 实现写入方法,该方法根据export_command_interfaces中定义的内部变量存储的值来控制硬件。
- (可选)框架管理发布者
实现 init_hardware_status_message 和 update_hardware_status_message 方法,通过 control_msgs/msg/HardwareStatus 消息发布框架支持的硬件状态报告:- `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; } -
`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; } -
在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 中的演示。
- `init_hardware_status_message`:此非实时方法在初始化期间仅调用一次。必须重写该方法以定义状态消息的静态结构。包括设置 hardware_id、调整 hardware_device_states 向量大小,并为每个设备调整其特定状态向量大小(如 generic_hardware_status、canopen_states),同时填充 device_id 和接口名称等静态字段。在此预分配消息结构对实时安全性至关重要。
- 重要提示:在文件末尾关闭命名空间后,添加 PLUGINLIB_EXPORT_CLASS 宏。
为此需包含头文件“pluginlib/class_list_macros.hpp”。首参数应精确指定硬件接口类,例如<my_hardware_interface_package>::<RobotHardwareInterfaceName>;次参数则为基类,即hardware_interface::(Actuator|Sensor|System)Interface。
- 为插件库编写导出定义
- 在包中创建 <my_hardware_interface_package>.xml 文件,并添加库及硬件接口类的定义,这些定义需对插件库可见。最简便的方法是参考硬件接口 mock_components 部分中 mock 组件的定义。
- 通常,插件名称由包(命名空间)和类名共同定义,例如<my_hardware_interface_package>/<RobotHardwareInterfaceName>。该名称决定了资源管理器搜索硬件接口时的类型标识。其余两个参数必须与 <robot_hardware_interface_name>.cpp 文件末尾宏定义中的内容保持一致。
- 编写一个简单测试以检查控制器是否可被找到并加载
- 在您的软件包中创建名为 test 的文件夹(若尚未存在),并添加一个名为 test_load_<机器人硬件接口名称>.cpp 的文件。
- 您可以复制 test_generic_system.cpp 软件包中定义的 load_generic_system_2dof 内容。
- 修改复制的测试文件名,并在最后一行指定硬件接口类型处,替换为 <my_hardware_interface_package>.xml 文件中定义的名称,例如 <my_hardware_interface_package>/<RobotHardwareInterfaceName>。
- 在 ``CMakeLists.txt`` 文件中添加编译指令
- 在 find_package(ament_cmake REQUIRED) 语句下方添加更多依赖项。至少需包含:hardware_interface、pluginlib、rclcpp 和 rclcpp_lifecycle。
- 添加共享库编译指令,提供<robot_hardware_interface_name>.cpp文件作为源代码。
- 为库添加目标包含目录。通常仅需添加include目录。
- 添加库所需的 ament 依赖项。至少应包含第 1 项下列出的依赖。
- 使用以下命令导出 pluginlib 描述文件:
.. code:: cmake pluginlib_export_plugin_description_file(hardware_interface <my_hardware_interface_package>.xml) - 为目标和包含目录添加安装指令。
- 在测试部分添加以下依赖项:ament_cmake_gmock、hardware_interface。
- 使用 ament_add_gmock 指令为测试添加编译定义。具体实现可参考 ros2_control 包中模拟硬件的处理方式。
- (可选)在 ament_package() 之前将硬件接口库添加至 ament_export_libraries。
- 在``package.xml``文件中添加依赖项
- 至少在<depend>标签中添加以下包:hardware_interface、pluginlib、rclcpp和rclcpp_lifecycle。
- 至少在<test_depend>标签中添加以下包:ament_add_gmock和hardware_interface。
- 编译和测试硬件组件
- 现在已准备就绪,可使用 colcon build <my_hardware_interface_package> 命令编译硬件组件。执行此命令前请确保进入工作区根目录。
- 若编译成功,请在安装目录中执行 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)分别用于定义只读设备与只写设备的语义组件。
现有语义组件接口列表(链接至头文件)及关联广播器(如有):
-
IMUSensor, used by IMU Sensor Broadcaster
-
PoseSensor, used by Pose Broadcaster
-
RangeSensor, used by Range Sensor Broadcaster
现有语义组件命令接口列表(链接至头文件)及其关联控制器(如有):
六、模拟组件
模拟组件是对硬件组件(即系统、传感器和执行器)的简单“仿真”。它们通过将命令映射到其状态来提供理想行为。在离线测试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 或传感器状态接口设置初始值。
4098

被折叠的 条评论
为什么被折叠?



