了解实时编程
背景
实时计算是许多机器人系统的关键特性,尤其是安全和任务关键型应用,例如自动驾驶汽车、航天器和工业制造。 我们在设计和制作 ROS 2 原型时考虑到了实时性能限制,因为这是 ROS 1 早期阶段未考虑的要求,现在很难重构 ROS 1 以使其实时友好。
本文档 概述了实时计算的要求和软件工程师的最佳实践。简而言之:
要构建实时计算机系统,我们的实时循环必须定期更新以满足最后期限。 我们只能容忍这些最后期限的微小误差(我们允许的最大抖动)。 为此,我们必须避免执行路径中的非确定性操作,例如:页面错误事件、动态内存分配/释放以及无限期阻塞的同步原语。
实时计算通常解决的控制问题的一个典型示例是平衡“倒立摆 <https://en.wikipedia.org/wiki/Inverted_pendulum>” 。 如果控制器阻塞的时间过长,摆锤就会掉落或变得不稳定。 但是,如果控制器以比控制摆锤的电机运行速度更快的速度可靠地更新,摆锤将成功适应传感器数据的反应以平衡摆锤。
现在您已经了解了有关实时计算的所有信息,让我们尝试演示吧!
安装并运行演示
实时演示是在考虑 Linux 操作系统的情况下编写的,因为许多进行实时计算的 ROS 社区成员都使用 Xenomai 或 RT_PREEMPT 作为实时解决方案。 由于演示中为优化性能而进行的许多操作都是特定于操作系统的,因此演示仅在 Linux 系统上构建和运行。 因此,如果您是 OSX 或 Windows 用户,请不要尝试此部分!
此外,这必须使用静态 DDS API 从源代码构建。目前唯一支持的实现是 Connext。
首先,按照说明使用 Connext DDS 作为中间件构建 ROS 2 from source。
运行测试
运行之前,请确保您至少有 8Gb 的可用 RAM。使用内存锁定,交换将不再起作用。
获取您的 ROS 2 setup.bash。
运行演示二进制文件,并重定向输出。如果出现权限错误,您可能需要使用“sudo”:
pendulum_demo > output.txt
到底发生了什么?
首先,即使您重定向了 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 中。 (有关更多信息,请参阅“实时设计文章 <https://design.ros2.org/articles/realtime_background.html#memory-management>”)。
发生这种情况时,演示将照常继续。 在演示生成的 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
。
保存文件后,注销并重新登录。
然后重新运行 pendulum_demo
调用。
您将在输出文件中看到零页面错误,或者看到一条错误,提示捕获了 bad_alloc 异常。 如果发生这种情况,则说明您没有足够的可用内存将分配给该进程的内存锁定到 RAM 中。 您需要在计算机中安装更多 RAM 才能看到零页面错误!
输出概览
要查看更多输出,我们必须运行“pendulum_logger”节点。
在一个以“install/setup.bash”为源的 shell 中,调用:
pendulum_logger
您应该会看到输出消息:
记录器节点已初始化。
在另一个以 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%。 因此,我们的平均延迟在这次运行中确实很好,但最大延迟是不可接受的,因为它实际上超出了我们的更新循环!发生了什么? 我们可能正遭受非确定性调度程序的困扰。 如果您正在运行 vanilla Linux 系统并且没有安装 RT_PREEMPT 内核,则可能无法满足我们为自己设定的实时目标,因为 Linux 调度程序不允许您在用户级别任意抢占线程。 有关详细信息,请参阅`实时设计文章 <https://design.ros2.org/articles/realtime_background.html#multithreaded-programming-and-synchronization>`__。
演示尝试将演示的调度程序和线程优先级设置为适合实时性能。 如果此操作失败,您将看到一条错误消息:“无法设置调度优先级和策略:不允许操作”。 按照下一节中的说明操作,您可以获得略微更好的性能:
设置调度程序的权限
Add to /etc/security/limits.conf
(as sudo):
<your username> - rtprio 98
rtprio(实时优先级)字段的范围是 0-99。 但是,不要将限制设置为 99,因为这样您的进程可能会干扰以最高优先级运行的重要系统进程(例如看门狗)。 此演示将尝试以优先级 98 运行控制循环。
绘制结果
您可以在演示运行后绘制此演示中收集的延迟和页面错误统计信息。
由于代码已使用 rttest 进行检测,因此可以使用有用的命令行参数:
Command |
Description |
Default value |
-i |
Specify how many iterations to run the real-time loop |
1000 |
-u |
Specify the update period with the default unit being microseconds. Use the suffix “s” for seconds, “ms” for milliseconds, “us” for microseconds, and “ns” for nanoseconds. |
1ms |
-f |
Specify the name of the file for writing the collected data. |
使用文件名再次运行演示以保存结果:
pendulum_demo -f pendulum_demo_results
使用文件名再次运行演示以保存结果:
rttest_plot pendulum_demo_results
该脚本将产生三个文件:
pendulum_demo_results_plot_latency.svg
pendulum_demo_results_plot_majflts.svg
pendulum_demo_results_plot_minflts.svg
您可以在自己选择的图像查看器中查看这些图表。