ros2_control 使用教程(一)(上)+https://developer.aliyun.com/article/1585399
2.2.2 控制器连锁/级联控制
2.2.3 ros2_control 的关节运动学
2.2.4 硬件组件
硬件组件是 ros2_control 框架中物理硬件的抽象。硬件有三种类型:执行器(Actuator)、传感器(Sensor)和系统(System)。有关每种类型的详细信息,请查看硬件组件说明。
指南和最佳实践
- 硬件接口类型
- 编写硬件组件
- 不同的更新速率
2.2.4.1 处理调用 read() 和 write() 时发生的错误
如果硬件接口类的 read() 或 write() 方法返回 hardware_interface::return_type::ERROR,则会调用 on_error(previous_state)方法来处理发生的错误。
错误处理遵循节点生命周期。如果成功,将返回 CallbackReturn::SUCCESS,硬件将再次处于 UNCONFIGURED 状态;如果发生任何 ERROR 或 FAILURE,硬件将结束于 FINALIZED 状态,无法恢复。唯一的选择是重新加载整个插件,但目前控制器管理器中还没有这方面的服务。
2.2.4.2 ros2_control 硬件接口类型
ros2_control 框架提供了一系列硬件接口类型,可用于实现特定机器人或设备的硬件组件。下文将介绍不同的硬件接口类型及其用法。
2.2.4.2.1 Joints(关节)
<joint> 标签将与物理机器人和致动器关节相关的接口分组。它们具有命令和状态接口,用于设置硬件的目标值和读取其当前状态。
控制器管理器收到的 URDF 中必须包含 <ros2_control> 标签中定义的所有关节。
关节的状态接口可以通过 joint_state_broadcaster 作为 ROS 主题发布。
2.2.4.2.2 Sensors(传感器)
<传感器>标签可将多个状态接口(例如硬件的内部状态)组合在一起。
根据传感器类型的不同,ros2_controllers 提供了一些带有广播器的特定语义组件,例如
- Imu 传感器广播器
- 力矩传感器广播器
2.2.4.2.3 GPIOs
<gpio> 标签用于描述机器人设备的输入和输出端口,这些端口不能与任何关节或传感器相关联。<gpio> 标签的解析与具有命令和状态接口的 <joint> 标签的解析类似。该标记必须至少有一个 <command>- 或 <state>- 标记作为子标记。
选择关键字 "gpio "是因为它的通用性。虽然它严格用于数字信号,但也可描述任何电气模拟信号、数字信号或物理值。
<gpio> 标签可作为所有三种硬件组件(即系统、传感器或执行器)的子标签。
由于以 <gpio> 标签实现的端口通常非常针对特定应用,因此在 ros2_control 框架中不存在通用发布器。因此,必须为每个应用程序定制一个 gpio 控制器。作为示例,请参阅演示库中的 GPIO 控制器示例。
2.2.4.2.4 硬件组
硬件组件组是复杂系统中的重要组织机制,有利于错误处理和容错。通过将相关硬件组件(如机械手中的致动器)分组,用户可以建立一个统一的错误检测和响应框架。
硬件组件组在相互连接的硬件组件之间传播错误方面发挥着至关重要的作用。例如,在机械手系统中,将执行器分组可实现错误传播。如果组内的一个执行器发生故障,错误就会传播到其他执行器,从而引发整个系统的潜在问题。默认情况下,致动器的错误会被隔离到各自的硬件组件中,使其他组件不受影响地继续运行。在提供的 ros2_control 配置中,每个 <ros2_control> 块中的 <group> 标记表示硬件组件的分组,从而在系统内启用错误传播机制。
2.2.4.2.5 示例
以下示例展示了如何在 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-DoF 平行机械手
- 吸力开/关
<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 FTS
- 以 °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>
机器人的多个硬件组件属于同一组:Group1
- 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>
2.2.4.3 编写硬件组件
在 ros2_control 中,硬件系统组件是库,由控制器管理器使用 pluginlib 接口动态加载。以下是为新硬件接口创建源文件、基本测试和编译规则的分步指南。
2.2.4.3.1 准备软件包
如果硬件接口的软件包不存在,则首先创建它。软件包的构建类型应为 ament_cmake。最简单的方法是上网搜索最新的手册。支持这一过程的有用命令是 ros2 pkg create。使用 --help 标志可获取更多关于如何使用该命令的信息。还有一个创建库源文件和编译规则的选项,可以帮助你完成下面的步骤。
2.2.4.3.2 准备源文件
创建软件包后,至少要有 CMakeLists.txt 和 package.xml 文件。如果 include/<PACKAGE_NAME>/ 和 src 文件夹还不存在,也请创建它们。在 include/<PACKAGE_NAME>/ 文件夹中添加 <robot_hardware_interface_name>.hpp 和 <robot_hardware_interface_name>.cpp 文件。可选择添加 visibility_control.h,其中包含 Windows 导出规则的定义。您可以从现有控制器软件包中复制该文件,并将名称前缀改为 <PACKAGE_NAME>。
2.2.4.3.3 在头文件(.hpp)中添加声明
- 注意使用头文件保护。ROS2 风格使用 #ifndef 和 #define 预处理器指令。(如需了解更多相关信息,请使用搜索引擎:)。
- 如果正在使用 "hardware_interface/$interface_type$_interface.hpp "和 visibility_control.h,请将其包括在内。根据使用的硬件类型,$interface_type$ 可以是执行器(Actuator)、传感器(Sensor)或系统(System),有关每种类型的详细信息,请查看硬件组件说明。
- 为硬件接口定义一个唯一的命名空间。这通常是以 snake_case 书写的软件包名称。
- 定义硬件接口的类,扩展 $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。有关硬件生命周期的更多解释,请查看拉取请求,有关方法的确切定义,请查看 "hardware_interface/$interface_type$_interface.hpp "头文件或 Actuator、Sensor 或 System 的 doxygen 文档。
2.2.4.3.4 在源文件(.cpp)中添加定义
- 包含硬件接口的头文件并添加命名空间定义,以简化进一步开发。
- 实现 on_init 方法。在此,应初始化所有成员变量并处理 info 参数中的参数。在第一行中,通常会调用父代 on_init 来处理标准值,如 name。具体方法是:hardware_interface::(Actuator|Sensor|System)Interface::on_init(info)。如果所有必要参数都已设置且有效,并且一切运行正常,则返回 CallbackReturn::SUCCESS 或返回 CallbackReturn::ERROR 否则返回 CallbackReturn::ERROR。
- 编写 on_configure 方法,通常在该方法中设置与硬件的通信,并设置好一切以便激活硬件。
- 实现与 on_configure 相反的 on_cleanup 方法。
- 执行 export_state_interfaces 和 export_command_interfaces 方法,定义硬件提供的接口。传感器类型硬件接口没有 export_command_interfaces 方法。需要提醒的是,完整的接口名称结构为 <joint_name>/<interface_type> 。
- (可选)对于执行器和系统类型的硬件接口,如果您的硬件接受多种控制模式,请执行 prepare_command_mode_switch 和 perform_command_mode_switch。
- 在启用硬件 "电源 "时,执行 on_activate 方法。
- 执行与 on_activate 相反的 on_deactivate 方法。
- 实现 on_shutdown 方法,优雅地关闭硬件。
- 实现 on_error 方法,处理所有状态下的不同错误。
- 实现 read 方法,从硬件获取状态并将其存储到 export_state_interfaces 中定义的内部变量。
- 实现写方法,根据存储在 export_command_interfaces 中定义的内部变量中的值对硬件发出指令。
- 重要:在命名空间关闭后的文件末尾,添加 PLUGINLIB_EXPORT_CLASS 宏。
- 为此,您需要包含 "pluginlib/class_list_macros.hpp "头文件。第一个参数应提供准确的硬件接口类,例如 <my_hardware_interface_package>::<RobotHardwareInterfaceName>, 第二个参数应提供基类,即 hardware_interface::(Actuator|Sensor|System)Interface。
2.2.4.3.5 为插件库编写导出定义
- 在软件包中创建 <my_hardware_interface_package>.xml 文件,并添加必须对 pluginlib 可见的库和硬件接口类的定义。最简单的方法是检查 hardware_interface mock_components 部分的模拟组件定义。
- 通常,插件名称是由软件包(命名空间)和类名定义的,例如,<my_hardware_interface_package>/<RobotHardwareInterfaceName>。当资源管理器搜索硬件接口时,该名称定义了硬件接口的类型。其他两个参数必须与 <robot_hardware_interface_name>.cpp 文件底部宏中的定义一致。
2.2.4.3.6 编写一个简单的测试来检查是否能找到并加载控制器
- 在软件包中创建 test 文件夹(如果还不存在),并添加名为 test_load_<robot_hardware_interface_name>.cpp 的文件。
- 您可以复制 test_generic_system.cpp 软件包中定义的 load_generic_system_2dof 内容。
- 更改复制的测试名称,并在最后一行指定硬件接口类型的地方加上 <my_hardware_interface_package>.xml 文件中定义的名称,例如 <my_hardware_interface_package>/<RobotHardwareInterfaceName>。
2.2.4.3.7 在``CMakeLists.txt``文件中添加编译指令
- 在 find_package(ament_cmake REQUIRED) 一行下添加更多依赖项。这些依赖至少包括:hardware_interface、pluginlib、rclcpp 和 rclcpp_lifecycle。
- 为共享库添加编译指令,提供 <robot_hardware_interface_name>.cpp 文件作为源代码。
- 为库添加目标 include 目录。通常只有 include 目录。
- 添加库所需的其他依赖项。至少应添加 1 中列出的依赖项。
- 使用以下命令导出 pluginlib 说明文件: ... 代码::: cmake
- pluginlib_export_plugin_description_file(hardware_interface <my_hardware_interface_package>.xml)
- 为目标和 include 目录添加安装指令。
- 在测试部分添加以下依赖项:amment_cmake_gmock、hardware_interface。
- 使用 ament_add_gmock 指令为测试添加编译定义。详情请参阅 ros2_control 软件包中的模拟硬件编译方法。
- (可选)在 ament_package() 之前将硬件接口库添加到 ament_export_libraries。
2.2.4.3.8 在 ``package.xml`` 文件中添加依赖包
- 在 <depend> 标记中至少添加以下软件包:hardware_interface、pluginlib、rclcpp 和 rclcpp_lifecycle。
- 在 <test_depend> 标记中至少添加以下软件包:ament_add_gmock 和 hardware_interface。
2.2.4.3.9 编译和测试硬件组件
- 现在一切准备就绪,可以使用 colcon build <my_hardware_interface_package> 命令编译硬件组件了。记住,执行该命令前要进入工作区的根目录。
- 如果编译成功,则从安装文件夹中获取 setup.bash 文件,然后执行 colcon test <my_hardware_interface_package> 检查新控制器是否能通过 pluginlib 库找到并被控制器管理器加载。
就是这样!尽情编写出色的控制器吧
2.2.4.3.10 有用的外部参考资料
用于生成控制器 shell 的模板和脚本
注意事项
该脚本目前仅推荐用于 Foxy,与 Galactic 及以后的 API 不兼容。
2.2.5 模拟组件
模拟组件是对硬件组件(即系统、传感器和执行器)的微不足道的 "仿真"。它们通过将命令镜像到其状态来提供理想的行为。在离线测试 ros2_control 框架时,可以添加相应的硬件接口来代替真实硬件。这样做的主要好处是,你可以在不接触硬件的情况下测试框架内的所有 "管道"。这意味着您可以测试控制器、广播器、启动文件,甚至与 MoveIt 等的集成。这样做的主要目的是减少在物理硬件上的调试时间,促进开发。
2.2.5.1 通用系统
该组件实现了 hardware_interface::SystemInterface,支持命令和状态接口。有关硬件组件的更多信息,请查看详细文档。
功能
- 支持从 URDF(参见 URDF wiki)解析的模仿关节
- 将命令镜像到有偏移和无偏移的状态
- 假命令接口,用于设置来自外部节点的传感器数据(与前向控制器相结合)
- 用于从外部节点设置传感器数据的假 gpio 接口(与正向控制器相结合)
2.2.5.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>
有关使用 calculate_dynamics 的示例,请参见示例_2;有关与 GPIO 接口结合使用的示例,请参见示例_10。
组件参数
calculate_dynamics(可选;布尔值;默认值:false)
使用欧拉积分或有限差分计算指令的状态。
custom_interface_with_following_offset (可选;字符串;默认值:"")
将偏移命令映射到自定义界面。
disable_commands(可选;布尔型;默认值:false)
禁止将命令镜像到状态。该选项有助于仿真当没有任何故障,但突然没有硬件接口反馈时与硬件的错误连接。或者,当硬件在无反馈(即开环配置)的情况下运行时,它也能帮助你测试你的设置。
mock_gpio_commands(可选;布尔型;默认:false)
创建假命令接口,用于通过外部命令伪造 GPIO 状态。这些接口通常用于前向控制器,以提供从 ROS 世界的访问。
mock_sensor_commands (可选;布尔;默认值:false)
创建假命令接口,用于通过外部命令伪造传感器测量结果。这些接口通常用于前向控制器,以提供从 ROS-world 的访问。
position_state_following_offset (可选;双;默认值:0.0)
镜像到状态时,添加到指令值的跟随偏移量。只有在 custom_interface_with_following_offset 为 false 时才会应用。
每个接口参数
initial_value (可选;双)
某些状态接口启动后的初始值。例如
<state_interface name="position"> <param name="initial_value">3.45</param> </state_interface>
注:该参数与用于联合接口的 gazebo 和 gazebo classic 插件共享。对于 Mock 组件,也可以为 gpio 或传感器状态接口设置初始值。
三、ros2_controllers
用于 ros2_control 框架的常用通用控制器,可与许多机器人、MoveIt2 和 Nav2 一起使用。
指南与最佳实践
- 轮式移动机器人运动学
- 编写新控制器
轮式移动机器人控制器
- 差分驱动控制器
- 转向控制器库
- 三轮车控制器
机械手和其他机器人控制器
控制器使用通用硬件接口定义,可根据以下命令接口类型使用命名空间:
- 位置控制器:hardware_interface::HW_IF_POSITION
- 速度控制器:hardware_interface::HW_IF_VELOCITY
- 力矩控制器: hardware_interface::HW_IF_ACCELERATION
- 力矩控制器: hardware_interface::HW_IF_EFFORT
- 导纳控制器
- 力矩控制器
- 前进指令控制器
- 夹爪控制器
- 关节轨迹控制器
- 轨迹表示
- 轨迹替换
- 关节轨迹控制器参数
- rqt_joint_trajectory_controller
- PID 控制器
- 位置控制器
- 速度控制器
广播器
广播器用于从硬件组件向 ROS 主题发布传感器数据。从 ros2_control 的意义上讲,广播器仍然是控制器,使用与上述其他控制器相同的控制器接口。
- 力矩传感器广播器
- IMU 传感器广播器
- 关节状态广播器
- 范围传感器广播器
常见控制器参数
每个控制器和广播器都有一些通用参数。这些参数是可选的,但如果需要,必须在 onConfigure 过渡到非活动状态前设置,请参阅生命周期文档。一旦控制器已加载,则可使用控制器管理器的 configure_controller 服务完成此转换。
- update_rate(更新率): 一个无符号整数参数,代表每个控制器/广播器运行更新周期的速率。未指定时,它们将以与控制器管理器相同的频率运行。
- is_async: 一个布尔参数,用于指定控制器更新是否需要异步运行。