🇨🇳
99 的博客
  • 久韭
  • Python
    • 你真的“创建”了类吗?
    • MRO 三定律
    • 描述器学习指南
    • 描述器实现 property
    • 如何做一个好中介
    • PEP 阅读清单
    • Python 双刃剑
    • 类型标注
    • “类”的渐进式剖析
  • Network
    • IPv6
    • 大道至简
    • DNS 根域
    • RFC 阅读清单
    • 基础公共服务
      • DNS
      • NTP
    • MAC 地址厂商信息
    • 点对点的中介
  • Windows
    • Windows 使用小技巧
  • Linux
    • SSH 技高一筹
    • nftables 速成
    • Linux 30 周年
    • 新时代 Linux 命令
    • 树莓派实时性优化
    • Bash DEBUG 一行搞定
  • 佳作收藏
    • 美文
      • 短篇
        • 爱
        • 弟弟
        • 暗途
        • 噩梦
        • 想象
        • 底线
        • 疯人
        • 雪兔
        • 夺妻
        • 闹钟
        • 虐猫
        • 做起来
        • 陈小手
        • 道德极限
        • 符号控制
        • 与人为友
        • 消失的人
        • 时间旅行
        • 情债肉偿
        • 铁血恋爱
        • 太阳黑子
        • 蚂蚁人生
        • 风骚和魅力
        • 我只要一种
        • 不朽的失眠
        • 秋天的怀念
        • 午夜的汽笛
        • 多美的故事
        • 相信不相信
        • 清晨的变故
        • 真实的高贵
        • 神秘领奖人
        • 幸福的夜晚
        • 幸福的生日
        • 我有事找你
        • 我是个窃贼
        • 最担心的是你
        • 客厅里的爆炸
        • 假如是你的话
        • 愿你慢慢长大
        • 民意与伪民意
        • 婚姻中没有天堂
        • 一种深久的不安
        • 非走不可的弯路
        • 单调产生的快乐
        • 我只是讨厌屈服
        • 跳槽只为“软福利”
        • 你的阅读造就了你
        • 聪明人和傻子和奴才
        • 你永远有做不完的事
        • 精神病院里的年轻人
        • 一位短跑运动员的孤独
        • 那些你所不知道的大事
        • 穿过大半个中国去睡你
        • 我觉得自己会永远生猛下去
      • 长篇
        • 早餐
        • 波心
        • 老王
        • 翻浆
        • 小猫
        • 良娼
        • 自信
        • 黑羊
        • 猫人
        • 斜眼
        • 识人
        • 身价
        • 肯肯舞
        • 狗和猫
        • 热包子
        • 黄裙子
        • 老江湖
        • 逃脱术
        • 侯银匠
        • 太原诅咒
        • 死亡花朵
        • 罗马惊艳
        • 刺青时代
        • 好嘴杨巴
        • 扼杀胎儿
        • 纪念照片
        • 紫色人形
        • 天外飞石
        • 戒烟公司
        • 蔡二少爷
        • 女人的星球
        • 一千张糖纸
        • 爱情与投资
        • 赌徒的遗书
        • 飞越流水线
        • 跟踪狂入门
        • 一桩自杀案
        • 机舱里的钟声
        • 桌子还是桌子
        • 一小时的故事
        • 不存在的女友
        • 谁在编造历史
        • 父亲坐在黑暗中
        • 一个小小的建议
        • 彬彬有礼的强盗
        • 18 本画册的爱恋
        • 河流最蓝的地方
        • 好人总会有人疼
        • 偷听谈话的妙趣
        • 公主整夜不能睡
        • 他们那时多有趣
        • 布莱克·沃兹沃斯
        • 坐在路边鼓掌的人
        • 最伟大的科幻小说
        • 你丈夫是干什么的
        • 谁也看不见的阳台
        • 上午打瞌睡的女孩
    • 段子
      • 程序员段子
      • 二次元段子
      • 大老师片段
      • 金句
      • 喝痰
      • 数学家
      • 牛子
      • 男 ♂ 语
      • 甲方乙方
      • 新编故事四则
      • 没人知道大东为何在此舞蹈
  • 日常随记
    • 坑
    • 豆知识
    • 逻辑推断
    • 观影后记
    • LaMDA 有意识吗?
    • VS Code Vim 速记
    • QQ 空间表情符号
    • ZEN of DEBUG
    • 数据收集的途径
    • 呼叫转移设置方式
    • 空中浩劫 ACI 经典
    • 联机海难特色简述
    • 智能家居入门指南
    • 财政学笔记
    • 行为与实验经济学笔记
    • 电商管理学笔记
由 GitBook 提供支持
在本页
  • 优化目的
  • 优化内容
  • 提升 Python 进程优先级
  • 绑定独立 CPU
  • 打内核 RT 补丁
  • rt-kernel.tgz
  • 性能测试
  • 测试方法
  • 测试数据
  • 测试结论
  • 参考源
在GitHub上编辑
  1. Linux

树莓派实时性优化

天下武功,唯快不破

上一页新时代 Linux 命令下一页Bash DEBUG 一行搞定

最后更新于1年前

优化目的

项目需要用 树莓派 4B 实现 毫秒级控制,并且误差控制在 5% 内(即 0.05ms = 50us)。 本身这种精度的控制应该考虑由 C 实现,但考虑到以下原因,最终还是选用了 Python 作为主语言。

  1. 用惯了 Python,好久好久没有写过 C 了,真的 不想写 C 啊啊啊啊 (/▽\)

  2. 控制过程中 Python 调用的 包多是封装了底层 C 库实现,实际性能并不差

  3. 整体程序框架中,需要 Python 实现的顶层控制非常简单

那么,为了实现 50 us 的控制精度,咱就需要针对树莓派上运行的 Raspberry Pi OS 及 Python 进程 进行优化。

优化内容

优化的主要思路参考了下面的两篇文章:

提升 Python 进程优先级

操作

1. 首先需要安装 wiringpi 包:

sudo pip3 install wiringpi

2. 其次需要在程序中加入以下内容:

import wiringpi
wiringpi.piHiPri(99)

原理

wiringpi 中的 piHiPri 函数通过调用 Linux 内核的 API,改变了当前进程的调度策略及优先级进一步提高了实时性。这和 Python 中标准库的 os.setpriority 不同,os.setpriority 并未改变调度策略,实际优化效果很有限。需要注意的是,此处设置的优先级是用于比较的相对值,严格地说并不是越大就越好。

The priority parameter works relative to others – so you can make one program priority 1 and another priority 2 and it will have the same effect as setting one to 10 and the other to 90 (as long as no other programs are running with elevated priorities)

priority 参数 相对 于其他参数有效 - 因此您可以将一个程序的优先级设置为 1,将另一个程序的优先级设置为 2,其效果与将一个设置为 10,另一个设置为 90 相同(只要没有其他程序以更高的优先级运行)。

绑定独立 CPU

操作

1. 首先需要更改内核启动参数:

sudo sed -i.bak -e 's/$/ isolcpus=3/' /boot/cmdline.txt

!!!请注意:单引号中的空格为必须!!! 其中 3 可以更改为你想要指定的 CPU 编号(树莓派 4B 中 CPU 编号为 0-3)。 考虑到未来可能需要还原,此步将原文件添加 .bak 后缀进行了备份。

2. 接着安装后面要用到的 psutil 包

sudo pip3 install psutil

3. 最后在程序中加入以下内容:

import psutil
current_process = psutil.Process()
current_process.cpu_affinity([3])

此处的 3 为上文提到的 CPU 编号

原理

首先让系统不会主动调度程序到指定 CPU 上运行,也就是 保证指定 CPU 默认是完全空闲 的状态。其次让 程序主动要求在指定 CPU 上运行。这样程序就完成了 对指定 CPU 的独享。

打内核 RT 补丁

操作

1. 这里作者选择直接下载编译好的内核:

链接源自 Github,所以需要科学上网,你懂的 ( ̄_ ̄ )

如果你想要亲手编译内核,可参考以下资料:

由于涉及内容相对庞杂,且作者在此方面经验尚浅,故不做展开。

2. 将下载的压缩包传至树莓派 /tmp 目录下:

基础操作,如不理解请善用搜索引擎

3. 依次运行以下命令:

sudo -i
cd /tmp
mkdir rt-kernel
tar -xzvf rt-kernel.tgz -C rt-kernel
cp -Rdf rt-kernel/boot/* /boot/
cp -Rdf rt-kernel/lib/* /lib/
cp -Rdf rt-kernel/overlays/* /boot/overlays/
cp -Rdf rt-kernel/bcm* /boot/
echo 'kernel=kernel7_rt.img' >> /boot/config.txt

4. 重启系统:

sync
reboot

5. 检查内核版本:

uanme -r

若输出为 4.19.59-rt23-v7l+,说明安装成功。

原理

Linux 是典型的 分时应用系统 ,对于实时性要求很高的应用,必须对内核本身动手术。而 RTLinux 则采取了一种比较聪明也比较折中的办法:他们实现一个最底层的精简的调度器,用于调度实时线程,原来的内核本身则成为实时调度器的一个优先级最低的任务。这样,当有实时任务时,普通内核已经建立于其上的普通进程被强制中断,实时线程被强制执行;只有当若有实时线程都让出 cpu 之后,普通内核才被运行,再由普通内核去调度执行普通的应用程序。

性能测试

测试方法

  1. 用 rt-tests 软件包中的 cyclictest 工具测试系统实时性:

    # 安装 rt-tests 软件包
    sudo apt install rt-tests
    # 实际运行测试的命令,--duration 指定测试时长,此处为 1 分钟
    cyclictest --mlockall --smp --priority=80 --interval=100 --distance=0 --duration=1m
  2. 用以下程序测试程序实时性:

    from time import monotonic, sleep
    from array import array
    from statistics import mean
    
    i = 0
    data = array("f")
    while i < 60000:
        t0 = monotonic()
        sleep(0.001)
        t1 = monotonic()
        data.append((t1 - t0 - 0.001) * 1000000)
        i += 1
    print("AVG: %.2f us" % mean(data))
    print("MAX: %.2f us" % max(data))
    print("MIN: %.2f us" % min(data))

测试数据

优化前

policy: fifo: loadavg: 0.55 0.66 0.36 1/293 2309

T: 0 ( 2303) P:80 I:100 C: 599954 Min:      6 Act:   18 Avg:   15 Max:     936
T: 1 ( 2304) P:80 I:100 C: 599498 Min:      5 Act:   18 Avg:   15 Max:     382
T: 2 ( 2305) P:80 I:100 C: 599062 Min:      5 Act:   18 Avg:   14 Max:    1482
T: 3 ( 2306) P:80 I:100 C: 598541 Min:      6 Act:   19 Avg:   14 Max:    5696

AVG: 82.94 us MAX: 6550.83 us MIN: 32.67 us

优化后

policy: fifo: loadavg: 2.38 1.39 0.64 1/308 1491


T: 0 ( 1471) P:80 I:100 C: 599898 Min:      5 Act:   20 Avg:   13 Max:      95
T: 1 ( 1472) P:80 I:100 C: 599418 Min:      6 Act:   18 Avg:   13 Max:     114
T: 2 ( 1473) P:80 I:100 C: 598916 Min:      5 Act:   18 Avg:   13 Max:     102
T: 3 ( 1474) P:80 I:100 C: 598534 Min:      6 Act:   21 Avg:   14 Max:      34

AVG: 82.94 us MAX: 6550.83 us MIN: 32.67 us

网上已有的树莓派 3B+ 上的具体性能测试数据

请注意,此处并非本次优化的数据。

测试结论

从以上数据可以看出,系统实时性提升主要体现在 极值 的降低上。

在实际程序中,均值与极值都有非常可观的降低。 优化完成后,手头 Python 运行的测试误差为 30 us 左右,考虑到项目可容许的偏差是 50 us,终于——咱不用去写 C 啦!o( ̄▽ ̄) ブ

参考源

树莓派提高实时性的几种方式 (使用 python 测试)
四足机器人高算力、低成本主控第一步:给树莓派打上 RT 实时补丁
rt-kernel.tgz
Raspberry Pi: Real Time System - Preempt-RT Patching Tutorial for Kernel 4.14.y
四足机器人高算力、低成本主控第一步:给树莓派打上 RT 实时补丁
Raspberry Pi OS Linux Kernel building
preemptrt_setup
树莓派提高实时性的几种方式 (使用 python 测试)
四足机器人高算力、低成本主控第一步:给树莓派打上 RT 实时补丁
Wiring Pi -- GPIO Interface library for the Raspberry Pi
preemptrt_setup
sched_setscheduler() 函数
Raspberry Pi OS Linux Kernel building
psutil.Process.cpu_affinity
Cyclictest Wiki
wiringPi/piHiPri.c
sched.7
Raspberry Pi: Real Time System - Preempt-RT Patching Tutorial for Kernel 4.14.y
Rapberry Pi: Preempt-RT Kernel Performance on Rasbperry PI 3 Model B+