动力学与平衡第8课/共30课

🤖 质心控制

通过足端力规划控制质心轨迹

⚖️ 质心控制的重要性

质心(Center of Mass, CoM)是机器人平衡的核心。控制质心位置就是在控制平衡:

📐 质心位置计算

多体系统的质心位置:

rCoM = Σ(mi · ri) / M

对于四足机器人,质心由躯干和4条腿共同决定。腿的运动会使质心偏移:

ΔrCoM = mleg · Δrleg / Mtotal

🔄 质心轨迹规划

Walk步态中,每次抬腿前需要先移动质心:

策略1:预偏移

在抬腿之前,先将CoM移到剩余3条腿的支撑三角形重心:

rtarget = Σrsupport / nsupport

策略2:线性倒立摆(LIPM)

将机器人简化为倒立摆,CoM动力学:

ẍ = ω² · (x - xZMP)
ω = sqrt(g / zCoM)

LIPM提供了CoM轨迹的解析解,是实时控制的基础。

🧮 仿真:质心控制

import math class CoMController: def __init__(self, mass=6.2, body_L=0.4, body_W=0.2): self.mass = mass self.g = 9.81 self.body_L = body_L self.body_W = body_W self.legs = { 'LF': ( body_L/2, body_W/2), 'RF': ( body_L/2, -body_W/2), 'LB': (-body_L/2, body_W/2), 'RB': (-body_L/2, -body_W/2), } def compute_com(self, foot_positions, contact_flags): total_force = [0, 0] for leg, pos in foot_positions.items(): if contact_flags.get(leg, False): total_force[0] += pos[0] total_force[1] += pos[1] n = sum(1 for v in contact_flags.values() if v) if n == 0: return (0, 0) return (total_force[0]/n, total_force[1]/n) def com_trajectory_planning(self, target_com, current_com, dt=0.01, kp=10.0): trajectory = [] com = list(current_com) vel = [0, 0] for i in range(200): fx = kp * (target_com[0] - com[0]) - 3.0 * vel[0] fy = kp * (target_com[1] - com[1]) - 3.0 * vel[1] ax = fx / self.mass ay = fy / self.mass vel[0] += ax * dt vel[1] += ay * dt com[0] += vel[0] * dt com[1] += vel[1] * dt trajectory.append((i*dt, com[0], com[1])) if abs(com[0]-target_com[0]) < 0.001 and abs(com[1]-target_com[1]) < 0.001: break return trajectory def swing_shift_strategy(self, swing_leg): # Shift CoM away from swing leg to maintain stability leg_pos = self.legs[swing_leg] # Target: centroid of remaining support legs support = {l: p for l, p in self.legs.items() if l != swing_leg} cx = sum(p[0] for p in support.values()) / len(support) cy = sum(p[1] for p in support.values()) / len(support) return (cx, cy) ctrl = CoMController() print("=" * 55) print(" Center of Mass Control Simulation") print("=" * 55) # CoM shift for each swing leg print("\n [CoM Shift Strategy for Walk Gait]") for swing in ['LF', 'RF', 'LB', 'RB']: target = ctrl.swing_shift_strategy(swing) current = (0, 0) shift = (target[0]-current[0], target[1]-current[1]) print(f" Lift {swing}: target CoM=({target[0]:.4f},{target[1]:.4f}), shift=({shift[0]*1000:.1f},{shift[1]*1000:.1f})mm") # CoM trajectory tracking print("\n [CoM Trajectory Tracking]") targets = [(0.05, 0), (-0.05, 0), (0, 0.03), (0, -0.03)] for target in targets: traj = ctrl.com_trajectory_planning(target, (0, 0)) t_final = traj[-1][0] x_final, y_final = traj[-1][1], traj[-1][2] overshoot = 0 for t, x, y in traj: d = math.sqrt(x**2 + y**2) target_d = math.sqrt(target[0]**2 + target[1]**2) if d > target_d * 1.1: overshoot = max(overshoot, d - target_d) print(f" Target ({target[0]*1000:.0f},{target[1]*1000:.0f})mm: " f"reached ({x_final*1000:.1f},{y_final*1000:.1f})mm in {t_final:.3f}s, " f"overshoot={overshoot*1000:.1f}mm") # Walk gait CoM trajectory print("\n [Walk Gait Full CoM Trajectory]") swing_order = ['LF', 'RF', 'LB', 'RB'] com_pos = [0, 0] for phase, swing in enumerate(swing_order): target = ctrl.swing_shift_strategy(swing) traj = ctrl.com_trajectory_planning(target, tuple(com_pos), kp=15.0) com_pos = [traj[-1][1], traj[-1][2]] print(f" Phase {phase} (lift {swing}): CoM -> ({com_pos[0]*1000:.1f},{com_pos[1]*1000:.1f})mm") print() print(" OK - CoM control simulation complete")

仿真结果:

======================================================= Center of Mass Control Simulation ======================================================= [CoM Shift Strategy for Walk Gait] Lift LF: target CoM=(-0.0667,-0.0333), shift=(-66.7,-33.3)mm Lift RF: target CoM=(-0.0667,0.0333), shift=(-66.7,33.3)mm Lift LB: target CoM=(0.0667,-0.0333), shift=(66.7,-33.3)mm Lift RB: target CoM=(0.0667,0.0333), shift=(66.7,33.3)mm [CoM Trajectory Tracking] Target (50,0)mm: reached (49.2,0.0)mm in 1.380s, overshoot=0.0mm Target (-50,0)mm: reached (-49.2,0.0)mm in 1.380s, overshoot=0.0mm Target (0,30)mm: reached (0.0,29.3)mm in 1.370s, overshoot=0.0mm Target (0,-30)mm: reached (0.0,-29.3)mm in 1.370s, overshoot=0.0mm [Walk Gait Full CoM Trajectory] Phase 0 (lift LF): CoM -> (-66.1,-33.0)mm Phase 1 (lift RF): CoM -> (-66.7,32.7)mm Phase 2 (lift LB): CoM -> (67.0,-33.5)mm Phase 3 (lift RB): CoM -> (66.7,32.7)mm OK - CoM control simulation complete

📈 线性倒立摆详解

LIPM是四足机器人步态控制的核心模型:

时间常数 ω-1 = sqrt(zc/g) 决定了系统的自然动态。对于zc=0.2m,ω-1 ≈ 0.14s。

💡 足端力分配

给定期望的CoM加速度,需要计算各足端力:

m·aCoM = ΣFi - m·g·ẑ
τCoM = Σ(ri - rCoM) × Fi

由于4条腿提供12个力分量,而CoM控制只需6个方程,有6个自由度的冗余。常用二次规划(QP)求解最优力分配。

📈 倒立摆模型详解

线性倒立摆(LIPM)是步态控制的基石模型:

ẍ = ω2(x - p)
ÿ = ω2(y - py)

其中 ω = sqrt(g/zc) 是自然频率,p是ZMP位置。

解析解

当ZMP固定时,CoM轨迹有解析解:

x(t) = (x0 - p)·cosh(ωt) + (ẋ0/ω)·sinh(ωt) + p

这个解有发散分量sinh/cosh,意味着除非精确控制,CoM会指数级偏离。

🔄 预观控制(Preview Control)

Kajita的预观控制方法是ZMP规划的经典解法:

  1. 给定未来N步的ZMP参考轨迹
  2. 通过预观窗口优化CoM轨迹
  3. 确保ZMP跟踪误差最小
min Σ(pref - p)2 + Qu·u2

预观长度通常取1-2秒。越长跟踪越精确,但延迟越大。

💡 多接触力分配

当4条腿都着地时,力的分配有无限多解。QP优化目标选择:

这些约束形成一个凸优化问题,可以用OSQP等求解器在1ms内求解。

📊 关键参数汇总

参数典型值范围说明
控制频率1 kHz500-2000 Hz越高动态性能越好
IMU带宽200 Hz100-500 Hz5倍过采样
力控精度5%2-10%取决于电流环精度
姿态估计精度0.5 deg0.2-2 deg取决于滤波器
CoM跟踪精度5 mm2-20 mm取决于控制器

📊 关键参数汇总

参数典型值范围说明
控制频率1 kHz500-2000 Hz越高动态性能越好
IMU带宽200 Hz100-500 Hz5倍过采样
力控精度5%2-10%取决于电流环精度
姿态估计精度0.5 deg0.2-2 deg取决于滤波器
CoM跟踪精度5 mm2-20 mm取决于控制器

📝 练习

  1. 实现LIPM模型,计算从(0,0)到(0.1,0)的CoM轨迹,ZMP保持在原点。
  2. 设计一个PD控制器跟踪CoM轨迹,分析Kp和Kd对超调和收敛时间的影响。
  3. 当腿质量从0.3kg增加到0.5kg时,质心偏移增加多少?对步态有何影响?
  4. 实现QP力分配器,最小化足端力范数同时跟踪CoM加速度。
  5. 比较预偏移策略和LIPM规划在Walk步态中的稳定裕度。
🏆
质心掌控者

掌握质心控制、LIPM模型和足端力分配

四足机器人课程 · 第8课/30 · 返回目录