目录
背景
安装并运行演示
运行测试
刚刚发生了什么事?
调整内存锁定权限
输出概述
延迟
为调度程序设置权限
绘图结果
背景
实时计算是许多机器人系统的关键特性,特别是自动驾驶汽车、航天器和工业制造等安全和任务关键型应用。我们在设计和原型开发 ROS 2 时考虑了实时性能约束,因为这是在 ROS 1 的早期阶段没有考虑到的需求,现在要将 ROS 1 重构为实时友好已经变得难以处理。
本文档概述了实时计算的要求和软件工程师的最佳实践。简而言之:
要创建一个实时计算机系统,我们的实时循环必须定期更新以满足截止日期。我们只能容忍这些截止日期的小误差(我们最大允许的抖动)。为此,我们必须避免在执行路径中出现非确定性操作,例如:页面错误事件、动态内存分配/释放以及无限期阻塞的同步原语。
一个经典的控制问题例子,通常通过实时计算解决的是平衡倒立摆。如果控制器意外地长时间阻塞,摆锤会倒下或变得不稳定。但如果控制器以比控制摆锤的电机运行速度更快的速率可靠地更新,摆锤将成功地适应传感器数据以平衡摆锤。
现在你已经了解了实时计算的所有内容,让我们尝试一个演示吧!
安装并运行演示程序
实时演示是针对 Linux 操作系统编写的,因为许多从事实时计算的 ROS 社区成员使用 Xenomai 或 RT_PREEMPT 作为他们的实时解决方案。由于演示中完成的许多操作都是特定于操作系统的,因此该演示仅在 Linux 系统上构建和运行。因此,如果您是 OSX 或 Windows 用户,请不要尝试这一部分!
此外,这必须使用静态 DDS API 从源代码构建。目前唯一支持的实现是 Connext。
首先,按照说明使用 Connext DDS 作为中间件从源代码构建 ROS 2。源码构建目录在
以下为手动构建:
cxy@cxy-Ubuntu2404:~/pendulum$ sudo apt install ros-jazzy-apex-test-tools
[sudo] password for cxy:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
ros-jazzy-osrf-testing-tools-cpp
The following NEW packages will be installed:
ros-jazzy-apex-test-tools ros-jazzy-osrf-testing-tools-cpp
0 upgraded, 2 newly installed, 0 to remove and 5 not upgraded.
Need to get 5,074 kB of archives.
After this operation, 5,391 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
0% [Working]
Get:1 http://packages.ros.org/ros2/ubuntu noble/main amd64 ros-jazzy-osrf-testing-tools-cpp amd64 2.0.0-3noble.20240702.042307 [5,066 kB]
1% [1 ros-jazzy-osrf-testing-tools-cpp 54.2 kB/5,066 kB 1%] 1% [Get:2 http://packages.ros.org/ros2/ubuntu noble/main amd64 ros-jazzy-apex-test-tools amd64 0.0.2-9noble.20240702.042443 [7,926 B]
Fetched 5,074 kB in 13min 15s (6,384 B/s)
Selecting previously unselected package ros-jazzy-osrf-testing-tools-cpp.
(Reading database ... 270244 files and directories currently installed.)
Preparing to unpack .../ros-jazzy-osrf-testing-tools-cpp_2.0.0-3noble.20240702.04
2307_amd64.deb ...
Unpacking ros-jazzy-osrf-testing-tools-cpp (2.0.0-3noble.20240702.042307) ...
Selecting previously unselected package ros-jazzy-apex-test-tools.
Preparing to unpack .../ros-jazzy-apex-test-tools_0.0.2-9noble.20240702.042443_am
d64.deb ...
Unpacking ros-jazzy-apex-test-tools (0.0.2-9noble.20240702.042443) ...
Setting up ros-jazzy-osrf-testing-tools-cpp (2.0.0-3noble.20240702.042307) ...
Setting up ros-jazzy-apex-test-tools (0.0.2-9noble.20240702.042443) ...
cxy@cxy-Ubuntu2404:~/pendulum$ colcon build
Starting >>> pendulum2_msgs
Starting >>> pendulum_utils
Finished <<< pendulum_utils [0.33s]
Finished <<< pendulum2_msgs [1.94s]
Starting >>> pendulum_controller
Starting >>> pendulum_driver
Starting >>> pendulum_state_publisher
Finished <<< pendulum_state_publisher [9.47s] --- stderr: pendulum_driver
/home/cxy/pendulum/pendulum_driver/src/pendulum_driver.cpp: In member function ‘void pendulum::pendulum_driver::PendulumDriver::set_controller_cart_force(double)’:
/home/cxy/pendulum/pendulum_driver/src/pendulum_driver.cpp:71:38: warning: ‘constexpr const T& rcppmath::clamp(const T&, const T&, const T&) [with T = double]’ is deprecated: use std::clamp instead, introduced in C++17 [-Wdeprecated-declarations]
71 | controller_force_ = rcppmath::clamp(force, -cfg_.get_max_cart_force(), cfg_.get_max_cart_force());
| ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/cxy/pendulum/pendulum_driver/src/pendulum_driver.cpp:17:
/home/cxy/ros2_jazzy/src/ros2/rcpputils/include/rcppmath/clamp.hpp:41:21: note: declared here
41 | constexpr const T & clamp(const T & v, const T & lo, const T & hi)
| ^~~~~
---
Finished <<< pendulum_driver [18.1s]
Finished <<< pendulum_controller [19.6s]
Starting >>> pendulum_demo
Finished <<< pendulum_demo [7.67s]
Starting >>> pendulum_bringup
Finished <<< pendulum_bringup [2.23s]
Starting >>> pendulum
Finished <<< pendulum [2.00s]
Summary: 8 packages finished [33.6s]
1 package had stderr output: pendulum_driver
cxy@cxy-Ubuntu2404:~/pendulum$ source install/setup.bash
运行测试
在运行之前,请确保至少有 8GB 的空闲 RAM。由于内存锁定,交换将不再起作用。
请配置您的 ROS 2 setup.bash。
运行演示二进制文件,并重定向输出。如果遇到权限错误,您可能需要使用 sudo
。
pendulum_demo > output.txt
cxy@cxy-Ubuntu2404:~/ros2_jazzy$ ros2 run pendulum_control pendulum_demo
Couldn't set scheduling priority and policy: Operation not permitted
Caught exception: std::bad_alloc
Unlocking memory and continuing.
Couldn't lock all cached virtual memory.
Pagefaults from reading pages not yet mapped into RAM will be recorded.
Initial major pagefaults: 0
Initial minor pagefaults: 963851
No results filename given, not writing results
rttest statistics:
- Minor pagefaults: 0
- Major pagefaults: 0
Latency (time after deadline was missed):
- Min: 53992 ns
- Max: 198680 ns
- Mean: 123688.674000 ns
- Standard deviation: 14301.050338
PendulumMotor received 498 messages
PendulumController received 499 messages
cxy@cxy-Ubuntu2404:~/ros2_jazzy$ ros2 run pendulum_control pendulum_demo > output.txt
Couldn't set scheduling priority and policy: Operation not permitted
Caught exception: std::bad_alloc
Unlocking memory and continuing.
Couldn't lock all cached virtual memory.
Pagefaults from reading pages not yet mapped into RAM will be recorded.
No results filename given, not writing results
刚刚发生了什么事?
首先,即使你重定向了 stdout,你仍然会看到一些输出到控制台(来自 stderr):
mlockall failed: Cannot allocate memory
Couldn't lock all cached virtual memory.
Pagefaults from reading pages not yet mapped into RAM will be recorded.
在演示程序的初始化阶段之后,它将尝试将所有缓存的内存锁定到 RAM 中,并使用 mlockall
防止将来动态内存分配。这是为了防止页面错误将大量新内存加载到 RAM 中。(有关更多信息,请参见实时设计文章。)
当这种情况发生时,演示将照常继续。在演示生成的 output.txt 文件的底部,您将看到执行期间遇到的页面错误数量。
rttest statistics:
- Minor pagefaults: 20
- Major pagefaults: 0
如果我们想让那些页面错误消失,我们将不得不..
调整内存锁定的权限
添加到 /etc/security/limits.conf
(以 sudo 身份):
<your username> - memlock <limit in kB>
-1
的限制是无限的。如果选择此选项,编辑文件后可能需要使用 ulimit -l unlimited
(作为 root)进行操作。
cxy@cxy-Ubuntu2404:~/Desktop$ sudo -i
[sudo] password for cxy:
root@cxy-Ubuntu2404:~# ulimit -l unlimited
root@cxy-Ubuntu2404:~# exit
logout
保存文件后,注销并重新登录。然后重新运行 pendulum_demo
调用。
您将会在输出文件中看到零页错误,或者看到一个错误,提示捕获了 bad_alloc 异常。如果发生这种情况,说明您没有足够的可用内存将进程分配的内存锁定到 RAM 中。您需要在计算机中安装更多的 RAM 才能看到零页错误!
输出概述
要查看更多输出,我们必须运行 pendulum_logger
节点。
在一个 shell 中使用您的 install/setup.bash
源,调用:
pendulum_logger
cxy@cxy-Ubuntu2404:~/Desktop$ cd ~/ros2_jazzy
cxy@cxy-Ubuntu2404:~/ros2_jazzy$ source ~/.bashrc
cxy@cxy-Ubuntu2404:~/ros2_jazzy$ pendulum_logger
pendulum_logger: command not found
cxy@cxy-Ubuntu2404:~/ros2_jazzy$ ros2 run pendulum_control pendulum_logger
Logger node initialized.
您应该看到输出消息:
Logger node initialized.
在另一个已加载 setup.bash 的 shell 中,再次调用 pendulum_demo
。
一旦这个可执行文件启动,您应该会看到另一个 shell 不断打印输出:
Commanded motor angle: 1.570796
Actual motor angle: 1.570796
Mean latency: 210144.000000 ns
Min latency: 4805 ns
Max latency: 578137 ns
Minor pagefaults during execution: 0
Major pagefaults during execution: 0
演示控制一个非常简单的倒立摆模拟。倒立摆模拟在其自己的线程中计算其位置。一个 ROS 节点模拟倒立摆的电机编码器传感器并发布其位置。另一个 ROS 节点充当简单的 PID 控制器并计算下一个命令消息。
记录器节点在执行阶段定期打印出摆锤的状态和演示的运行时性能统计数据。
在 pendulum_demo
完成后,你需要按 CTRL-C 退出记录器节点以退出。
延迟
在 pendulum_demo
执行时,您将看到为演示收集的最终统计数据:
rttest statistics:
- Minor pagefaults: 0
- Major pagefaults: 0
Latency (time after deadline was missed):
- Min: 3354 ns
- Max: 2752187 ns
- Mean: 19871.8 ns
- Standard deviation: 1.35819e+08
PendulumMotor received 985 messages
PendulumController received 987 messages
延迟字段显示更新循环的最小、最大和平均延迟(以纳秒为单位)。在这里,延迟是指更新预期发生后的时间量。
实时系统的要求取决于应用程序,但在这个演示中,我们假设有一个 1kHz(1 毫秒)的更新循环,并且我们的目标是最大允许延迟为更新周期的 5%。
所以,我们这次运行的平均延迟非常好,但最大延迟是不可接受的,因为它实际上超过了我们的更新循环!发生了什么事?
我们可能正在遭受非确定性调度程序的困扰。如果您运行的是原生 Linux 系统,并且没有安装 RT_PREEMPT 内核,您可能无法达到我们为自己设定的实时目标,因为 Linux 调度程序不允许您在用户级别任意抢占线程。
请参阅实时设计文章以获取更多信息。
演示尝试将演示的调度程序和线程优先级设置为适合实时性能。如果此操作失败,您将看到一条错误消息:“无法设置调度优先级和策略:操作不允许”。您可以通过遵循下一部分中的说明获得稍好的性能:
为调度程序设置权限
添加到 /etc/security/limits.conf
(以 sudo 身份):
<your username> - rtprio 98
rtprio(实时优先级)字段的范围是 0-99。然而,不要将限制设置为 99,因为这样您的进程可能会干扰以最高优先级运行的重要系统进程(例如看门狗)。此演示将尝试以优先级 98 运行控制循环。
绘图结果
您可以在演示运行后绘制在此演示中收集的延迟和页面错误统计信息。
由于代码已使用 rttest 进行了检测,因此有一些有用的命令行参数可用:
命令 |
描述 |
默认值 |
-i |
指定运行实时循环的迭代次数 |
1000 |
-u </ |