给机器人装「眼睛」— 距离传感器选型、服务化与可视化¶
这一章讲什么: 从「看不见的世界」到「看得见的世界」。三段: ① 选传感器——光学飞行时间(ToF)阵列 vs 超声波,顺带讲清 3.3 V/5 V 逻辑电平这条会烧板子的红线; ② 把传感器装上车、读出 64 路距离、包成 MQTT 服务; ③ 在手机上把 64 个数画成一幅会动的灰度图,并修掉 I²C 总线太慢的性能问题。 下一章的避障行为,全靠本章的数据吃饭。
1. 先看现象:机器人终于知道前面有墙了¶
到上一章为止,机器人一直是瞎的:撞了墙,它不知道,你知道——因为是你走过去把它搬开1。 本章结束时的画面完全不同:手机屏幕上一幅 8×8 的灰度网格,越近越黑, 你挥挥手,图上的黑块跟着动——你看到的是机器人的「视野」。
这个画面值得停一秒:数据流的完整通路是 物理世界 → 激光反射 → I²C 总线 → Python 服务 → MQTT → WebSocket → 浏览器 → 你的眼睛, 八站接力,每一站都是前面章节亲手搭的。本章的任务就是把中间空缺的那几站补上。
2. 顶层全景:四个工位¶
① 选型 光学 ToF 阵列 vs 超声波 → VL53L5CX(一个顶 64 个)
② 上架 钻两个 2.5 mm 孔 → COM3600 支架 → QW/ST 线接 HAT
③ 服务化 distance_sensor_service.py:订阅 control,发布 sensors/distance_mm(64 个数)
④ 可视化 Chart.js 灰度网格;顺手把 I²C 从 100 kHz 提到 400 kHz
图说:① 决定看什么,② 决定装得稳,③ 决定谁都能读,④ 决定人看得懂。
3. 核心原理之一:测距的共同原理——飞行时间¶
绝大多数距离传感器都在做同一件事:发一个脉冲,等它反射回来,用时间换算距离2。 差别只在脉冲是什么:
- 超声波:用的是人耳听不见的高频声(还可能惹恼狗;手机麦克风能听见它的脉冲当咔哒声)。 声速慢、好测时,所以便宜;但体积偏大,软的、不反射声音的东西(比如布料)测不到; 回波要代码精确计时;量程约 2 cm 到 4 m;还会被哗啦响的钥匙、吱呀的轮子、底盘振动干扰3。
- 光(红外激光,多不可见):光速快得多,所以更贵,但小巧、快速、能连续精确测量; 毛病是怕强光——直晒阳光或卤素灯会让它饱和;好在室内多是斜射光,室内机器人完全够用; 有些型号(包括我们这块)还能报告每个测量的质量,坏数据可以剔除4。
书里选的 VL53L5CX 是光学里的特殊型号:它不发一束,而是同时测 8×8 = 64 个区域—— 「可以替代 64 个简单传感器」5。对照图解很直观:HC-SR04 视场约 15°, 只报正前方最近物体的距离;VL53L5CX 视场约 63°,64 个区域各有独立读数—— 前面的台阶,超声波只报最近的 75 cm,阵列能分别报墙上 90 cm、第一级 80 cm、第二级 75 cm6。 分辨率就是信息:一个传感器看出「哪里有台阶」,要多个超声波拼出同样的画面。 光学型号还常用 I²C,省引脚——但多个 I²C 设备同总线要处理地址冲突(本书只用一块,豁免)7。
还有一条硬性选型约束书里单独设节:电平匹配。
4. 核心原理之二:逻辑电平——3.3 V 和 5 V 之间的生死线¶
Raspberry Pi 的 I/O 引脚只吃 3.3 V;而市面上很多模块用 5 V 逻辑8。 电压是「电流的压力」;数字器件用高/低电平表示 1/0,电平必须压过阈值(判定「算不算」的那条界限)才算数9。 书里那张图把五个区域标得清楚:
| 电平 | 3.3 V 器件(如 Pi)看 | 5 V 器件看 |
|---|---|---|
| 0 V | 明确的 0 | 明确的 0 |
| 1.8 V(「不清楚」区) | 介于阈值之间,必须避免 | — |
| 3.3 V | 明确的 1 | 低于 4.2 V,可能认不出是 1 |
| 5 V | 会损坏 Pi | 明确的 1 |
警告原话:传感器和控制器的连接必须电压匹配,否则会随机数据、没数据,或损坏器件; 不匹配要加电平转换器(level shifter)10。VL53L5CX 模块恰是 3.3 V 器件—— 选型时这是暗中的决定性一票10。买任何电子件前,先问一句:「它说什么电压?我的控制器说什么电压?」
5. 核心原理之三:上架——钻两个孔,学一手钳工¶
装传感器没有现成孔位:COM3600 支架的孔和底盘对不上,要自己加两个 2.5 mm 孔(M2.5 螺丝用)。 书里把钳工流程教得很完整,值得当通用手艺接住:
- 划线:台钳固定支架,游标卡尺量出「离后边 3 mm、两侧各进 18 mm、孔距 14 mm」, 铅笔画十字——铅笔线淡,但够钻孔用11;
- 钻孔:手捻钻(pin vise)先打 0.5 mm 导向孔(小钻头好定位,求准不求快), 再换 2.5 mm 扩到螺栓径12;
- 装配:铜柱穿支架、背面螺母固定(尖嘴钳),传感器锁到铜柱上, 支架整体用底盘前部两个孔位装上,居中拧紧13。
接线用 300 mm 四芯 QW/ST(JST-SH)线,一端插传感器背面插座,一端插 HAT 的 QW/ST 口—— 方向唯一,别硬插,插座会断14。别忘了撕掉传感器元件上的橙色保护贴15。
6. 核心原理之四:读出 64 个数——库、分辨率、频率¶
软件侧装两样:python3-ujson 和 python3-numpy——NumPy 是「更有意思的 Python 机器人」
的必备库(快速操作数值数组、变换形状)16;pip 侧装 Pimoroni 的 vl53l5cx-ctypes17。
测试脚本 robot/tests/test_distance_sensor.py 的核心配置只有四行:
sensor = vl53l5cx_ctypes.VL53L5CX()
sensor.set_resolution(8 * 8) # 64 路
sensor.set_ranging_frequency_hz(10) # 每秒 10 帧(Hz = 次/秒)
sensor.start_ranging()
atexit.register(sensor.stop_ranging) # 用完要体面地停
主循环里 data.distance_mm 是一个二维数组:外层的列表只有一项,里面是 64 个数;
书里的图给了排列:索引(编号)0 是右下角,索引 7 是左下角——
测试脚本打印 [0][0] 和 [0][7],正好是机器人前方的右、左两个角18。
上机体验:初始化要 20 秒左右(一大段固件代码要灌进传感器),然后每行吐出两个毫米数;
拿块大于 5 cm² 的不透明板子在前面挥,数字跟着变19。
排错四个要点:RuntimeError: VL53L5CX not detected on 0x29 = 没插好
(i2cdetect -y 1 应看到 29 这个新地址,和 HAT 的 17 并存);
没输出多半是没等够初始化;数值飘先查被测物是不是透明/强反光(玻璃、手机屏幕会骗它);
还有保护贴撕没撕20。
一条关键纪律在排错区:测试脚本不能和服务同时跑——同时只有一个程序能直接控制传感器21。 这是独占硬件资源的基本事实,后面章节还会遇到。
7. 核心原理之五:服务化——坏数据怎么处理,比好数据更重要¶
robot/distance_sensor_service.py 照第 6 章的模板走:类 + loop_forever() +
systemd 自启。topic 设计两个方向:
- 收:
sensors/distance/control/start_ranging、.../stop_ranging—— 谁都能遥控传感器; - 发:
sensors/distance_mm—— 64 个数的 JSON 数组,几何和传感器阵列一致; 外加sensors/distance/status: ready作为「我活了」信号22。
服务化时真正的知识点在 update_data() 里:
data = self.sensor.get_data()
as_list = list(data.distance_mm[0])
for n, status in enumerate(list(data.target_status[0])):
if status not in VALID_STATUSES:
as_list[n] = 3000 # 坏点用最大量程 3 米顶替
publish_json(self.client, "sensors/distance_mm", as_list)
机制:传感器每个区域都带一个 target_status(测量质量);光太强、没目标时该格的数据不可信。
服务的做法不是丢掉(丢会改变数组形状),而是用 3000(3 米,量程上限)顶替坏点——
远 = 安全 = 「那里没东西」,下游(避障、可视化)无需特判23。
这是传感器工程的第一课:输出契约要稳(永远是 64 个数),质量信息另走一路编码进数据里。
另有一个 systemd 层的细节:这个传感器库的 stop_ranging 不太利索,服务停的时候会拖。
解法是 unit 模板加一行 TimeoutStopSec=2——SIGINT 2 秒后还不退,就用更强的信号强杀24。
「优雅退出有预算」,这是给所有慢清理服务的一般配方。
验证闭环(本章主线例子):
终端1 mosquitto_sub -t 'sensors/#' -v → 看到 sensors/distance/status ready
终端2 mosquitto_pub -t sensors/distance/control/start_ranging -m ""
终端1 滚动起 64 个数一行的 JSON:[172,184,201,193,3000,134,...]
↑ 注意里面混着的 3000——那就是被剔除的坏点
停止 mosquitto_pub -t sensors/distance/control/stop_ranging -m ""
8. 核心原理之六:可视化——一条数据管线,四级变换¶
原始数据没法直接看:「3000」的大数会把近处的细节全淹黑25。书里给了一条四级管线, 照原书接住(这是数据可视化的通用模式,以后第 15、17 章还会用):
原始 64 个数 → ① 裁剪 clip:min(d, 1000) 超出 1 米的都按 1 米算
→ ② 归一化(缩放)÷1000 全部压进 0..1
→ ③ 网格化 x = 8-(i%8+0.5) 平铺数组变成 8×8 坐标(减 8 是左右镜像翻转)
y = floor(i/8)+0.5
→ ④ 上色 灰度 = d×255 0..1 的距离变成 0..255 的亮度
图说:近=黑、远=白;四步各自简单,连起来就是把「毫米」翻译成「像素」。
实现上是 Chart.js:数据集(data.datasets)+ 每个点用 backgroundColor 回调按距离算灰度、
点型设成 25 单位的矩形(pointStyle: 'rect'),scatter(散点)图类型,画布(canvas)画26。
页面端 mqttClient.subscribe('sensors/distance_mm'),每条消息
JSON.parse → chart.update_data(...) → 图刷新——MQTT 只管推数,Chart.js 只管画,
两者用 update_data 这一个函数解耦27。
调试时书给了一个金句级的提醒:当心镜像效应! 机器人面朝你时左右是反的; 测试时让机器人背对你挥手28。传感器装反、坐标翻错,是这一章最高发的 bug。
页面工程还有两个顺手升级:① HTTP 服务换到 80 端口(免输端口号,http.server ... 80);
② 页面 HTML 用 Jinja 模板(page.html.j2 定义 {% block %} 骨架),
PyInfra 的 files.template 成批渲染 robot_control/ 下所有文件——
以后每加一个页面,只需写 title 和 content 两个块29。
最后把图表和摇杆装进同一页(drive_with_distance.html),
边看边开——人和机器人第一次共用同一张「视野」。
9. 核心原理之七:性能——一条总线上的带宽(单位时间能传多少数据)战争¶
合体页跑起来,问题暴露了:传感器更新慢,电机指令也开始卡顿。 原因书里一句道破:传感器数据量大,而它走的 I²C 总线和电机控制共用, 100 kHz 的默认波特率被传感器流量占满,电机的指令得排队30。
解法粗暴有效:改 /boot/firmware/config.txt(旧版系统叫 /boot/config.txt),
把 I²C 提到 400 kHz——默认 100 kHz 的四倍31:
PyInfra 的 files.line 负责找到旧配置行、替换、确保没被注释掉;改完重启生效31。
这一幕值得记住的不是命令,而是诊断思路:症状在应用层(网页卡),
病根在总线层(带宽),修在配置层(波特率)——性能问题要顺着数据通路逐层往下查。
10. 作者的判断与证据¶
- 判断: 一个 8×8 阵列替代多个单点传感器。证据书里用同一场景(台阶)做了对照: 超声波只报最近 75 cm,阵列报出三级台阶的分别距离——信息量不在一个量级6。
- 判断: 光学优先于超声波,主因是室内光环境可控 + 数据质量标志 + I²C 省引脚; 代价(强光饱和、更贵)如实列出47。
- 事实层: 电平阈值数字(0.8 V 判 0、2.3 V 判 1、3.3 V 以上伤 Pi、5 V 器件要 4.2 V)、 15°/63° 视场、2 cm–4 m 量程、初始化约 20 秒、0x17/0x29 地址、400 kHz——均可查数据手册复核91931。
- 工程观: 「坏点用最大量程顶替」「优雅退出设 2 秒预算」「独占设备同时只许一个程序」, 这些不是某块板子的特性,是书里反复示范的通用工程处置232421。
11. 边界与局限¶
- 强光与透明面仍是盲区。 直接阳光、玻璃、镜面都会出坏点;坏点被编码成 3 米, 下游若不分辨「真空无一物」和「测不出来」,会误判。第 17 章的概率场才正面处理这个。
- 量程只有 3 米级、只看正前方 63°。 身后和侧后方全盲——书里机器人永远「面朝危险」。
- 可视化是调试工具,不是产品界面。 灰度图没有世界坐标、没有持久化; 「地图」要等第 12 章的概率模型。
- 64 个数的 JSON 每秒 10 帧,带宽开销不小。 本书靠提 I²C 波特率硬扛,没有讲数据压缩 或「只在有变化时发」的节流策略。
- NumPy 装了但本章几乎没用上——它真正的戏份在第 15、17 章。
12. 可带走的¶
- 测距 = 发脉冲量回波时间;声慢(便宜、怕软物、怕吵),光快(小巧精确、怕强光)234。
- 阵列传感器的分辨率就是信息量:一个 8×8 顶一堆单点56。
- 电平红线:Pi 只认 3.3 V,5 V 会烧;不匹配就上电平转换器10。
- 装小零件:卡尺划线 → 0.5 mm 导向孔 → 扩孔 → 铜柱装配,求准不求快1112。
- 传感器独占:同时只有一个程序能直接控制它21。
- 坏数据策略:输出形状不变,坏点用「最远」编码——下游免特判23。
- 服务退出要设预算:
TimeoutStopSec=2之类的强杀兜底24。 - 可视化管线四步:裁剪 → 归一化 → 网格化 → 上色;每步只做一件事2526。
- 调试机器人视野先排除镜像效应:让它背对你28。
- 总线症状顺着数据通路查:网页卡 → MQTT → I²C 带宽 → 波特率3031。
13. 原文地图¶
| 主题 | 原书位置 |
|---|---|
| 飞行时间原理 | text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:17(搜「fire off a pulse」) · text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:21(搜「otherwise known as the time of」) |
| 光学 vs 超声波对照 | text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:34(搜「saturated by bright lights」) · text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:50(搜「64 simpler sensors」) · text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:85(搜「precisely timed by code」) |
| 逻辑电平 | text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:115(搜「damage may occur」) |
| 视场角对照 | text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:161(搜「15-degree field of view」) · text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:165(搜「63-degree」) |
| 钻孔与装配 | text/76-fm-attaching-and-reading-a-distance-sensor.txt:55(搜「pin vise」) · text/76-fm-attaching-and-reading-a-distance-sensor.txt:63(搜「pilot hole」) · text/76-fm-attaching-and-reading-a-distance-sensor.txt:86(搜「QW/ST cable」) · text/76-fm-attaching-and-reading-a-distance-sensor.txt:104(搜「protective orange cover」) |
| 库与测试脚本 | text/76-fm-attaching-and-reading-a-distance-sensor.txt:118(搜「essential library for more interesting Python」) · text/76-fm-attaching-and-reading-a-distance-sensor.txt:187(搜「two-dimensional array」) · text/76-fm-attaching-and-reading-a-distance-sensor.txt:197(搜「20 seconds to initialize」) |
| 排错与独占 | text/76-fm-attaching-and-reading-a-distance-sensor.txt:219(搜「not detected on 0x29」) · text/77-fm-making-sensor-data-available-in-mqtt.txt:222(搜「program can directly control the sensor」) |
| 服务与坏点处理 | text/77-fm-making-sensor-data-available-in-mqtt.txt:52(搜「sensors/distance_mm」) · text/77-fm-making-sensor-data-available-in-mqtt.txt:112(搜「3000 for 3 meters」) · text/77-fm-making-sensor-data-available-in-mqtt.txt:169(搜「TimeoutStopSec=2」) |
| 可视化管线 | text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:24(搜「web servers traditionally serve HTTP on port 80」) · text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:57(搜「we will clip this to readings」) · text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:287(搜「mirror effect」) |
| 页面模板 | text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:334(搜「page.html.j2」) |
| I²C 提速 | text/79-fm-summary.txt:10(搜「shared with the motors」) · text/79-fm-summary.txt:20(搜「i2c_arm_baudrate=400000」) |
-
出处:「Following a path with MQTT」(
text/64-fm-summary.txt:2,搜「runs into a wall」)。原文:没有传感器,机器人无法知道自己在哪、是否撞了东西;撞墙只能人去搬。这是第 6 章末为传感器埋的引子。 ↩ -
出处:「Choosing between optical and ultrasonic sensors」第 17 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:17,搜「fire off a pulse」)。原文:最常见的测距方式是超声波或光,共同原理是发出脉冲、感知其反射回波、用时序测距。 ↩↩ -
出处:「Choosing between optical and ultrasonic sensors」第 74 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:74,搜「annoy some animals」)与第 70 段(搜「precisely timed by code」)。原文:超声波频率超出入耳上限,但可能惹恼狗等动物;比光学大但便宜;不反射声音的软物难测;HC-SR04 两个大罐一发一收,回波需代码精确计时,量程 2 cm–4 m,会被响动和振动干扰。 ↩↩ -
出处:「Choosing between optical and ultrasonic sensors」第 34 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:34,搜「saturated by bright lights」)。原文:光学传感器小而快、能连续精确测量;会被直射阳光/卤素灯饱和,不适合一切任务;室内多是斜射光,室内机器人很好用;有些能报告测量质量,坏数据可剔除。 ↩↩↩ -
出处:「Choosing between optical and ultrasonic sensors」第 50 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:50,搜「64 simpler sensors」)。原文:VL53L5CX 能测一个距离阵列,「can be used in place of 64 simpler sensors」;还附数据质量与反射率测量。 ↩↩ -
出处:「Choosing between optical and ultrasonic sensors」第 161 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:161,搜「15-degree field of view」)与第 150 段(搜「63-degree」)。原文:HC-SR04 视场约 15°,只报最近物体;VL53L5CX 测 64 区域、视场 63°;示例:HC-SR04 漏掉台阶只报 75 cm,阵列报墙 90 cm、第一级 80 cm、第二级 75 cm。 ↩↩↩ -
出处:「Choosing between optical and ultrasonic sensors」第 65 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:65,搜「share pins」)。原文:光学常更贵但常用 I²C,便于与其他设备共用引脚;多 I²C 传感器要处理地址变更,本书只用一块故不涉及。 ↩↩ -
出处:「Choosing between optical and ultrasonic sensors」第 94 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:94,搜「only suitable for inputs of 3.3 V」)。原文:Pi 的 I/O 引脚只适合 3.3 V 输入;许多设备用 5 V 逻辑。 ↩ -
出处:「Choosing between optical and ultrasonic sensors」第 99 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:99,搜「pressure there is on an electrical flow」)。原文:电压描述电流的压力;高电压可损坏器件,低电压测不出或读数不清;逻辑器件以高/低电压表 1/0,须过阈值。 ↩↩ -
出处:「Choosing between optical and ultrasonic sensors」第 115 段(
text/75-fm-choosing-between-optical-and-ultrasonic-sensors.txt:115,搜「damage may occur」)与第 112 段(搜「Level shifters」)。原文:0.8 V 以下判 0,2.3 V 以上多数 3.3 V 器件判 1,3.3 V 以上可能损坏 Pi,4.2 V 以上才算 5 V 器件的 1;警告:连接须匹配电压,否则随机数据/无数据/损坏;不匹配用电平转换器;VL53L5CX 是 3.3 V 器件正合用。 ↩↩↩ -
出处:「Attaching and reading a distance sensor」第 55 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:55,搜「pin vise」)。原文:台钳固定、卡尺量 3 mm/18 mm/14 mm、铅笔标十字;线淡但够钻。 ↩↩ -
出处:「Attaching and reading a distance sensor」第 63 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:63,搜「pilot hole」)。原文:先打 0.5 mm 导向孔(小钻头好引导,求准不求速),再换 2.5 mm 扩孔。 ↩↩ -
出处:「Attaching and reading a distance sensor」第 80 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:80,搜「pliers」)。原文:铜柱穿支架、尖嘴钳上背螺母,传感器锁铜柱,支架用底盘前部两孔固定,拧紧时保持居中。 ↩ -
出处:「Attaching and reading a distance sensor」第 86 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:86,搜「QW/ST cable」)。原文:四芯 JST-SH 300 mm 线;插头只有一个方向;Tip 警告别硬插,插座会断;线绕开轮子走。 ↩ -
出处:「Attaching and reading a distance sensor」第 104 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:104,搜「protective orange cover」)。原文 Note:传感器元件上有橙色保护贴,用 Tiny 拉片 gently 撕掉再用。 ↩ -
出处:「Attaching and reading a distance sensor」第 118 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:118,搜「essential library for more interesting Python」)。原文:ujson 更高效地产 JSON(64 元素/条);NumPy(读 numb-pie)快速操纵数值数组、变形变换,是更有意思的 Python 机器人的必备库。 ↩ -
出处:「Attaching and reading a distance sensor」第 139 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:139,搜「vl53l5cx-ctypes」)。原文:Pimoroni 的 vl53l5cx-ctypes 库从 Python 操作该器件;virtual_env.py 引 base_packages 保证先装 NumPy。 ↩ -
出处:「Attaching and reading a distance sensor」第 187 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:187,搜「two-dimensional array」)。原文:distance_mm 是二维数组,外层单项、内层 64 项;10 Hz = 每秒 10 次(Hz 为每秒周期数);索引 0 是右下角、7 是左下角(图 8.13)。 ↩ -
出处:「Attaching and reading a distance sensor」第 197 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:197,搜「20 seconds to initialize」)。原文:初始化约 20 秒(一大段代码要送进传感器);然后每行打印右下、左下两个毫米数;用 >5 cm² 不透明板测试。 ↩↩ -
出处:「Attaching and reading a distance sensor」第 219 段(
text/76-fm-attaching-and-reading-a-distance-sensor.txt:219,搜「not detected on 0x29」)。原文排错:语法/缩进;pyinfra 部署;0x29 未见=连接问题(i2cdetect 应见 29 与 17 并存);无输出等初始化;玻璃/手机屏等透明反光面会骗人;撕保护贴。 ↩ -
出处:「Making sensor data available in MQTT」第 222 段(
text/77-fm-making-sensor-data-available-in-mqtt.txt:222,搜「program can directly control the sensor」)。原文排错:不能让 test_distance_sensor.py 一直跑着,因为只有一个程序能直接控制传感器。 ↩↩↩ -
出处:「Making sensor data available in MQTT」第 52 段(
text/77-fm-making-sensor-data-available-in-mqtt.txt:52,搜「sensors/distance_mm」)。原文:服务订阅 sensors/distance/control 启停测距;发布 64 元素 JSON 到 sensors/distance_mm;发 ready 状态。 ↩ -
出处:「Making sensor data available in MQTT」第 112 段(
text/77-fm-making-sensor-data-available-in-mqtt.txt:112,搜「3000 for 3 meters」)。原文:target_status 逐格报告测量状态;不在 VALID_STATUSES 的格用 3000(3 米最大量程)顶替再发布;光太强时部分元件产坏数据。 ↩↩↩ -
出处:「Making sensor data available in MQTT」第 169 段(
text/77-fm-making-sensor-data-available-in-mqtt.txt:169,搜「TimeoutStopSec=2」)。原文:传感器库停不干净,systemd 里 SIGINT 后发 2 秒再有较不客气的终止信号;同时 unit 的 ExecStart 直接指向 robotvenv 的 python。 ↩↩↩ -
出处:「Viewing sensor data in a smartphone app」第 57 段(
text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:57,搜「we will clip this to readings」)。原文:原始数据里 3000 段把近处细节淹没;裁到 1000 以下、归一化到 0..1、排进网格(坐标移 0.5 居中,x 用 8 减做镜像翻转);MQTT 不适合流图帧,所以由 Chart.js 把数据变成视觉。 ↩↩ -
出处:「Viewing sensor data in a smartphone app」第 123 段(
text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:123,搜「multiply the normalized distance by 255」)。原文:灰度 = 归一化距离 × 255;JS 反引号字符串内嵌变量;pointStyle 'rect' + radius 25、scatter 类型、canvas 渲染。 ↩↩ -
出处:「Viewing sensor data in a smartphone app」第 246 段(
text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:246,搜「mqttClient.subscribe」)。原文:订阅 sensors/distance_mm,消息到则 JSON.parse 并 chart.update_data;按钮捷径 publish_when_clicked(id, topic, data)。 ↩ -
出处:「Viewing sensor data in a smartphone app」第 287 段(
text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:287,搜「mirror effect」)。原文 Tip:当心镜像效应;让机器人背对你测,朝着你时左右是反的。 ↩↩ -
出处:「Viewing sensor data in a smartphone app」第 334 段(
text/78-fm-viewing-sensor-data-in-a-smartphone-app.txt:334,搜「page.html.j2」)。原文:Jinja 模板抽公共骨架({% block title %}/{% block content %});deploy_services 用 os.listdir + files.template 批量渲染,changed 汇总触发 web_server 重启。 ↩ -
出处:「Summary」第 10 段(
text/79-fm-summary.txt:10,搜「shared with the motors」)。原文:更新太慢、电机消息也慢;传感器数据量大,它用的 I²C 总线带宽和电机共享;提高 I²C 总线速度能大幅改善。 ↩↩ -
出处:「Summary」第 20 段(
text/79-fm-summary.txt:20,搜「i2c_arm_baudrate=400000」)。原文:直接管理 /boot/firmware/config.txt(旧版为 /boot/config.txt),确保 i2c_arm=on 未被注释并加 400 kHz 波特率——默认 100 kHz 的四倍;pyinfra 下发后重启生效。 ↩↩↩↩