接触估计 Contact Estimation
这一课在人形机器 人里非常关键,因为从这里开始,你会发现:
光知道身体姿态还不够,机器人还必须知道自己现在“靠什么支撑”。
机器人并没有“脚底感觉”,机器人必须自己判断:
我哪只脚现在真的接触地面?
传感器 ↓ ┌─────────────────┐ │ 状态估计 │ │ │ │ 身体姿态 │ │ 身体速度 │ │ 身体位置 │ │ 关节状态 │ │ 接触状态 ← 新的 │ └────────┬────────┘ ↓ MPC/WBC所以:
“脚有没有踩地”本身就是机器人状态的一部分。
机器人怎么判断脚踩没踩地?
足底力传感器
比如脚底测到:
Fz = 400 N很明显:
这只脚正在承受机器人重量。
于是:
contact = true滞回 Hysteresis
不用一个阈值:
30 N而是使用两个阈值:
接触阈值:40 N 离地阈值:20 N于是:
从离地变成接触
必须:
Fz > 40 N才宣布:
踩地了。
已经踩地以后
只有:
Fz < 20 N才宣布:
离地了。
中间:
20 ~ 40 N怎么办?
保持之前状态。
这样刚才:
35 28 40 32就不会疯狂:
0 1 0 1来回切。
这是工程控制中特别常见的思想:
不要因为传感器一点点抖动,就不断改变系统状态。
但是如果没有脚底力传感器怎么办?有些机器人可以结合:
关节编码器 + IMU + 电机力矩 + 运动学去推测。例如机器人认为:
脚正在往下运动突然发现:
脚的位置不再继续下降同时:
关节力矩发生变化那么:
很可能脚碰地了。
所以接触状态不一定必须靠一个传感器,也可以:多信息融合
现在把编码器、IMU、接触一起放进去
你之前学的东西终于可以合起来了:
IMU ┌─────┴─────┐ ↓ ↓ 角速度 加速度 │ │ └─────┬─────┘ ↓ 身体运动预测 │ │ 编码器 ─────────┼────→ 腿部运动学 │ ↓ 状态估计器 ↑ │ 脚接触状态 │ 足底力 / 力矩 / 运动学 │ ↓ ┌──────────────┐ │ Base姿态 │ │ Base速度 │ │ Base位置 │ │ 关节状态 │ │ 接触状态 │ └───────┬──────┘ ↓ WBC/MPC这已经非常接近真正人形机器人的系统结构了。
Base
以后你会经常看到:
base base_pos base_vel base_quat base_omega这里的base可以粗略理解:
机器人身体主体的参考坐标系。
在人形机器人里通常会选择类似:
pelvis torso base link作为主体,于是状态估计很重要的任务就是估:
Base position Base orientation Base linear velocity Base angular velocity翻译:
身体在哪里 身体朝哪 身体移动多快 身体旋转多快“接触”和“无滑动接触”
必须区分:
Contact和:
Stable Contact也就是:
接触地面不等于:
牢牢固定在地面所以高级一点的状态估计还要判断:
有没有打滑?例如:
脚底力: 说我踩地但是运动学估出来:
脚相对于世界似乎在横向快速移动同时 IMU 也发现:
身体运动和预期对不上那么:
这只脚可能正在滑。
这时候状态估计器应该:
降低这只脚的信息可信度而不是死信它。你会发现:
状态估计真正核心不是“算一个公式”,而是不断判断“现在该相信谁”。
刚开始可能觉得状态估计就是:
IMU滤一滤其实远远不是。
人形机器人真正需要的状态可能包括:
关节位置 q 关节速度 dq 身体姿态 R / quaternion 身体角速度 ω 身体位置 p 身体线速度 v 左脚是否接触 右脚是否接触 脚是否打滑甚至还可能包括:
IMU bias 外力 地面状态所以:
State Estimation
真正可以理解成:
把一堆不完美的传感器信息拼起来,尽可能还原机器人此时此刻真实的物理状态。
真实机器人 │ ┌─────────────────┼─────────────────┐ ↓ ↓ ↓ 编码器 IMU 足部信息 │ │ │ └─────────────────┼─────────────────┘ ↓ State Estimator │ ┌──────────────┼───────────────┐ ↓ ↓ ↓ q、dq Base状态 Contact状态 │ │ │ └──────────────┼───────────────┘ ↓ MPC / WBC ↓ 控制命令 ↓ 电机 ↓ 机器人这一张图,你后面会反复用。
Base Velocity Estimation
编码器 + IMU + 支撑脚接触 + 机器人运动学 ↓ Base Velocity也就是:
机器人怎么知道自己的身体正在往哪个方向移动、移动多快。
编码器能测:
髋关节角 膝关节角 踝关节角 肩关节角 ……还能估:
各个关节转得多快也就是:
q dq但是编码器不知道:
整个机器人相对于房间到底有没有移动。
关节运动 ≠ 整个机器人在世界中的运动。
IMU主要给你:
陀螺仪 → 角速度 加速度计 → 比力 / 与重力相关的加速度测量它没有一个传感器直接说:
“现在机器人向前 0.43 m/s”没有。所以 Base Velocity 必须:估出来,这就是为什么它属于:
State Estimation加速度积分
我们先假装世界很完美,如果机器人向前加速:
a = 1 m/s²那么1秒后大概:
v = 1 m/s所以理论上:
加速度 ↓ 积分 速度重力
加速度计受到姿态影响特别大,因为我们真正想知道:
机器人平移加速度但传感器读数里会混进:
重力方向所以需要先知道:
机器人当前姿态再把重力分量处理掉。链路大概是:
Accelerometer ↓ 原始测量 ↓ 利用姿态估计 ↓ 处理重力影响 ↓ 得到线性加速度估计 ↓ 积分 ↓ 速度Leg Odometry 腿式里程计
它的核心思想之一就是:
利用腿部编码器 + 运动学 + 支撑脚约束,反推出身体的运动。
大致:
编码器 q、dq ↓ 腿部运动学 ↓ 算脚相对 Base 的运动 ↓ 知道支撑脚世界速度 ≈ 0 ↓ 反推出 Base velocityIMU / \ / \ 加速度 角速度 │ │ │ │ └──────┬──────┘ │ ▼ Base预测 ▲ │ 编码器 q、dq │ ▼ 腿部运动学 │ ▼ 支撑脚约束 │ ▼ Base速度修正这才是真正的 Sensor Fusion 传感器融合
IMU的优点
IMU更新很快。对:
突然运动 身体快速转动 短时间动态变化非常敏感,所以:
短时间变化通常很好。
IMU的问题
主要是:
bias 噪声 重力处理误差 积分漂移所以:
时间长了容易漂。
腿部运动学的优点
如果:
支撑脚真的牢牢踩地那么:
脚速度≈0是一个很强的物理约束,可以帮助纠正:
IMU速度漂移腿部运动学的问题
假如:
脚打滑那:
脚速度≈0这条假设就错了,于是 Leg Odometry 也会骗人。
具体的走路过程理解
假设机器人正在向前走,当前:
左脚 = 支撑 右脚 = 摆动状态估计开始工作。
第一步:IMU
IMU告诉我们:
身体角速度 身体加速度比如:
身体正在稍微向前加速第二步:编码器
编码器告诉我们:
左腿髋关节速度 左膝速度 左踝速度也就是:
dq_left第三步:运动学
根据:
q_left + dq_left计算:
左脚相对于Base怎么运动比如:
向后0.45 m/s第四步:接触估计
系统判断:
左脚 = 稳定接触因此:
左脚世界速度 ≈ 0第五步:反推
于是:
Base大约向前0.45m/s第六步:和IMU融合
IMU可能预测:
0.48 m/s腿运动学估计:
0.45 m/s状态估计器综合后可能输出:
0.46 m/s注意,这里的数字只是为了理解。
真正系统不会简单地:
(0.48+0.45)/2完事,而会根据:
传感器噪声 可信度 接触状态 历史状态 模型做融合。
EKF 和 KF
因为机器人运动通常是:
旋转 三维姿态 非线性运动学不是简单的:
直线匀速小车很多关系不是简单线性的。
“浮动基座机器人” Floating-base Robot
浮动基座机器人。
为什么?
机械臂通常:
底座 ████████████ 固定在桌子上所以 Base:
位置固定你根本不用估:
base velocity但人形机器人:
O /|\ | / \整个身体可以:
前后移动 左右移动 上下移动 翻滚 俯仰 转向所以 Base 并没有固定在世界上。
这就叫:
Floating Base。
因此状态里除了关节:
q_joint还必须考虑:
Base position Base orientation Base velocity Base angular velocity状态估计的输出
你以后看人形机器人代码,可能会看到类似:
state.base_position state.base_velocity state.base_orientation state.base_angular_velocity state.joint_position state.joint_velocity state.left_contact state.right_contact也可能名称完全不同。
但意思差不多:
身体位置 身体速度 身体姿态 身体角速度 关节位置 关节速度 左右脚接触状态然后:
State Estimator ↓ MPC ↓ WBC编码器 ↓ q、dq ↓ 知道机器人关节怎么动陀螺仪 ↓ 身体角速度 ↓ 帮助估计姿态变化加速度计 ↓ 身体受到的惯性/重力相关信息 ↓ 帮助预测运动姿态估计 ↓ 知道身体朝哪 ↓ 正确处理重力方向Contact Estimation ↓ 知道哪只脚踩地Leg Kinematics ↓ 利用支撑脚约束 ↓ 估计Base VelocityKalman / EKF ↓ 融合所有信息最终:
State Estimate │ ├─ q ├─ dq ├─ base position ├─ base velocity ├─ base orientation ├─ base angular velocity └─ contact state再交给:
MPC / WBC这已经非常接近真正的人形机器人运控架构了。
┌──────────────┐ │ Encoder │ │ q / dq │ └──────┬───────┘ │ ↓ Leg Kinematics │ │ ┌──────────────┐ │ │ IMU │ │ │ gyro / acc │ │ └──────┬───────┘ │ │ │ ↓ ↓ Orientation Foot Velocity Estimation │ │ │ │ ┌──────┴──────┐ │ │ Contact │ │ │ Estimation │ │ └──────┬──────┘ │ │ └─────────┬──────────┘ ↓ ┌──────────────────┐ │ State Estimator │ │ EKF / etc. │ └────────┬─────────┘ ↓ ┌──────────────────┐ │ Base position │ │ Base velocity │ │ Base orientation │ │ Base omega │ │ q / dq │ │ Contact │ └────────┬─────────┘ ↓ MPC / WBC ↓ Robot加速度积分能得到速度,但:
积分会放大 bias、噪声和姿态误差,长期会漂。
利用:
编码器 + 腿部运动学 + 支撑脚约束可以反推出:
Base velocity这就是腿式里程计的重要思想。
IMU负责:
短时间快速预测。
支撑脚/腿部运动学负责:
提供物理约束、纠正漂移。
滤波器负责:
把它们融合起来。
IMU ↘ 编码器 → State Estimator → Base State → MPC/WBC ↗ 接触而不是:
某一个传感器 ↓ 直接得到机器人真实状态机器人永远没有这么幸运。
Odometry —— 里程计
最直觉的理解:
速度 ↓ 不断累积 ↓ 位置比如机器人向前以:
1 m/s走1秒。那大概前进:
1 m再走1秒:
2 m所以:
当前位置 = 上一时刻的位置 + 这一小段时间走过的距离程序思维大概就是:
position = position + velocity * dt;例如控制周期:
dt = 0.01 s当前速度:
vx = 0.5 m/s这一帧就大概前进:
0.5 × 0.01 = 0.005 m也就是5毫米。然后下一帧继续加。
机器人领域里的 odometry :
根据自身传感器,估计“我相对于刚开始的位置,移动了多少”。
它通常告诉你的是相对运动。
而不是:
“我现在在东京某实验室的精确经纬度和绝对坐标。”
Leg Odometry
上一课我们讲过:
编码器 q、dq + 支撑脚 + 腿部运动学 ↓ Base Velocity现在只要继续:
Base Velocity ↓ 积分 ↓ Base Position于是:
Encoder ↓ Leg Kinematics ↓ 支撑脚约束 ↓ Base Velocity ↓ 时间积分 ↓ Base Position这就是腿式机器人 odometry 的基本思路之一。甚至可以不用先算速度,直接利用支撑脚位置
还有另外一种直觉。假设机器人左脚落地以后,我们说:
左脚在世界的位置暂时固定例如:
left_foot_world = (1.0, 0.1, 0)编码器告诉我们:
Base相对于左脚在哪里那么反过来:
已知左脚世界位置 + 腿部正运动学 ↓ 推算Base世界位置例如:
地面 ────────────● 左脚 \ \ [Base]脚作为锚点,这时候机器人身体怎么移动,可以通过腿的几何关系推出来,但机器人走一步之后,锚点变了,比如:
阶段1 左脚支撑 右脚向前迈此时:
左脚 = 世界锚点然后右脚落地:
阶段2 左右脚都接触接下来左脚抬起来:
阶段3 右脚支撑现在:
右脚 = 新的世界锚点于是整个过程中:
左脚锚定 ↓ 右脚落地 ↓ 锚点切换 ↓ 右脚锚定 ↓ 左脚落地 ↓ 再次切换这就是腿式机器人走路时很典型的过程。
“外部纠偏”
假设机器人 odometry 认为:
我在 x = 10.4 m但另一个外部传感器告诉它:
实际上你应该在 x = 9.9 m那状态估计器就可以发现:
我漂了0.5 m然后进行修正。
所以完整思路逐渐变成:
IMU + Encoder + Leg Odometry ↓ 高频运动估计 ↓ 但是会漂 ↓ 外部定位信息 ↓ 修正漂移谁可以提供外部位置参考?
GPS / GNSS:室外可以直接给全球位置参考,但室内通常不好用。
VIO:Visual-Inertial Odometry,视觉+IMU,根据摄像头看到的环境特征估运动。
LiDAR Odometry / SLAM:用激光雷达扫描环境,根据前后扫描的匹配估计机器人移动。
Motion Capture:实验室里的光学动作捕捉系统可以非常准确地给机器人世界位置。
UWB / Beacon:通过外部基站提供位置约束。
这些东西看起来五花八门,其实共同目的就是:
给机器人一个来自“外部世界”的参考,不让自己的内部 odometry 无限漂下去。
Localization
Odometry:我从刚才到现在动了多少。
Localization:我现在到底在地图的哪里。
例如:
Odometry: 我向前走了5米。而:
Localization: 我现在位于实验室地图坐标 x = 12.3 y = 4.7所以可以粗略理解:
Odometry ↓ 关注相对运动 Localization ↓ 关注世界/地图中的位置当然实际工程里两者会深度融合,不一定严格分开。
State Estimation 和 Localization
State Estimation 更大。
它问:
机器人现在整个状态是什么?
可能包括:
Base Position Base Velocity Base Orientation Base Angular Velocity q dq Contact State IMU Bias ...而 Localization 主要关心:
我在哪里 + 我朝哪个方向所以你可以把关系暂时理解成:
State Estimation │ ┌────────────┼────────────┐ ↓ ↓ ↓ Orientation Velocity Position │ ↓ Localization不是严格的数学分类,但非常适合你现在建立框架。
odom
机器人根据自己的传感器“推算出来的局部运动坐标系”。
它不是某个传感器,也不是某个关节,而通常是一个坐标系 / 里程计参考系,在 ROS / ROS2 里,你以后经常会看到:
map ↓ odom ↓ base_link这里分别可以先这样理解:
map:全局世界坐标系,强调“我在整个地图哪里”odom:局部连续坐标系,强调“我从启动以后走了多少”base_link:机器人身体自身的坐标系
例如机器人启动时:
odom 原点 O | | Robot假设机器人向前走了 1 m。
那么可能有:
base_link 相对 odom: x = 1.0 m y = 0 yaw = 0也就是说:
机器人根据编码器、IMU等信息估计:
“我相对于刚开始的位置,往前走了1米。”
这就是 odometry,中文叫:里程计 / 里程估计,所以常见 TF 树:
map │ └── odom │ └── base_link │ ├── torso ├── left_foot ├── right_foot └── ...对人形机器人来说怎么理解?
例如你的机器人刚启动:
odom原点 ↓ 机器人骨盆附近机器人往前走:
odom O────────────────→ Robot你可以问:
机器人 base_link 相对于 odom 走了多远?比如:
x = 2.3 m y = 0.1 m yaw = 5°这表示:
从里程计参考系看,机器人已经向前移动约2.3m,横向偏了0.1m,朝向转了5°。
odom和我们刚才讲的World有什么区别?
这个地方很关键。
我们前面为了讲运动学,经常用:
World泛指“外部固定参考系”。
但在真正 ROS 系统里,可能会进一步细分成:
map odom base_link所以你可以暂时理解:
World是一个泛称。而:
odom是实际工程中一种具体的世界参考系——由里程计积分得到的局部世界坐标系。
odom和base_link一定不要混
base_link:
跟着机器人身体一起走。
odom:
通常固定在机器人启动附近,不跟着机器人移动。
例如机器人往前走:
odom O | |-----------> base_link机器人继续走:
odom O | |---------------------> base_link所以:
odom → base_link这个 Transform 一直在变化。
一句话记住
odom = 机器人通过编码器、IMU等状态估计得到的 “从启动位置开始,我现在移动到哪里了” 的参考坐标系而它最大的特点:
连续、平滑,但会累计漂移。
你现在顺便把这三个记住就够了:
map = 全局定位参考系 odom = 局部里程计参考系 base_link = 机器人本体参考系等我们后面讲 ROS2 TF 时,我会专门把map → odom → base_link → pelvis/foot/hand这整棵树给你彻底讲通。
控制状态和导航状态
这是一个非常工程化的概念,导航系统关心:
我在整个地图里准确在哪控制系统更在意:
我最近这10毫秒到底怎么动了控制需要:
高频 低延迟 连续 平滑而全局定位可能:
频率比较低 偶尔修正 甚至发生跳变所以真正机器人系统里,可能不会把某个全局定位结果:
原封不动直接塞给底层控制器,而会进行融合和坐标系管理。
EKF
现在 EKF 又多了一层含义,状态可能是:
x = Base position Base velocity Base orientation IMU bias ...IMU负责:
快速预测腿部 odometry:
提供支撑约束视觉或者 LiDAR:
提供环境参考于是:
IMU │ ↓ 预测 │ ▼ ┌─────────────┐ Encoder →│ │← Leg Odometry │ EKF / State │ Vision →│ Estimator │← LiDAR │ │ └──────┬──────┘ ↓ Base State Estimate你现在再看“多传感器融合”这句话,应该不再只是一个抽象词了。
状态估计 —— 可信度
IMU:
短期很可信 长期位置不可信支撑脚:
稳定接触时很可信 打滑时不可信视觉:
看到丰富纹理时可信 黑暗/快速运动时可能变差LiDAR:
结构丰富环境很好 某些退化环境可能不好于是状态估计器真正不断做的是:
根据当前情况决定“这一刻更应该相信谁”。
这其实就是你前面学 Kalman Filter 时那个思想的机器人版本:
预测有多可信? 测量有多可信? ↓ 决定各相信多少IMU ┌─────┴─────┐ ↓ ↓ 角速度 加速度 │ │ └─────┬─────┘ ↓ 短期运动预测 │ │ Encoder q,dq ─────┤ │ │ ↓ │ Leg Kinematics │ │ │ ↓ │ Contact / Foot │ Constraint │ │ │ └──────┬────┘ ↓ Local Odometry │ ┌─────┴─────┐ ↓ ↓ Base Velocity Base Position │ │ └─────┬─────┘ ↓ State Estimator ↑ 外部定位信息 Vision/LiDAR/GNSS │ ↓ 长期漂移修正Odometry = “我移动了多少?”Localization = “我现在在世界哪里?”State Estimation = “我现在整个机器人是什么状态?”其中:
State Estimation范围最大。