我的前两篇具身智能相关博客:
具身智能VLA方向模型fine-tune(单臂)(24.12.26已完结)
具身智能VLA方向–基于仿真数据的单/双臂VLA模型训练(RDT与openpi)

发布时间定为4.23号,因为论文查重会查github,所以答辩完我再开源…

正式开始前的碎碎念

距离上一篇文章的发布已经过去两个月了, 期间也有许多朋友或是私信提问, 或是留言, 向我抛出了诸多问题, 结合了大家的种种困惑, 并融合了这半年来我在具身智能领域上的学习与收获, 我决定将我的毕业设计制定为制作一个好用的具身智能上手程序:
使用者只需要编写一套简单明了的控制代码, 或者使用在我提供接口名单上的机械臂和视觉传感器等等, 就可以快速组装出一个可以实时获取指定信息, 操控状态的机器人控制套件.
为了能让我的代码更加简单直白, 又要保证能很好的适应不同机械部件, 并且底层调用接口要有一致性, 来保证使用者能够通过统一的接口实现不同机械部件之间的丝滑切换, 我重构了两遍代码, 最终将这个开源项目展现在诸位同志面前.
由于我现在能接触到的机械臂比较有限, 因此暂时项目提供的机械臂封装函数种类会比较少, 不过我也在努力拓展机械臂了, 并且在机器人控制方面准备支持底盘操控, 灵巧手操控, 传感方面添加触觉传感器等等, 希望不久的将来control your robot真的能成为一个优秀的, 涵盖大量机械操作部件的开源项目吧~

项目介绍

在开始使用项目前请先看完整个介绍, 一定会对你很有帮助的哦~

我的项目链接: control your robot
项目整体示意图

注册机器人

本项目将机器人部件分为两种:

  1. 控制器: 控制器例如机械臂, 灵巧手, 移动底盘, 其数据感知主要面向于机器人本体, 而非对环境进行感知, 并且拥有操控机器人行动的能力.
  2. 传感器: 传感器目前有两种, 视觉传感器与触觉传感器, 其数据感知是面向环境的, 并且没有操控机器人运动的模块.

通过上述的两种划分, 本项目对不同的控制器/传感器实现了对应的基类, 添加具体的设备只需要在这些基类上面继承一个具体设备的类就行啦.

# 机械臂的基类
class ArmController(BaseController):
	pass
# agilex的Piper机械臂实现类
class PiperController(ArmController): 
	pass

在注册完成对应的机械臂与传感器后, 就可以按照example/中的几个机器人示例, 组装一个你自己的机器人啦.

class Robot():
	def __init__(self):
		self.controller = {
			"left_arm":PiperController(),
			...
		}
		self.sensor = {
			"head_cam":RealsenseSensor(),
			...
		}

由于基类已经提供了相关的统一接口,获取数据就是get(), 控制器的运动就是move(), 因此我们可以实现对于我们自己的机器人的各种操作需求:

class Robot():
	...
	# 实现一个移动函数
	def move(self, move_data:Dict):
		for key,data in move_data.items():
			self.controller[key].move(data)
	
	# 实现一个获取数据的函数
	def get(self):
		controller_data = {}
		if self.arm_controllers is not None:    
			for controller_name, controller in self.arm_controllers.items():
      			controller_data[controller_name] = controller.get()
		sensor_data = {}
		if self.image_sensors is not None:  
			for sensor_name, sensor in self.image_sensors.items():
				sensor_data[sensor_name] = sensor.get()
		return [controller_data, sensor_data]

这两个函数基本上就是整个机器人的核心了, 通过统一化定义的接口, 我们可以很简单的就实现对于机器人进行控制与数据的实时获取啦.
这时候肯定就会有疑问了, 只是一个统一的操控获取接口而已, 我自己给我的机械臂适配一套也用不着用这个吧?
接下来才是我的项目的重点:
数据采集-模型微调-模型部署的pipeline

采集-微调-部署框架

我在本项目中实现了一个简易化的数据采集类CollectAny, 可以快速保存所有控制器与传感器的数据, 不需要做额外处理(但是需要数据是数字, 我统一在保存的时候转为numpy了)

在保存完原始的采集数据后, 训练模型又需要我们使用不同格式的数据, 为此我目前提供了两个转化的数据格式, 分别是LerobotDataset, 对应openpi训练;符合具身智能需求的hdf5存储格式, 对应RDT训练.我也会慢慢增加其他的优秀VLA模型的支持~

我已经在policy/中添加了openpi与RDT经过优化后的代码, 并提供了详细的REAME.md, 方便使用者们快速上手.

为了方便模型部署, 我在两个模型目录下都实现了infernece_model.py,里面封装了便于调用的类, 并且在example/deploy中提供了对应的部署示例~

使用教程

上面已经提供了整个项目的总体介绍了, 接下来将为大家讲解怎么去使用这个项目~

注册机器人

由于现在机械臂百花齐放, 各种品牌的机械臂的接口都完全不同, 所以我想通过本项目提供一个统一的底层控制api, 对于不同机械臂, 只需要将厂家提供的python sdk接入, 就能够通过统一的接口进行数据获取.
机器人底盘大部分厂家似乎都没有提供python sdk, 全部采用了ros封装, 所以我也简单封装了ros的subscriber和publisher, 这样一来, 就算是用的设备没有提供python sdk, 也一样能比较容易的获取数据啦.

怎么去实现一个controller?

对于controller基类, 我为其设置了三个基础接口:

  1. self.set_collect_info()
    设置要获取的数据类型, 如机械臂的关节角, 夹爪张合度…
  2. self.get()
    根据设置需要获取的数据, 去调用函数获取
  3. self.move()
    根据输入的运动指令, 操作控制器运动

对于这三个基础函数, 所有的controller都继承, 实现其调用的函数, 以便于捕获异常状态.

ArmController
ArmController介绍

对于机械臂, 我目前设置了几个基础信息:

  1. joint: 关节角, 机械臂控制的最优选择
  2. qpos: 末端位姿, 目前机械臂主流有两种EEF表示, 我推荐使用欧拉角的6D pose, 而不是四元数, 但是取决于机械臂厂家
  3. velocity: 这个基本默认是关节角的移动速度, 和厂家有关, 基本在VLA模型中是没有用的, 如果需要可以按照你的需求进行设置, 末端也好, 关节角速度也行
  4. force: 对应电机的控制力度, 如果有夹爪的控制力度, 也一样通过这函数获取就行
  5. action: 某些机械臂的运动似乎可以不是关节角与四元数,而是一些特殊的电机设置?如果你没有用到那就是没有需求, 可以不管对应的函数, 直接pass
  6. gripper: 大部分的夹爪信息是绑定机械臂的, 所以我直接将其加入到机械臂控制器中了.

那么我该怎么继承ArmController实现一个自己机械臂的controller呢?

需要实现的函数

基础函数:

  1. set_up()
    不同机械臂的初始化需要的参数不同, 这点需要根据机械臂来写, 用来绑定机械臂
  2. get_state()
    返回机械臂状态state, 主要用于获取前面提到的1~6的信息, 是一个dict, 大部分机械臂厂家的api应该会直接返回一个dict, 包含一些对应信息, 如果和我提供的数据名称不一样, 建议直接在字典内加入一个新的key进行索引, 不要改变基类.
    对于夹爪状态, 对于VLA训练, 建议是使用张合度, 如果厂家提供的sdk返回的是一个夹爪状态的字典, 就提取张合度, 并归一化到[0,1]
  3. set_position()
    根据输入的numpy.Array, 设置机械臂的末端位姿
  4. set_joint()
    根据输入的numpy.Array, 设置机械臂的对应关节角度
  5. set_gripper()
    根据输入的张合度, 控制夹爪开关/ 张合度

所有set相关函数, 关节角与末端位姿都支持在调用时使用相对位置, 只需要move(is_delta=True)就行,如果使用相对位置, 默认是直接基于当前状态, 使用加法得到目标位置进行运动.
其余set不支持相对运动, 不会受到is_delta=True的影响.

可选函数

  1. set_velocity()
    设置机械臂的移动速度, 建议是直接使用一个固定值, 不要改变机械臂的运动速度
  2. set_force()
    控制对应电机力度, VLA中都是默认固定力控的
  3. set_action()
    我也没遇到过, 只是有些机械臂是这样的?好像是某些仿真环境的机械臂有特殊的控制参数, 所以这里留着.
HandController
暂时没有实现具体支持, 但是有写好基类

暂时没有灵巧手, 先跳过啦.

MobileController

由于大部分底盘都是做为基础元件售卖的,因此大部分厂家只会提供ros接口。
为了解决这一问题,本项目完成了基于pyros的对于ROS接口的使用封装,并完成了相关测试。如果你的机器人有一些设备是使用ROS通讯的,一样可以通过本项目操控哦。就如底盘控制器提供的唯一样例,agilex的Tracers2.0底盘控制器就是使用ROS进行相关操作的。

MobileController介绍

对于底盘移动,会涉及到的信息就少多了,基本信息就只有底盘移动的方向,移动的速度两点,因此目前只写了一个基础信息:

  1. **move_velocity:**通过获取该信息,我们可以得到底盘当前的运动状态[x,y,z,rx,ry,yz]
需要实现的函数

基础函数:

  1. set_up()
    建立底盘连接,如果是ros的话就是绑定订阅接口与发布接口啦~
  2. get_state()
    目前只有获取move_velocity信息~
  3. set_move_velocity()
    设置底盘的相关运动速度~

可选函数

  1. move_to()
    有些底盘会有SLAM,所以可能支持移动到指定位置,所以我特意留这个参数出来。

怎么去实现一个sensor?

对于sensor基类, 我为其设置了三个基础接口:

  1. self.set_collect_info()
    设置要获取的数据类型, 如机械臂的关节角, 夹爪张合度…
  2. self.get()
    根据设置需要获取的数据, 去调用函数获取

对于这两个基础函数, 所有的sensor都继承, 实现其调用的函数, 以便于捕获异常状态.

VisionSensor

视觉传感器的话,目前在具身智能行业使用比较多的似乎就是RealSense系列了吧,哈哈,所以我就暂时只完成了RealSense2.0的系统接口,顺便在tools/中为大家提供了一些小工具,方便绑定序列号和测试相机信息。

需要实现的函数
  1. set_up
    建立视觉传感器的连接,基本应该都是通过绑定序列号来唯一匹配的吧。
  2. get_image
    获取图像,通过你设置需要获取的数据类型来接续对应管道数据,如果很需要实时性,建议可以不采集深度图~
TouchSensor
暂时还有没触觉传感器,所以只提供了基类,欢迎补充~

怎么去注册一个robot?

看到这里,如果你已经完成了你机械臂涉及到的控制器与传感器的注册,那么恭喜你,注册机器人是最简单的啦。

绑定控制器与传感器

绑定控制器与传感器
是的,就是那么简单,控制一个机器人本质就是控制机器人所带有的控制器与传感器啦,所以在完成对应的注册后,我们自然就能得到我们的机器人了,如果你想改变机器人的构成组件,那么只需要在这里修改就行啦。
需要注意的是,这里我们绑定了一个CollectAny( ),这个是用来帮我们采集数据的。

实现三个基础的函数

三个基础函数
是不是看着也很简单,只要实现了这三个基础函数,我们就能采集数据和控制机器人完成制定动作了。

数据采集相关

恭喜你已经成功适配完成机器人啦~!
如果你有添加新的设备支持,欢迎提PR,我们进行merge,开源项目需要各位的一同参与~

我的机器人如何采集数据?

对于采集数据,我在example/collect中提供了两种采集示例。

  1. collect.py
    该代码采用串行采集数据,也是最简单的采集方式,如果你对采集数据的延迟没有那么高的要求,那么使用这个是最贱的。
  2. collect_mp.py
    该代码提供了基于我多进程封装的并行数据采集,通过一个时间控制器+机器人数据采集接口,能保证采集帧之间的延迟不会累计,不过由于某些设备可能进程不安全,所以我提供的采集在设备采集中仍然是串行的,如果你确认你的设备通讯都是进程安全的(没报错就是),那么就可以通过一个时间控制器同时控制多个控制器与传感器来获取信息~

如何使用pika/自己diy遥操设备遥控操作?

我已经和松灵那边联系好了,相关遥控数据采集部分会由松灵提供一个基础pika使用示例。
如果是自己的遥操设备,只需要继承controller/teleoperation_controller.pyTeleoperationController类,实现你的遥操控制器的数据获取就行啦。

如何读取采集的原始的hdf5数据?

由于原始数据使用hdf5格式存储,并为了通用型,所以存储为如下结构:

{
"controller1":{
				"state"
				"gripper"
				...
				}
...
"sensor1":{
			"image"
			...
			}
}

因此如果你想一个个看你的数据,可以模仿提供的数据转化脚本中读取的示例,来读取对应数据啦。
当前,我也提供了一个基于RDT版hdf5数据可视化,你可以转化为RDT hdf5格式来可视化。

转化为LerobotDataset

目前Lerobot已经到了2.1版本了,并且openpi只支持1.7版本的格式,因此我提供了两版本的数据转化方式,详细请见REAME.md

转化为hdf5(RDT)

由于设计单臂与双臂,我提供了单臂与双臂两种数据转化的格式,详细见scripts/convert2rdt_hdf5.py的开头注释,你需要设置对应的map,并且稍微修改一些地方,我默认是单臂的,所有部分双臂被注释了,需要取消注释。

模型微调相关

详细请看对对应模型的README.md哦,我已经编写了完整的流程。这里就强调一下一些小细节吧~

RDT

  1. 可以多卡训练,并且总batch_sizegpu_num * batch_size
  2. 如果显存小,可以先将对应语言指令编码,不用在训练阶段加载t5模型

openpi

  1. fsdp的操作是将模型平均加载到对应数目的显卡的显存中,所以你如果单卡内存少也没关系
  2. 如果你显存不够,那么请修改finetune.sh,设置XLA_PYTHON_CLIENT_MEM_FRACTION=false,关闭XLA优化后能降低显存需求

模型部署相关

example/deploy中提供了RDT和openpi的模型部署示例,可以参考修改。

如何将模型部署到我的机器人上?

如果你的机器人实现了里面的一些函数:

  1. robot.reset()
    这个是用来重置机械臂位置的
  2. robot.get_observation()
    这个可选,本质就是robot.get()后转化了下数据格式,所以我在两个deploy文件中都提供了transform_data()函数,你可以直接在这里转化你的数据格式。

为我们补充代码~

Logo

「智能机器人开发者大赛」官方平台,致力于为开发者和参赛选手提供赛事技术指导、行业标准解读及团队实战案例解析;聚焦智能机器人开发全栈技术闭环,助力开发者攻克技术瓶颈,促进软硬件集成、场景应用及商业化落地的深度研讨。 加入智能机器人开发者社区iRobot Developer,与全球极客并肩突破技术边界,定义机器人开发的未来范式!

更多推荐