基于 VisionFive 2、YOLOE、视觉闭环、Agent API 与 MCP 的机械臂工程实践。
识别目标 → 持续跟踪 → 调整方向 → 分阶段接近
→ 近距离确认 → 抓取 → 抬升 → 搬运 / 放置先上DEMO视频:
一、整套系统用了什么
主控使用 StarFive VisionFive 2,搭载赛昉 JH7110 RISC-V SoC,板端运行 Ubuntu 24.04。摄像头使用 Logitech C270,通过 USB 接入;机械臂是一套六舵机结构,通过 CH340 串口适配器连接控制板。
另外还有一台 Windows 主机,主要承担计算量更大的目标识别,以及 Telegram 和大语言模型相关的上层交互。VisionFive 2 则保留所有真实硬件资源和物理动作:摄像头、串口、任务状态、视觉闭环和机械臂控制都在板端统一管理。
Windows 主机
├─ YOLOE 目标识别
├─ Telegram / LLM
└─ MCP Client
│ 局域网
▼
VisionFive 2
├─ Robot Agent API
├─ RobotSession
├─ Vision / PracticalFSM
└─ ArmDriver → CH340 → 六舵机机械臂二、视觉识别分成两条路线
已校准颜色 → 板端 HSV → TargetObservation
普通目标 → Windows YOLOE → TargetObservation
↓
统一控制器三、机械臂不是执行一段脚本,而是在不断重新看目标
SEARCH → TRACK → APPROACH → GRAB → MANUAL_PLACE四、接近目标时不断重复“移动—观察”
移动一点
↓
读取新的摄像头帧
↓
重新判断目标位置
↓
决定继续接近 / 重新对准 / 停止五、目标短暂消失以后怎么找回来
这不是目标在世界坐标中的速度估计,只是一个短时间重新捕获策略。它的目的也不是预测很远,而是在目标刚刚离开画面时,别立刻从完全相反的方向重新扫描。
六、真正难调的是最后几厘米
机械臂距离目标较远时反而比较好处理。进入夹爪附近以后,目标会越来越靠近画面底部,机械臂再往前一点,摄像头看到的场景就可能发生很大变化。
所以近距离抓取单独增加了预抓取动作。中等近距离先做一次很小的 reach,再判断;极近距离会连续进行两次“小幅接近 + 新鲜帧确认”。参考控制路径类似下面这样:
500/125
→ 507/126
→ 新鲜目标帧
→ 514/126
→ 新鲜目标帧
→ GRAB这里的 500/125、514/126 都是软件下发的舵机命令空间参数,不是厘米,也不是编码器实时测得的关节角。真正重要的是每一步之后重新看一次:如果目标已经不可见,就不继续把后面的近距离动作盲目执行完。
七、抓住以后先抬升,再做水平移动
夹爪闭合后,物体可能还贴着桌面。如果马上回收机械臂或者旋转底座,方块很容易擦过桌面。所以 GRAB 后的动作顺序是先保持 reach,单独抬升,再带着这个 carry-height 回收和转动。
闭合夹爪
→ 保持 ID5,抬升 ID4
→ 保持抬升状态回收
→ 保持抬升状态回正 / 搬运
→ MANUAL_PLACE当前参考 carry-height 是 ID4 相对增加 50 个命令单位。它同样只是当前机械安装下的经验控制参数,不把它描述成实际抬升了多少厘米。
放置时也区分 PLACE 和 RELEASE。PLACE 会先去掉搬运高度,再等待下降、张开夹爪、回收到参考姿态;RELEASE 则只是在当前位置直接松开夹爪,不附带自动回收和归位。这样上层调用时语义比较明确。
八、把机械臂封装成一个有状态的 Robot Agent
为了让命令行、MCP、Telegram 或其他程序都能使用同一套机械臂,我没有让每个入口各自去打开摄像头和串口,而是在 VisionFive 2 上运行一个 Robot Agent。它通过 HTTP/JSON 暴露一组比较清晰的任务接口:
GET /health
GET /v1/status
POST /v1/grab
POST /v1/move
POST /v1/release
POST /v1/place
POST /v1/home
POST /v1/stopAgent 本身不重新实现一套机械臂控制器。它下面只有一个 RobotSession,所有真实硬件任务都从这里进入。RobotSession 负责摄像头和串口的打开与关闭、硬件独占、目标解析、感知路径选择、自动抓取 worker、放置命令队列、STOP/HOME 协调以及运行状态管理。
这样做的好处是,不论上层入口是什么,最后使用的都是同一套物理运行时。不会出现 Telegram 启动一套控制器、CLI 又启动另一套控制器,然后同时抢摄像头和串口。
Agent 不只是“发一个动作”,还维护任务状态
机器人任务通常不是一次 HTTP 请求就能同步完成。例如 POST /v1/grab 返回成功,只表示抓取任务已经被接受,不表示物体已经抓到。客户端需要继续通过 GET /v1/status 查看状态。
IDLE → PICKING → PLACEMENT_READY → PLACING → COMPLETE
异常分支:STOPPED / ERROR状态里还会保留 mission_id、target、fsm_state、last_error 等信息。对于上层程序来说,机械臂因此不再只是一个“发完串口命令就不知道发生了什么”的设备,而是一个可以查询任务生命周期的服务。
软件状态和物理事实要分开
状态接口里特意区分 holding_assumed 和 physical_grasp_verified。holding_assumed=true 只表示控制流程已经执行到了持物阶段;当前参考硬件没有独立的抓取确认传感器,因此不能据此声称物体已经被传感器确认夹住。
同样,程序能够确定自己发出了什么 pulse、FSM 处在哪个状态,但不能把这些软件记录当成真实关节角、末端 XYZ 或抓取力。这个边界在 Agent 层统一处理,上层接口就不会把没有测量到的量包装成“机器人真实状态”。
网络重试也不能让机械臂重复动作
普通 Web 请求失败以后重试通常没什么问题,但机器人动作不一样。假设“向左移动一步”已经执行,只是返回包刚好丢了,客户端如果直接重试,机械臂就可能多走一步。
因此动作请求可以带 request_id。Agent 会在有界的进程内 replay cache 中保存已完成请求的结果;同一个请求因为网络问题重新发送时,可以返回之前的结果,而不是故意再执行一次相同的物理动作。真正的新动作则必须使用新的 request_id。
九、MCP 是 Agent 上面的一层语义接口
Agent API 解决的是“程序怎么调用机械臂”,但 HTTP 路径、JSON 字段这些还是比较偏程序接口。如果希望让大语言模型或者 Agent 框架使用,就再往上加一层 MCP,把底层动作收敛成有限的语义工具。
robot_status
robot_grab
robot_move
robot_release
robot_place
robot_home
robot_stopCP 不直接接串口,也不维护第二套 FSM。比如 robot_grab("red cube") 最后仍然调用 Agent 的 /v1/grab;robot_move(direction="left", steps=2) 也只是经过语义参数检查以后调用对应的 Agent 接口。
这一层最重要的作用是限制能力边界。MCP 不暴露原始 servo ID、任意 pulse、任意串口报文、Shell、SSH 或任意 Python,也没有随意给出绝对 XYZ 轨迹的接口。模型能决定的是“抓哪个”“往哪边移动”“什么时候放下”,不是底层舵机怎么转。
MCP 里的移动是“语义步长”,不是伪装成厘米
robot_move 当前主要提供 left、right、inward、farther 四个方向,并使用项目内部校准过的 semantic step。以参考配置为例,左右一个 semantic step 对应 ID6 增减 24 pulse;前后则由 ID5 和 ID4 联动完成。
这些单位属于机械臂命令空间,不写成“移动 3 cm”或者“转 10°”,因为当前硬件没有足够的传感器反馈来保证这种说法。MCP 也会检查请求到底执行了多少,如果因为机械运动边界只完成了一部分,就不会把它当成完整动作继续执行后面的计划。
十、Telegram 和大语言模型只是最上层入口
Telegram message
↓
text LLM
↓
semantic plan JSON
↓
deterministic validation
↓
MCP tools → Agent API → RobotSession → 机械臂十一、当前软件结构
roboticarm/
├─ vision/ 目标解析与视觉观测
├─ inference/ YOLOE Client
├─ hardware/ Camera / Serial / ArmDriver
├─ control/ PracticalFSM
├─ runtime/ RobotSession
├─ agent_api/ Robot Agent HTTP API
└─ integrations/
├─ mcp/ MCP 语义工具
└─ telegram/ Telegram + LLM十二、这套系统能确定什么,不能确定什么
可以确定:发送过的动作命令、FSM 状态、任务状态、软件记录的 pulse、当前是否进入 placement-ready。
不能可靠测量:实际关节角、精确末端 XYZ、抓取力、物体是否还在夹爪内、放开后是否已经完全脱离,以及任意路径是否绝对无碰撞。
十三、RISC-V 在这套系统里负责什么
用户 / Telegram
↓
LLM:理解任务
↓
MCP:限制成语义工具
↓
Agent API:维护机器人任务和状态
↓
VisionFive 2:视觉闭环 + 硬件控制
↓
机械臂:执行动作这种结构并不要求一块 RISC-V 板子把所有 AI 工作全部包下来。对机器人来说,谁负责推理并不是唯一重点,真正需要理清的是哪一层可以做高延迟决策,哪一层必须保留确定性、低延迟和硬件独占。
目前这套系统已经把目标识别、持续跟踪、目标丢失后的重新捕获、分阶段接近、近距离新鲜帧确认、抓取、抬升、搬运和放置串到了一条完整控制链里。上层又通过 Agent API 和 MCP 把硬件控制封装成有限、可查询、可复用的语义动作,Telegram 只是其中一个交互入口。
还有一些问题值得继续做,比如更可靠的抓取结果确认、真实关节位置反馈、更完整的空间建模,以及移动过程中的定位精度。不过对于一套普通六舵机机械臂和腕部 USB 摄像头来说,从“看到一个目标”到“让不同上层程序安全地调用真实机械臂”,现在已经形成了一套比较完整的工程结构。
参考仓库:
https://github.com/vinaro-sopic/roboticarm
作者介绍
周匡正,筑波大学情报学学位项目在读硕士生。主要研究方向为计算机视觉与多模态图像处理。关注 RISC-V 生态与具身智能技术的结合与落地。
林琛博,纽卡斯尔大学工程学院电气与电子工程专业在读本科生。关注 RISC-V、嵌入式系统、计算机视觉与智能控制。
本篇文章由两位作者共同完成。
关于上海开放处理器产业创新中心
上海开放处理器产业创新中心(简称:创新中心)于2024年9月在上海浦东张江正式成立,坐落于集创路52号创芯天地一号楼15楼。创新中心是由国内领先的半导体设计公司芯原股份、芯来科技,以及达摩院(上海)共同发起设立的一家民办非企业单位。
创新中心旨在协同RISC-V产业链上下游企业,包括芯片设计企业、IP供应商、系统厂商、软件开发商、终端厂商以及顶尖科研院所,共建RISC-V关键共性技术平台,用开放的硬件平台构建开源的软件生态,从而有力促进RISC-V技术的产业化应用和商业化落地。
此外,创新中心还将与国内重点高校展开深度合作,通过共建RISC-V特色课程体系、设立联合实验室、建立实训实习基地等方式,系统性地培养具备RISC-V专业技能的复合型人才,为产业发展提供持续的人才支撑。