系列文章目录
前言
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 文件中的 标记来描述其组件,即硬件设置。所选结构可将多个 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 的配置示例)
- 用所需的 标记扩展机器人的 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。 标记中定义的所有关节都必须存在于 URDF 中。
2.2.1.3 参数
hardware_components_initial_state (硬件组件初始状态)
用于硬件组件受控生命周期管理的参数图。组件名称定义为机器人描述(robot_description)中 标签的属性。在机器人描述中找到的硬件组件,如果没有明确的状态定义,将立即被激活。各参数的详细说明如下。下面的示例给出了地图的完整结构:
hardware_components_initial_state: unconfigured: - "arm1" - "arm2" inactive: - "base3"
hardware_components_initial_state.unconfigured (可选;list;默认为空)
定义控制器管理器启动时,哪些硬件组件只能立即加载。
hardware_components_initial_state.inactive (可选;list;默认值:空)
定义哪些硬件组件将在控制器管理器启动时立即配置。
update_rate (必选项;整数)
控制器管理器实时更新循环的频率。该循环从硬件读取状态、更新控制器并向硬件写入命令。
.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,控制器管理器将停用相应的控制器。今后,控制器管理器将尝试启动任何可用的后备控制器。
ros2_control 使用教程(一)(下)+https://developer.aliyun.com/article/1585401