从当前 UVC 实现出发,分阶段向 ZMP/LIPM 架构演进。
graph LR
IMU[YIS321 IMU] -->|pitch, roll| UVC[UVC Controller]
CPG[CPG Counter] --> UVC
UVC -->|dxi, dyi, fh| IK[footCont IK]
IK -->|5 angles| SERVO[LX Servos ×10]
特点: UVC 本质是一个 积分式倾斜补偿器 — 检测到倾斜 → 修正支撑腿横/纵偏移 → IK 解算新角度。简洁但粗糙。
修复 walk_imu.py 中与 robot_ctrl.c 的已知差异
uvc_sub() 中补入着地时腿长缩短逻辑:
# 着地期: 缩短腿长吸收冲击
if self.fwct > self.fwctEnd - self.landB and rollt > 0:
shock = (abs(self.dyi - self.dyib) + abs(self.dxi - self.dxib)) * 0.02
self.autoH -= shockdef sw_cont(self):
k = self.swMax * math.sin(math.pi * self.fwct / self.fwctEnd)
t = math.atan2(abs(self.dxi), max(self.dyi, 0.01))
if self.dxi > 0:
self.swx = k * math.sin(t)
else:
self.swx = -k * math.sin(t)
self.swy = k * math.cos(t)用于 --mode balance 跌倒后恢复:
def uvc_sub2(self):
"""恢复步态: 所有偏移按剩余周期线性归零"""
remain = self.fwctEnd - self.fwct + 1
k1 = self.dyi / remain
k0 = self.dxi / remain
self.dyi -= k1
self.dxi -= k0
if self.fwct <= self.landF:
self.dyis += k1
self.dxis -= k0
else:
self.dyis -= self.dyis / remain
self.dxis -= self.dxis / remain
self.autoH += (HEIGHT - self.autoH) / remain当前用 sin() 曲线驱动关节,起止点速度/加速度不为零,会产生冲击。
替换简单正弦抬脚,改用摆线保证起止零速度零加速度:
其中
def cycloid_trajectory(phase, stride, lift_height):
"""摆线轨迹: 起止速度=0, 加速度=0"""
x = stride * (phase - math.sin(2*math.pi*phase) / (2*math.pi))
z = lift_height * (1 - math.cos(2*math.pi*phase)) / 2
return x, z好处: 落地冲击从 sin 的 ~50% 峰值速度降为 0,极大保护舵机齿轮。
更灵活,可控制中间点:
def bezier_foot_trajectory(phase, start, end, lift):
"""三次贝塞尔足端轨迹"""
P0 = (start, 0) # 起点(地面)
P1 = (start, lift) # 控制点1(抬起)
P2 = (end, lift) # 控制点2(最高点)
P3 = (end, 0) # 终点(地面)
t = phase
x = (1-t)**3*P0[0] + 3*(1-t)**2*t*P1[0] + 3*(1-t)*t**2*P2[0] + t**3*P3[0]
z = (1-t)**3*P0[1] + 3*(1-t)**2*t*P1[1] + 3*(1-t)*t**2*P2[1] + t**3*P3[1]
return x, z替换 UVC 的纯积分补偿,改用直接的 PD 反馈:
class AnkleStrategy:
def __init__(self, kp=0.3, kd=0.05):
self.kp, self.kd = kp, kd
self.prev_error = 0
def compute(self, pitch_error, dt):
d_error = (pitch_error - self.prev_error) / dt
self.prev_error = pitch_error
return self.kp * pitch_error + self.kd * d_error好处: 比 UVC 的积分式响应更快,不容易过冲。可与 UVC 叠加使用。
当前 footCont 是几何法简化解,忽略了 Hip Yaw(偏航)轴。
利用机器人结构特点 (Hip Roll/Pitch 轴交汇, Ankle Pitch/Roll 轴交汇):
给定: 骨盆位姿 T_pelvis, 足端目标位姿 T_foot
求解: 6个关节角 [hip_yaw, hip_roll, hip_pitch, knee, ankle_pitch, ankle_roll]
解法 (利用相交轴解耦):
- Hip Yaw: 从骨盆到足端的投影方向直接求解
-
膝关节 (余弦定理):
$$\theta_{knee} = \cos^{-1}!\left(\frac{l_1^2 + l_2^2 - d^2}{2 l_1 l_2}\right)$$ -
Hip Roll + Pitch: 用
atan2从剩余几何关系解出 - Ankle Pitch + Roll: 保持脚掌与地面平行, 从 IMU 数据补偿
好处: 支持 hip_yaw 实现转向行走,且避免 IK 奇异点。
从 CPG 正弦曲线 → 基于物理的动态步态
核心动力学:
其中
给定落脚点序列 → ZMP 参考轨迹 → 反推质心轨迹。
离散化 LIPM 为状态空间:
LQR 最优控制 + N步前瞻:
class PreviewController:
def __init__(self, dt, com_height, preview_steps=160):
self.dt = dt
omega = math.sqrt(9.81 / com_height)
# 状态空间矩阵 [x, dx, ddx]
self.A = np.array([[1, dt, dt**2/2],
[0, 1, dt],
[0, 0, 1]])
self.B = np.array([[dt**3/6], [dt**2/2], [dt]])
self.C = np.array([[1, 0, -com_height/9.81]])
# 求解 LQR 增益 (离线计算一次)
self.K_x, self.K_I, self.f = self._solve_preview_gain(preview_steps)def plan_footsteps(n_steps, stride, width):
"""交替左右脚落脚点"""
steps = []
for i in range(n_steps):
x = i * stride
y = width/2 if i % 2 == 0 else -width/2
steps.append((x, y))
return stepsgraph TD
A[Footstep Planner] -->|落脚点序列| B[ZMP Reference]
B --> C[Preview Controller]
C -->|CoM 轨迹| D[Swing Leg Trajectory]
D -->|足端位置| E[Analytical IK 6-DoF]
IMU -->|pitch, roll| F[Ankle Strategy PD]
E --> G[+]
F --> G
G -->|joint angles| H[LX Servos]
| 方向 | 说明 | 硬件需求 |
|---|---|---|
| 触地检测 | 读舵机电流突变判断是否着地 | 无 (LX 支持电流读取) |
| 力矩估计 | 从舵机电流反推脚底反力 | 无 |
| FSR 压力 | 脚底贴薄膜压力传感器 | ~¥10/片 |
| 地形自适应 | 前一步触地高度差 → 调整下一步抬脚 | FSR |
| 步态学习 | 强化学习优化步态参数 | GPU/仿真 |
gantt
title 优化路线图
dateFormat X
axisFormat %s
section Phase 0
补全 UVC 差距 :done, 0, 2
section Phase 1
摆线轨迹 :active, 2, 4
PD 踝关节补偿 :4, 6
section Phase 2
解析 IK 6-DoF :6, 10
section Phase 3
LIPM + ZMP 预瞄 :10, 20
section Phase 4
触地检测/自适应 :20, 28
Tip
建议先做 Phase 0 + Phase 1.1 (摆线轨迹),实测后再决定是否进入 Phase 2/3。ZMP/LIPM 是重型方案,对机器人重量分布和舵机精度有较高要求,不一定在舵机平台上能体现优势。UVC + 好的轨迹 + PD 踝关节补偿可能已经够用。