系列文章目录
前言
0.1 欢迎阅读 ros2_control 文档!
ros2_control 是一个使用(ROS 2)对机器人进行(实时)控制的框架。其软件包是对 ROS(机器人操作系统)中使用的 ros_control 软件包的重写。ros2_control 的目标是简化新硬件的集成并克服一些缺点。
如果您不熟悉控制理论,请先了解一些相关知识(如维基百科),以便熟悉本手册中使用的术语。
0.2 ros2_control 资源库
ros2_control 框架由以下 Github 仓库组成:
- ros2_control - 框架的主要接口和组件;
- ros2_controllers - 广泛使用的控制器,如正向指令控制器、联合轨迹控制器、差分驱动控制器;
- control_toolbox - 控制器使用的一些广泛使用的控制理论实现(如 PID);
- realtime_tools - 实时支持的通用工具包,如实时缓冲器和发布器;
- control_msgs - 常用消息;
- kinematics_interface - 用于使用 C++ 运动学框架;
此外,还有以下(未发布的)软件包与启动和项目管理有关:
- ros2_control_demos - 常见用例的示例实现,以便顺利启动;
- roadmap - 项目规划和设计文档。
0.3 开发组织和交流
0.3.1 问题
请使用机器人堆栈交换(Robotics Stack Exchange),并将您的问题标记为 ros2_control。
0.3.2 工作组会议
每隔一周的周三都会召开工作组会议。要参加会议,请查看 ROS Discourse 上的公告。您可以通过谷歌群组或直接在谷歌会议上参加会议(查看公告)。要提出新的讨论点,或查看以前会议的记录,请查看此文档。
0.3.3 项目
ros-control 组织下的 GitHub 项目用于跟踪工作。
0.3.4 错误报告和功能请求
使用相应版本库中的问题跟踪器。提供问题的简短摘要 确保提供一份最基本的步骤列表,以便重现您发现的问题 提供操作系统、ROS 发行版等相关信息。
0.3.5 一般讨论
请使用 ROS Discourse。
一、开始使用
1.1 安装
1.1.1 二进制包
ros2_control 框架是为 ROS 2 滚动而发布的。要使用它,必须安装 ros-rolling-ros2-control 和 ros-rolling-ros2-controllers 软件包。
1.1.2 从源代码构建
滚动分支与 Humble 和 Iron ROS 发行版兼容。你可以在本页面的 Humble 和 Iron 版本中找到更多关于兼容性的信息。
如果你想从源代码安装框架,例如为框架做贡献,请使用以下命令:
- 下载所有软件源
mkdir -p ~/ros2_ws/src cd ~/ros2_ws/ wget https://raw.githubusercontent.com/ros-controls/ros2_control_ci/master/ros_controls.$ROS_DISTRO.repos vcs import src < ros_controls.$ROS_DISTRO.repos
-
安装依赖项:
rosdep update --rosdistro=$ROS_DISTRO sudo apt-get update rosdep install --from-paths src --ignore-src -r -y
- 构建一切,例如:
. /opt/ros/${ROS_DISTRO}/setup.sh colcon build --symlink-install
-
不要忘记从安装文件夹中获取 setup.bash 源文件!
1.2 架构
ros2_control 框架的源代码可在 ros2_control 和 ros2_controllers GitHub 存储库中找到。下图显示了 ros2_control 框架的架构。
下面的 UML 类图描述了 ros2_control 框架的内部实现。
1.2.1 控制器管理器
控制器管理器(CM)连接着 ros2_control 框架的控制器和硬件抽象。它也是用户通过 ROS 服务的入口点。CM 实现了一个没有执行器的节点,因此可以集成到自定义设置中。不过,通常建议使用 controller_manager 软件包中 ros2_control_node 文件实现的默认节点设置。本手册假定您使用该默认节点设置。
一方面,CM 管理(如加载、激活、停用、卸载)控制器及其所需的接口。另一方面,它可以(通过资源管理器)访问硬件组件,即它们的接口。控制器管理器会匹配所需的接口和提供的接口,在启用时允许控制器访问硬件,如果存在访问冲突,则会报错。
控制环的执行由 CM 的 update() 方法管理。它从硬件组件读取数据,更新所有激活控制器的输出,并将结果写入组件。
1.2.2 资源管理器
资源管理器(RM)为 ros2_control 框架抽象了物理硬件及其驱动程序(称为硬件组件)。RM 使用 pluginlib 库加载组件,管理其生命周期、组件状态和命令接口。RM 提供的抽象功能允许重用已实施的硬件组件,如机器人和抓手,而无需任何实施,并可灵活应用硬件来实现状态和命令接口,如用于电机控制和编码器读取的独立硬件/通信库。
在控制循环执行中,RM 的 read() 和 write() 方法处理与硬件组件的通信。
1.2.3 控制器
ros2_control 框架中的控制器基于控制理论。它们将参考值与测量输出进行比较,并根据这一误差计算系统的输入。控制器是从 ControllerInterface(ros2_control 中的 controller_interface 包)派生出来的对象,并使用 pluginlib-library 作为插件导出。有关控制器的示例,请查看 ros2_controllers 软件库中的 ForwardCommandController 实现。控制器的生命周期基于 LifecycleNode 类,该类实现了节点生命周期设计文档中描述的状态机。
执行控制循环时,会调用 update() 方法。该方法可访问最新的硬件状态,并使控制器能够写入硬件命令接口。
1.2.4 用户接口
用户使用控制器管理器的服务与 ros2_control 框架交互。有关服务及其定义的列表,请查看 controller_manager_msgs 软件包中的 srv 文件夹。
虽然服务调用可以直接从命令行或通过节点使用,但还有一个与 ros2 cli 集成的用户友好型命令行界面(CLI)。它支持自动完成,并提供一系列常用命令。基本命令是 ros2 control。有关 CLI 功能的说明,请参阅命令行界面 (CLI) 文档。
1.3 硬件组件
硬件组件实现了与物理硬件的通信,并在 ros2_control 框架中代表了物理硬件的抽象。这些组件必须使用 pluginlib-library 作为插件导出。资源管理器动态加载这些插件并管理其生命周期。
组件有三种基本类型:
系统
复杂(多自由度)机器人硬件,如工业机器人。执行器组件的主要区别在于可以使用复杂的传动装置,如人形机器人手部所需的传动装置。该组件具有读写功能。当硬件(如 KUKA-RSI)只有一个逻辑通信通道时,就需要使用该组件。
传感器
机器人硬件用于感知周围环境。传感器组件与关节(如编码器)或链接(如力矩传感器)相关。该组件类型仅具有读取功能。
执行器
简单(1 DOF)的机器人硬件,如电机、阀门等。执行器只与一个关节相关。该组件类型具有读写功能。如果读取功能不可行(例如使用 Arduino 电路板控制直流电机),则读取功能不是必须的。如果硬件可以进行模块化设计,例如可以与每个电机进行独立的 CAN 通信,那么这种类型的执行器也可以用于多自由度机器人。
有关硬件组件的详细说明,请参阅 "通过控制器进行硬件访问 "设计文档。
1.3.1 URDF 中的硬件描述
ros2_control 框架使用机器人 URDF 文件中的 <ros2_control> 标记来描述其组件,即硬件设置。所选结构可将多个 xacro 宏合并为一个宏,而无需进行任何更改。下面的示例展示了一个具有 2-DOF 的位置控制机器人(RRBot)、一个外部 1-DOF 力矩传感器和一个外部控制的 1-DOF 平行抓手作为其末端执行器。更多示例和详细说明,请查阅 ros2_control_demos 网站和 ROS 2 Control Components URDF Examples 设计文档。
<ros2_control name="RRBotSystemPositionOnly" type="system">
<hardware>
<plugin>ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware</plugin>
<param name="example_param_write_for_sec">2</param>
<param name="example_param_read_for_sec">2</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>
</ros2_control>
<ros2_control name="RRBotForceTorqueSensor1D" type="sensor">
<hardware>
<plugin>ros2_control_demo_hardware/ForceTorqueSensor1DHardware</plugin>
<param name="example_param_read_for_sec">0.43</param>
</hardware>
<sensor name="tcp_fts_sensor">
<state_interface name="force"/>
<param name="frame_id">rrbot_tcp</param>
<param name="min_force">-100</param>
<param name="max_force">100</param>
</sensor>
</ros2_control>
<ros2_control name="RRBotGripper" type="actuator">
<hardware>
<plugin>ros2_control_demo_hardware/PositionActuatorHardware</plugin>
<param name="example_param_write_for_sec">1.23</param>
<param name="example_param_read_for_sec">3</param>
</hardware>
<joint name="gripper_joint ">
<command_interface name="position">
<param name="min">0</param>
<param name="max">50</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
</ros2_control>
1.3.2 为机器人运行框架
要运行 ros2_control 框架,请执行以下操作。示例文件可在 ros2_control_demos 资源库中找到。
- 创建一个 YAML 文件,其中包含控制器管理器和两个控制器的配置。(RRBot 的配置示例)
- 用所需的 <ros2_control> 标记扩展机器人的 URDF 描述。建议使用宏文件 (xacro) 代替纯 URDF。(RRBot URDF 示例)
- 创建一个启动文件,以便使用控制器管理器启动节点。您可以使用默认的 ros2_control 节点(推荐),也可以在软件栈中集成控制器管理器。(RRBot 的启动文件示例)
注意:您也可以使用我们的维护者提供的脚本来创建 "hardware_interface "软件包的骨架。
二、ros_control
2.1 应用程序接口文档
API 文档由 doxygen 解析,可在此处找到
2.2 概念
2.2.1 控制器管理器
控制器管理器是 ros2_control 框架的主要组件。它管理控制器的生命周期、访问硬件接口,并为 ROS-world 提供服务。
2.2.1.1 确定性
为了在控制硬件时获得最佳性能,我们希望控制器管理器在主控制环路中的抖动越小越好。
控制器管理器的主线程会尝试配置优先级为 50 的 SCHED_FIFO,这与安装的内核无关。默认情况下,用户无权设置如此高的优先级。要赋予用户此类权限,请添加一个名为 realtime 的组,并将控制机器人的用户添加到该组中:
sudo addgroup realtime
sudo usermod -a -G realtime $(whoami)
然后,在 /etc/security/limits.conf 中为实时组添加以下限制:
@realtime soft rtprio 99
@realtime soft priority 99
@realtime soft memlock 102400
@realtime hard rtprio 99
@realtime hard priority 99
@realtime hard memlock 102400
注销并重新登录后,限制将被应用。
普通 Linux 内核针对计算吞吐量进行了优化,因此不太适合硬件控制。标准内核的替代方案包括
- Ubuntu 22.04 LTS 测试版上的实时内核
- Debian Bullseye 上的 linux-image-rt-amd64
- 任何 Ubuntu 上的 lowlatency 内核(sudo apt install linux-lowlatency)。
虽然安装实时内核肯定能获得最佳的低抖动效果,但使用低延迟内核也能改善很多,而且安装非常简单。
2.2.1.2 订阅者
~/robot_description [std_msgs::msg::String] (机器人描述)
带有 URDF xml 的字符串,例如来自 robot_state_publisher。目前还不支持重新加载 URDF。<ros2_control> 标记中定义的所有关节都必须存在于 URDF 中。
2.2.1.3 参数
hardware_components_initial_state (硬件组件初始状态)
用于硬件组件受控生命周期管理的参数图。组件名称定义为机器人描述(robot_description)中 <ros2_control> 标签的属性。在机器人描述中找到的硬件组件,如果没有明确的状态定义,将立即被激活。各参数的详细说明如下。下面的示例给出了地图的完整结构:
hardware_components_initial_state: unconfigured: - "arm1" - "arm2" inactive: - "base3"
hardware_components_initial_state.unconfigured (可选;list<string>;默认为空)
定义控制器管理器启动时,哪些硬件组件只能立即加载。
hardware_components_initial_state.inactive (可选;list<string>;默认值:空)
定义哪些硬件组件将在控制器管理器启动时立即配置。
update_rate (必选项;整数)
控制器管理器实时更新循环的频率。该循环从硬件读取状态、更新控制器并向硬件写入命令。
<controller_name>.type
使用 pluginlib 导出的控制器插件名称。这是一个类,从中创建名称为 "controller_name "的控制器实例。
2.2.1.3.1 处理多个控制器管理器
在处理多个控制器管理器时,您有两种选择来管理不同的机器人描述:
- 使用命名空间: 您可以将 robot_state_publisher 节点和 controller_manager 节点放在同一个命名空间中。
control_node = Node( package="controller_manager", executable="ros2_control_node", parameters=[robot_controllers], output="both", namespace="rrbot", ) robot_state_pub_node = Node( package="robot_state_publisher", executable="robot_state_publisher", output="both", parameters=[robot_description], namespace="rrbot", )
- 使用重映射: 您可以使用重映射来处理不同的机器人描述。这包括使用重映射标签转发主题,允许您为每个控制器管理器指定自定义主题。
control_node = Node( package="controller_manager", executable="ros2_control_node", parameters=[robot_controllers], output="both", remappings=[('robot_description', '/rrbot/robot_description')] ) robot_state_pub_node = Node( package="robot_state_publisher", executable="robot_state_publisher", output="both", parameters=[robot_description], namespace="rrbot", )
2.2.1.4 辅助脚本
有两个脚本可通过启动文件与控制器管理器交互:
- spawner - 加载、配置和启动控制器。
- unspawner - 停止并卸载控制器。
spawner
$ ros2 run controller_manager spawner -h
usage: spawner [-h] [-c CONTROLLER_MANAGER] [-p PARAM_FILE] [-n NAMESPACE] [--load-only] [--inactive] [-t CONTROLLER_TYPE] [-u]
[--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT]
controller_name
positional arguments:
controller_name Name of the controller
options:
-h, --help show this help message and exit
-c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER
Name of the controller manager ROS node
-p PARAM_FILE, --param-file PARAM_FILE
Controller param file to be loaded into controller node before configure
-n NAMESPACE, --namespace NAMESPACE
Namespace for the controller
--load-only Only load the controller and leave unconfigured.
--inactive Load and configure the controller, however do not activate them
-t CONTROLLER_TYPE, --controller-type CONTROLLER_TYPE
If not provided it should exist in the controller manager namespace
-u, --unload-on-kill Wait until this application is interrupted and unload controller
--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT
Time to wait for the controller manager
unspawner
$ ros2 run controller_manager unspawner -h
usage: unspawner [-h] [-c CONTROLLER_MANAGER] controller_name
positional arguments:
controller_name Name of the controller
optional arguments:
-h, --help show this help message and exit
-c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER
Name of the controller manager ROS node
2.2.1.5 在进程中使用控制器管理器
ControllerManager 也可以作为一个类在进程中实例化,但这样做时必须小心谨慎。原因是 ControllerManager 类继承自 rclcpp::Node。
如果进程中有多个 Node,全局节点名称重映射规则会强行更改 ControllerManager 的节点名称,从而导致节点名称重复。无论节点是同级节点还是存在于层次结构中,都会出现这种情况。
解决这一问题的办法是在传递给 ControllerManager 节点的 NodeOptions 中指定另一个节点名称重映射规则(使其忽略全局规则),或确保任何重映射规则都针对特定节点。
auto options = controller_manager::get_cm_node_options();
options.arguments({
"--ros-args",
"--remap", "_target_node_name:__node:=dst_node_name",
"--log-level", "info"});
auto cm = std::make_shared<controller_manager::ControllerManager>(
executor, "_target_node_name", "some_optional_namespace", options);
2.2.1.6 概念
重启所有控制器
重启所有控制器的最简单方法是使用 switch_controllers 服务或 CLI,并将所有控制器添加到启动和停止列表中。请注意,并非所有控制器都必须重启,例如广播器。
重启硬件
如果硬件被重启,则应再次经历其生命周期,以便重新配置和导出接口
硬件和控制器错误
如果硬件在读取或写入方法中返回 return_type::ERROR,控制器管理器将停止使用该硬件命令和状态接口的所有控制器。同样,如果控制器的更新方法返回 return_type::ERROR,控制器管理器将停用相应的控制器。今后,控制器管理器将尝试启动任何可用的后备控制器。
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 不兼容。
三、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: 一个布尔参数,用于指定控制器更新是否需要异步运行。