让机器人动起来 — 电机测试、差速转向与 MQTT 服务层
让机器人动起来 — 电机测试、差速转向与 MQTT 服务层
这一章讲什么: 三段递进。第一段让电机转起来(装库、开 I²C、跑第一个电机测试); 第二段搞清这台两轮机器人怎么转向(差速); 第三段是重点——把电机包成一个 MQTT 服务,让「行为」代码只发消息就能开车, 从此硬件换不换、行为都不用重写。 读完你会得到全书反复使用的那套消息接口:
motors/wheels带两个 -1.0 到 1.0 的数, 左轮右轮的速度就定了。
1. 先看现象:轮子转了,但问题也来了
这一章开场很直白:先用最简单的代码证明电机能转,把花活留到后面1。 但第一次测试藏着全书第一个「机器人式麻烦」:车会自己跑偏。
书里让你把机器人垫起来(用包装盒就行),因为通电后「它可能不会朝你预期的方向走」2。 等代码跑通了,才换电池、放地板、再试。 这不是胆小:两轮机器人的两个电机、两个轮子、一块地板,没有任何两样是完全对称的, 跑偏(书里叫 veer)是默认结局,不是意外。这一章结尾会告诉你为什么、以及现在的治标办法; 彻底治它的工具(编码器)要第 11 章才上场。
2. 顶层全景:本章代码的四层楼
① 基础设施 I²C 总线开启 → 虚拟环境 → inventorhatmini 库(mosquitto 稍后)
② 硬件测试 test_motors.py:enable + speed(0.8) + sleep(2) → 轮子转 2 秒
③ 服务层 inventor_hat_service.py:订阅 MQTT topic,把消息翻译成电机指令
④ 行为层 behavior_line.py / behavior_path.py:只发消息,不碰硬件
图说:①→② 证明身体活着;③ 是全书最重要的架构决定;④ 以后每章都在这一层加新行为。3. 核心原理之一:先通总线,再装库——I²C、虚拟环境与「看见」设备
第一步:开 I²C。 Inventor HAT Mini 挂在 Pi 的 I²C 总线上(第 02 章选的总线,这里兑现)。
开启动作写成 PyInfra 脚本 deploy/configure_pi.py,一行核心:
server.shell("raspi-config nonint do_i2c 0", _sudo=True)raspi-config 是 Pi 的配置工具(菜单驱动,从 0 编号),nonint 表示不进菜单、直接执行
第 0 号选项(启用 I²C)3。改完要 sudo reboot 重启生效——这是 I²C 这类硬件开关的规矩。
第二步:建虚拟环境(virtual environment,常叫 venv 或 Python env)。
为什么?往系统 Python 里装包是坏习惯——可能撞坏系统工具;虚拟环境是隔出来的一个小天地,
包都装在里面,互不干扰4。书里用 pyinfra 的 pip.venv 建好一个叫 robotvenv 的环境,
再做一个全局小脚本 robotpython——以后所有在机器人上跑的 Python 都经它进虚拟环境5:
sudo robotpython robot/tests/test_motors.py # 实际跑的是 robotvenv 里的 python3第三步:装 HAT 库。 deploy_inventor_hat_mini.py 装两样:python3-smbus2
(SMBus 与 I²C 兼容,共用这套库)和 inventorhatmini(Pimoroni 的官方 Python 库)6。
第四步:确认「看得见」。 通电重启后跑:
danny@learnrob3:~ $ sudo i2cdetect -y 1
0 1 2 ... e f
10: -- -- -- -- -- -- -- 17 -- -- -- -- -- -- -- --这张表是 I²C 总线 1 的地址扫描表:每个 I²C 设备在总线上有个地址(像门牌号,
因为一根总线上可以挂很多设备,靠地址区分跟谁说话)7。表里 17 那一格就是 HAT——
地址 0x17(十六进制,数字 0-9 之后用 A-F 接着数,前面加 0x 表示)7。
i2cdetect 干了两件事:证明板子连着且应答,给出它的地址7。
扫不到?三条排错:断电重插 HAT;deploy_all 里加进了 configure_pi;跑完 deploy_all 后重启过7。
4. 核心原理之二:最小电机测试——四行会跑的代码
robot/tests/test_motors.py 全文骨架:
from time import sleep
import atexit
import inventorhatmini
board = inventorhatmini.InventorHATMini()
left_motor = board.motors[1]
right_motor = board.motors[0]
def stop_motors():
left_motor.stop()
right_motor.stop()
atexit.register(stop_motors)
left_motor.enable()
left_motor.speed(0.8)
right_motor.enable()
right_motor.speed(0.8)
sleep(2)逐块拆开:
- import 三行:拉进
sleep(延时)、atexit(退出时执行点什么)、inventorhatmini(我们刚装的 HAT 库)8。 - 连板子:
InventorHATMini()创建一个代表板子的对象(object),存进board;board.motors[1]和[0]是左右电机的快捷方式9。注意左右编号是反的—— 左电机在索引(从 0 数起的编号)1、右电机在索引 0,这是这块板子的实际排布。 - atexit 保险:
atexit.register(stop_motors)保证程序退出时电机一定停, 哪怕程序是报错退出的。理由很机器人:「机器人有冲下桌子、撞进墙里的习惯」, 会伤电机、驱动板和电池10。这个技巧后面每章都用。 - 动起来:
enable()上电,speed(0.8)给速度。这套库的速度范围是 0 到 1, 0.8 就是八成速11;sleep(2)让它跑 2 秒,否则刚启动就结束了。
跑法:pyinfra 同步过去,SSH 上去 sudo robotpython robot/tests/test_motors.py。
机器人应该大致向前。书里的验收口径很宽松也很实际:「可以稍微偏一侧,但不该倒退,
两个电机都该在转」——偏,就是这个阶段的常态12。
排错表(书里的一张对照表,原样接住):ImportError: No module named smbus2 = 包没装对;
Python 报错 = 检查代码文本、确认用 sudo robotpython 跑;单侧倒退 = 电机红黑线接反,
在电机端调换;板子灯亮但电机不动 = WAGO 里线没压好;板子没灯 = 电池没电;
转弯/跑偏严重 = 查接触、查轮子和编码器盘有没有蹭底盘13。
5. 核心原理之三:差速转向——这台机器人的全部驾驶技术
前进只是开始,问题是怎么拐弯。书里把轮式车辆的转向分成两大类14:
转向轮式(steerable wheels):某个轮子的朝向和别的不同,靠改变轮子指向来转弯—— 汽车的前轮转向(齿条齿轮或直线执行器带动)、自制卡丁车那种铰接转向 (整个前桥转)都属于此类15。代价书里写得很清楚:活动部件多、要占空间; 转弯需要行驶空间(转弯半径/turning circle 更长);要么一根轴上大电机, 要么复杂的动力分配机构;转向机构不归中,车就偏15。
固定轮式(fixed-wheel steering):轮轴相对底盘固定,靠两侧轮的相对速度差转向—— 一侧快、一侧慢,车就往慢的一侧弯;一侧正转一侧反转,车原地打转。 这叫打滑转向(skid steering),两个驱动轮 + 一个万向轮的机器人更精确的名字是 差速驱动(differential drive)16。
左轮 0.8 / 右轮 0.8 → 直行
左轮 0.2 / 右轮 0.8 → 向左缓弯(左慢右快,绕慢侧转)
左轮-0.8 / 右轮 0.8 → 原地左转(反转 + 正转)
图说:没有方向盘,全部驾驶都浓缩进这两个数里。这也是 Mars 车以外的移动机器人最常见的方案。为什么选差速?书里的理由链:机械上最简单(每侧一个电机就行); 可以原地 360° 转,转弯空间就是机器人自身的宽度;上履带的话必须用它17。 代价也诚实:转弯时轮子侧向拖滑,有摩擦损耗; 电机、齿轮、控制器输出的任何细微差异都会变成跑偏17。
书里补了一个几何细节:四轮以上的布局要掂量轴距(wheelbase)与轮距(track width); 轴距长,转向得斜着拽轮子,要更多扭矩,通常得加宽轮距18。 两轮 + 万向轮的组合轴距实际是零(绕两轮连线的中点转),最简单, 室内平地完全够用——我们的机器人就是这么定的18。火星车是混合方案: 前后轮可独立转向 + 两侧差速18。
落到代码上就是三行改动:原地转 = 一正一负 speed(0.8) / speed(-0.8);
缓弯 = 两个不同的正数 speed(0.4) / speed(0.8);方向反过来就是另一边19。
6. 核心原理之四:MQTT——为什么要把电机包成服务
电机能转了、会转向了,按理可以开始写「追线」「避障」了。书里在这里踩了一脚刹车,
问了一个架构问题:行为代码里直接调 left_motor.speed(),好不好?
答案是否定的,理由第 02 章就埋下过:不同的电机控制器,接口各不相同; 行为代码和硬件代码混在一起,换硬件就要重写行为20。书里画的对比图很说明问题: Panel A 是把行为、电机、传感器揉成一团的代码——加个行为或换个驱动板都很痛苦; Panel B 是分层——行为层指挥「电机接口」「传感器接口」,不关心接口底下是什么21。
实现这个分层,书里选的工具是 MQTT(Message Queue Telemetry Transport): 一个在服务与硬件之间传递轻量小消息的系统,程序可以在机器人上,也可以在手机这类 别的设备上22。它的工作方式有三个词就够了:
- broker(代理/中转站):所有消息都经过它;程序连上它就能收发;
- topic(主题):消息的分类名,如
motors/wheels; - publish / subscribe(发布/订阅):程序向某个 topic 发消息,或订阅某个 topic 收消息;
#通配该层之下的一切22。
书里给的示例就是你以后天天会发的消息:
mosquitto_pub -t motors/wheels -m "[0.8,0.4]" -u robot -P robot
图说:左电机 80% 速、右电机 40% 速——差速缓左弯,一条消息说完。broker 用 mosquitto(两个 t)——它本身就是个软件包,装在 Pi 上;
PyInfra 脚本 deploy_mqtt.py 负责装包、设用户名密码、用 systemd(Linux 的服务管理器)
让它开机自启23。书里对密码有个坦白的警告:示例里用户名密码都是 robot/robot、
明文写在代码里,是占位符;别在进 Git 的代码里放真密码,加固留给第 18 章24。
装好验证只需两个 SSH 窗口:一个 mosquitto_sub -t "hello/#" -u robot -P robot -v 守着,
另一个 mosquitto_pub -t hello/robot -m "hello" -u robot -P robot 发——
守着的窗口立刻打印 hello/robot hello,消息系统通了25。
7. 核心原理之五:电机服务——订阅 topic,翻译消息
robot/inventor_hat_service.py 是全书出镜率最高的文件之一。它的设计(照原书接住):
对外接口只有三个 topic:
| topic | 消息体(payload) | 作用 |
|---|---|---|
motors/wheels | JSON 两个数,-1.0 到 1.0 | 设置左右轮速 |
motors/stop | 空 | 停电机 |
all/stop | 空 | 停所有活动(各服务都听它) |
外加一条自动保险:超过 1 秒没收到任何消息,自动停车——保证联系中断时机器人不会 一直冲26。
内部结构(每条机制都对应代码里的一段):
连接 client.connect("localhost", 1883) —— broker 就在本机,1883 是 MQTT 默认端口
订阅 on_connect 里订阅 motors/# 和 all/# —— # 通配子层
分派 message_callback_add:每个 topic 配一个函数
motors/wheels → set_motor_wheels(解码 JSON → enable → speed)
motors/stop、all/stop → stop_motors
看门狗 while True:若 now - last_message > 1 秒 → stop_motors();每 0.1 秒查一次
双保险 atexit.register(stop_motors) —— 进程退出也停车几个 Python/MQTT 细节值得带走:json.loads(msg.payload) 把消息文本解码成两个数;
stop_motors(client=None, userdata=None, msg=None) 的参数默认值让同一个函数
既能当消息回调(收到消息时被自动调用的函数)、又能当 atexit 处理器;client.loop_start() 让消息收发在后台线程跑,
主线程专心看门狗27。
跑起来(sudo robotpython robot/inventor_hat_service.py)看到
Connected with result code 0 就是连上了28。然后,从另一个 SSH 窗口发消息——
注意这个画面:开车的人和车之间,隔着一台 broker;你再也不是「在机器人上跑代码」,
而是「向机器人发消息」。
mosquitto_pub -t motors/wheels -m "[0.8,0.8]" -u robot -P robot
# 电机转起来,1 秒后自动停(看门狗超时)8. 核心原理之六:行为层——公共连接代码、last will,与第一条路径
服务层立住了,行为层薄得惊喜。公共代码收进 robot/common/mqtt_behavior.py,
两个函数:connect()(连接 broker、起消息循环)和 publish_json()(发 JSON 消息)29。
其中藏着 MQTT 的第二个保险机制:
last will(遗嘱):连接时通过 client.will_set("all/stop", "") 预设一句「遗言」——
如果这个程序意外断开,broker 会替它把 all/stop 发出去30。
效果和 atexit 类似(都是「出事了要收场」),但管的是「程序突然没了」这种连 atexit
都来不及跑的情况。电机服务的看门狗 + 行为的遗嘱 + atexit,三重停车保险齐了。
第一条行为 behavior_line.py 就是这么薄:
client = connect()
publish_json(client, "motors/wheels", [0.8, 0.8])
sleep(1)
publish_json(client, "motors/wheels", [0.8, 0.8]) # 必须再发一次:否则服务 1 秒超时停车
sleep(1)注意两个点:① 它不需要 sudo——碰硬件的是服务,行为只发消息,这是分层白捡的安全; ② 连发两次是因为服务的 1 秒看门狗:想连续走 2 秒,就得在 1 秒内续一次「心跳」31。
第二条行为 behavior_path.py——本章高潮,也是主线例子的落点。
书里先画一条路径(直线 1 秒 + 弯 + 直线…… 终点一个原地「胜利旋转」),
然后写了四个帮手函数,函数名就是人话:
def forward(client, seconds): # 双 0.8,走 seconds 秒
def drive_left(client, seconds): # [0.2, 0.8],左缓弯
def drive_right(client, seconds): # [0.8, 0.2],右缓弯
def spin_left(client, seconds): # [-0.8, 0.8],原地左转书里点了一句看似随意、实则关键的话:帮手函数用的词,就是我们描述行为时用的词32——
「先直走,再右弯」翻译成代码就是 forward(client, 1)、drive_right(client, 1)。
代码可读性的本质是词汇对齐。最后把路径写成一行行序列:
client = connect()
forward(client, 1); drive_right(client, 1); forward(client, 1); drive_left(client, 1)
forward(client, 1); drive_left(client, 1); forward(client, 1); spin_left(client, 1)跑起来的真实结果:「如果你那台和我的差不多,你会看到它走也走了、弯也弯了,
但转弯有的冲过头、有的没转够,整车还可能往一边飘。」33
于是进入调校(calibration):转弯时间从 1 秒改成 0.6 秒逼近 90°;
直行里把一侧改慢(如 [0.8, 0.7])对冲跑偏33。书里把话说得很透:
调校代码是对机器人脾气的一种粗糙校准(crude calibration); 你的机器人的校准值和我的会不一样;换了地面(木地板↔地毯)、 换了机器人、电池新旧,这些时间都要变33。
为什么飘? 书末给了完整清单:同样的电机速度也会有差异,轮子直径、轴的位置、 重量分布、地面滑溜程度、线阻、电机控制器输出——这些差异叠在一起, 时间驱动的机器人永远走不出完美的直线。出路预告:用编码器(第 11 章)34。
9. 作者的判断与证据
- 判断(本章最核心的架构决定): 把硬件包成 MQTT 服务、行为只发消息。
证据书里给了两条:其一,作者明说这套接口他已经移植到别的控制器上,
同一个
behavior_line.py能在他另一台机器人 ArmBot 上原样跑35——分层的回报是真金白银; 其二,服务还可以换成仿真器来接行为,不开真车也能测复杂行为36。 - 事实层: I²C 地址 0x17、速度范围 0–1、1883 端口、库名与包名,可上机复核71127。
- 诚实边界: 时间驱动的路径「不精确、不可重复」,书里明说,并把它变成 第 11 章编码器的引子34;密码明文问题主动挂账到第 18 章24。
10. 边界与局限
- 1 秒看门狗是双刃剑。 它保证了失联自停,也逼着行为代码每 1 秒内续消息—— 对连续控制(如循线)意味着高频重发;第 9 章以后你会看到这个张力怎么被处理。
all/stop语义在增长。 本章只有电机一个听众;到第 14、15 章,云台、视觉都会订阅它, 停的含义从「停车」变成「停一切」——服务间靠 topic 约定耦合,这是 MQTT 架构的隐性成本。- 没有 QoS、没有 retained 消息的讲解。 MQTT 的这些进阶特性本书一概不用, 消息可能丢失的语义没展开。
- 行为的时间精度被电机差异锁死。 本章的所有调校都是治标;想治本只能引入传感—— 这正是 Part 2 后半段的走向。
11. 可带走的
- 硬件项目的开机顺序:先通总线(I²C)→ 装库 → 用
i2cdetect确认看得见 → 跑最小测试7。 - Python 包装进虚拟环境,配一个
robotpython入口脚本,别污染系统 Python45。 atexit.register(stop_motors):机器人程序的第一行保险;机器人会「冲下桌子」, 退出必须收场10。- 差速驱动:全部驾驶 = 左右两个 -1…1 的数;原地转 = 一正一负,缓弯 = 两个不等的正数1619。
- MQTT 三词:broker(中转)、topic(分类)、publish/subscribe(收发);
#通配子层22。 - 服务层三个 topic 起步:
motors/wheels(JSON 两个数)、motors/stop、all/stop; 加一条「1 秒没消息自动停」26。 - last will:连接时预设的「遗言」,程序意外断开由 broker 代发
all/stop30。 - 行为层不碰硬件、不需要 sudo;好帮手函数的名字 = 描述行为的人话3132。
- 时间驱动的路径必然跑偏:电机差异、轮径、地面、电池都会改时间——调校是粗糙校准, 想精确只能上编码器3334。
12. 原文地图
| 主题 | 原书位置 |
|---|---|
| 先测电机的理由与垫高警告 | text/59-fm-using-python-to-test-robot-motors.txt:16(搜「iron out any problems」) |
| 开 I²C(raspi-config) | text/59-fm-using-python-to-test-robot-motors.txt:32(搜「raspi-config nonint do_i2c 0」) |
| 虚拟环境 | text/59-fm-using-python-to-test-robot-motors.txt:63(搜「virtual environment」) |
| 装 HAT 库与 smbus2 | text/59-fm-using-python-to-test-robot-motors.txt:121(搜「inventorhatmini」) |
| i2cdetect 与 0x17 | text/59-fm-using-python-to-test-robot-motors.txt:160(搜「i2cdetect」) · text/59-fm-using-python-to-test-robot-motors.txt:175(搜「address 17」) |
| atexit 保险 | text/60-fm-steering-a-robot.txt:12(搜「driving off tables」) |
| speed 范围与八成速 | text/60-fm-steering-a-robot.txt:27(搜「80% of the speed」) |
| 转向两大类 | text/60-fm-steering-a-robot.txt:46(搜「major categories」) |
| 差速/打滑转向 | text/60-fm-steering-a-robot.txt:109(搜「skid steering」) · text/60-fm-steering-a-robot.txt:145(搜「differential drive」) |
| 轴距轮距 | text/60-fm-steering-a-robot.txt:132(搜「track width」) |
| 原地转与缓弯代码 | text/60-fm-steering-a-robot.txt:182(搜「spin_robot」) |
| MQTT 是什么与三词 | text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:8(搜「Message Queue Telemetry Transport」) · text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:44(搜「central broker」) |
| 示例消息 [0.8,0.4] | text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:48(搜「publish messages on this topic」) |
| mosquitto 部署与明文密码警告 | text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:90(搜「mosquitto」) · text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:113(搜「plain text and are a placeholder」) |
| 两窗口验证 | text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:207(搜「mosquitto_sub」) |
| 三个 topic 与 1 秒超时 | text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:173(搜「no messages for 1 second」) |
| 服务代码结构 | text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:260(搜「message_callback_add」) |
| last will | text/62-fm-troubleshooting.txt:53(搜「a last message that the broker will send」) |
| 行为与 sudo | text/62-fm-troubleshooting.txt:79(搜「because the other service is handling the hardware」) |
| 帮手函数即人话 | text/63-fm-following-a-path-with-mqtt.txt:46(搜「same language we used to describe」) |
| 调校与粗糙校准 | text/63-fm-following-a-path-with-mqtt.txt:85(搜「crude form of calibration」) |
| 跑偏成因清单 | text/63-fm-following-a-path-with-mqtt.txt:101(搜「Motor speeds can vary」) |
| 分层回报:行为跨机器人复用 | text/63-fm-following-a-path-with-mqtt.txt:3(搜「behavior_line.py code on ArmBot」) |
| 章节总结 | text/64-fm-summary.txt:15(搜「layer for a behavior」) |
Footnotes
-
出处:「Using Python to test robot motors」第 16 段(
text/59-fm-using-python-to-test-robot-motors.txt:16,搜「iron out any problems」)。原文:做花哨动作之前,先装好电机并测试,确保工作、排除问题。 ↩ -
出处:「Using Python to test robot motors」第 223 段(
text/59-fm-using-python-to-test-robot-motors.txt:223,搜「wheels aren't in contact」)。原文 Tip:这一步把机器人垫高,轮子不接触桌面——动起来可能不朝预期方向;机器人电子产品的包装盒就很好用。 ↩ -
出处:「Using Python to test robot motors」第 32 段(
text/59-fm-using-python-to-test-robot-motors.txt:32,搜「raspi-config nonint do_i2c 0」)。原文:server.shell 跑 raspi-config,nonint 非交互执行第 0 号选项(启用 I²C);建议配置后 sudo reboot。 ↩ -
出处:「Using Python to test robot motors」第 63 段(
text/59-fm-using-python-to-test-robot-motors.txt:63,搜「virtual environment」)。原文:改系统包是坏主意,所以建 Python 虚拟环境(venv/Python env),在里面装所需、不干扰系统。 ↩ ↩2 -
出处:「Using Python to test robot motors」第 89 段(
text/59-fm-using-python-to-test-robot-motors.txt:89,搜「robotpython」)。原文:生成 /usr/local/bin/robotpython 脚本(bash,$@把参数传给 robotvenv 的 python3),mode 755 可执行;robotvenv 路径由 get_fact(Home) 拼出。 ↩ ↩2 -
出处:「Using Python to test robot motors」第 121 段(
text/59-fm-using-python-to-test-robot-motors.txt:121,搜「inventorhatmini」)。原文:装 python3-smbus2(HAT 库需要;SMBus 与 I²C 兼容)与 inventorhatmini 包到虚拟环境。 ↩ -
出处:「Using Python to test robot motors」第 160 段(
text/59-fm-using-python-to-test-robot-motors.txt:160,搜「i2cdetect」)与第 185 段(搜「address 17」)。原文:I²C 设备靠地址区分(如沿街门牌);扫描总线 1,17 即 HAT;十六进制用 0x 前缀;i2cdetect 证明连接应答并给出地址 0x17;扫不到的三条排错。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
出处:「Using Python to test robot motors」第 304 段(
text/59-fm-using-python-to-test-robot-motors.txt:304,搜「Imports are how Python code pulls in」)。原文:import 是 Python 引入其他代码库的方式;sleep 用于启停间延时;atexit 在程序退出时调用。 ↩ -
出处:「Using Python to test robot motors」第 315 段(
text/59-fm-using-python-to-test-robot-motors.txt:315,搜「makes an InventorHATMini object」)。原文:第一行创建 InventorHATMini 对象存入 board;随后基于 board 建 left_motor、right_motor 快捷方式(索引 1 和 0)。 ↩ -
出处:「Steering a robot」第 12 段(
text/60-fm-steering-a-robot.txt:12,搜「driving off tables」)。原文:atexit.register(stop_motors) 保证即使出错退出也停车;「Robots have a habit of driving off tables and into walls」,会损伤电机、控制器、电池。 ↩ ↩2 -
出处:「Steering a robot」第 27 段(
text/60-fm-steering-a-robot.txt:27,搜「80% of the speed」)。原文:这套控制器/库的速度范围 0 到 1,0.8 即八成速;enable 后设定速度,sleep(2) 让车先跑一会儿再停。 ↩ ↩2 -
出处:「Using Python to test robot motors」第 240 段(
text/59-fm-using-python-to-test-robot-motors.txt:240,搜「roughly forward」)。原文:机器人应大致向前;电池在尾部;可略偏,但不应倒退,两电机都该转——「这是提交 git 的好时机」。 ↩ -
出处:「Using Python to test robot motors」第 251 段(
text/59-fm-using-python-to-test-robot-motors.txt:251,搜「ImportError」)。原文表 6.1:ImportError=包没装;代码报错=查文本与 sudo robotpython;倒退=电机端红黑对调;灯亮不动=WAGO 线没压好;无灯=电池;严重跑偏=查接触与卡滞。 ↩ -
出处:「Steering a robot」第 46 段(
text/60-fm-steering-a-robot.txt:46,搜「major categories」)。原文:轮式车辆最常见转向技术分两大类——转向轮与固定轮。 ↩ -
出处:「Steering a robot」第 64 段(
text/60-fm-steering-a-robot.txt:64,搜「rack-and-pinion」)。原文:汽车式前轮转向(齿条齿轮或直线执行器)、铰接/货车式转向(整个前梁转,自制卡丁车常见);还有可横移的独立重定向轮、Ackermann(各轮转角不同)等变体。 ↩ ↩2 -
出处:「Steering a robot」第 109 段(
text/60-fm-steering-a-robot.txt:109,搜「skid steering」)与第 140 段(搜「differential drive」)。原文:固定轮式靠各轮相对速度定方向;典型即 skid steering;两轮+万向轮轴距等效为零(绕两轮共轴中点转),更准确地叫差速驱动;室内平地是正确选择。 ↩ ↩2 -
出处:「Steering a robot」第 122 段(
text/60-fm-steering-a-robot.txt:122,搜「turn on the spot」)。原文:打滑转向机械简单(每轮一个驱动电机即可);可原地转满 360°,转弯空间即机器人最宽/最长处;上履带必须用;代价是侧向拖滑摩擦与电机差异导致的跑偏。 ↩ ↩2 -
出处:「Steering a robot」第 132 段(
text/60-fm-steering-a-robot.txt:132,搜「track width」)。原文:四轮及以上要权衡轴距与轮距——轴距长则转向要斜着拽轮子、需更多扭矩,通常加宽轮距;火星车用混合方案(前后轮独立转向 + 两侧差速组合)。 ↩ ↩2 ↩3 -
出处:「Steering a robot」第 182 段(
text/60-fm-steering-a-robot.txt:182,搜「spin_robot」)。原文:一正一反转原地转(0.8 / -0.8),换符号换方向;0.4 / 0.8 则前行缓左转;时间控弯幅、速度控弯急。 ↩ ↩2 -
出处:「Making an MQTT service for the main breakout」第 6 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:6,搜「generally not a good idea」)。原文:把行为代码和处理硬件差异的底层代码混在一起通常不是好主意。 ↩ -
出处:「Making an MQTT service for the main breakout」第 23 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:23,搜「Separating code concerns」)。原文图 6.6:Panel A 混合代码难改;Panel B 分离系统中行为掌控电机与传感器接口;服务可在他机器人上外观一致,行为照旧工作;还能换仿真器测行为。 ↩ -
出处:「Making an MQTT service for the main breakout」第 8 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:8,搜「Message Queue Telemetry Transport」)与第 52 段(搜「central broker」)及第 48 段(搜「publish messages on this topic」)。原文:MQTT 为服务/硬件间发送轻量控制与数据消息而设计;经中央 broker,客户端可同机或跨网络(如手机);程序订阅(subscribe)topic(如 /motors/wheels),或发布(publish)消息(如 payload [0.8,0.4])。 ↩ ↩2 ↩3 -
出处:「Making an MQTT service for the main breakout」第 90 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:90,搜「mosquitto」)。原文:mosquitto(两个 T)是 broker 软件;同装 mosquitto-clients 与 python3-paho-mqtt;systemd 是 Linux 管理服务的工具,确保开机启动、改动后重启。 ↩ -
出处:「Making an MQTT service for the main breakout」第 113 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:113,搜「plain text and are a placeholder」)。原文 Note 警告:MQTT 密码当前明文、是占位符;不要把真密码放进要进 Git 的代码;第 18 章再加固。 ↩ ↩2 -
出处:「Making an MQTT service for the main breakout」第 207 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:207,搜「mosquitto_sub」)。原文:一窗 mosquitto_sub -t "hello/#" -u robot -P robot -v 订阅;另一窗 mosquitto_pub -t hello/robot -m "hello" 发布;订阅窗打印 hello/robot hello。 ↩ -
出处:「Making an MQTT service for the main breakout」第 173 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:173,搜「no messages for 1 second」)。原文:三 topic——motors/wheels(JSON 两个 -1.0..1.0 的数)、motors/stop(空)、all/stop(空);超 1 秒无消息自动停车,确保失联也停。 ↩ ↩2 -
出处:「Making an MQTT service for the main breakout」第 260 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:260,搜「message_callback_add」)。原文:每个 topic 配一个回调函数;json.loads 解码 payload;stop_motors 参数默认 None 使其兼作 atexit 处理器;connect("localhost", 1883) 连本机 broker 默认端口;loop_start 后台收发,主循环每 0.1 秒查超时。 ↩ ↩2 -
出处:「Making an MQTT service for the main breakout」第 284 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:284,搜「result code 0」)。原文:服务须 sudo 运行;看到 Connected with result code 0 即成功;非 0 查用户名密码与 mosquitto 部署。 ↩ -
出处:「Troubleshooting」第 22 段(
text/62-fm-troubleshooting.txt:22,搜「mqtt_behavior.py」)。原文:connect() 集中连接逻辑(含 will_set、loop 启动、等待 is_connected);publish_json 发 JSON 消息自动编码。 ↩ -
出处:「Troubleshooting」第 53 段(
text/62-fm-troubleshooting.txt:53,搜「a last message that the broker will send」)。原文:client.will_set 创建 MQTT last will——程序意外断开时,broker 替它发的最后消息(这里即 all/stop);与 atexit 类似,保证出事收场。 ↩ ↩2 -
出处:「Troubleshooting」第 79 段(
text/62-fm-troubleshooting.txt:79,搜「because the other service is handling the hardware」)。原文:behavior_line 连发两条 wheels 消息(1 秒间隔)——2 秒不续会触发服务超时;行为不需要 sudo,因为硬件由服务经手。 ↩ ↩2 -
出处:「Following a path with MQTT」第 46 段(
text/63-fm-following-a-path-with-mqtt.txt:46,搜「same language we used to describe」)。原文:帮手函数(forward/drive_left/drive_right/spin_left)用的正是描述行为时的语言;它们不进服务,因为别的行为可能需要更连续的控制。 ↩ ↩2 -
出处:「Following a path with MQTT」第 85 段(
text/63-fm-following-a-path-with-mqtt.txt:85,搜「crude form of calibration」)。原文:转弯可能冲头/不足、整车会飘;把弯的时间从 1 改 0.6 逼近 90°,直行[0.8,0.7]对冲飘;调校需耐心;换地面/换机器人/电池新旧都会改时间——这是「粗糙的校准」。 ↩ ↩2 ↩3 ↩4 -
出处:「Following a path with MQTT」第 101 段(
text/63-fm-following-a-path-with-mqtt.txt:101,搜「Motor speeds can vary」)。原文:电机速度(同厂)也有差异;轮径、轴位、重量分布、地面、线阻、控制器输出——叠加后靠这种方式得不到完美直线;后文用编码器/速度传感器开得更准。 ↩ ↩2 ↩3 -
出处:「Following a path with MQTT」第 3 段(
text/63-fm-following-a-path-with-mqtt.txt:3,搜「behavior_line.py code on ArmBot」)。原文:这套接口是通用的;behavior_line.py 能在 ArmBot(第 1 章那台)或作者其他 Pi 机器人上原样跑——大家都以八成速前进 2 秒。 ↩ -
出处:「Making an MQTT service for the main breakout」第 35 段(
text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:35,搜「simulations」)。原文:有建造者用接口把真实控制器换成机器人的仿真来测试复杂行为。 ↩