Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

week1

一、框架核心工作

本周完成了桌面端机器人 Demo 第一版的整体技术框架设计与基础代码搭建。目标是构建一个可扩展、可替换、便于多人协作开发的 Agent + VLA + LLM 解耦框架,使上层 Agent 可以根据用户自然语言输入,选择调用具身智能模型或通用大语言模型。

框架目录位于:

/home/czw1/ChenLong-Robot-Internship/agent_vla

整体架构设计如下:

用户输入
  |
  v
Agent API
  |
  +-- 机器人任务 -> VLA Service -> SmolVLA / LeRobot / 后续 Ascend NPU
  |
  +-- 通用问答 -> LLM Service -> OpenAI / Ollama / 本地大模型

本周实现了三个核心服务:

  1. Agent API

    • 作为系统统一入口,接收用户输入。
    • 根据用户输入判断是机器人任务还是通用问答。
    • 将机器人任务映射为固定的 skill_id,例如 ball_pick_v1
    • 根据路由结果调用 VLA Service 或 LLM Service。
  2. VLA Service

    • 负责管理机器人技能。
    • 提供 GET /v1/skillsPOST /v1/execute 接口。
    • 当前实现了 mock provider 和 lerobot_rollout provider。
    • 后续可以新增 ascend_vla provider,用于 Orange Pi AI Pro / Ascend NPU 推理。
  3. LLM Service

    • 负责通用语言模型能力。
    • 提供 POST /v1/generate 接口。
    • 当前实现了 mock provider。
    • 后续可以扩展 OpenAI、Ollama、Qwen、本地大模型等 provider。

当前核心代码结构如下:

agent_vla/
  configs/
    app.yaml                  # 全局配置:路由规则、技能、模型路径、服务地址

  src/
    common/                   # 共享 schema、配置读取、HTTP 工具
    agent_api/                # Agent 入口、意图路由、工具调用
    vla_service/              # VLA 服务和 provider
    llm_service/              # LLM 服务和 provider

  scripts/
    run_agent_api.sh
    run_vla_service.sh
    run_llm_service.sh
    run_mock_stack_test.sh
    smoke_test.sh

  docs/
    architecture.md
    contracts.md
    development_plan.md

框架中的核心解耦点是 src/common/schemas.py。Agent、VLA、LLM 三部分统一使用这里定义的请求和响应结构,包括:

UserRequest
AgentResponse
ToolCall
VLAExecuteRequest
VLAExecuteResponse
LLMGenerateRequest
LLMGenerateResponse

通过这种方式,Agent 不需要关心 VLA 底层是 LeRobot、PyTorch、ONNX、CANN 还是 Orange Pi NPU;也不需要关心 LLM 底层是 OpenAI、Ollama 还是本地模型。只要接口协议不变,底层 provider 可以独立替换。

本周还完成了 mock 栈测试:

cd /home/czw1/ChenLong-Robot-Internship/agent_vla
bash scripts/run_mock_stack_test.sh

测试结果:

  • 输入“帮我把球捡起来”可以正确路由到 robot.pick_ball,并调用 VLA Service。
  • 输入 pick ball 可以正确路由到 robot.pick_ball
  • 输入“北京有什么旅游景点”可以正确路由到 llm.general_qa,并调用 LLM Service。

整体来看,第一版框架已经具备以下能力:

  • Agent、VLA、LLM 三方解耦。
  • 支持通过配置注册机器人技能。
  • 支持通过 provider 替换 VLA 或 LLM 后端。
  • 支持 dry-run,便于在不控制真实机械臂的情况下联调接口。
  • 为后续 Orange Pi AI Pro / Ascend NPU 部署预留了扩展位置。

二、模型核心工作

本周完成了 LeRobot / SmolVLA 相关环境调试、硬件链路验证、数据采集参数设计和训练部署方案设计。

当前硬件与环境约定如下:

系统环境:WSL2 Ubuntu 24.04
Python 环境:conda env lerobot
机器人框架:LeRobot
具身模型:SmolVLA
从臂:SO101 follower,/dev/ttyACM0
主臂:SO101 leader,/dev/ttyACM1
摄像头:USB Camera,/dev/video0

已完成的硬件链路验证包括:

  • SO101 follower 从臂可以正常连接。
  • SO101 leader 主臂可以正常连接。
  • 主从臂 teleoperation 可以跑通。
  • USB 摄像头可以被 OpenCV 识别。
  • SmolVLA 相关依赖和 GPU 训练环境可用。

摄像头调试过程中确认了一个关键参数:摄像头实际回报 FPS 为 30。如果在 LeRobot 配置中写成 fps=10,会出现校验错误:

OpenCVCamera(0) failed to set fps=10 (actual_fps=30.0)

因此后续摄像头配置统一固定为:

--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 160, height: 120, fps: 30, fourcc: MJPG} }"

这里选择 160x120 是为了保证 WSL2 + USB 摄像头 + SmolVLA 推理链路足够稳定。摄像头硬件 FPS 使用 30,而数据集保存频率使用 --dataset.fps=10,两者不是同一个参数。

第一版 SmolVLA 任务定义为:

Pick up the ball and place it in the target area.

也就是桌面静态捡球并放到目标区域。当前设计的数据集名称为:

czw1/so101_ball_pick_v1

正式采集命令设计如下:

source /home/czw1/miniforge3/etc/profile.d/conda.sh
conda activate lerobot
cd /home/czw1/lerobot

lerobot-record \
  --robot.type=so101_follower \
  --robot.port=/dev/ttyACM0 \
  --robot.id=follower \
  --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 160, height: 120, fps: 30, fourcc: MJPG} }" \
  --teleop.type=so101_leader \
  --teleop.port=/dev/ttyACM1 \
  --teleop.id=leader \
  --dataset.repo_id=czw1/so101_ball_pick_v1 \
  --dataset.single_task="Pick up the ball and place it in the target area." \
  --dataset.fps=10 \
  --dataset.episode_time_s=15 \
  --dataset.reset_time_s=8 \
  --dataset.num_episodes=60 \
  --dataset.video=true \
  --dataset.push_to_hub=false \
  --display_data=false \
  --resume=false

后续如果需要追加数据,保持同一个 dataset.repo_id,并将:

--resume=true

打开即可。

为了保证后续可迭代,本周还确定了数据集版本规则:

czw1/so101_ball_pick_v1        静态桌面捡球并放到目标区
czw1/so101_ball_pick_v2        增加球位置、光照、背景、起始姿态变化
czw1/so101_ball_handover_v1    人手递球,机械臂夹住后放置
czw1/so101_ball_roll_v1        慢速滚动球任务
czw1/so101_light_switch_v1     关灯任务

训练方面,由于当前只有一个真实摄像头,而 SmolVLA base 默认期望多个摄像头输入,因此训练时需要做两个处理:

--rename_map="{ observation.images.front: observation.images.camera1 }"
--policy.empty_cameras=2

其中 rename_map 用于把数据集中的 front 摄像头映射到 SmolVLA 期望的 camera1empty_cameras=2 用于补齐另外两个空摄像头输入。

第一版训练命令设计如下:

source /home/czw1/miniforge3/etc/profile.d/conda.sh
conda activate lerobot
cd /home/czw1/lerobot

lerobot-train \
  --policy.path=lerobot/smolvla_base \
  --policy.repo_id=czw1/so101_ball_pick_smolvla_v1 \
  --policy.push_to_hub=false \
  --dataset.repo_id=czw1/so101_ball_pick_v1 \
  --rename_map="{ observation.images.front: observation.images.camera1 }" \
  --policy.empty_cameras=2 \
  --batch_size=1 \
  --steps=2000 \
  --save_freq=500 \
  --output_dir=outputs/train/so101_ball_pick_smolvla_v1 \
  --job_name=so101_ball_pick_smolvla_v1 \
  --policy.device=cuda \
  --wandb.enable=false

如果效果不够,继续训练到 5000 steps:

lerobot-train \
  --policy.path=lerobot/smolvla_base \
  --policy.repo_id=czw1/so101_ball_pick_smolvla_v1 \
  --policy.push_to_hub=false \
  --dataset.repo_id=czw1/so101_ball_pick_v1 \
  --rename_map="{ observation.images.front: observation.images.camera1 }" \
  --policy.empty_cameras=2 \
  --batch_size=1 \
  --steps=5000 \
  --save_freq=500 \
  --output_dir=outputs/train/so101_ball_pick_smolvla_v1 \
  --job_name=so101_ball_pick_smolvla_v1 \
  --policy.device=cuda \
  --wandb.enable=false \
  --resume=true

部署测试方案使用 lerobot-rollout。考虑到当前链路推理速度较慢,先使用低频安全测试:

lerobot-rollout \
  --robot.type=so101_follower \
  --robot.port=/dev/ttyACM0 \
  --robot.id=follower \
  --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 160, height: 120, fps: 30, fourcc: MJPG} }" \
  --policy.path=/home/czw1/lerobot/outputs/train/so101_ball_pick_smolvla_v1/checkpoints/002000/pretrained_model \
  --rename_map="{ observation.images.front: observation.images.camera1 }" \
  --task="Pick up the ball and place it in the target area." \
  --fps=1 \
  --duration=15 \
  --display_data=false \
  --return_to_initial_position=false

这一部分的核心成果是:确定了第一版 SmolVLA 模型的任务、采集参数、训练参数、继续训练方式和部署测试方式,并且将这些参数与 agent_vla 框架中的 VLA Service 对接方式统一起来。训练完成后,只需要把 checkpoint 路径写入 configs/app.yaml,即可通过 Agent 调用 VLA 模型。

此外,本周还调研了可复用的“捡物块 / pick-place”基础模型方案。选择先从捡球、捡物块这类任务入手,主要是因为 pick-place 是桌面机器人最基础、最通用的动作单元,很多后续任务都可以拆解为类似能力:

捡球 -> 抓取物体 -> 移动到目标区域 -> 放下
放到桌子上 -> 抓取物体 -> 移动到桌面指定位置 -> 放下
整理桌面 -> 多次执行 pick-place
人手递物 -> 接近物体 -> 闭合夹爪 -> 移动到目标区

当前调研结论是:第一版不直接追求大而全的多任务模型,而是先训练一个稳定的 so101_ball_pick_smolvla_v1,把它作为可复用的桌面抓取基础模型。后续如果捡球效果稳定,再把数据扩展为更通用的捡物块模型,例如:

czw1/so101_pick_object_v1

该数据集可以逐步加入:

  • 方块
  • 小积木
  • 轻量日用品
  • 不同颜色和不同材质的小物体

对应的任务描述可以统一为:

Pick up the object and place it in the target area.

这样可以把模型从“只会捡球”扩展为“能捡常见小物体”,提高后续复用价值。对于 Agent 框架来说,这类能力可以注册成一个通用 VLA 技能:

pick_object_v1:
  name: Pick up object
  task_prompt: "Pick up the object and place it in the target area."
  policy_path: /home/czw1/lerobot/outputs/train/so101_pick_object_smolvla_v1/checkpoints/...

后续 Agent 在理解用户指令时,可以把“捡球”“拿积木”“把这个东西放到目标区”等不同说法统一路由到同一个可复用的 pick_object_v1 技能,从而减少重复训练和重复开发成本。

三、下周主要工作

下周的主要目标是把当前框架从 mock 联调推进到真实模型闭环。

计划工作如下:

  1. 正式采集 czw1/so101_ball_pick_v1 数据集,目标至少 60 条高质量演示。
  2. 使用 SmolVLA base 训练 so101_ball_pick_smolvla_v1,先训练 2000 steps。
  3. 使用 lerobot-rollout 对捡球任务进行低频部署测试,统计基础成功率。
  4. 根据测试效果决定是否继续训练到 5000 steps 或补充采集数据。
  5. 将训练好的 checkpoint 路径写入 agent_vla/configs/app.yaml
  6. 将 VLA Service 从 mock provider 切换为 lerobot_rollout provider。
  7. 让 Agent 通过 /v1/chat 真实调用 VLA Service 执行捡球任务。
  8. 接入一个真实 LLM provider,例如 OpenAI、Ollama 或本地模型,用于替换当前 mock LLM。
  9. 优化 Agent 的意图识别方式,减少纯关键词匹配带来的误判。
  10. 在捡球任务稳定后,规划第二个技能,例如关灯或放到桌子上。

后续长期方向是将当前 WSL2 桌面端 Demo 逐步迁移到 Orange Pi AI Pro。初期可以让 Orange Pi 只运行 Agent,VLA 推理仍调用 WSL2 服务;等 SmolVLA 到 ONNX / Ascend NPU 的部署链路验证后,再新增 ascend_vla provider,使底层推理切换到 Orange Pi AI Pro / Ascend NPU,而 Agent 层接口保持不变。