有没有「反馈」?------ 伺服加减速 · AI Agent · 五个语法坑
2026-09-13 一天三块,其实是一条主线:有没有反馈 。
伺服靠编码器反馈闭环;Agent 靠「想→做→看」循环反馈;而那五个语法坑,恰恰是「没有反馈」的静默失败。
一、伺服 & 加减速(运动控制的底座)
1. 步进 vs 伺服 ------ 开环 vs 闭环
电机要走到一个位置,两种走法:
- 步进电机 = 开环 :你发一个脉冲,它就转一个固定角度。至于它到底转了没、转到位没,控制器不知道。
- 伺服电机 = 闭环 :它带一个编码器,实际位置会回报回来;没到位就补一点。
生活类比:步进像闭着眼走路 ------迈了一步,但不知道踩没踩到台阶;伺服像睁着眼走路------看到偏了,马上纠正。
开环(步进): 发脉冲 → 转 →(到此为止,没人回报)
闭环(伺服): 发指令 → 转 → 编码器回报实际位置 → 有偏差就补
为什么要闭环?检测设备要 微米级定位 + 高速往复,丢一步就偏了,所以用伺服。
2. 脉冲 vs 总线 ------ 谁来画曲线
上位机让电机动,有两种发号方式:
- 脉冲控制 :上位机自己算好整条速度曲线,一个脉冲一个脉冲地发。
- 总线控制 :上位机只写「目标坐标 + 触发」,剩下的曲线由 PLC / 驱动器自己画。
类比:脉冲像指挥家对着每个乐手逐个喊拍子 ;总线像指挥家只给「第几小节、什么速度」,乐团自己演奏。
(我们的设备是总线控制,所以上位机「管算什么」,PLC「管怎么动」。)
3. 梯形 vs S 曲线 ------ 加速度的平滑度
速度曲线有两种形状:
- 梯形 :加速段匀加速,到拐点加速度突然变(从 +a 一下跳到 0)→ 冲击、振动。
- S 曲线 :加速度平滑过渡,起步柔、快停也柔。
类比:梯形像急刹车 ------车猛地顿一下;S 曲线像老司机踩油门刹车------乘客感觉不到晃动。
梯形: 速度 ↗↗↗ → 突然 → → →(拐点是尖的)
S曲线:速度 ↗ 逐渐 ↗ ↗ 逐渐 → →(拐点是圆滑的)
4. 为什么必须加减速 ------ 惯性
电机 + 负载有质量,速度不能瞬间从 0 跳到最高,否则是巨大冲击、过冲、丢步。
所以一条铁律:发完指令 ≠ 到位。要等它真正到位(闭环回读),不能发完就撒手。
# 示意代码(伪代码,通用逻辑)
send_move(target=100) # 只是「发指令」
while not reached(target): # 轮询「到位了没」
wait(10) # 没到位就继续等
两个真实事故,正好对应这节的两个概念:
- 限位事故 :本该「先抬起挡板再移动」的防撞逻辑被注释掉了 → 撞上挡板。对应「安全联锁」------联锁是果(没联锁才出事),不是因。
- 命令超时 :上位机发了设置指令,设备回了「收到」,但读回实际值还是没变。对应「发指令 ≠ 生效」------要读回实际值确认。
二、AI Agent ------ 有手有循环的「大脑」
1. LLM vs Agent ------ 只会说 vs 会做
普通 LLM 只会说不会做 :你问它 999999×999999,它只能瞎猜,因为它没手去真算。
Agent = LLM + 工具 + 循环,它有手、还能反复做。
一句话:LLM 是大脑,Agent 是大脑 + 手 + 循环。
2. 核心循环 ReAct ------ 想 → 做 → 看
Agent 干活就靠一个 for 循环:
想:模型看「记忆」,决定要不要调工具
做:真要执行(按名字找到工具,传入参数,真算一遍)
看:把结果塞回「记忆」,再喂回模型 → 回到「想」
直到模型不再要工具,才把最终答案吐出来。
类比:像一个学徒做题------看题(想)→ 翻计算器(做)→ 看结果(看)→ 再决定下一步,直到做完。
3. 三部件
| 部件 | 是什么 | 类比 |
|---|---|---|
| 大脑 | LLM(语言模型) | 会思考的脑 |
| 手 | 工具(真函数 + 说明书 + 名字对应表) | 真能干活的手 |
| 记忆 | 不断 append 的消息列表 | 草稿纸 |
TOOLS = [ { "name": "calculator", "desc": "算四则运算", "args": {...} } ] # 说明书
def run_agent(question):
messages = [("user", question)] # 记忆,先放题目
for _ in range(MAX): # 循环 = 想→做→看
answer = model(messages) # 想
if answer.wants_tool: # 要干活?
result = TOOL_MAP[answer.tool](**answer.args) # 做
messages.append(result) # 看(塞回记忆)
else:
return answer # 不干了 → 交卷
4. .py 是「载体」,agent 是「内容」
别把两者画等号:
.py= 文件格式,装代码的盒子 (跟.js、.cs一样,只是容器)。- Agent = 代码跑起来之后的行为------那个「想→做→看」的循环。
所以:同一个 agent 可以用别的语言写;而一个只算 1+1 的 .py 也不是 agent(它没有循环)。
一句话:文件是盒子,agent 是盒子里代码干的事。
三、五个语法坑 ------ 都是「静默失败」
这五个错题有一个共同点:悄悄出错,不报错,所以特别阴。
1. 整数除法 ------ 砍掉小数还不吭声
两个整数相除,结果的小数部分被直接砍掉,不四舍五入、也不报错:
a = 5 / 2 # 5 和 2 都是整数 → 结果是 2,不是 2.5!
想保留小数,得先转成浮点:5 / 2.0 或 (float)5 / 2。
坑在哪 :2 和 2.5 差 25%,但程序一句话都不提醒,结果就悄悄错了。
2. catch 顺序 ------ 大网先兜住,小的够不着
异常处理里,子类必须写在前,父类写在后:
try:
...
except Exception: # ❌ 大网先兜住
except MySpecialError: # ❌ 永远够不着 → 编译直接报错
MySpecialError 是 Exception 的子类,大网已经接走了,后面的就是死代码 。
正确顺序:先写具体(子类),再写兜底(父类)。
3. 空集合 [] ≠ null ------ 有抽屉 vs 没抽屉
-
[]:有抽屉,但抽屉里没东西 (长度 0,能.Add、能遍历)。 -
null:连抽屉都没有(一碰就炸)。if result is None: # 判断「有没有抽屉」
if len(result) == 0: # 判断「抽屉里有没有东西」
坑在哪 :判断时得看清变量定义处默认给的是哪个,两个是不同的病。
4. 位运算拼数 ------ 十位 ×10 + 个位
把两个 16 位数拼成一个 32 位数:高 16 位「顶上去」,低 16 位「填进去」。
value = (high << 16) | low # 高位左移顶上去,再和低位拼
先记住 <<:x << N = x × 2ᴺ 。所以 high << 16 就是把 high 放大 2¹⁶ 倍、顶到高位。
类比:拼一个两位数 = 十位 × 10 + 个位 ------高位左移就像「十位 × 10」,| 就像「加个位」。
5. 字典覆盖 ------ 悄悄改写不报错
dict[key] = v # key 已存在 → 悄悄覆盖旧值,不报错
dict.Add(key, v) # key 已存在 → 抛异常,提醒你
坑在哪 :用 [] 赋值会静默覆盖,你都不知道原来的值被改了。
收个尾
今天三块,一根线串起来:
- 伺服 :发指令要读回到位了没(闭环)。
- Agent:想了要做、做了要看(循环反馈)。
- 语法坑:凡是「不报错的错误」都要特别小心(静默失败 = 没有反馈)。
有没有反馈,是判断一个东西靠不靠谱的第一眼。