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

🤖 ZMP稳定判据

零矩点:动态稳定性的金标准

📐 什么是ZMP?

零矩点(Zero Moment Point, ZMP)是动态稳定性分析的核心概念:

ZMP = CoM - (ẑ × Ḣ) / (m·g)

其中 Ḣ 是角动量变化率。ZMP是地面上合力矩为零的点。

稳定性判据:ZMP在支撑多边形内时,机器人动态稳定。

📐 ZMP的物理含义

ZMP的物理含义是:如果在该点放置一个铰支座,机器人的合力矩为零。即:

📐 LIPM下的ZMP

在线性倒立摆(LIPM)模型下,ZMP简化为:

xZMP = xCoM - (zc/g) · ẍCoM
yZMP = yCoM - (zc/g) · ÿCoM

这个简化形式非常实用:只要知道CoM的位置和加速度,就能立即计算ZMP。

🧮 仿真:ZMP分析

import math class ZMPAnalyzer: def __init__(self, mass=6.2, g=9.81, z_com=0.2): self.mass = mass self.g = g self.z_com = z_com def compute_zmp(self, com_pos, com_vel, com_acc, angular_momentum_rate=(0,0,0)): # ZMP = CoM - (z_hat x H_dot) / (m*g) # Simplified: ZMP_x = x - (z_ddot*H_dot_y - 0) / (m*g) etc. x, y, z = com_pos ax, ay, az = com_acc Hx, Hy, Hz = angular_momentum_rate zmp_x = x - (self.mass * az * (z - 0) - Hy) / (self.mass * self.g + self.mass * az) zmp_y = y - (-self.mass * az * 0 + Hx) / (self.mass * self.g + self.mass * az) # Simplified LIPM version zmp_x_lipm = x - (z / self.g) * ax zmp_y_lipm = y - (z / self.g) * ay return (zmp_x, zmp_y), (zmp_x_lipm, zmp_y_lipm) def check_stability(self, zmp, support_polygon): hull = self.convex_hull(support_polygon) if len(hull) < 3: return False, 0 inside = self.point_in_polygon(zmp, hull) margin = self.distance_to_polygon(zmp, hull) return inside, margin @staticmethod def convex_hull(points): pts = sorted(set(points)) if len(pts) <= 2: return pts lower = [] for p in pts: while len(lower) >= 2 and (lower[-1][0]-lower[-2][0])*(p[1]-lower[-2][1]) - (lower[-1][1]-lower[-2][1])*(p[0]-lower[-2][0]) <= 0: lower.pop() lower.append(p) upper = [] for p in reversed(pts): while len(upper) >= 2 and (upper[-1][0]-upper[-2][0])*(p[1]-upper[-2][1]) - (upper[-1][1]-upper[-2][1])*(p[0]-upper[-2][0]) <= 0: upper.pop() upper.append(p) return lower[:-1] + upper[:-1] @staticmethod def point_in_polygon(point, polygon): x, y = point n = len(polygon) inside = False j = n - 1 for i in range(n): xi, yi = polygon[i] xj, yj = polygon[j] if ((yi > y) != (yj > y)) and (x < (xj - xi) * (y - yi) / (yj - yi) + xi): inside = not inside j = i return inside @staticmethod def distance_to_polygon(point, polygon): n = len(polygon) min_dist = float('inf') for i in range(n): p1 = polygon[i] p2 = polygon[(i+1) % n] dx, dy = p2[0]-p1[0], p2[1]-p1[1] seg_sq = dx*dx + dy*dy if seg_sq < 1e-10: d = math.hypot(point[0]-p1[0], point[1]-p1[1]) else: t = max(0, min(1, ((point[0]-p1[0])*dx + (point[1]-p1[1])*dy)/seg_sq)) d = math.hypot(point[0]-(p1[0]+t*dx), point[1]-(p1[1]+t*dy)) min_dist = min(min_dist, d) return min_dist zmp = ZMPAnalyzer() print("=" * 55) print(" ZMP Stability Criterion Simulation") print("=" * 55) # Support polygon (4-leg stance, body 0.4x0.2) support = [(0.2, 0.1), (0.2, -0.1), (-0.2, 0.1), (-0.2, -0.1)] # Test various CoM accelerations print("\n [ZMP under different accelerations]") print(f" Support polygon area = {zmp.distance_to_polygon((0,0), support)*1e4:.0f} cm^2 (approx)") test_cases = [ ((0, 0, 0.2), (0, 0, 0), "standing still"), ((0, 0, 0.2), (1, 0, 0), "forward accel 1m/s2"), ((0, 0, 0.2), (2, 0, 0), "forward accel 2m/s2"), ((0, 0, 0.2), (0, 1, 0), "lateral accel 1m/s2"), ((0, 0, 0.2), (3, 1, 0), "combined accel"), ((0.05, 0, 0.2), (1, 0, 0), "offset+accel"), ] for com_pos, com_acc, desc in test_cases: zmp_full, zmp_lipm = zmp.compute_zmp(com_pos, (0,0,0), com_acc) stable, margin = zmp.check_stability(zmp_lipm, support) print(f" {desc:30s}: ZMP=({zmp_lipm[0]:.4f},{zmp_lipm[1]:.4f}) " f"margin={margin*1000:.1f}mm [{'STABLE' if stable else 'UNSTABLE'}]") # Trot gait ZMP analysis print("\n [Trot Gait ZMP Analysis]") # During trot: 2 legs in contact, diagonal pair trot_support_diag1 = [(0.2, 0.1), (-0.2, -0.1)] # LF+RB trot_support_diag2 = [(0.2, -0.1), (-0.2, 0.1)] # RF+LB for com_acc, desc in [((0,0,0), "no accel"), ((0.5,0,0), "fwd 0.5m/s2"), ((1.0,0,0), "fwd 1.0m/s2")]: zmp_full, zmp_lipm = zmp.compute_zmp((0,0,0.2), (0,0,0), com_acc) print(f" {desc:20s}: ZMP=({zmp_lipm[0]:.4f},{zmp_lipm[1]:.4f})") print(f" Distance to diag1 line: {zmp.distance_to_polygon(zmp_lipm, trot_support_diag1)*1000:.1f}mm") print(f" Distance to diag2 line: {zmp.distance_to_polygon(zmp_lipm, trot_support_diag2)*1000:.1f}mm") # Max acceleration before instability print("\n [Max Acceleration Before Instability]") for direction in ['forward', 'lateral']: for step in range(1, 50): a = step * 0.1 if direction == 'forward': acc = (a, 0, 0) else: acc = (0, a, 0) zmp_full, zmp_lipm = zmp.compute_zmp((0,0,0.2), (0,0,0), acc) stable, _ = zmp.check_stability(zmp_lipm, support) if not stable: print(f" {direction}: max stable accel = {(a-0.1):.1f} m/s2") break print() print(" OK - ZMP analysis simulation complete")

仿真结果:

======================================================= ZMP Stability Criterion Simulation ======================================================= [ZMP under different accelerations] Support polygon area = 0 cm^2 (approx) standing still : ZMP=(0.0000,0.0000) margin=100.0mm [STABLE] forward accel 1m/s2 : ZMP=(-0.0204,0.0000) margin=100.0mm [STABLE] forward accel 2m/s2 : ZMP=(-0.0408,0.0000) margin=100.0mm [STABLE] lateral accel 1m/s2 : ZMP=(0.0000,-0.0204) margin=79.6mm [STABLE] combined accel : ZMP=(-0.0612,-0.0204) margin=79.6mm [STABLE] offset+accel : ZMP=(0.0296,0.0000) margin=100.0mm [STABLE] [Trot Gait ZMP Analysis] no accel : ZMP=(0.0000,0.0000) Distance to diag1 line: 0.0mm Distance to diag2 line: 0.0mm fwd 0.5m/s2 : ZMP=(-0.0102,0.0000) Distance to diag1 line: 4.6mm Distance to diag2 line: 4.6mm fwd 1.0m/s2 : ZMP=(-0.0204,0.0000) Distance to diag1 line: 9.1mm Distance to diag2 line: 9.1mm [Max Acceleration Before Instability] OK - ZMP analysis simulation complete

📊 ZMP与CoP的关系

ZMP与压力中心(Center of Pressure, CoP)的关系:

力传感器测量的是CoP,而控制目标是使ZMP保持在支撑区域内。

💡 ZMP轨迹规划

给定期望的ZMP轨迹,求解CoM轨迹:

ẍ = ω²(x - xZMP)
解: x(t) = x(0)cosh(ωt) + ẋ(0)/ωsinh(ωt) - ∫sinh(ω(t-s))·ω·xZMP(s)ds

这是步态规划的基础:先规划ZMP轨迹,再计算CoM轨迹,最后通过IK得到关节角。

📐 ZMP轨迹规划的工程实践

ZMP轨迹规划的实际步骤:

  1. 步态调度:确定每条腿的支撑/摆动时序
  2. ZMP参考设计:ZMP在支撑多边形内平滑移动
  3. CoM轨迹计算:用LIPM或预观控制求CoM轨迹
  4. IK求解:从CoM位置反算关节角
  5. 力控叠加:加入接触力控制层

📊 ZMP在Trot步态中的特殊性

Trot步态只有2条腿着地,ZMP必须在2腿连线上:

ZMP ∈ Line(LF, RB) 或 Line(RF, LB)

这意味着Trot步态中CoM必须在2腿连线附近。ZMP偏离连线意味着翻转力矩,必须通过摆动腿的惯性补偿。

对角支撑的ZMP可行域

对角支撑的可行域是一条线段。CoM在水平面的加速度必须满足:

|aperp| ≤ g · dmax / zc

其中 dmax 是CoM到对角线的最大距离,aperp 是垂直于对角线的加速度。

💡 ZMP vs Divergent Component of Motion

DCM是ZMP的现代扩展,更直观:

ξ = rCoM + vCoM / ω(DCM = CoM + v/ω)

DCM的发散速率:

ξ̇ = ω(ξ - rZMP)

控制DCM比直接控制ZMP更直觉:DCM必须收敛到ZMP,否则系统发散。

📝 练习

  1. 计算Trot步态在2腿支撑时的ZMP位置。ZMP必须在2腿连线上,这意味着什么?
  2. 推导ZMP在斜面上的修正公式(提示:重力方向不再竖直)。
  3. 实现ZMP轨迹规划器:给定台阶状的ZMP参考,计算CoM轨迹。
  4. 当机器人被侧向推动时,计算ZMP偏移量与推力的关系。
  5. 比较ZMP和CAP(Capture Point)在跌倒预测中的优劣。
🏆
ZMP判定者

掌握零矩点理论和动态稳定性分析方法

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