Berkeley Humanoid Lite Lowlevel 的 robot/humanoid.py
humanoid.py 运行在机器人机载计算机(如 Jetson Orin 或 NUC)上,负责连接真实的传感器和执行器,并管理机器人的运行状态。
State 类
1 | class State: |
在后面的 Humanoid 类的 step 方法中的 match-case 结构中,根据当前状态执行不同的控制逻辑:
1 | match (self.state): |
linear_interpolate 函数
linear_interpolate 函数用于在初始化阶段平滑过渡机器人姿态,从趴下到站立。它接受起始位置、目标位置和当前的过渡百分比,返回一个新的目标位置。
1 | def linear_interpolate(start: np.ndarray, end: np.ndarray, percentage: float) -> np.ndarray: |
Humanoid 类
Humanoid 类代表真实的机器人实体,它封装了所有硬件通信细节。
__init__ 方法
硬件通信链路初始化 (CAN总线)
- 左臂绑定到
can0 - 右臂绑定到
can1 - 左腿绑定到
can2 - 右腿绑定到
can3
1 | self.left_arm_transport = recoil.Bus("can0") |
关节映射列表 self.joints
格式:(总线对象, 电机ID, 关节名称)
左腿 ID 均为奇数 (1, 3, 5, 7, 11, 13),右腿 ID 均为偶数 (2, 4, 6, 8, 12, 14)
每条腿 6 个自由度:跨部(Roll/Yaw/Pitch)、膝盖(Pitch)、脚踝(Pitch/Roll)
1 | self.joints = [ |
惯性测量单元(IMU)初始化
1 | self.imu = SerialImu(baudrate=Baudrate.BAUD_460800) |
遥控手柄(Joystick/Gamepad)初始化
1 | # Start joystick thread |
系统状态机初始化
1 | self.state = State.IDLE |
self.state:当前状态self.next_state:下一个状态
关键参数更新
1 | # 3. 更新 RL 初始位置 (长度必须为 22) |
RL 初始化控制器变量
用于平滑过渡(Interpolation)
1 | # 初始化进度(0.0 到 1.0) |
加载硬件校准文件
在实验室组装机器人时,电机的编码器零点和物理上的“腿部垂直”位置通常会有几度的偏差。这些偏差需要通过校准文件进行补偿。
1 | config_path = "calibration.yaml" |
安全性检查与赋值
1 | # 强制检查:确保校准文件中的偏移量数量与实际电机数量一致(12个或22个) |
enter_damping 方法
进入阻尼模式(Damping Mode)
在阻尼模式下,电机不会主动旋转到某个角度,但会像“液压杆”一样产生阻力,防止机器人因为重力瞬间瘫痪倒地,同时也保护电机不被突发的巨大力矩烧毁。
初始化参数数组,长度与电机数量一致
1 | self.joint_kp = np.zeros((len(self.joints),), dtype=np.float32) # 比例增益(刚度) |
设定阻尼模式下的安全参数
1 | # Kp=20: 较低的刚度,电机不会剧烈反弹 |
遍历每一个关节,逐个通过 CAN 总线发送配置
1 | for i, entry in enumerate(self.joints): |
bus 是 recoil.Bus 对象(在Berkeley-Humanoid-Lite/source/berkeley_humanoid_lite_lowlevel/berkeley_humanoid_lite_lowlevel/recoil/core.py中定义),表示与电机通信的 CAN 总线接口
stop 方法
关闭异步传感器线程
1 | # 停止 IMU 串口读取线程 |
进入阻尼模式
1 | for entry in self.joints: |
阻塞等待
1 | try: |
彻底断电
1 | for entry in self.joints: |
清理底层通信接口
*_transport.stop() 是为了关闭 Linux 系统的 socketcan 接口
1 | # 如果有手臂,关闭手臂的 CAN 总线(当前注释中) |
get_observations 方法
内存视图切片
1 | imu_quaternion = self.lowlevel_states[0:4] # 0-3: 四元数 (w, x, y, z) |
更新传感器数据
它的核心任务是将来自不同硬件(IMU、电机、手柄)的离散数据,封装成一个符合神经网络输入要求的 ** 35 维观测向量**
1 | # 从 IMU 线程读取最新的四元数数据 |
更新用户指令
1 | # 读取手柄上的模式开关(如:切换到 3.0 代表开启 RL 行走) |
update_joint_group 方法
从算法到硬件 (Sim -> Real)
计算相对位置
1 | position_target_l = (self.joint_position_target[joint_id_l] + self.position_offsets[joint_id_l]) * self.joint_axis_directions[joint_id_l] |
发送 CAN 数据包
1 | # self.joints[id][0] 是 Bus 对象,[id][1] 是电机 ID |
从硬件到算法 (Real -> Sim)
接收电机反馈
1 | position_measured_l, velocity_measured_l = self.joints[joint_id_l][0].receive_pdo_2(self.joints[joint_id_l][1]) |
逆向转换
1 | if position_measured_l is not None: |
step 方法
该函数通过一个有限状态机 (Finite State Machine) 来管理机器人的行为,确保机器人能够安全地从静止状态过渡到行走状态。
1 | def step(self, actions: np.ndarray): |
update_joints 方法
这段代码最核心的地方在于它将 左腿(索引 0-5) 和 右腿(索引 6-11) 的对应关节进行了“成对更新”。
1 | def update_joints(self): |
check_connection 方法
遍历电机列表,逐一发送 Ping 指令并等待反馈。
1 | for entry in self.joints: |