做自动化的人都有个执念:确定性

在我的舒适区里,世界是严丝合缝的。电机转动 90 度就是 90 度,PID 的参数调好了,波形就是收敛的。但是,这种确定性也意味着死板

大二做垃圾分类比赛的时候,我写了大概几百行 if-else

  • 如果是红色 -> 舵机转到 A 点。
  • 如果是蓝色 -> 舵机转到 B 点。
  • 如果光线太暗识别失败 -> 呃,听天由命。

最近 LLM(大语言模型)火得一塌糊涂,我就在想:能不能把这玩意儿塞进我的机器人里,让它不再是只会执行死命令的“人工智障”?

于是,我开始了这个名为 “Robo-Agent” 的坑爹(划掉)探索项目。

🛠️ 硬件与架构:草台班子

我的目标很简单:对着机械臂说一句“把那个红色的方块放到蓝色盒子里”,它能自己搞定剩下的一切。

手头只有之前比赛剩下的破烂:

  • 本体:一个 6 自由度的机械臂(也就是 6 个串联的舵机)。
  • 大脑:树莓派 4B(负责跑 Agent 逻辑 + 视觉处理)。
  • 小脑:STM32F103(负责解算运动学,控制舵机 PWM)。
  • 眼睛:一个 30 块钱的 USB 摄像头。

架构设计上,我决定用 LangChain 来做胶水。

🧠 第一回合:让 LLM 学会“用工具”

刚开始,我天真地以为把 API 接进去就行了。结果 LLM 根本不知道怎么控制硬件。它会一本正经地给我输出一段 Python 代码,让我自己去跑。

不行,我要的是 Agent,是它自己去跑!

我引入了 ReAct (Reasoning + Acting) 模式。简单来说,就是教 LLM 一个思考范式:

思考 (Thought) -> 选择工具 (Action) -> 观察结果 (Observation) -> 再思考…

我给它定义了几个“工具 (Tools)”:

  1. scan_environment(): 调用 OpenCV 识别视野里的物体,返回一个 JSON 列表(比如 [{'obj': 'red_block', 'pos': [100, 200]}, ...])。
  2. calculate_ik(x, y, z): 逆运动学解算,把坐标变成 6 个舵机的角度。
  3. execute_move(angles): 通过串口把角度发给 STM32。
  4. gripper_control(state): 控制爪子开合。

遇到的第一个坑:幻觉

第一次测试,我输入:“把螺丝刀递给我”。 Agent 输出:

Thought: 用户想要螺丝刀。我需要先找到螺丝刀。 Action: scan_environment() Observation: [{'obj': 'red_block', ...}, {'obj': 'blue_box', ...}] (根本没有螺丝刀) Thought: 没找到螺丝刀,但我看那个红色方块挺像的,我就拿那个吧。 Action: execute_move(...)

我:??? 它居然学会了“指鹿为马”!

为了解决这个问题,我在 Prompt 里加了超强的约束(System Prompt):“严禁操作未识别到的物体!如果找不到目标,必须立刻停止并报错!” 调整后,它终于老实了:“对不起,我瞎了,没看到螺丝刀。”

🐢 第二回合:无法忍受的延迟

逻辑跑通了,但体验极差。

流程是这样的:

  1. 我说一句话 -> 录音转文字 (Whisper API) -> 2秒
  2. 发送给 LLM 思考 -> 3秒
  3. LLM 决定调用视觉工具 -> 拍照处理 -> 0.5秒
  4. 把视觉结果回传给 LLM,它再次思考下一步动作 -> 3秒

执行一个简单的抓取动作,中间的“发呆”时间加起来超过 10 秒。看着机械臂悬在半空中一动不动,像是在思考人生,我真的很想帮它掰过去。

优化尝试: 我试着把一些简单的逻辑从 LLM 剥离。比如“视觉识别”不作为工具被动调用,而是作为一个后台线程,实时维护一个 World State(世界状态)。 当 LLM 收到指令时,直接从本地内存读取当前环境状态,不需要再通过网络这一层。 这把响应时间压缩到了 5 秒左右——虽然还是很慢,但至少能用了。

✨ 第三回合:它好像真的“懂”了

最让我震撼的一刻,是我测试复杂指令的时候。

我故意刁难它:“把所有不是红色的东西都扔掉。”

我没有写过“扔掉”这个函数,也没有定义“不是红色”的逻辑。 但是 Agent 的思维链(Chain of Thought)是这样的:

  1. Thought: 用户想清理非红色物体。我需要先扫描环境。
  2. Action: scan_environment() -> 得到 [红块, 蓝块, 绿球]
  3. Thought: 蓝块和绿球不是红色的。我需要依次抓取它们。对于“扔掉”这个动作,我可以把它们移动到视野外的区域(比如坐标 [0, 0, 0])然后松开爪子。
  4. Action: pick_and_place('blue_block', target=[0,0,0])

看到它把蓝块抓起来,移动到边缘,松手,然后又去抓绿球。 那一刻,我鸡皮疙瘩都起来了。 它不是在执行代码,它是在理解意图。

📝 总结与反思

这次折腾让我对“智能”有了新的理解。

  • 传统控制小脑,负责精准、快速、稳定(走直线、不抖动)。
  • AI Agent大脑,负责模糊、决策、泛化(理解意图、处理未知)。

现在的 Agent 还很笨重,延迟高,容易发疯。但看着它第一次自主规划出一条我没写过的路径时,我感觉自己窥见了未来机器人的一角。

接下来的计划?我打算试试在树莓派上部署 Llama-3-8B 的量化版本,看看能不能把“大脑”也搬到本地,彻底告别网络延迟。


代码已开源在我的 GitHub,欢迎来提 Issue (或者帮我修 Bug) 😂