来源:https://zhuanlan.zhihu.com/p/1895609105902704289

前段时间一直在看具身智能的一些技术,包括openvlardt等模型,也想着在一个实际的设备上进行一些测试,当然预算有限,通过调研发现了一个较好的开源项目叫lerobot,可以在千元级满足测试需求。

一,材料购买

lerobot是hugging face的一个开源项目,它包含一些具身智能所需的低成本硬件,包括6/7自由度的机械臂,底盘小车等,这些硬件可以通过3D打印获得,所以具备较低的成本,我在闲鱼和京东上购买了相关配件,明细如下:

名称明细价格来源
so100机械臂主从机械臂一套+100万像素相机一个
(主臂为虚拟舵机,只能人工控制,用于人工操作采集数据,从臂为有动力的舵机,模型训练完成后,由从臂进行自动化操作演示;按惯例在抓手上安装一个相机,所以就和机械臂一起购买,若没有在抓手上安装相机的需求,可以单独从电商购买也可以)
1180+80(包含200元安装费用,若自行安装可省)闲鱼
相机100万像素相机一个,一般模拟人眼的视角,放在高处92京东

lerobot项目中的机械臂也有7自由度的,还有移动底坐,我根据需要买的是6自由度的设备叫so-100。除了硬件,lerobot项目中也有丰富的代码,可以实现机械臂的标定,数据采集,可视化,模型训练,模型测试等,所以还是非常方便的。

lerobot项目相关资料:

lerobot机械臂中文资料docs.qq.com/sheet/DQnFNc0pVdUV2bEpZ?tab=BB08J2

软件:

GitHub – huggingface/lerobot: 🤗 LeRobot: Making AI for Robotics more accessible with end-to-end learninggithub.com/huggingface/lerobot

二,硬件配置

机械臂的安装可能需要花费一些时间(若没有购买安装服务的话),上面的文档中有一些安装视频可参考。本节主要讲安装前后的一些配置工作。

舵机的ID设置

一个臂有6个自由度,就是6个舵机,所以需要根据顺序进行编号,主臂和从臂都是1-6,一般建议在装配之前进行配置,如果机械臂安装好再配置,会很麻烦,可能要部分折掉才能把数据线头取出来。下面这篇文章写得较好可参考(此文中高亮提示:“安装过程中不要去转动齿轮!”,可以忽略,因为安装过程中很难保证不转动舵机,可以通过后续的中位设置来调整)。

https://blog.csdn.net/u010634066/article/details/145903175?spm=1001.2014.3001.5506blog.csdn.net/u010634066/article/details/145903175?spm=1001.2014.3001.5506

官方文档“10_use_so100.md”中也有详细的步骤,请大家结合自身的硬件型号/官方文档/网友文章多方对比,避免过于相信某一个来源,因为大家的硬件型号可能都不太一样,所以建议多对比一下各个文档。有些文章中还有拆舵机齿轮的操作,我买的这一款是不需要的。

有很多文章中,包括官方文档中在配置舵机id时,使用的都是命令行,因为我买的产品 ,主臂用的是虚拟舵机(表格中有讲),不是一个完备的舵机,虚拟舵机只能输出信号,不能输入信号控制它,所以价格会便宜一些。但问题是虚拟舵机通过脚本来配置会失败,需要通过官方的软件进行配置才行,链接如下:

软件-深圳飞特模型有限公司www.feetech.cn/software.html

下载1.9.8.3在windows下面运行,这个视频有详细讲解:

https://www.bilibili.com/video/BV1ccwpebEn8?spm_id_from=333.788.recommend_more_video.14&vd_source=97eef304c85db18d718168a0857d9d2cwww.bilibili.com/video/BV1ccwpebEn8?spm_id_from=333.788.recommend_more_video.14&vd_source=97eef304c85db18d718168a0857d9d2c

中位配置

主从机械臂安装后,需要进行中位配置,因为某个舵机的默认输出范围是1-4096,但在安装过程中,舵机可能被转动过,例如当整个机械臂底部的朝向物理上居中时,它正常应该输出2048,但大概率经过安装后,它不是输出的2048,这时候可以通过下面视频中的方法进行中位配置,配置成功的标志是1-6各关节在视频中所示的角度时,都稳定输出2048。注意,配置后可能不会立即生效,所以看起来好像没成功,需要重启一下。若还不行,请咨询商家。

https://www.bilibili.com/video/BV1n7wHeZEdT?spm_id_from=333.788.recommend_more_video.-1&vd_source=97eef304c85db18d718168a0857d9d2cwww.bilibili.com/video/BV1n7wHeZEdT?spm_id_from=333.788.recommend_more_video.-1&vd_source=97eef304c85db18d718168a0857d9d2c

整体上硬件成功安装+配置的标志应该是两个机械臂都能在软件(官方那个)中找到1-6舵机,并且在转动这2×6=12个关节时,在软件中可以看到每个关节的值在a->b之间连续变化,大部分关节都不能360度转,所以每个关节的a,b值不一样,大部分应该在1000->3000中间。

相机位置

我把相机的位置也放在了硬件章节中。我的机械臂共有2个相机,一个默认是全局视野,用于算法在全局视野中定位目标物体; 一个是局部视野,一般用于抓手的抓取过程中的定位,所以很多演示是直接安装在抓手上。我试过3组相机摆放位置,发现方案3模型训练出来效果最好,不过只是目前经验,与模型选型,训练数据量都有关系,所以仅供参考。

全局相机位置局部相机位置备注
桌面高处,机械臂左侧抓手上左侧导致机械臂在右边时,容易遮挡目标物体,导致相机看不见目标物体
桌面高处,机械臂正上方抓手上抓手在稍微偏离目标物体时,导致相机看不见目标物体
桌面高处,机械臂正上方桌面低处,机械臂旁边固定2个相机视野中都会有目标物体

图片(上面白色为高处相机,右侧支架为局部相机):

三,软件安装与校准

软件安装与端口配置

软件环境安装与端口配置,上面列的官方文档和网友文章写得都差不多,可自行参考。不再赘述。不过我这边在安装过程中,遇到av安装失败的问题,后面排查后发现默认安装的ffmpeg是7.1.1,后来换成低版本的6.1.1就可以。另外,官方文档10_use_so100.md和README.md的环境安装过程稍微有一些不一样,建议先README.md再10_use_so100.md,相关环境安装命令全部执行。

conda list|grep ffmpeg
#ffmpeg                    6.1.1

主从校准

主从校准的目标是可以由主机械臂控制从机械臂,它们之间没有明显的diff,校准的目标是方便数据采集。例如主机械臂向左旋转15度,从机械臂应该也是向左旋转15度,人工控制过程中,没有发现明显的偏差就行。校准成功的标志是:1,从臂跟随主臂没有明显偏差。2,各关节的活动范围也没有明显偏差,例如不能出现主臂可以向左旋转90度,但从臂只能向左旋转45度。官方文档和网友文章写得已经很详细了,请参考。

校准后,在项目目录中的.cache/calibration/so100/文件夹下面会生成2个校准文件,2个文件格式是一样的,如下:

{"homing_offset": [-1984, 3075, -1052, -1975, 2025, -2455], 
"drive_mode": [0, 1, 0, 0, 1, 0], 
"start_pos": [2060, 3061, 1039, 1898, 2023, 2087], 
"end_pos": [3008, -2051, 2076, 2999, -1001, 3479], 
"calib_mode": ["DEGREE", "DEGREE", "DEGREE", "DEGREE", "DEGREE", "LINEAR"], 
"motor_names": ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"]}

从原理上讲(若不关心原理,可以忽略此节),校准文件的核心就是对每个关节定义了一个零位(校准过程中的零位就是软件层面定义的零位)和正方向,上面讲的输出2048只是硬件层面的输出,在软件层面有软件层面的定义与量纲。例如某个关节在定义的零位时,硬件输出值是1984,那么homing_offset对应的校准值就是-1984,目标就是把硬件值映射到0。正方向就是舵机往哪个方向旋转是正向,正向0,负向为1,上面的drive_mode就是这个值。通过上面的标定文件,可以将舵机物理输出值(一般是0-4096)转换成软件内部用的值(一般是-180~180度)。

举例来讲,若一号舵机硬件值是2048,转换过程:(2048-1984)/2048 * 180度 = 5.625度。若二号舵机的硬件值是868,转换过程:(系数 * 868 + 3075 )/2048 * 180度 = 193度,drive_mode==0时,系数是1, drive_mode==1时,系数是-1。

start_pos和end_pos是校准过程中,关节的活动范围,校准完成后一般就不用了(6号关节除外)。calib_mode的DEGREE就代表着上面的角度转换过程。LINEAR代表的是另一种转换过程,对6号关节,若物理值是2048,转换过程:(2048-2087)/(3479-2087) * 100。

整体的转换过程参考:FeetechMotorsBus:apply_calibration函数。

四,数据记录与模型训练/测试

此部分参考官方文档即可,非常详细了。

数据采集后格式如下:

.
├── data
│   └── chunk-000
│       ├── episode_000000.parquet
│       ├── episode_000001.parquet
│       ├── episode_000002.parquet
│       ...
├── meta
│   ├── episodes.jsonl
│   ├── episodes_stats.jsonl
│   ├── info.json
│   └── tasks.jsonl
└── videos
    └── chunk-000
        ├── observation.images.laptop
        │   ├── episode_000000.mp4
        │   ├── episode_000001.mp4
        │   ├── episode_000002.mp4
            ...
        └── observation.images.phone
            ├── episode_000000.mp4
            ├── episode_000001.mp4
            ...

可以看到图片是用mp4存储的,非常节省空间。上面的parquet文件就是state与action信息,就是各关节的角度。模型训练在默认配置(ACT模型)下,需要5G左右的显存,本地的3090显卡训练100K个steps需要3个小时左右。同时 ,我也使用了autodl平台上的算力,4090D相对于3090会快个40%左右,一小时2元。关于模型后面有单独的文章会讲其原理和实际效果。在玩具块在数据采集时刻那个固定的位置上,成功率是非常高的,但换个位置的话,成功率就很低,这与训练数据有很大的关联,所以为啥叫模仿学习^v^,在文章最后贴一个成功抓取的视频吧:

00:31

高处相机

00:31

侧面相机

上面的内容撰写于2025.4月

————————–分割线———————

下面的内容撰写于2025.10月

最近发现lerobot的架构更新了很多,包括以前的一些命令写法,机械臂的标定方法,记录数据的方法等有很多更新,所以在这里补充一下最新版本的lerobot的一些命令使用方法:

文档

文档在新的框架下面也汇总在了一起。看起来更集中,更统一了一些。大家按照github根目录中的README.md环境安装完成后,再按照./docs目录中README.md把文档生成一下。然后就可以在本地用网页统一查看了,如下:

标定

标定方法与标定文件的格式与以前相比都发生重大变化,所以需要重新标定,过程如下:

标定主臂

lerobot-calibrate     --teleop.type=so100_leader     --teleop.port=/dev/ttyACM0 --teleop.id=my_awesome_leader_arm

标定从臂:

lerobot-calibrate     --robot.type=so100_follower     --robot.port=/dev/ttyACM1 --robot.id=my_awesome_follower_arm

标定后在下面的文件夹内生成标定参数:

(主)/home/ubuntu/.cache/huggingface/lerobot/calibration/teleoperators/so100_leader/my_awesome_leader_arm.json
(从)/home/ubuntu/.cache/huggingface/lerobot/calibration/robots/so100_follower/my_awesome_follower_arm.json

其中主臂的标定参数结果如下,可以发现与以前相比格式和内容都发生较大变化:

{
    "shoulder_pan": {
        "id": 1,
        "drive_mode": 0,
        "homing_offset": -1077,
        "range_min": 942,
        "range_max": 3497
    },
    "shoulder_lift": {
        "id": 2,
        "drive_mode": 0,
        "homing_offset": -61,
        "range_min": 886,
        "range_max": 3306
    },
    "elbow_flex": {
        "id": 3,
        "drive_mode": 0,
        "homing_offset": -968,
        "range_min": 824,
        "range_max": 3063
    },
    "wrist_flex": {
        "id": 4,
        "drive_mode": 0,
        "homing_offset": 2027,
        "range_min": 779,
        "range_max": 3259
    },
    "wrist_roll": {
        "id": 5,
        "drive_mode": 0,
        "homing_offset": -2047,
        "range_min": 0,
        "range_max": 4095
    },
    "gripper": {
        "id": 6,
        "drive_mode": 0,
        "homing_offset": -586,
        "range_min": 2025,
        "range_max": 3194
    }
}

teleoperate(遥操)

不启用相机:

lerobot-teleoperate     --robot.type=so100_follower     --robot.port=/dev/ttyACM1     --robot.id=my_awesome_follower_arm     --teleop.type=so100_leader     --teleop.port=/dev/ttyACM0 --teleop.id=my_awesome_leader_arm

启用相机:

lerobot-teleoperate \
  --robot.type=so100_follower \
  --robot.port=/dev/ttyACM1 \
  --robot.id=my_awesome_follower_arm \
  --robot.cameras='{
    left: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30},
    top: {"type": "opencv", "index_or_path": 0, "width": 640, "height": 480, "fps": 30}
  }' \
  --teleop.type=so100_leader \
  --teleop.port=/dev/ttyACM0 \
  --teleop.id=my_awesome_leader_arm \
  --display_data=true

同时会打开一个统一的可视化界面,这个是以前没有的:

record数据

lerobot-record \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM1 \
    --robot.cameras='{
      left: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30},
      top: {"type": "opencv", "index_or_path": 0, "width": 640, "height": 480, "fps": 30}
    }' \
    --robot.id=my_awesome_follower_arm \
    --teleop.type=so100_leader \
    --teleop.port=/dev/ttyACM0 \
    --teleop.id=my_awesome_leader_arm \
    --dataset.repo_id=hxdoso/new_frame \
    --dataset.num_episodes=2 \
    --dataset.single_task="Grab the cube" \
    --display_data=true \
    --dataset.reset_time_s=10
# --dataset.reset_time_s=10代表Reset the environment打印出来后,10秒后开始下一个episode(若记录多个record时)

文件树:

可以发现与以前相比也有变化,其中的dataset版本号从以前的2.1升级为3.0,所以以前的数据在新版式中是用不了的,需要转换一下。

.
├── data
│   └── chunk-000
│       └── file-000.parquet
├── meta
│   ├── episodes
│   │   └── chunk-000
│   │       └── file-000.parquet
│   ├── info.json
│   ├── stats.json
│   └── tasks.parquet
└── videos
    ├── observation.images.left
    │   └── chunk-000
    │       └── file-000.mp4
    └── observation.images.top
        └── chunk-000
            └── file-000.mp4

replay

可以通过replay回放数据,查看机械臂的复现动作方面的一致性如何。

lerobot-replay     --robot.type=so100_follower     --robot.port=/dev/ttyACM1     --robot.id=my_awesome_follower_arm     --dataset.repo_id=hxdoso/new_frame     --dataset.episode=1

train

训练一个模型(用act模型):

lerobot-train   --dataset.repo_id=hxdoso/new_frame   --policy.type=act   --output_dir=outputs/train/act_so100_test   --job_name=act_so100_test   --policy.device=cuda    --policy.push_to_hub=false

pi0.5模型

python src/lerobot/scripts/lerobot_train.py    --dataset.repo_id=hxdoso/new_frame     --policy.type=pi05     --output_dir=./outputs/pi05_training     --job_name=pi05_training     --policy.repo_id=lerobot/pi05_base     --policy.pretrained_path=lerobot/pi05_base     --policy.compile_model=true     --policy.gradient_checkpointing=true        --policy.dtype=bfloat16     --steps=3000     --policy.device=cuda     --batch_size=1

infer&eval

lerobot-record \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM1 \
    --robot.cameras='{
      left: {"type": "opencv", "index_or_path": 2, "width": 640, "height": 480, "fps": 30},
      top: {"type": "opencv", "index_or_path": 0, "width": 640, "height": 480, "fps": 30}
    }' \
    --robot.id=my_awesome_follower_arm \
    --dataset.repo_id=hxdoso/eval_new_frame \
    --dataset.num_episodes=1 \
    --dataset.single_task="Grab the cube" \
    --dataset.reset_time_s=10 \
    --policy.type=pi05 \
    --policy.pretrained_path=lerobot/pi05_base
0

评论0

没有账号?注册  忘记密码?