了解实时编程
背景介绍
实时计算是许多机器人系统的关键特征,尤其是对安全和任务至关重要的应用,如自动驾驶汽车、航天器和工业制造。我们在设计 ROS 2 和原型时考虑到了实时性能限制,因为在 ROS 1 的早期阶段并没有考虑到这一要求,而且现在要重构 ROS 1 使其对实时性友好也很困难。
本文件 概述了实时计算的要求和软件工程师的最佳实践。简而言之
要制作一个实时计算机系统,我们的实时循环必须定期更新,以满足最后期限的要求。我们只能容忍这些截止日期出现很小的误差(我们允许的最大抖动)。为此,我们必须避免执行路径中的非确定性操作,例如:页面故障事件、动态内存分配/去分配,以及无限阻塞的同步原语。
实时计算通常解决的控制问题的一个典型例子是平衡一个 倒摆.如果控制器意外地长时间阻塞,摆锤就会倒下或变得不稳定。但是,如果控制器可靠地以比控制摆锤的电机运行速度更快的速度更新,摆锤就会成功地对传感器数据作出适应性反应,以平衡摆锤。
现在,您已经对实时计算有了一定的了解,让我们来试试演示!
安装并运行演示版
实时演示的编写考虑到了 Linux 操作系统,因为许多从事实时计算的 ROS 社区成员都使用 Xenomai 或 RT_PREEMPT 作为他们的实时解决方案。由于演示中为优化性能而进行的许多操作都与操作系统有关,因此演示只能在 Linux 系统上构建和运行。 因此,如果您是 OSX 或 Windows 用户,请不要尝试这一部分!
此外,这必须使用静态 DDS API 从源代码构建。 目前唯一支持的实现是 Connext.
首先,按照说明构建 ROS 2 从源头 使用 Connext DDS 作为中间件。
运行测试
运行前,请确保至少有 8GB 可用内存。内存锁定后,交换将不再起作用。
获取 ROS 2 setup.bash 的源代码。
运行演示二进制文件,并重定向输出。您可能需要使用 苏都
以防出现权限错误:
摆锤演示 >; output.txt
刚才到底发生了什么?
首先,即使你重定向了 stdout,你还是会看到一些输出到控制台(来自 stderr):
()()()() 失败: 不能 分配 内存
不能't 锁定所有缓存虚拟内存。
页面默认设置 从 阅读 页面 不 还 映射 成 内存 将 是 记录的.
在演示程序的初始化阶段之后,它会尝试将所有缓存内存锁定到 RAM 中,并使用 ()()()()
.这是为了防止分页默认设置将大量新内存加载到 RAM 中。参见 实时设计文章 了解更多信息)。
出现这种情况时,演示将照常进行。在演示生成的 output.txt 文件底部,您将看到执行过程中遇到的分页错误次数:
rttest 统计:
- 未成年人 页面默认设置: 20
- 主要 页面默认设置: 0
如果我们想取消这些页面默认设置,就必须...
调整内存锁定的权限
添加到 /etc/security/limits.conf
(作为 sudo):
<;您的 用户名>; - 内存锁 <;限额 于 kB>;
限值 -1
是无限的。如果您选择这种方式,您可能需要同时提供 限制 -l 无限
(以根用户身份)编辑文件。
保存文件后,退出并重新登录。然后重新运行 摆锤演示
调用。
你要么会在输出文件中看到零分页默认值,要么会看到一个错误,提示捕获到 bad_alloc 异常。如果出现这种情况,就说明没有足够的可用内存将分配给进程的内存锁定到 RAM 中。你需要在电脑中安装更多内存,才能看到零分页默认值!
输出概述
要查看更多输出,我们必须运行 摆锤记录器
节点。
与您的 install/setup.bash
sourced, invoke:
摆锤记录器
您将看到输出信息:
记录仪 网站 初始化.
在另一个带有 setup.bash 的 shell 中,调用 摆锤演示
又来了
这个可执行文件一启动,你就会看到另一个 shell 不断打印输出:
命令 发动机 观点: 1.570796
实际 发动机 观点: 1.570796
平均值 潜伏期: 210144.000000 ns
最小 潜伏期: 4805 ns
最大 潜伏期: 578137 ns
未成年人 页面默认设置 期间 处决: 0
主要 页面默认设置 期间 处决: 0
该演示正在控制一个非常简单的倒立摆模拟。倒立摆模拟在自己的线程中计算其位置。一个 ROS 节点为摆模拟电机编码器传感器,并发布其位置。另一个 ROS 节点充当简单的 PID 控制器,并计算下一条指令信息。
日志节点会定期打印出摆锤的状态以及演示程序在执行阶段的运行性能统计数据。
在 摆锤演示
完成后,必须按 CTRL-C 键退出日志记录器节点。
延迟
在 摆锤演示
执行后,您将看到为演示收集的最终统计数据:
rttest 统计:
- 未成年人 页面默认设置: 0
- 主要 页面默认设置: 0
延迟 (时间 后 期限 是 失去的):
- 最小: 3354 ns
- 最大: 2752187 ns
- 平均值: 19871.8 ns
- 标准 偏差: 1.35819e+08
摆线马达 收到 985 信息
PendulumController 收到 987 信息
延迟字段以纳秒为单位显示更新循环的最小、最大和平均延迟。这里的延迟是指预计更新发生后的时间。
实时系统的要求取决于应用,但假设在这个演示中,我们有一个 1kHz(1 毫秒)的更新循环,我们的目标是最大允许延迟为更新周期的 5%。
因此,在这次运行中,我们的平均延迟非常好,但最大延迟却令人无法接受,因为它实际上超出了我们的更新循环!发生了什么?
我们可能受到了非确定性调度程序的影响。如果你运行的是普通 Linux 系统,并且没有安装 RT_PREEMPT 内核,那么你很可能无法实现我们为自己设定的实时目标,因为 Linux 调度器不允许你在用户级任意抢占线程。
参见 实时设计文章 了解更多信息。
该演示尝试将演示的调度程序和线程优先级设置为适合实时性能。如果操作失败,你会看到一条错误信息:"无法设置调度优先级和策略:Operation not permitted"(无法设置调度优先级和策略:不允许操作)。按照下一节的说明操作,可以获得稍好的性能:
设置调度程序的权限
添加到 /etc/security/limits.conf
(作为 sudo):
<;您的 用户名>; - rtprio 98
rtprio(实时优先级)字段的范围是 0-99。但请勿将限制设置为 99,因为这样您的进程可能会干扰以最高优先级运行的重要系统进程(如看门狗)。本演示将尝试以 98 的优先级运行控制循环。
绘制结果图
您可以在演示运行后绘制本演示中收集的延迟和页面故障统计数据。
因为代码已被 rttest,有一些有用的命令行参数可用:
指挥 |
说明 |
默认值 |
-i |
指定实时循环的迭代次数 |
1000 |
-u |
指定更新周期,默认单位为微秒。 使用后缀 "s "表示秒,"ms "表示毫秒、 "us "表示微秒,"ns "表示纳秒。 |
1ms |
-f |
指定用于写入所收集数据的文件名。 |
使用文件名再次运行演示以保存结果:
摆锤演示 -f 摆锤结果
然后运行 rttest_plot
脚本:
玫瑰2 运行 rttest rttest_plot 摆锤结果
该脚本将生成多个文件:
摆锤_演示_结果_绘图_延迟.svg
pendulum_demo_results_plot_latency_hist.svg
摆锤_示例_结果_绘图_majflts.svg
摆锤_示例_结果_绘图_minflts.svg
您可以在自己选择的图像浏览器中查看这些绘图。