编程语言研究书库

让机器人动起来 — 电机测试、差速转向与 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/wheelsJSON 两个数,-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. 可带走的

  1. 硬件项目的开机顺序:先通总线(I²C)→ 装库 → 用 i2cdetect 确认看得见 → 跑最小测试7。
  2. Python 包装进虚拟环境,配一个 robotpython 入口脚本,别污染系统 Python45。
  3. atexit.register(stop_motors):机器人程序的第一行保险;机器人会「冲下桌子」, 退出必须收场10。
  4. 差速驱动:全部驾驶 = 左右两个 -1…1 的数;原地转 = 一正一负,缓弯 = 两个不等的正数1619。
  5. MQTT 三词:broker(中转)、topic(分类)、publish/subscribe(收发);# 通配子层22。
  6. 服务层三个 topic 起步:motors/wheels(JSON 两个数)、motors/stop、all/stop; 加一条「1 秒没消息自动停」26。
  7. last will:连接时预设的「遗言」,程序意外断开由 broker 代发 all/stop30。
  8. 行为层不碰硬件、不需要 sudo;好帮手函数的名字 = 描述行为的人话3132。
  9. 时间驱动的路径必然跑偏:电机差异、轮径、地面、电池都会改时间——调校是粗糙校准, 想精确只能上编码器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 库与 smbus2text/59-fm-using-python-to-test-robot-motors.txt:121(搜「inventorhatmini」)
i2cdetect 与 0x17text/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 willtext/62-fm-troubleshooting.txt:53(搜「a last message that the broker will send」)
行为与 sudotext/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

  1. 出处:「Using Python to test robot motors」第 16 段(text/59-fm-using-python-to-test-robot-motors.txt:16,搜「iron out any problems」)。原文:做花哨动作之前,先装好电机并测试,确保工作、排除问题。 ↩

  2. 出处:「Using Python to test robot motors」第 223 段(text/59-fm-using-python-to-test-robot-motors.txt:223,搜「wheels aren't in contact」)。原文 Tip:这一步把机器人垫高,轮子不接触桌面——动起来可能不朝预期方向;机器人电子产品的包装盒就很好用。 ↩

  3. 出处:「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。 ↩

  4. 出处:「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

  5. 出处:「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

  6. 出处:「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 包到虚拟环境。 ↩

  7. 出处:「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

  8. 出处:「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 在程序退出时调用。 ↩

  9. 出处:「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)。 ↩

  10. 出处:「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

  11. 出处:「Steering a robot」第 27 段(text/60-fm-steering-a-robot.txt:27,搜「80% of the speed」)。原文:这套控制器/库的速度范围 0 到 1,0.8 即八成速;enable 后设定速度,sleep(2) 让车先跑一会儿再停。 ↩ ↩2

  12. 出处:「Using Python to test robot motors」第 240 段(text/59-fm-using-python-to-test-robot-motors.txt:240,搜「roughly forward」)。原文:机器人应大致向前;电池在尾部;可略偏,但不应倒退,两电机都该转——「这是提交 git 的好时机」。 ↩

  13. 出处:「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 线没压好;无灯=电池;严重跑偏=查接触与卡滞。 ↩

  14. 出处:「Steering a robot」第 46 段(text/60-fm-steering-a-robot.txt:46,搜「major categories」)。原文:轮式车辆最常见转向技术分两大类——转向轮与固定轮。 ↩

  15. 出处:「Steering a robot」第 64 段(text/60-fm-steering-a-robot.txt:64,搜「rack-and-pinion」)。原文:汽车式前轮转向(齿条齿轮或直线执行器)、铰接/货车式转向(整个前梁转,自制卡丁车常见);还有可横移的独立重定向轮、Ackermann(各轮转角不同)等变体。 ↩ ↩2

  16. 出处:「Steering a robot」第 109 段(text/60-fm-steering-a-robot.txt:109,搜「skid steering」)与第 140 段(搜「differential drive」)。原文:固定轮式靠各轮相对速度定方向;典型即 skid steering;两轮+万向轮轴距等效为零(绕两轮共轴中点转),更准确地叫差速驱动;室内平地是正确选择。 ↩ ↩2

  17. 出处:「Steering a robot」第 122 段(text/60-fm-steering-a-robot.txt:122,搜「turn on the spot」)。原文:打滑转向机械简单(每轮一个驱动电机即可);可原地转满 360°,转弯空间即机器人最宽/最长处;上履带必须用;代价是侧向拖滑摩擦与电机差异导致的跑偏。 ↩ ↩2

  18. 出处:「Steering a robot」第 132 段(text/60-fm-steering-a-robot.txt:132,搜「track width」)。原文:四轮及以上要权衡轴距与轮距——轴距长则转向要斜着拽轮子、需更多扭矩,通常加宽轮距;火星车用混合方案(前后轮独立转向 + 两侧差速组合)。 ↩ ↩2 ↩3

  19. 出处:「Steering a robot」第 182 段(text/60-fm-steering-a-robot.txt:182,搜「spin_robot」)。原文:一正一反转原地转(0.8 / -0.8),换符号换方向;0.4 / 0.8 则前行缓左转;时间控弯幅、速度控弯急。 ↩ ↩2

  20. 出处:「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」)。原文:把行为代码和处理硬件差异的底层代码混在一起通常不是好主意。 ↩

  21. 出处:「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 分离系统中行为掌控电机与传感器接口;服务可在他机器人上外观一致,行为照旧工作;还能换仿真器测行为。 ↩

  22. 出处:「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

  23. 出处:「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 管理服务的工具,确保开机启动、改动后重启。 ↩

  24. 出处:「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

  25. 出处:「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。 ↩

  26. 出处:「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

  27. 出处:「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

  28. 出处:「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 部署。 ↩

  29. 出处:「Troubleshooting」第 22 段(text/62-fm-troubleshooting.txt:22,搜「mqtt_behavior.py」)。原文:connect() 集中连接逻辑(含 will_set、loop 启动、等待 is_connected);publish_json 发 JSON 消息自动编码。 ↩

  30. 出处:「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

  31. 出处:「Troubleshooting」第 79 段(text/62-fm-troubleshooting.txt:79,搜「because the other service is handling the hardware」)。原文:behavior_line 连发两条 wheels 消息(1 秒间隔)——2 秒不续会触发服务超时;行为不需要 sudo,因为硬件由服务经手。 ↩ ↩2

  32. 出处:「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

  33. 出处:「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

  34. 出处:「Following a path with MQTT」第 101 段(text/63-fm-following-a-path-with-mqtt.txt:101,搜「Motor speeds can vary」)。原文:电机速度(同厂)也有差异;轮径、轴位、重量分布、地面、线阻、控制器输出——叠加后靠这种方式得不到完美直线;后文用编码器/速度传感器开得更准。 ↩ ↩2 ↩3

  35. 出处:「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 秒。 ↩

  36. 出处:「Making an MQTT service for the main breakout」第 35 段(text/61-fm-making-an-mqtt-service-for-the-main-breakout.txt:35,搜「simulations」)。原文:有建造者用接口把真实控制器换成机器人的仿真来测试复杂行为。 ↩

On this page