机器人仿真实践(第0卷)------002机器人定义与发展史:数学/物理原理
机器人为什么能够移动、转向、保持平衡,并准确到达目标位置?如果继续向机器人内部深入,会发现这些能力最终都建立在数学和物理规律之上。本文从机器人的数学与物理基础出发,认识坐标、位置、姿态、速度、加速度、力、力矩、质量与惯性等核心概念,并初步理解向量、矩阵、三角函数以及牛顿力学在机器人中的作用,建立"现实运动如何转化为数学模型"的基本认识,为后续运动学、动力学、控制、SLAM、路径规划及机器人仿真打下基础。

一、机器人为什么离不开数学和物理?
我们前面一直在讨论机器人能做什么:机械臂可以抓取零件,AMR 可以在仓库里自主移动,四足机器人可以在复杂地形中行走,无人机可以悬停,人形机器人甚至可以完成跑、跳、搬运等复杂动作。

但如果继续往里面追问一步:
机器人凭什么知道自己应该怎么动?
比如我们告诉机械臂:
"把夹爪向前移动 20 cm。"
对于人来说,这句话很好理解。但机器人面对的却是一系列完全不同的问题:
夹爪现在在哪里?
"向前"到底是哪个方向?
20 cm 在当前坐标系里对应多少?
六个关节分别应该旋转多少度?
运动过程中速度应该多快?
需要多大的力矩?
机械臂会不会因为惯性冲过目标位置?
有没有可能撞到旁边的设备?
到了这里,我们会发现:
一句非常简单的人类指令,一旦交给机器人执行,最后几乎一定会变成数学和物理问题。
1. 数学负责把现实世界"说清楚"
先来看一个最简单的问题:
机器人在哪里?
我们用眼睛看一眼就知道:
"机器人在桌子旁边。"
但计算机不理解"旁边"这种模糊描述。
它需要更加准确的信息,例如:
text
x = 2.3 m
y = 1.6 m
z = 0.0 m
这样,现实世界中的"位置"第一次变成了计算机能够处理的数据。
但知道位置还不够。
机器人可能还需要知道:
"我现在朝哪个方向?"
于是又会出现角度、方向、姿态。
如果机械臂有六个关节,我们还需要描述:
text
Joint 1 = 30°
Joint 2 = -15°
Joint 3 = 48°
Joint 4 = 10°
Joint 5 = 72°
Joint 6 = 0°
这时候,我们实际上已经在做一件非常重要的事情:
把现实世界中的机器人,转换成数学能够描述的机器人。
所以机器人里面会不断出现:
坐标、向量、角度、三角函数、矩阵、坐标变换......
这些东西并不是数学家为了增加机器人开发难度硬塞进来的。
而是因为机器人生活在一个真实的三维世界里。
我们必须找到一种足够准确的方法,把这个世界描述给计算机。
2. 机器人一旦开始运动,数学问题就更多了
假设机器人已经知道自己现在位于:
text
x = 0 m
y = 0 m
1 秒以后,它到了:
text
x = 1 m
y = 0 m
那么我们就可以说:
它发生了位移。
如果继续考虑时间:
1 秒移动 1 米。
于是出现:
速度(Velocity)。
如果机器人原来速度是 0,后来越来越快:
0 m/s → 0.5 m/s → 1 m/s → 2 m/s
又出现:
加速度(Acceleration)。
如果机器人不是直线前进,而是在转弯:
又会出现:
角度、角速度、角加速度。
所以机器人只要一动起来,我们马上就会遇到:
text
位置
↓
位移
↓
速度
↓
加速度
以及
角度
↓
角速度
↓
角加速度
这也是为什么我们刚刚认识的 Encoder(编码器) 和 **IMU(惯性测量单元)**如此重要。
编码器可以帮助机器人知道:
轮子或者关节转了多少。
IMU 则可以帮助机器人测量:
身体发生了怎样的加速和旋转。
也就是说,数学不仅负责描述机器人长什么样、在哪里,还负责描述:
机器人正在怎样变化。
【插图位置:这里插入"现实机器人 → 数学 / 物理 → 模型与仿真"结构示意图】
3. 数学告诉我们"怎么动",物理进一步解释"为什么会这样动"
现在假设我们已经通过数学知道:
一个机器人正在以 2 m/s 的速度向前运动。
但新的问题马上出现:
它为什么会运动?
如果想让它从静止加速到 2 m/s,需要多大的力?
机器人越重,是不是越难加速?
机械臂拿着 1 kg 的东西和拿着 20 kg 的东西,电机需要输出的力一样吗?
轮子为什么有时候会打滑?
机器人急停的时候,为什么不会瞬间停在原地?
人形机器人身体稍微倾斜,为什么可能直接摔倒?
这些问题,仅仅知道位置和速度已经不够了。
这时候就轮到物理学登场。
最经典的例子就是:
text
F = ma
也就是牛顿第二定律。
它告诉我们:
物体的运动状态发生变化,需要力。
同样一个机器人:
质量越大,要产生相同的加速度,通常就需要更大的合力。
这也是为什么机器人不能只研究"我要它怎么运动",还必须研究:
为了产生这种运动,现实世界究竟需要什么条件?
于是我们开始遇到:
- 质量(Mass)
- 重力(Gravity)
- 力(Force)
- 摩擦力(Friction)
- 力矩(Torque)
- 惯性(Inertia)
- 动量(Momentum)
- 能量(Energy)
这些概念。
比如机械臂。
如果机械臂水平伸出去:
text
基座 ──────── 机械臂 ──────── 负载
↓
重力
负载越重、距离关节越远,对关节产生的力矩通常就越大。
所以机械臂厂家为什么会告诉我们:
最大负载多少 kg。
为什么还要告诉我们:
最大臂展多少 mm。
因为机器人面对的不是一个纯粹的几何世界。
它最终还要遵守真实世界的物理规律。
4. 一个很重要的区别:运动学和动力学
到了这里,我们就可以第一次认识机器人领域两个非常重要的词:
运动学(Kinematics)
和
动力学(Dynamics)。
它们研究的东西并不完全一样。
运动学主要关心:
机器人是怎么运动的?
例如:
机械臂每个关节分别转多少度,末端会到哪里?
轮子分别以什么速度旋转,机器人会向哪个方向移动?
这里重点研究的是:
位置、姿态、速度以及它们之间的几何关系。
而动力学会继续追问:
为什么会产生这样的运动?
比如:
机械臂关节需要输出多大的力矩?
机器人受到外力以后会产生多大的加速度?
一个 50 kg 的机器人和一个 5 kg 的机器人,想达到相同加速度,需要的驱动力有什么区别?
于是动力学开始把:
质量、惯性、力、力矩、重力、摩擦
全部加入进来。
可以先用一句非常简单的话记:
| 领域 | 主要关心的问题 |
|---|---|
| 运动学 Kinematics | 机器人怎么动? |
| 动力学 Dynamics | 机器人为什么这样动,需要多大的力? |
这两个概念后面会贯穿机械臂、移动机器人、四足机器人和人形机器人。
5. 数学和物理最终会在"控制"这里重新汇合
知道机器人应该怎么运动,也知道现实世界中的力是怎么回事以后,事情还没有结束。
因为机器人真正运行的时候,会存在大量误差。
比如我们告诉一个关节:
转到 30°。
控制器发出命令以后,电机开始旋转。
但由于:
- 摩擦
- 负载
- 惯性
- 机械间隙
- 电机性能
- 外部扰动
实际关节可能只到了:
text
28.6°
这时候编码器测量实际角度:
text
目标角度 = 30°
实际角度 = 28.6°
误差 = 1.4°
控制器发现:
还没到。
于是继续调整电机。
#mermaid-svg-FGYpYgrLhKSN82yN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FGYpYgrLhKSN82yN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FGYpYgrLhKSN82yN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FGYpYgrLhKSN82yN .error-icon{fill:#552222;}#mermaid-svg-FGYpYgrLhKSN82yN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FGYpYgrLhKSN82yN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FGYpYgrLhKSN82yN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FGYpYgrLhKSN82yN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FGYpYgrLhKSN82yN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FGYpYgrLhKSN82yN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FGYpYgrLhKSN82yN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FGYpYgrLhKSN82yN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FGYpYgrLhKSN82yN .marker.cross{stroke:#333333;}#mermaid-svg-FGYpYgrLhKSN82yN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FGYpYgrLhKSN82yN p{margin:0;}#mermaid-svg-FGYpYgrLhKSN82yN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FGYpYgrLhKSN82yN .cluster-label text{fill:#333;}#mermaid-svg-FGYpYgrLhKSN82yN .cluster-label span{color:#333;}#mermaid-svg-FGYpYgrLhKSN82yN .cluster-label span p{background-color:transparent;}#mermaid-svg-FGYpYgrLhKSN82yN .label text,#mermaid-svg-FGYpYgrLhKSN82yN span{fill:#333;color:#333;}#mermaid-svg-FGYpYgrLhKSN82yN .node rect,#mermaid-svg-FGYpYgrLhKSN82yN .node circle,#mermaid-svg-FGYpYgrLhKSN82yN .node ellipse,#mermaid-svg-FGYpYgrLhKSN82yN .node polygon,#mermaid-svg-FGYpYgrLhKSN82yN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FGYpYgrLhKSN82yN .rough-node .label text,#mermaid-svg-FGYpYgrLhKSN82yN .node .label text,#mermaid-svg-FGYpYgrLhKSN82yN .image-shape .label,#mermaid-svg-FGYpYgrLhKSN82yN .icon-shape .label{text-anchor:middle;}#mermaid-svg-FGYpYgrLhKSN82yN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FGYpYgrLhKSN82yN .rough-node .label,#mermaid-svg-FGYpYgrLhKSN82yN .node .label,#mermaid-svg-FGYpYgrLhKSN82yN .image-shape .label,#mermaid-svg-FGYpYgrLhKSN82yN .icon-shape .label{text-align:center;}#mermaid-svg-FGYpYgrLhKSN82yN .node.clickable{cursor:pointer;}#mermaid-svg-FGYpYgrLhKSN82yN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FGYpYgrLhKSN82yN .arrowheadPath{fill:#333333;}#mermaid-svg-FGYpYgrLhKSN82yN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FGYpYgrLhKSN82yN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FGYpYgrLhKSN82yN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FGYpYgrLhKSN82yN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FGYpYgrLhKSN82yN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FGYpYgrLhKSN82yN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FGYpYgrLhKSN82yN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FGYpYgrLhKSN82yN .cluster text{fill:#333;}#mermaid-svg-FGYpYgrLhKSN82yN .cluster span{color:#333;}#mermaid-svg-FGYpYgrLhKSN82yN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FGYpYgrLhKSN82yN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FGYpYgrLhKSN82yN rect.text{fill:none;stroke-width:0;}#mermaid-svg-FGYpYgrLhKSN82yN .icon-shape,#mermaid-svg-FGYpYgrLhKSN82yN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FGYpYgrLhKSN82yN .icon-shape p,#mermaid-svg-FGYpYgrLhKSN82yN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FGYpYgrLhKSN82yN .icon-shape .label rect,#mermaid-svg-FGYpYgrLhKSN82yN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FGYpYgrLhKSN82yN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FGYpYgrLhKSN82yN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FGYpYgrLhKSN82yN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 目标状态
控制器
电机与机械结构
机器人产生运动
传感器测量
计算误差
这时候你会发现:
数学、物理、传感器和控制已经全部连接起来了。
数学帮助我们计算:
目标和实际到底差多少。
物理决定:
施加一个控制量以后,机器人会发生怎样的运动。
传感器负责告诉计算机:
现实中的机器人现在到底怎么样。
控制算法则不断根据这些信息调整输出。
这就是一个真正的机器人闭环。
6. 所以机器人里的数学和物理,不是为了"算题"
这也是我们学习机器人数学和物理时非常重要的一个原则。
我们不是重新上一遍高等数学课,也不是为了重新做大学物理习题。
真正需要研究的是:
这些数学和物理工具,在机器人里面到底解决什么问题?
比如以后看到:
向量
我们要想到:
可以表示位置、方向、速度、力。
看到:
矩阵
我们要想到:
可以描述坐标变换、旋转以及机器人不同坐标系之间的关系。
看到:
微积分
我们要想到:
位置、速度、加速度之间存在变化关系。
看到:
F = ma
我们要想到:
为什么不同质量的机器人产生相同加速度需要不同的力。
看到:
力矩
我们要想到:
为什么机械臂伸得越远、拿得越重,关节负担可能越大。
最终,我们真正要建立的是这样一条链:
#mermaid-svg-2gs7UG9dcVjiatRi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2gs7UG9dcVjiatRi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2gs7UG9dcVjiatRi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2gs7UG9dcVjiatRi .error-icon{fill:#552222;}#mermaid-svg-2gs7UG9dcVjiatRi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2gs7UG9dcVjiatRi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2gs7UG9dcVjiatRi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2gs7UG9dcVjiatRi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2gs7UG9dcVjiatRi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2gs7UG9dcVjiatRi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2gs7UG9dcVjiatRi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2gs7UG9dcVjiatRi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2gs7UG9dcVjiatRi .marker.cross{stroke:#333333;}#mermaid-svg-2gs7UG9dcVjiatRi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2gs7UG9dcVjiatRi p{margin:0;}#mermaid-svg-2gs7UG9dcVjiatRi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2gs7UG9dcVjiatRi .cluster-label text{fill:#333;}#mermaid-svg-2gs7UG9dcVjiatRi .cluster-label span{color:#333;}#mermaid-svg-2gs7UG9dcVjiatRi .cluster-label span p{background-color:transparent;}#mermaid-svg-2gs7UG9dcVjiatRi .label text,#mermaid-svg-2gs7UG9dcVjiatRi span{fill:#333;color:#333;}#mermaid-svg-2gs7UG9dcVjiatRi .node rect,#mermaid-svg-2gs7UG9dcVjiatRi .node circle,#mermaid-svg-2gs7UG9dcVjiatRi .node ellipse,#mermaid-svg-2gs7UG9dcVjiatRi .node polygon,#mermaid-svg-2gs7UG9dcVjiatRi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2gs7UG9dcVjiatRi .rough-node .label text,#mermaid-svg-2gs7UG9dcVjiatRi .node .label text,#mermaid-svg-2gs7UG9dcVjiatRi .image-shape .label,#mermaid-svg-2gs7UG9dcVjiatRi .icon-shape .label{text-anchor:middle;}#mermaid-svg-2gs7UG9dcVjiatRi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2gs7UG9dcVjiatRi .rough-node .label,#mermaid-svg-2gs7UG9dcVjiatRi .node .label,#mermaid-svg-2gs7UG9dcVjiatRi .image-shape .label,#mermaid-svg-2gs7UG9dcVjiatRi .icon-shape .label{text-align:center;}#mermaid-svg-2gs7UG9dcVjiatRi .node.clickable{cursor:pointer;}#mermaid-svg-2gs7UG9dcVjiatRi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2gs7UG9dcVjiatRi .arrowheadPath{fill:#333333;}#mermaid-svg-2gs7UG9dcVjiatRi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2gs7UG9dcVjiatRi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2gs7UG9dcVjiatRi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2gs7UG9dcVjiatRi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2gs7UG9dcVjiatRi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2gs7UG9dcVjiatRi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2gs7UG9dcVjiatRi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2gs7UG9dcVjiatRi .cluster text{fill:#333;}#mermaid-svg-2gs7UG9dcVjiatRi .cluster span{color:#333;}#mermaid-svg-2gs7UG9dcVjiatRi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2gs7UG9dcVjiatRi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2gs7UG9dcVjiatRi rect.text{fill:none;stroke-width:0;}#mermaid-svg-2gs7UG9dcVjiatRi .icon-shape,#mermaid-svg-2gs7UG9dcVjiatRi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2gs7UG9dcVjiatRi .icon-shape p,#mermaid-svg-2gs7UG9dcVjiatRi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2gs7UG9dcVjiatRi .icon-shape .label rect,#mermaid-svg-2gs7UG9dcVjiatRi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2gs7UG9dcVjiatRi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2gs7UG9dcVjiatRi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2gs7UG9dcVjiatRi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 真实机器人
数学描述
物理模型
计算与预测
控制机器人
真实运动
这条链非常重要。
因为后面我们要学习的:
机械臂运动学、机器人动力学、PID、MPC、SLAM、卡尔曼滤波、路径规划、Gazebo仿真......
看起来是完全不同的技术。
但继续往下拆,它们最终都在做同一件事情:
把现实世界转换成可以计算的数学模型,再利用数学和物理规律预测、规划和控制机器人的行为。
所以,机器人为什么离不开数学和物理?
答案其实非常简单:
因为机器人最终生活在真实的物理世界里,而数学,是我们让计算机理解这个世界最重要的语言之一。
二、描述机器人运动,首先要知道"它在哪里"
假设现在桌子上放着一瓶水,我们对一个人说:
"帮我把右边那瓶水拿过来。"
人通常不会觉得这句话有什么问题。
我们看一眼桌面,大概就能判断水在哪里、自己在哪里、手应该往哪个方向伸。
但如果把完全相同的任务交给机械臂,事情马上就麻烦了。
因为机器人并不知道什么叫:
"右边一点。"
也不知道:
"往前伸一些。"
更不知道:
"差不多到这里。"
这些人类非常自然的空间概念,对于计算机来说都太模糊了。
机器人真正需要的是:
目标在哪里?我在哪里?目标相对于我又在哪里?
所以无论我们以后研究机械臂、移动机器人、无人机还是人形机器人,只要机器人需要在现实空间中运动,首先就必须解决一个最基础的问题:
怎样用数学准确描述"在哪里"?
1. 人说"这里",机器人需要一个坐标
我们先从最简单的二维世界开始。
假设一台移动机器人在一个房间里。
人可以说:
"机器人现在就在房间左下角附近。"
但计算机更希望得到这样的信息:
text
x = 2.0 m
y = 1.5 m
这两个数字的意义是:
以某个提前约定好的位置作为原点 (0, 0),机器人沿 X 方向走了 2.0 m,沿 Y 方向走了 1.5 m。
于是原本模糊的:
"机器人在那里。"
第一次变成了一个可以计算的东西:
text
P = (2.0, 1.5)
这就是位置(Position)。
如果进入三维空间,就再增加一个 Z:
text
P = (x, y, z)
例如:
text
P = (2.0, 1.5, 0.8)
这时候我们不仅可以描述机器人在地面上的位置,还可以描述高度。
对于无人机来说,Z 可以表示飞行高度;
对于机械臂来说,Z 可以表示末端执行器离基准面的高度;
对于人形机器人来说,则可以用来描述身体、手、脚等不同部位在三维空间中的位置。
所以机器人里面为什么总是出现:
text
X
Y
Z
原因并不复杂。
因为计算机需要把现实世界中的"这里、那里、上面、下面、左边、右边",转换成可以计算的数字。
2. 只有位置还不够,我们还要知道机器人"朝哪边"

假设现在有两台机器人,它们的位置完全相同:
text
x = 2 m
y = 3 m
但是第一台朝东。
第二台朝北。
那么它们的状态一样吗?
当然不一样。
如果我们同时告诉两台机器人:
"向前走 1 米。"
它们最终会到达两个完全不同的位置。
这说明:
只知道机器人在哪里,还不足以完整描述机器人。
我们还需要知道:
机器人朝哪个方向。
这就是姿态(Orientation)。
对于一个在平面上运动的小车,我们可以暂时用一个角度表示:
text
θ = 0°
表示朝向 X 轴正方向。
如果:
text
θ = 90°
那么机器人已经旋转了 90°。
于是描述一台二维移动机器人时,经常会看到:
text
x
y
θ
也就是:
我在哪里 + 我朝哪里。
到了三维空间,事情会更加复杂。
无人机不仅可以向左、向右转,还可以:
抬头、低头、侧倾、翻滚。
机械臂末端也是一样。
一个夹爪即使到达完全相同的位置:
text
x = 500 mm
y = 200 mm
z = 300 mm
它仍然可能:
text
夹爪朝下
夹爪朝前
夹爪倾斜
夹爪翻转
这些状态显然不是一回事。
所以机器人领域通常不会只说:
Position(位置)
而是经常使用一个更加完整的概念:
Pose(位姿) = Position(位置) + Orientation(姿态)
这个词以后会非常非常常见。
ROS 2、SLAM、Nav2、机械臂运动学、视觉定位里面都会不断出现 Pose。
3. 更麻烦的问题来了:你说的位置,到底是"相对于谁"?

现在假设机械臂的摄像头发现了一个零件。
摄像头告诉我们:
text
零件位置:
x = 0.3 m
y = 0.1 m
z = 0.8 m
看起来数据已经很完整了。
但是还有一个致命的问题:
这三个数字是相对于谁测出来的?
如果是相对于摄像头:
text
Camera
↓
零件在前方 0.3 m
这是一个结果。
如果是相对于机械臂基座:
text
Robot Base
↓
零件在前方 0.3 m
可能完全是另外一个位置。
如果是相对于整个工厂:
text
World
↓
零件坐标 (0.3, 0.1, 0.8)
含义又变了。
所以:
一个坐标如果没有说明参考坐标系,很多时候是没有完整意义的。
这就引出了机器人领域另一个极其重要的概念:
坐标系(Coordinate Frame)。
一台机械臂里面可能同时存在:
text
世界坐标系 World
机械臂基座坐标系 Base
关节坐标系 Joint
末端执行器坐标系 Tool
摄像头坐标系 Camera
目标物体坐标系 Object
同一个物体,在这些不同坐标系下得到的 (x, y, z) 很可能完全不同。
但现实中的物体并没有移动。
变化的只是:
我们站在哪个参考系里描述它。
这和现实生活其实非常像。
假设我问:
"你在哪里?"
你回答:
"我在前面 3 米。"
这句话本身其实是不完整的。
因为我还要问:
"在谁的前面 3 米?"
只有确定参考对象以后,"前面 3 米"才真正具有意义。
机器人坐标系解决的,本质上就是这个问题。
4. 机器人真正需要的,是不断在不同坐标系之间"翻译"
现在我们终于可以理解为什么机器人里面会有那么多坐标系了。
假设机械臂头顶安装了一台摄像头。
摄像头发现:
杯子在 Camera 坐标系中的某个位置。
但是机械臂控制器真正需要知道的是:
杯子相对于机械臂 Base 在哪里?
于是系统必须进行一次"翻译":
text
Camera 坐标系中的杯子
↓
坐标变换
↓
Base 坐标系中的杯子
机械臂拿到这个位置以后,才能进一步计算:
各个关节应该怎么运动,才能让夹爪到达杯子的位置?
这就是后面我们学习:
向量、旋转矩阵、齐次变换矩阵、四元数、TF2、正运动学、逆运动学
时真正要解决的问题。
这些数学工具表面上看起来越来越复杂,但它们背后其实一直在回答一个非常朴素的问题:
"同一个东西,如果换一个观察位置,它的坐标应该怎么算?"
所以我们现在完全不需要急着记矩阵公式。
先把这个思想建立起来就够了。
对于机器人来说:
位置告诉我们"在哪里";
姿态告诉我们"朝哪里";
坐标系告诉我们"相对于谁";
而:
位姿(Pose) + 坐标系(Frame),才真正构成机器人理解空间的基础。
等机器人能够准确描述"自己在哪里、目标在哪里、它们之间是什么关系"以后,我们才有资格继续讨论下一个问题:
机器人从这里移动到那里以后,位置是怎样随时间变化的?
这就会自然进入下一节:
速度、加速度与角速度。
三、从位置到运动:速度、加速度与角速度
上一节我们解决了一个最基础的问题:
机器人在哪里?
我们用位置(Position)描述它所处的位置,用姿态(Orientation)描述它朝向哪里,再通过坐标系(Coordinate Frame)说明这些数据究竟是"相对于谁"而言的。
但机器人显然不会永远停在那里。
机械臂的关节会旋转,AMR 会向前行驶,无人机会升降和转向,人形机器人走路时身体的位置和姿态更是一直在变化。
所以一旦机器人开始运动,我们就必须在"位置"之外再加入一个非常重要的东西:
时间(Time)。
同一个位置,如果只告诉我们:
text
x = 5 m
它只能回答:
"机器人现在在哪里?"
但如果我们开始连续记录:
text
0 秒:x = 0 m
1 秒:x = 1 m
2 秒:x = 3 m
3 秒:x = 6 m
事情就不一样了。
我们不仅知道机器人在哪里,还开始知道:
它的位置是怎样随着时间变化的。
而这正是理解机器人运动的起点。
1. 位置发生变化,我们就得到了速度
假设一台移动机器人沿着直线向前运动。
第一秒,它走了 1 m。
第二秒,它又走了 1 m。
第三秒,它还是走了 1 m。
那么我们很自然地会说:
这台机器人正在以 1 m/s 的速度向前运动。
最简单的平均速度可以写成:
text
平均速度 = 位移变化 ÷ 时间变化
v = Δx / Δt
这里的 Δ 可以先理解成:
"变化了多少"。
所以:
text
Δx
表示位置变化了多少;
text
Δt
表示经过了多少时间。
例如机器人在 2 秒内从:
text
x = 1 m
移动到:
text
x = 5 m
那么:
text
Δx = 5 - 1 = 4 m
Δt = 2 s
平均速度 = 4 / 2 = 2 m/s
这就是最基础的运动描述。
但机器人里的"速度"不能只理解成一个数字。
比如:
text
2 m/s 向东
和:
text
2 m/s 向西
速度大小虽然一样,但机器人的运动显然完全不同。
因此在机器人中,速度通常还包含方向。
例如在二维平面中,可以写成:
text
vx = 1.0 m/s
vy = 0.5 m/s
意思就是机器人同时在 X 和 Y 两个方向发生运动。
这时候,速度就不再只是"跑得多快",而是在描述:
机器人在空间中的位置正在朝什么方向、以多快的速度发生变化。
这也是为什么后面学习 ROS 2 时,我们会看到类似:
text
linear.x
linear.y
linear.z
这样的数据。
它们本质上就是在描述机器人沿不同方向的线速度(Linear Velocity)。
2. 速度也会变化,于是出现了加速度
现在换一个场景。
机器人刚刚启动:
text
0 秒:0 m/s
1 秒:0.5 m/s
2 秒:1.0 m/s
3 秒:1.5 m/s
4 秒:2.0 m/s
虽然机器人一直向前走,但它显然不是在做匀速运动。
因为:
它越来越快了。
于是我们又需要一个新的量来描述:
速度变化得有多快?
这就是加速度(Acceleration)。
最简单地可以理解为:
text
加速度 = 速度变化 ÷ 时间变化
a = Δv / Δt
例如:
text
原来的速度 = 0 m/s
1 秒后的速度 = 2 m/s
那么:
text
Δv = 2 m/s
Δt = 1 s
a = 2 m/s²
这里的 m/s² 刚开始看可能有点奇怪。
其实它表达的就是:
速度每秒变化多少。
2 m/s² 可以理解成:
text
每经过 1 秒
速度增加约 2 m/s
于是我们现在已经建立起第一条非常重要的关系:
text
位置发生变化
↓
速度
速度发生变化
↓
加速度
这也是机器人为什么需要 IMU 的原因之一。
IMU 中的加速度计(Accelerometer),就是机器人感知自身加速度的重要传感器。
例如机器人突然启动、急刹车或者发生碰撞时,加速度数据都会发生明显变化。
但这里要注意一个以后会专门研究的问题:
加速度计的实际测量并不等于"把位置求两次导数"这么简单,它还会受到重力、噪声、安装方向、零偏等因素影响。
所以现在我们只需要先建立概念:
位置描述"在哪里",速度描述"位置变化得多快",加速度描述"速度变化得多快"。
3. 机器人不仅会直线移动,还会旋转
到目前为止,我们讨论的主要还是:
text
向前
向后
向左
向右
上下移动
也就是线运动(Linear Motion)。
但机器人还有另一种非常重要的运动:
旋转(Rotation)。
比如一台移动机器人原来朝东:
text
θ = 0°
一段时间以后,它转向了北方:
text
θ = 90°
这时候发生变化的已经不是位置,而是:
角度(Angle)。
如果我们进一步问:
它转得有多快?
就会出现:
角速度(Angular Velocity)。
例如:
text
1 秒旋转 90°
可以粗略理解成:
text
角速度 = 90°/s
工程和数学计算中通常不会一直使用"度",而会大量使用:
弧度(Radian,rad)。
例如:
text
180° = π rad
360° = 2π rad
所以一个机器人绕某个轴旋转时,我们经常会看到:
text
ω = 1 rad/s
这里的:
text
ω
就是角速度经常使用的符号。
这和前面的线速度非常相似:
| 运动类型 | 描述状态 | 描述变化速度 |
|---|---|---|
| 直线运动 | 位置 x |
线速度 v |
| 旋转运动 | 角度 θ |
角速度 ω |
于是机器人运动就开始分成两条非常重要的线:
text
平移运动:
位置 → 线速度 → 线加速度
旋转运动:
角度 → 角速度 → 角加速度
这也是为什么 IMU 里面除了加速度计之外,通常还会有:
陀螺仪(Gyroscope)。
加速度计主要帮助测量线性加速度相关信息;
陀螺仪则主要测量:
机器人绕各个轴旋转的角速度。
比如机器人正在左转,陀螺仪并不是直接告诉我们:
"你现在朝向 37°。"
它更直接测到的是:
"你现在正在以多快的速度旋转。"
如果想进一步知道角度,就需要结合时间对角速度进行积分,并且实际系统还要面对零偏、噪声和漂移等问题。
这也是后面研究 IMU 时非常重要的一条线。
4. 一个真实机器人,往往同时存在"移动"和"旋转"
现在再来看一台真正的移动机器人。
它从 A 点开往 B 点的时候,很少永远沿着一条绝对直线运动。
它可能:
text
向前行驶
↓
开始左转
↓
继续向前
↓
减速
↓
调整朝向
↓
到达目标
所以在某一个时刻,机器人可能同时拥有:
text
线速度:
vx
vy
vz
角速度:
ωx
ωy
ωz
也就是说:
机器人既可能在空间中平移,同时也可能发生旋转。
对于普通二维移动小车,我们通常最关心:
text
向前速度 v
+
绕竖直方向旋转的角速度 ω
比如:
text
v = 1.0 m/s
ω = 0 rad/s
机器人基本直行。
如果:
text
v = 1.0 m/s
ω = 0.5 rad/s
机器人一边向前走,一边转弯。
如果:
text
v = 0
ω = 0.5 rad/s
对于能够原地转向的差速机器人来说,它就可能站在原地旋转。
这时候,我们已经开始非常接近以后 ROS 2 里面经常出现的一个概念:
Twist。
它就是用来描述一个刚体的:
线速度 + 角速度。
所以以后当我们看到:
text
linear.x
linear.y
linear.z
angular.x
angular.y
angular.z
就不会觉得这是凭空设计出来的一组参数。
它实际上来自我们刚刚建立起来的运动描述方式。
5. 从位置到速度再到加速度,其实是在观察"变化"
走到这里,我们可以把这一节最重要的关系放到一起:
#mermaid-svg-FlmvS6Bacz28p3OQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FlmvS6Bacz28p3OQ .error-icon{fill:#552222;}#mermaid-svg-FlmvS6Bacz28p3OQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FlmvS6Bacz28p3OQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FlmvS6Bacz28p3OQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FlmvS6Bacz28p3OQ .marker.cross{stroke:#333333;}#mermaid-svg-FlmvS6Bacz28p3OQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FlmvS6Bacz28p3OQ p{margin:0;}#mermaid-svg-FlmvS6Bacz28p3OQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FlmvS6Bacz28p3OQ .cluster-label text{fill:#333;}#mermaid-svg-FlmvS6Bacz28p3OQ .cluster-label span{color:#333;}#mermaid-svg-FlmvS6Bacz28p3OQ .cluster-label span p{background-color:transparent;}#mermaid-svg-FlmvS6Bacz28p3OQ .label text,#mermaid-svg-FlmvS6Bacz28p3OQ span{fill:#333;color:#333;}#mermaid-svg-FlmvS6Bacz28p3OQ .node rect,#mermaid-svg-FlmvS6Bacz28p3OQ .node circle,#mermaid-svg-FlmvS6Bacz28p3OQ .node ellipse,#mermaid-svg-FlmvS6Bacz28p3OQ .node polygon,#mermaid-svg-FlmvS6Bacz28p3OQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FlmvS6Bacz28p3OQ .rough-node .label text,#mermaid-svg-FlmvS6Bacz28p3OQ .node .label text,#mermaid-svg-FlmvS6Bacz28p3OQ .image-shape .label,#mermaid-svg-FlmvS6Bacz28p3OQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-FlmvS6Bacz28p3OQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FlmvS6Bacz28p3OQ .rough-node .label,#mermaid-svg-FlmvS6Bacz28p3OQ .node .label,#mermaid-svg-FlmvS6Bacz28p3OQ .image-shape .label,#mermaid-svg-FlmvS6Bacz28p3OQ .icon-shape .label{text-align:center;}#mermaid-svg-FlmvS6Bacz28p3OQ .node.clickable{cursor:pointer;}#mermaid-svg-FlmvS6Bacz28p3OQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FlmvS6Bacz28p3OQ .arrowheadPath{fill:#333333;}#mermaid-svg-FlmvS6Bacz28p3OQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FlmvS6Bacz28p3OQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FlmvS6Bacz28p3OQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FlmvS6Bacz28p3OQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FlmvS6Bacz28p3OQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FlmvS6Bacz28p3OQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FlmvS6Bacz28p3OQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FlmvS6Bacz28p3OQ .cluster text{fill:#333;}#mermaid-svg-FlmvS6Bacz28p3OQ .cluster span{color:#333;}#mermaid-svg-FlmvS6Bacz28p3OQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FlmvS6Bacz28p3OQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FlmvS6Bacz28p3OQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-FlmvS6Bacz28p3OQ .icon-shape,#mermaid-svg-FlmvS6Bacz28p3OQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FlmvS6Bacz28p3OQ .icon-shape p,#mermaid-svg-FlmvS6Bacz28p3OQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FlmvS6Bacz28p3OQ .icon-shape .label rect,#mermaid-svg-FlmvS6Bacz28p3OQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FlmvS6Bacz28p3OQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FlmvS6Bacz28p3OQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FlmvS6Bacz28p3OQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 随时间变化
随时间变化
随时间变化
随时间变化
位置 Position
我在哪里
速度 Velocity
我移动得多快
加速度 Acceleration
我的速度变化得多快
角度 Angle
我朝哪里
角速度 Angular Velocity
我旋转得多快
角加速度 Angular Acceleration
旋转速度变化得多快
如果以后进入微积分,这些关系还会写得更加严格:
text
位置
↓ 对时间求导
速度
↓ 对时间求导
加速度
反过来也可以理解:
text
加速度
↓ 随时间累积
速度
↓ 随时间累积
位置
这件事情在机器人里极其重要。
因为现实中的传感器并不会神奇地把所有答案直接告诉我们。
编码器可能给我们轮子的旋转变化;
IMU 给我们加速度和角速度;
LiDAR 给我们与环境之间的距离;
摄像头给我们图像。
机器人真正需要做的是:
根据这些不同形式的测量数据,不断推算自己现在的位置、姿态、速度以及运动状态。
所以我们后面学习:
Odometry(里程计)、IMU、Sensor Fusion(传感器融合)、Kalman Filter(卡尔曼滤波)、SLAM
时,其实一直都没有离开这一节的问题。
它们只是在用越来越复杂的方法回答:
机器人刚才怎么动了?现在在哪里?现在运动得有多快?下一时刻又可能到哪里?
而一旦我们知道了机器人"怎么运动",下一个问题自然就出现了:
为什么机器人会产生这样的运动?
为什么同样一个电机,推动 5 kg 的机器人和推动 100 kg 的机器人,产生的加速度完全不同?
为什么机械臂伸得越远,关节可能越吃力?
为什么机器人急刹车不能瞬间停下来?
为什么轮子会打滑?
这就需要从单纯描述运动,进一步进入真实世界中的:
力、力矩、质量与惯性。
四、机器人为什么会动:力、力矩、质量与惯性
上一节我们已经能够描述机器人的运动了。
机器人在哪里,可以用位置描述;朝向哪里,可以用姿态描述;移动得多快,可以用速度描述;速度变化得多快,可以用加速度描述。
但到这里,我们其实只回答了:
机器人"怎么动"。
还没有回答另一个更加现实的问题:
机器人为什么会动?又为什么不能想怎么动就怎么动?
假设现在有两台完全一样的 AMR。
第一台空载,只有 30 kg;第二台上面装满货物,总质量达到 150 kg。
我们给两台机器人完全相同的电机输出,它们会不会以完全相同的速度加速?
通常不会。
再比如机械臂。
同一个关节,机械臂空着手的时候可以轻松抬起来;如果末端突然挂上一个 20 kg 的重物,电机可能明显吃力,甚至根本抬不起来。
为什么?
因为机器人并不是生活在计算机里的一个坐标点。
它有质量,会受到重力,会产生惯性,轮子和地面之间存在摩擦,机械臂的关节还必须承担力矩。
这时候,我们就真正从描述运动的运动学(Kinematics) ,开始进入研究运动产生原因的动力学(Dynamics)。
1. 想让机器人改变运动状态,首先需要"力"
先想一个非常生活化的场景。
地面上放着两个箱子:
text
箱子 A:5 kg
箱子 B:100 kg
如果我们分别去推它们,感受肯定不一样。
5 kg 的箱子可能轻轻一推就开始移动,而 100 kg 的箱子则需要更大的力。
这背后就是最经典的牛顿第二定律:
text
F = ma
其中:
text
F = Force 力
m = Mass 质量
a = Acceleration 加速度
这条公式对于机器人非常重要。
它告诉我们:
力并不是简单决定"机器人能不能动",而是决定机器人运动状态改变得有多快。
例如一台质量为:
text
m = 50 kg
的移动机器人,如果希望产生:
text
a = 1 m/s²
的加速度,在忽略其他因素的理想情况下,需要的合力就是:
text
F = ma
F = 50 × 1
F = 50 N
如果机器人质量变成:
text
100 kg
但我们仍然希望它获得同样的加速度,那么:
text
F = 100 × 1
F = 100 N
需要的力也随之增加。
这就是为什么机器人设计中:
重量从来不是一个无关紧要的参数。
机器人越重,想让它快速启动、加速、减速和改变运动方向,通常就需要更大的驱动力。
而这些力最终来自哪里?
对于轮式机器人,主要来自:
电机 → 轮子 → 地面之间的作用力。
对于机械臂,主要来自:
电机 → 减速器 → 关节 → 连杆。
对于无人机,则来自:
电机 → 螺旋桨 → 空气 → 推力。
所以机器人软件里的一句:
"向前加速。"
最终一定要落实成真实世界中的:
执行器到底应该产生多大的力或力矩?
2. 机器人为什么不能瞬间启动、瞬间停止?------惯性
假设一台机器人正在以:
text
2 m/s
的速度向前行驶。
现在我们突然告诉它:
"速度变成 0。"
计算机修改一个变量可能只需要一瞬间:
text
target_velocity = 0
但真实机器人绝不会在这一瞬间凭空停下来。
因为它有:
惯性(Inertia)。
惯性可以先非常简单地理解成:
物体倾向于保持自己原来的运动状态。
原来静止的东西,不会无缘无故突然运动;
原来正在运动的东西,也不会无缘无故突然停止。
这也是牛顿第一定律所描述的基本规律。
所以机器人正在高速行驶时,即使电机已经停止继续驱动,它仍然可能向前运动一段距离。
如果我们希望它快速停下来,就必须施加反方向的作用,让它产生减速度。
这就是为什么机器人控制中经常不会直接:
text
0 m/s
↓
瞬间变成
↓
3 m/s
而是会设计一个加速过程:
text
0
↓
0.5
↓
1.0
↓
1.5
↓
2.0
↓
2.5
↓
3.0 m/s
停止的时候也是一样。
这不仅仅是为了让运动"看起来平滑"。
更重要的是为了减少:
- 机械冲击
- 电机瞬时负载
- 轮胎打滑
- 货物晃动
- 机械结构振动
- 机器人失稳
对于机械臂,这个问题更加明显。
如果一个很重的机械臂高速运动,然后突然要求某个关节立即停止,巨大的惯性可能给关节、减速器和机械结构带来非常大的冲击。
所以后面我们学习:
速度规划、加速度限制、Jerk(加加速度)、梯形速度曲线、S 曲线
时,会发现它们背后的物理原因都和这里有关。
计算机里的目标值可以瞬间改变,但现实世界中的物体不能无视惯性瞬间改变运动状态。
这就是机器人仿真和普通动画最大的区别之一。
动画可以直接把一个物体下一帧放到另一个位置。
机器人仿真却必须考虑:
它到底能不能这样过去?
3. 电机明明在"转",为什么机器人里面总是在谈"力矩"?
前面说的力比较容易理解:
推一个东西,需要力。
但是机械臂里面,我们会反复看到另外一个词:
Torque(力矩 / 转矩),通常记作 τ。
为什么机器人关节不只讨论"力",还要讨论"力矩"?
因为机械臂关节主要做的是:
旋转。
我们先不看公式,想一个生活中的例子。
拧门的时候,你通常不会把手放在门轴旁边推,而是会推门把手。
为什么?
因为:
同样大小的力,离旋转中心越远,通常越容易让物体发生旋转。
机械臂也是一样。
假设一个机械臂水平伸出去,末端拿着一个重物:
text
关节 ●──────────────■
负载
↓
重力
这个负载会对关节产生一个让机械臂向下旋转的作用。
如果负载离关节很近,影响可能还不大。
但如果机械臂继续伸长:
text
关节 ●────────────────────────■
负载
↓
重力
即使负载重量完全没有变化,关节承担的力矩也会明显增加。
所以力矩不仅和力有关,还和:
力作用的位置距离旋转中心有多远。
有关。
在更一般的情况下,力矩还与力的方向有关。其大小可以表示为:
text
τ = rF sinθ
其中:
text
τ = 力矩
r = 力作用点到旋转中心的距离
F = 力
θ = 力与力臂之间的夹角
因此我们以后看机器人电机参数时,经常会看到:
text
N·m
也就是:
牛·米(Newton-meter)
这就是力矩常用的单位。
于是机械臂设计中的很多现象马上就可以理解了。
为什么机械臂越长,关节负担可能越大?
为什么末端负载越重,靠近基座的关节往往需要承担更大的力矩?
为什么机器人关节经常需要减速器?
为什么一个看起来并不大的机械臂关节,却要求很高的输出转矩?
这些问题背后都绕不开:
力矩。
4. 质量和惯性,不只是"机器人有多重"
这里还有一个很容易产生误解的地方。
我们平时说:
"这个机器人 50 kg。"
说的是它的质量(Mass)。
但对于机器人运动来说,只知道总质量往往还不够。
因为:
质量分布在哪里,也非常重要。
我们还是拿机械臂举例。
假设两个机械臂末端都增加了 5 kg。
第一种情况:
text
5 kg 靠近关节
第二种情况:
text
5 kg 远离关节
虽然增加的质量完全相同,但它们对关节旋转产生的影响并不一样。
这时候就会进入:
转动惯量(Moment of Inertia)。
它可以先简单理解成:
一个物体有多"不愿意改变自己的旋转状态"。
质量越大,通常越难改变运动状态;
而对于旋转运动来说,质量离旋转轴越远,往往越难让它快速改变旋转状态。
这也是为什么一个人旋转的时候:
双臂张开,
和
双臂收紧,
旋转状态会明显不同。
质量没有变化。
但:
质量相对于旋转轴的分布发生了变化。
机器人同样如此。
机械臂伸出去以后,整个系统的惯性特性会改变;
人形机器人把手臂举起来以后,身体质量分布也会改变;
四足机器人抬起一条腿以后,整个身体的动力学状态同样发生变化。
所以到了复杂机器人领域,我们不会只研究:
text
机器人有多重?
还要研究:
text
质量在哪里?
质心在哪里?
绕不同轴的转动惯量是多少?
这些信息最终会形成机器人动力学模型中的重要参数。
5. 现实机器人还要面对重力和摩擦
如果机器人生活在一个理想世界里,事情会简单很多。
但真实世界还有两个无处不在的东西:
重力(Gravity)
和
摩擦(Friction)。
先看重力。
机械臂水平伸出去以后,即使它完全不运动,关节也可能仍然需要持续输出力矩。
为什么?
因为地球一直在把机械臂和负载向下拉。
如果控制器什么都不做,机械臂可能就会掉下来。
所以很多机械臂控制中都会涉及:
Gravity Compensation(重力补偿)。
控制器提前计算:
按照机器人当前姿态,重力会对每个关节产生多大的影响?
然后主动输出相应的力矩进行抵消。
再来看摩擦。
一台轮式机器人为什么能够向前走?
并不是因为轮子"转了",机器人就一定会前进。
真正发生的是:
text
电机带动轮子旋转
↓
轮子作用于地面
↓
地面对轮子产生摩擦作用
↓
机器人获得向前的驱动力
如果地面非常滑:
text
电机:疯狂旋转
编码器:轮子已经转了很多圈
机器人:几乎没走多远
这就是:
打滑(Slip)。
这件事情非常重要。
因为编码器可能根据轮子的转数认为:
"我已经前进了 5 m。"
但机器人真实只移动了:
text
4.2 m
于是位置估计开始产生误差。
这也解释了为什么移动机器人不能只依赖编码器。
后面我们需要:
IMU、LiDAR、Camera、GNSS
以及:
Sensor Fusion、SLAM
来不断纠正这些误差。
所以你会发现,机器人里面很多看似属于"软件算法"的问题,往下追究以后,经常会发现:
最开始的误差,其实来自真实物理世界。
6. 从运动学进入动力学,我们才真正开始面对"真实机器人"
现在我们可以重新整理一下前面的关系。
运动学告诉我们:
text
机器人在哪里?
机器人朝哪里?
机器人移动多快?
关节转多少?
末端执行器在哪里?
而动力学继续告诉我们:
text
为什么会产生这样的运动?
需要多大的力?
需要多大的力矩?
机器人质量是多少?
惯性有多大?
重力产生什么影响?
摩擦又会造成什么影响?
可以简单总结成:
| 概念 | 最直观的问题 |
|---|---|
| 位置 Position | 我在哪里? |
| 速度 Velocity | 我移动多快? |
| 加速度 Acceleration | 我的速度变化多快? |
| 质量 Mass | 我有多大的惯性基础? |
| 力 Force | 什么让我的直线运动状态发生变化? |
| 力矩 Torque | 什么让我的旋转运动状态发生变化? |
| 转动惯量 Moment of Inertia | 我有多难改变旋转状态? |
| 重力 Gravity | 地球一直在怎样影响我? |
| 摩擦 Friction | 我和环境接触时会产生什么作用? |
于是机器人运动开始形成一条越来越完整的逻辑:
#mermaid-svg-CYbPGzuoxuG2nHyC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CYbPGzuoxuG2nHyC .error-icon{fill:#552222;}#mermaid-svg-CYbPGzuoxuG2nHyC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CYbPGzuoxuG2nHyC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CYbPGzuoxuG2nHyC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CYbPGzuoxuG2nHyC .marker.cross{stroke:#333333;}#mermaid-svg-CYbPGzuoxuG2nHyC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CYbPGzuoxuG2nHyC p{margin:0;}#mermaid-svg-CYbPGzuoxuG2nHyC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CYbPGzuoxuG2nHyC .cluster-label text{fill:#333;}#mermaid-svg-CYbPGzuoxuG2nHyC .cluster-label span{color:#333;}#mermaid-svg-CYbPGzuoxuG2nHyC .cluster-label span p{background-color:transparent;}#mermaid-svg-CYbPGzuoxuG2nHyC .label text,#mermaid-svg-CYbPGzuoxuG2nHyC span{fill:#333;color:#333;}#mermaid-svg-CYbPGzuoxuG2nHyC .node rect,#mermaid-svg-CYbPGzuoxuG2nHyC .node circle,#mermaid-svg-CYbPGzuoxuG2nHyC .node ellipse,#mermaid-svg-CYbPGzuoxuG2nHyC .node polygon,#mermaid-svg-CYbPGzuoxuG2nHyC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CYbPGzuoxuG2nHyC .rough-node .label text,#mermaid-svg-CYbPGzuoxuG2nHyC .node .label text,#mermaid-svg-CYbPGzuoxuG2nHyC .image-shape .label,#mermaid-svg-CYbPGzuoxuG2nHyC .icon-shape .label{text-anchor:middle;}#mermaid-svg-CYbPGzuoxuG2nHyC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CYbPGzuoxuG2nHyC .rough-node .label,#mermaid-svg-CYbPGzuoxuG2nHyC .node .label,#mermaid-svg-CYbPGzuoxuG2nHyC .image-shape .label,#mermaid-svg-CYbPGzuoxuG2nHyC .icon-shape .label{text-align:center;}#mermaid-svg-CYbPGzuoxuG2nHyC .node.clickable{cursor:pointer;}#mermaid-svg-CYbPGzuoxuG2nHyC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CYbPGzuoxuG2nHyC .arrowheadPath{fill:#333333;}#mermaid-svg-CYbPGzuoxuG2nHyC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CYbPGzuoxuG2nHyC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CYbPGzuoxuG2nHyC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CYbPGzuoxuG2nHyC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CYbPGzuoxuG2nHyC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CYbPGzuoxuG2nHyC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CYbPGzuoxuG2nHyC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CYbPGzuoxuG2nHyC .cluster text{fill:#333;}#mermaid-svg-CYbPGzuoxuG2nHyC .cluster span{color:#333;}#mermaid-svg-CYbPGzuoxuG2nHyC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CYbPGzuoxuG2nHyC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CYbPGzuoxuG2nHyC rect.text{fill:none;stroke-width:0;}#mermaid-svg-CYbPGzuoxuG2nHyC .icon-shape,#mermaid-svg-CYbPGzuoxuG2nHyC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CYbPGzuoxuG2nHyC .icon-shape p,#mermaid-svg-CYbPGzuoxuG2nHyC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CYbPGzuoxuG2nHyC .icon-shape .label rect,#mermaid-svg-CYbPGzuoxuG2nHyC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CYbPGzuoxuG2nHyC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CYbPGzuoxuG2nHyC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CYbPGzuoxuG2nHyC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 力 / 力矩
产生加速度 / 角加速度
速度 / 角速度发生变化
位置 / 姿态发生变化
机器人产生运动
这条链非常重要。
因为后面我们做机器人仿真的时候,计算机真正需要做的事情之一,就是不断重复类似的计算:
text
当前机器人状态
↓
当前受到哪些力和力矩?
↓
计算加速度
↓
计算速度变化
↓
计算位置和姿态变化
↓
得到下一时刻机器人状态
↓
继续计算......
这就是为什么 Gazebo、Isaac Sim 等机器人仿真软件需要物理引擎(Physics Engine)。
如果只想做一个动画,我们完全可以直接告诉模型:
"下一秒移动到这里。"
但如果想做机器人仿真,我们更关心:
给它这样的质量、电机输出、摩擦系数、关节结构和外部作用以后,它按照物理规律究竟会怎么运动?
到了这里,我们已经从"看机器人怎么动",真正开始进入:
用数学和物理建立一个能够描述真实机器人的模型。
而这正是下一节要解决的问题:
现实中的电机、轮子、机械臂、质量、传感器,究竟怎样一步一步变成计算机里面可以计算的数学模型?
五、机器人如何把真实世界变成数学模型?
前面我们已经认识了位置、姿态、速度、加速度、力、力矩、质量和惯性。
但这里会出现一个非常关键的问题:
真实机器人明明是一堆钢铁、电机、轮子、传感器和电路,计算机又不可能真的把这些东西"装进程序里",它到底是怎么计算机器人的?
答案就是:
建模(Modeling)。
所谓建模,并不是在计算机里重新造一台一模一样的机器人,而是从真实机器人身上提取出与当前问题有关的关键特征,再用数字、变量、几何关系和方程把它描述出来。
比如一台移动机器人真实长这样:
- 车身有长度、宽度和质量
- 左右各有驱动轮
- 轮子有半径
- 左右轮之间有距离
- 电机能够驱动轮子旋转
- 轮子和地面之间存在摩擦
- 机器人上还有编码器、IMU、LiDAR 等传感器
但如果我们现在只想研究:
左右两个轮子分别以不同速度旋转时,机器人会往哪里走?
那么摄像头是什么型号、电池装在哪里、外壳是什么颜色,这些信息暂时都没有必要进入模型。
这就是数学建模最重要的思想之一:
模型不是现实世界的完整复制,而是为了研究某一个问题,对现实进行有目的的抽象。
1. 第一步不是写公式,而是先决定"我要研究什么"
假设桌子上放着一辆真实的差速移动机器人。
我们可以研究很多问题。
如果研究:
机器人在平面上怎么移动?
那么可能只需要关心:
text
轮子半径 r
左右轮距离 L
左轮速度 vL
右轮速度 vR
机器人位置 x、y
机器人朝向 θ
于是现实中非常复杂的一台机器人,就被我们暂时抽象成:
text
↑ 前进方向
┌─────────┐
│ ● │
│ (x,y) │
│ θ │
└─────────┘
○ ○
左轮 右轮
vL vR
←── L ──→
真实机器人可能有几千个零件,但对于这个问题来说,我们真正关心的可能只有几个变量。
如果换一个问题:
机器人为什么加速这么慢?
刚才的模型可能就不够了。
这时候还要加入:
text
机器人质量 m
电机输出力矩 τ
轮子转动惯量 J
地面摩擦系数 μ
阻力
如果再问:
机器人在湿滑草地上为什么会出现定位误差?
我们又需要进一步考虑:
text
轮胎打滑
编码器误差
IMU噪声
地面摩擦变化
所以建模并不存在一个永远正确、永远完整的模型。
真正的问题是:
为了回答当前的问题,我至少需要保留现实世界中的哪些因素?
保留得太少,模型可能失真;
保留得太多,计算又会变得非常复杂。
机器人建模,本质上一直是在这两者之间寻找平衡。
2. 把真实结构变成"参数 + 变量 + 关系"
确定研究问题以后,下一步就是把真实机器人中的关键东西转换成数学能够处理的对象。
比如一个机械臂。
现实中我们看到的是:
text
基座
↓
电机
↓
关节
↓
金属连杆
↓
关节
↓
金属连杆
↓
夹爪
但到了数学模型里,我们可能把它简化成:
text
Link 1 长度 = L1
Link 2 长度 = L2
Joint 1 角度 = θ1
Joint 2 角度 = θ2
于是一个真实机械臂:
text
末端
●
/
L2/
/
●
/
L1/
/
●
基座
就变成了一组数字:
text
L1 = 0.5 m
L2 = 0.3 m
θ1 = 30°
θ2 = 45°
接下来,我们就可以利用几何和三角函数计算末端的位置。
对于一个最简单的二维两连杆机械臂:
text
x = L1 cosθ1 + L2 cos(θ1 + θ2)
y = L1 sinθ1 + L2 sin(θ1 + θ2)
这时候发生了一件非常重要的事情。
真实世界里的:
"机械臂弯成了这个样子。"
已经被转换成:
"给定 L1、L2、θ1、θ2,可以计算末端 x、y。"
计算机不需要真正看到那两根金属杆。
只要这些参数和关系足够准确,它就能够计算机械臂的运动。
这就是一个最基础的:
运动学模型(Kinematic Model)。
3. 如果还想知道"为什么这样动",模型就要加入物理规律
前面的机械臂模型能够回答:
关节转成这样以后,末端在哪里?
但它回答不了:
为了让关节这样运动,电机到底需要多大的力矩?
因为我们还没有告诉模型:
text
连杆有多重?
质心在哪里?
转动惯量是多少?
末端拿了多重的东西?
重力是多少?
运动速度是多少?
加速度是多少?
于是我们必须继续往模型里加入物理属性。
例如:
text
Link 1:
长度 L1
质量 m1
质心位置 c1
转动惯量 J1
Link 2:
长度 L2
质量 m2
质心位置 c2
转动惯量 J2
再结合:
牛顿定律、力矩关系、能量关系以及刚体动力学。
模型就开始从:
"机器人会怎么运动?"
进一步变成:
"什么样的力和力矩会让机器人产生这样的运动?"
这就是:
动力学模型(Dynamic Model)。
因此我们可以先建立一个非常重要的区分:
| 模型 | 主要保留什么 | 主要解决什么问题 |
|---|---|---|
| 几何模型 | 尺寸、位置、形状 | 机器人长什么样、空间关系如何 |
| 运动学模型 | 关节、位置、姿态、速度 | 机器人怎么运动 |
| 动力学模型 | 质量、惯性、力、力矩 | 为什么这样运动、需要多大作用力 |
| 传感器模型 | 测量范围、噪声、偏差 | 机器人会测到什么数据 |
| 环境模型 | 地面、障碍物、摩擦等 | 机器人和外部世界如何发生作用 |
现实中的机器人仿真,往往就是把这些模型逐渐组合起来。
4. 传感器同样需要被"数学化"
这里还有一个很容易被忽略的问题。
我们模拟机器人时,不仅要模拟机器人怎么运动,还要模拟:
机器人自己能够"感觉到什么"。
比如真实机器人安装了一个编码器。
现实中的编码器并不会直接告诉机器人:
"你向前移动了 1.273 m。"
它真正测量的可能是:
text
轮子转过了多少脉冲
于是仿真中的编码器也需要建立对应的测量模型:
text
轮子旋转
↓
转换成编码器计数
↓
加入分辨率限制
↓
得到模拟编码器数据
IMU 也是一样。
理想情况下,我们可能已经知道机器人真正的加速度:
text
a = 1.0 m/s²
但真实 IMU 测出来的可能是:
text
1.03
0.98
1.01
1.05
......
因为现实传感器存在:
Noise(噪声)、Bias(零偏)、Drift(漂移)、采样频率、测量范围等问题。
所以一个更加真实的传感器模型可能是:
text
真实运动状态
↓
理想测量值
↓
加入噪声 / 零偏 / 延迟
↓
模拟传感器输出
LiDAR、Camera、GNSS、Force Sensor 同样如此。
这也是为什么专业机器人仿真并不只是:
"把一个 3D 模型画出来让它动。"
真正的机器人仿真还必须让虚拟机器人获得与真实机器人类似的虚拟传感器数据。
这样我们写的 SLAM、导航和控制算法,才有可能在仿真环境中接受和真实机器人类似的输入。
5. 建模过程中最重要的一件事:允许简化,但必须知道自己简化了什么
假设我们模拟一台移动机器人。
最开始可以规定:
text
地面绝对平整
轮子不会打滑
电机没有延迟
编码器完全准确
机器人是刚体
没有空气阻力
这显然不是真实世界。
但这样的模型有没有意义?
当然有。
因为如果我们现在只是研究:
差速机器人左右轮速度与运动轨迹之间的关系。
那么这些简化反而能够帮助我们先把核心问题看清楚。
等基础模型跑通以后,再逐渐加入:
text
轮胎打滑
↓
摩擦变化
↓
电机响应延迟
↓
传感器噪声
↓
地面坡度
↓
外部扰动
模型就会一步一步接近现实。
所以建模不是:
第一次就把现实世界 100% 复制出来。
而更像:
#mermaid-svg-cWZcTq3F4P6hAe7t{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cWZcTq3F4P6hAe7t .error-icon{fill:#552222;}#mermaid-svg-cWZcTq3F4P6hAe7t .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cWZcTq3F4P6hAe7t .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cWZcTq3F4P6hAe7t .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cWZcTq3F4P6hAe7t .marker.cross{stroke:#333333;}#mermaid-svg-cWZcTq3F4P6hAe7t svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cWZcTq3F4P6hAe7t p{margin:0;}#mermaid-svg-cWZcTq3F4P6hAe7t .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cWZcTq3F4P6hAe7t .cluster-label text{fill:#333;}#mermaid-svg-cWZcTq3F4P6hAe7t .cluster-label span{color:#333;}#mermaid-svg-cWZcTq3F4P6hAe7t .cluster-label span p{background-color:transparent;}#mermaid-svg-cWZcTq3F4P6hAe7t .label text,#mermaid-svg-cWZcTq3F4P6hAe7t span{fill:#333;color:#333;}#mermaid-svg-cWZcTq3F4P6hAe7t .node rect,#mermaid-svg-cWZcTq3F4P6hAe7t .node circle,#mermaid-svg-cWZcTq3F4P6hAe7t .node ellipse,#mermaid-svg-cWZcTq3F4P6hAe7t .node polygon,#mermaid-svg-cWZcTq3F4P6hAe7t .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cWZcTq3F4P6hAe7t .rough-node .label text,#mermaid-svg-cWZcTq3F4P6hAe7t .node .label text,#mermaid-svg-cWZcTq3F4P6hAe7t .image-shape .label,#mermaid-svg-cWZcTq3F4P6hAe7t .icon-shape .label{text-anchor:middle;}#mermaid-svg-cWZcTq3F4P6hAe7t .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cWZcTq3F4P6hAe7t .rough-node .label,#mermaid-svg-cWZcTq3F4P6hAe7t .node .label,#mermaid-svg-cWZcTq3F4P6hAe7t .image-shape .label,#mermaid-svg-cWZcTq3F4P6hAe7t .icon-shape .label{text-align:center;}#mermaid-svg-cWZcTq3F4P6hAe7t .node.clickable{cursor:pointer;}#mermaid-svg-cWZcTq3F4P6hAe7t .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cWZcTq3F4P6hAe7t .arrowheadPath{fill:#333333;}#mermaid-svg-cWZcTq3F4P6hAe7t .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cWZcTq3F4P6hAe7t .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cWZcTq3F4P6hAe7t .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cWZcTq3F4P6hAe7t .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cWZcTq3F4P6hAe7t .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cWZcTq3F4P6hAe7t .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cWZcTq3F4P6hAe7t .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cWZcTq3F4P6hAe7t .cluster text{fill:#333;}#mermaid-svg-cWZcTq3F4P6hAe7t .cluster span{color:#333;}#mermaid-svg-cWZcTq3F4P6hAe7t div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cWZcTq3F4P6hAe7t .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cWZcTq3F4P6hAe7t rect.text{fill:none;stroke-width:0;}#mermaid-svg-cWZcTq3F4P6hAe7t .icon-shape,#mermaid-svg-cWZcTq3F4P6hAe7t .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cWZcTq3F4P6hAe7t .icon-shape p,#mermaid-svg-cWZcTq3F4P6hAe7t .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cWZcTq3F4P6hAe7t .icon-shape .label rect,#mermaid-svg-cWZcTq3F4P6hAe7t .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cWZcTq3F4P6hAe7t .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cWZcTq3F4P6hAe7t .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cWZcTq3F4P6hAe7t :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 真实机器人
确定研究问题
保留关键因素
忽略次要因素
建立基础模型
实验与验证
发现误差
继续完善模型
这个过程在工程上非常重要。
因为模型越真实,通常意味着:
参数更多、计算量更大、调试更困难。
如果一个简单模型已经足以回答问题,就没有必要为了"看起来专业"而把所有现实细节全部塞进去。
好的模型不是最复杂的模型,而是在当前问题下足够准确,同时又足够简单的模型。
6. 到这里,我们终于可以理解"机器人仿真"到底在干什么
现在再来看 Gazebo、Isaac Sim、MATLAB 或其他机器人仿真工具,就会发现它们其实没有那么神秘。
假设我们要在 Gazebo 里模拟一台移动机器人。
首先要告诉计算机:
text
机器人长什么样?
于是建立:
几何模型。
然后:
text
机器人有几个轮子?
轮子怎么连接?
关节允许怎么运动?
建立:
运动结构模型。
接着:
text
机器人多重?
惯量是多少?
地面摩擦多大?
建立:
动力学和物理模型。
然后:
text
LiDAR装在哪里?
IMU装在哪里?
摄像头参数是多少?
建立:
传感器模型。
最后再把:
text
ROS 2
SLAM
Nav2
控制算法
连接进来。
于是整个系统开始运行:
#mermaid-svg-9LzRvHjQskxtyB1Z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9LzRvHjQskxtyB1Z .error-icon{fill:#552222;}#mermaid-svg-9LzRvHjQskxtyB1Z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9LzRvHjQskxtyB1Z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9LzRvHjQskxtyB1Z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9LzRvHjQskxtyB1Z .marker.cross{stroke:#333333;}#mermaid-svg-9LzRvHjQskxtyB1Z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9LzRvHjQskxtyB1Z p{margin:0;}#mermaid-svg-9LzRvHjQskxtyB1Z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9LzRvHjQskxtyB1Z .cluster-label text{fill:#333;}#mermaid-svg-9LzRvHjQskxtyB1Z .cluster-label span{color:#333;}#mermaid-svg-9LzRvHjQskxtyB1Z .cluster-label span p{background-color:transparent;}#mermaid-svg-9LzRvHjQskxtyB1Z .label text,#mermaid-svg-9LzRvHjQskxtyB1Z span{fill:#333;color:#333;}#mermaid-svg-9LzRvHjQskxtyB1Z .node rect,#mermaid-svg-9LzRvHjQskxtyB1Z .node circle,#mermaid-svg-9LzRvHjQskxtyB1Z .node ellipse,#mermaid-svg-9LzRvHjQskxtyB1Z .node polygon,#mermaid-svg-9LzRvHjQskxtyB1Z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9LzRvHjQskxtyB1Z .rough-node .label text,#mermaid-svg-9LzRvHjQskxtyB1Z .node .label text,#mermaid-svg-9LzRvHjQskxtyB1Z .image-shape .label,#mermaid-svg-9LzRvHjQskxtyB1Z .icon-shape .label{text-anchor:middle;}#mermaid-svg-9LzRvHjQskxtyB1Z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9LzRvHjQskxtyB1Z .rough-node .label,#mermaid-svg-9LzRvHjQskxtyB1Z .node .label,#mermaid-svg-9LzRvHjQskxtyB1Z .image-shape .label,#mermaid-svg-9LzRvHjQskxtyB1Z .icon-shape .label{text-align:center;}#mermaid-svg-9LzRvHjQskxtyB1Z .node.clickable{cursor:pointer;}#mermaid-svg-9LzRvHjQskxtyB1Z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9LzRvHjQskxtyB1Z .arrowheadPath{fill:#333333;}#mermaid-svg-9LzRvHjQskxtyB1Z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9LzRvHjQskxtyB1Z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9LzRvHjQskxtyB1Z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9LzRvHjQskxtyB1Z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9LzRvHjQskxtyB1Z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9LzRvHjQskxtyB1Z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9LzRvHjQskxtyB1Z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9LzRvHjQskxtyB1Z .cluster text{fill:#333;}#mermaid-svg-9LzRvHjQskxtyB1Z .cluster span{color:#333;}#mermaid-svg-9LzRvHjQskxtyB1Z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9LzRvHjQskxtyB1Z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9LzRvHjQskxtyB1Z rect.text{fill:none;stroke-width:0;}#mermaid-svg-9LzRvHjQskxtyB1Z .icon-shape,#mermaid-svg-9LzRvHjQskxtyB1Z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9LzRvHjQskxtyB1Z .icon-shape p,#mermaid-svg-9LzRvHjQskxtyB1Z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9LzRvHjQskxtyB1Z .icon-shape .label rect,#mermaid-svg-9LzRvHjQskxtyB1Z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9LzRvHjQskxtyB1Z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9LzRvHjQskxtyB1Z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9LzRvHjQskxtyB1Z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 真实机器人
抽象关键特征
数学 / 物理模型
计算机仿真
虚拟传感器数据
机器人算法
控制虚拟机器人
现在我们就能够在完全没有真实机器人的情况下研究:
路径规划能不能工作?
SLAM会不会漂移?
控制器参数合不合适?
机器人会不会撞墙?
机械臂能不能到达目标?
传感器出现噪声以后算法还能不能正常运行?
这也是机器人仿真的真正价值。
它并不是为了制作一段:
"看起来像机器人在运动"的动画。
而是在计算机中建立一个尽可能有意义的虚拟物理世界,让我们的机器人算法能够提前在那里接受验证。
所以从真实机器人到仿真机器人,中间真正发生的事情可以概括成:
现实世界 → 抽象 → 参数 → 数学关系 → 物理规律 → 数学模型 → 数值计算 → 仿真结果。
而下一节,我们就可以把这条路线真正收回来:
当数学模型已经建立以后,计算机究竟怎样让时间不断向前推进,让一台原本静止的"数学机器人"真正动起来?
六、从数学模型到机器人仿真:我们究竟在模拟什么?
前面我们已经完成了一次非常重要的转换:
把现实中的机器人,变成计算机能够理解的数学模型。
比如一台真实的移动机器人,我们可以把它抽象成质量、轮径、轮距、位置、速度、摩擦系数等参数;一台机械臂,则可以抽象成连杆长度、关节角度、质量、质心和转动惯量。
但数学模型建立以后,它仍然只是一些数字和方程。
机器人还没有真正"动起来"。
那么 Gazebo、Isaac Sim、MATLAB 这些仿真软件,到底是怎样让一个数学模型变成一台会运动、会碰撞、会读取传感器,甚至可以运行 ROS 2 和 SLAM 的虚拟机器人的?
这里真正需要理解的,其实不是某一个软件,而是:
机器人仿真究竟在模拟现实世界中的什么?
1. 仿真的第一件事:让"时间"开始流动
假设现在有一个非常简单的小车。
已知:
text
当前位置:
x = 0 m
当前速度:
v = 1 m/s
如果时间完全停止,那么这两个数字永远不会变化。
机器人当然也不会动。
现在我们让时间向前推进:
text
Δt = 0.1 s
如果暂时假设机器人一直保持:
text
v = 1 m/s
那么经过 0.1 秒以后,它大约前进:
text
Δx = v × Δt
Δx = 1 × 0.1
Δx = 0.1 m
于是:
text
t = 0.0 s
x = 0.0 m
下一次计算:
text
t = 0.1 s
x = 0.1 m
再下一次:
text
t = 0.2 s
x = 0.2 m
然后:
text
0.3 m
0.4 m
0.5 m
......
机器人就这样在计算机里"走起来了"。
所以仿真软件真正做的事情,并不是让一个 3D 模型神奇地自己运动。
而是:
让时间一点一点向前推进,并在每一个时间点重新计算机器人的状态。
这个很小的时间间隔通常可以叫作:
时间步长(Time Step)。
例如:
text
Δt = 0.01 s
意味着计算机每隔 0.01 秒计算一次。
也就是理想情况下:
text
1 秒大约计算 100 次
于是我们第一次看到机器人仿真的基本节奏:
text
计算当前状态
↓
时间向前推进一点
↓
计算下一状态
↓
再推进一点
↓
再计算
↓
......
最终,大量离散的计算结果连续显示出来,我们看到的就是机器人在运动。
2. 真正的物理仿真,不是直接修改位置
但是刚才那个例子还太简单。
我们直接规定:
text
v = 1 m/s
然后不断计算位置。
这更像是在播放一个按照数学规则生成的动画。
真正的机器人仿真往往还要继续向前追问:
机器人为什么会产生这个速度?
比如现在不是告诉小车:
text
速度 = 1 m/s
而是告诉它:
电机产生了一个驱动力。
假设机器人当前:
text
质量 m = 20 kg
合力 F = 10 N
根据:
text
F = ma
可以得到:
text
a = F / m
a = 10 / 20
a = 0.5 m/s²
现在我们知道的就不再是最终速度,而是:
机器人正在以 0.5 m/s² 的加速度改变自己的速度。
于是计算机开始一层一层往下算:
text
力
↓
加速度
↓
速度变化
↓
位置变化
每经过一个很小的时间步:
text
Δt
就重新计算一次。
这时候,机器人运动已经不是我们直接"画"出来的了。
而是:
根据数学模型和物理规律一步一步算出来的。
这就是物理仿真和普通动画之间非常重要的区别。
普通动画更关心:
"下一帧我要让它出现在哪里?"
物理仿真更关心:
"按照当前状态和受到的作用,它下一时刻应该在哪里?"
3. 仿真的不只是机器人,还包括机器人所在的世界
如果机器人永远漂浮在空无一物的空间里,仿真其实没有太大意义。
因为真实机器人一定会和环境发生作用。
比如移动机器人:
轮子会接触地面。
机械臂:
夹爪会接触物体。
四足机器人:
脚会不断踩在地面上。
无人机:
螺旋桨会与空气发生作用。
所以机器人仿真除了机器人自身模型,还必须建立一个:
环境模型(Environment Model)。
比如 Gazebo 中的一块地面,不应该只是一张看起来像地面的图片。
它还需要告诉物理引擎:
text
这里存在一个碰撞表面
机器人不能直接穿过去
轮子和地面之间存在摩擦
机器人受到重力作用
如果前面放一堵墙:
text
机器人
↓
向前运动
↓
接触墙壁
↓
发生碰撞
↓
产生接触力
↓
机器人的运动状态发生变化
如果地面摩擦非常小:
text
轮子快速旋转
↓
发生打滑
↓
轮子转了很多
↓
机器人实际移动距离却没有那么大
如果机械臂夹爪抓住一个箱子:
text
夹爪接触箱子
↓
产生接触力
↓
摩擦力阻止箱子滑落
↓
机械臂带着箱子运动
所以真正的机器人仿真并不是只模拟:
"机器人自己怎么动。"
还要模拟:
"机器人和现实世界怎样发生物理交互。"
这就是为什么专业机器人仿真软件会出现:
Gravity(重力)、Collision(碰撞)、Contact(接触)、Friction(摩擦)、Mass(质量)、Inertia(惯性)
这些参数。
它们共同决定了虚拟世界里的机器人究竟会怎样运动。
4. 机器人还需要"感觉"这个虚拟世界
现在我们已经有了一台会运动、会碰撞的虚拟机器人。
但还有一个非常关键的问题:
机器人自己知道发生了什么吗?
如果真实机器人安装了 LiDAR,那么虚拟机器人也应该能够:
"看见"虚拟墙壁距离自己有多远。
如果安装了 IMU,它应该能够得到:
虚拟机器人的加速度和角速度。
如果安装了编码器,它应该知道:
虚拟轮子到底转了多少。
如果安装摄像头,它应该得到:
虚拟摄像头看到的图像。
于是仿真系统还需要模拟:
传感器(Sensor)。
例如 LiDAR:
text
虚拟 LiDAR 发射射线
↓
射线碰到虚拟墙壁
↓
计算距离
↓
生成 LaserScan / PointCloud 数据
IMU:
text
物理引擎计算机器人运动状态
↓
得到加速度和角速度
↓
加入噪声 / Bias 等模型
↓
生成虚拟 IMU 数据
Camera:
text
虚拟摄像机
↓
渲染虚拟环境
↓
生成图像
↓
交给视觉算法
到了这里,一件非常有意思的事情发生了。
对于上层算法来说,它甚至不一定需要知道:
这些数据到底来自真实机器人,还是来自 Gazebo。
例如一个 ROS 2 SLAM 节点只关心:
text
我有没有收到 LiDAR 数据?
我有没有收到 IMU 数据?
数据格式是不是正确?
如果仿真系统能够提供与真实传感器类似的数据接口,那么:
text
真实 LiDAR
↓
ROS 2 Topic
↓
SLAM
和:
text
虚拟 LiDAR
↓
ROS 2 Topic
↓
SLAM
对于 SLAM 算法来说,就可以尽量保持相似的使用方式。
这也是机器人仿真非常强大的地方:
我们可以让算法在没有真实机器人的情况下,先面对一个"虚拟现实世界"。
5. 所以 Gazebo 里真正运行的,其实是一个不断循环的闭环
现在我们把前面的东西全部串起来。

假设我们在 Gazebo 中放一台 AMR,并通过 ROS 2 控制它。
首先控制程序发送:
text
向前运动
控制器把命令交给虚拟电机。
物理引擎根据:
text
电机输出
机器人质量
轮子半径
地面摩擦
当前速度
当前姿态
计算机器人下一时刻的状态。
机器人向前移动以后:
LiDAR 看到的环境发生变化;
IMU 测到新的运动状态;
编码器产生新的轮子数据。
这些数据再交给:
SLAM、定位、Nav2。
导航算法重新计算:
我现在在哪里?下一步应该怎么走?
然后再次发送新的控制命令。
整个过程不断循环:
#mermaid-svg-zvVEFcpGh0q9ql7o{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zvVEFcpGh0q9ql7o .error-icon{fill:#552222;}#mermaid-svg-zvVEFcpGh0q9ql7o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zvVEFcpGh0q9ql7o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zvVEFcpGh0q9ql7o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zvVEFcpGh0q9ql7o .marker.cross{stroke:#333333;}#mermaid-svg-zvVEFcpGh0q9ql7o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zvVEFcpGh0q9ql7o p{margin:0;}#mermaid-svg-zvVEFcpGh0q9ql7o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zvVEFcpGh0q9ql7o .cluster-label text{fill:#333;}#mermaid-svg-zvVEFcpGh0q9ql7o .cluster-label span{color:#333;}#mermaid-svg-zvVEFcpGh0q9ql7o .cluster-label span p{background-color:transparent;}#mermaid-svg-zvVEFcpGh0q9ql7o .label text,#mermaid-svg-zvVEFcpGh0q9ql7o span{fill:#333;color:#333;}#mermaid-svg-zvVEFcpGh0q9ql7o .node rect,#mermaid-svg-zvVEFcpGh0q9ql7o .node circle,#mermaid-svg-zvVEFcpGh0q9ql7o .node ellipse,#mermaid-svg-zvVEFcpGh0q9ql7o .node polygon,#mermaid-svg-zvVEFcpGh0q9ql7o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zvVEFcpGh0q9ql7o .rough-node .label text,#mermaid-svg-zvVEFcpGh0q9ql7o .node .label text,#mermaid-svg-zvVEFcpGh0q9ql7o .image-shape .label,#mermaid-svg-zvVEFcpGh0q9ql7o .icon-shape .label{text-anchor:middle;}#mermaid-svg-zvVEFcpGh0q9ql7o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zvVEFcpGh0q9ql7o .rough-node .label,#mermaid-svg-zvVEFcpGh0q9ql7o .node .label,#mermaid-svg-zvVEFcpGh0q9ql7o .image-shape .label,#mermaid-svg-zvVEFcpGh0q9ql7o .icon-shape .label{text-align:center;}#mermaid-svg-zvVEFcpGh0q9ql7o .node.clickable{cursor:pointer;}#mermaid-svg-zvVEFcpGh0q9ql7o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zvVEFcpGh0q9ql7o .arrowheadPath{fill:#333333;}#mermaid-svg-zvVEFcpGh0q9ql7o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zvVEFcpGh0q9ql7o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zvVEFcpGh0q9ql7o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zvVEFcpGh0q9ql7o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zvVEFcpGh0q9ql7o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zvVEFcpGh0q9ql7o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zvVEFcpGh0q9ql7o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zvVEFcpGh0q9ql7o .cluster text{fill:#333;}#mermaid-svg-zvVEFcpGh0q9ql7o .cluster span{color:#333;}#mermaid-svg-zvVEFcpGh0q9ql7o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zvVEFcpGh0q9ql7o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zvVEFcpGh0q9ql7o rect.text{fill:none;stroke-width:0;}#mermaid-svg-zvVEFcpGh0q9ql7o .icon-shape,#mermaid-svg-zvVEFcpGh0q9ql7o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zvVEFcpGh0q9ql7o .icon-shape p,#mermaid-svg-zvVEFcpGh0q9ql7o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zvVEFcpGh0q9ql7o .icon-shape .label rect,#mermaid-svg-zvVEFcpGh0q9ql7o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zvVEFcpGh0q9ql7o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zvVEFcpGh0q9ql7o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zvVEFcpGh0q9ql7o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 控制命令
执行器模型
物理引擎
机器人产生运动
虚拟传感器
SLAM / 定位 / 感知
规划与控制
这就是一个真正的:
Closed-loop Simulation(闭环仿真)。
它已经不是:
"让一个机器人模型在屏幕里跑来跑去。"
而是:
控制改变机器人 → 机器人改变环境中的状态 → 传感器观察新的状态 → 算法重新计算 → 再次控制机器人。
这和真实机器人工作的逻辑已经非常接近。
6. 我们做仿真,最终是在验证什么?
到了这里还有最后一个问题:
既然仿真永远不可能 100% 等于现实,那为什么还要仿真?
因为仿真的价值从来不是:
完全复制现实世界。
而是:
在成本更低、风险更小、条件更可控的环境里,提前验证我们的模型、算法和系统设计。
例如路径规划算法:
我们可以故意在地图里增加:
text
狭窄通道
动态障碍物
死胡同
复杂转弯
看看机器人还能不能找到合理路线。
研究 SLAM 时,可以故意加入:
text
LiDAR噪声
IMU Bias
轮子打滑
弱特征环境
观察定位什么时候开始漂移。
研究机械臂时,可以:
text
增加负载
改变关节速度
设置障碍物
改变目标位置
看看是否发生:
碰撞、超出工作空间、关节超限或者控制失稳。
这也是为什么仿真特别适合学习机器人。
现实机器人有时候非常"残酷"。
参数设置错了:
机械臂可能真的撞设备。
控制器写错了:
机器人可能直接冲出去。
人形机器人控制失稳:
可能直接摔倒。
而在仿真环境中:
text
撞了
↓
重置
摔了
↓
重置
算法崩了
↓
重新运行
我们可以不断制造问题,再不断观察机器人为什么失败。
这恰恰是理解机器人原理非常有效的方式。
所以整个"数学 / 物理原理"这一篇走到最后,我们真正建立起来的是这样一条完整链路:
#mermaid-svg-bBqrPSLVbdsby8Jw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bBqrPSLVbdsby8Jw .error-icon{fill:#552222;}#mermaid-svg-bBqrPSLVbdsby8Jw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bBqrPSLVbdsby8Jw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bBqrPSLVbdsby8Jw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bBqrPSLVbdsby8Jw .marker.cross{stroke:#333333;}#mermaid-svg-bBqrPSLVbdsby8Jw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bBqrPSLVbdsby8Jw p{margin:0;}#mermaid-svg-bBqrPSLVbdsby8Jw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bBqrPSLVbdsby8Jw .cluster-label text{fill:#333;}#mermaid-svg-bBqrPSLVbdsby8Jw .cluster-label span{color:#333;}#mermaid-svg-bBqrPSLVbdsby8Jw .cluster-label span p{background-color:transparent;}#mermaid-svg-bBqrPSLVbdsby8Jw .label text,#mermaid-svg-bBqrPSLVbdsby8Jw span{fill:#333;color:#333;}#mermaid-svg-bBqrPSLVbdsby8Jw .node rect,#mermaid-svg-bBqrPSLVbdsby8Jw .node circle,#mermaid-svg-bBqrPSLVbdsby8Jw .node ellipse,#mermaid-svg-bBqrPSLVbdsby8Jw .node polygon,#mermaid-svg-bBqrPSLVbdsby8Jw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bBqrPSLVbdsby8Jw .rough-node .label text,#mermaid-svg-bBqrPSLVbdsby8Jw .node .label text,#mermaid-svg-bBqrPSLVbdsby8Jw .image-shape .label,#mermaid-svg-bBqrPSLVbdsby8Jw .icon-shape .label{text-anchor:middle;}#mermaid-svg-bBqrPSLVbdsby8Jw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bBqrPSLVbdsby8Jw .rough-node .label,#mermaid-svg-bBqrPSLVbdsby8Jw .node .label,#mermaid-svg-bBqrPSLVbdsby8Jw .image-shape .label,#mermaid-svg-bBqrPSLVbdsby8Jw .icon-shape .label{text-align:center;}#mermaid-svg-bBqrPSLVbdsby8Jw .node.clickable{cursor:pointer;}#mermaid-svg-bBqrPSLVbdsby8Jw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bBqrPSLVbdsby8Jw .arrowheadPath{fill:#333333;}#mermaid-svg-bBqrPSLVbdsby8Jw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bBqrPSLVbdsby8Jw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bBqrPSLVbdsby8Jw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bBqrPSLVbdsby8Jw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bBqrPSLVbdsby8Jw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bBqrPSLVbdsby8Jw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bBqrPSLVbdsby8Jw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bBqrPSLVbdsby8Jw .cluster text{fill:#333;}#mermaid-svg-bBqrPSLVbdsby8Jw .cluster span{color:#333;}#mermaid-svg-bBqrPSLVbdsby8Jw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bBqrPSLVbdsby8Jw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bBqrPSLVbdsby8Jw rect.text{fill:none;stroke-width:0;}#mermaid-svg-bBqrPSLVbdsby8Jw .icon-shape,#mermaid-svg-bBqrPSLVbdsby8Jw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bBqrPSLVbdsby8Jw .icon-shape p,#mermaid-svg-bBqrPSLVbdsby8Jw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bBqrPSLVbdsby8Jw .icon-shape .label rect,#mermaid-svg-bBqrPSLVbdsby8Jw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bBqrPSLVbdsby8Jw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bBqrPSLVbdsby8Jw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bBqrPSLVbdsby8Jw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 真实世界
观察与抽象
数学模型
物理规律
数值计算
机器人仿真
算法验证
真实机器人
数学负责把现实世界变成计算机能够描述的东西;
物理负责告诉我们这些东西应该遵循什么规律;
仿真软件则负责:
让时间不断向前推进,反复计算这些数学和物理关系,让虚拟机器人按照我们建立的模型运行起来。
所以我们以后打开 Gazebo,看见一个机器人在里面移动时,看到的其实不只是一个 3D 动画。
它背后正在不断进行:
坐标计算、碰撞检测、刚体动力学、力与力矩计算、传感器模拟、控制计算以及时间积分。
而我们自己制作的交互模拟,则可以把其中某一个原理单独拆出来。
例如:
text
编码器模拟
→ 专门研究"旋转如何变成脉冲"
IMU模拟
→ 专门研究"运动如何变成惯性测量数据"
机械臂模拟
→ 专门研究"关节角度如何决定末端位置"
PID模拟
→ 专门研究"误差如何驱动控制器不断纠偏"
于是两种仿真方式并不冲突。
交互模拟帮助我们把一个原理看懂,专业机器人仿真帮助我们验证这些原理组合起来以后,整个系统能不能真正工作。
这也正是《机器人仿真实践》这个系列接下来要做的事情:
把机器人这个巨大的黑盒,一层一层拆开;把看不见的数学、物理和算法变成可以观察、可以操作、可以验证的模拟;最后,再把这些零散的能力重新组合成一台完整的机器人。
七、关于八荒启
八荒启是一家专注于交互体验产品与解决方案的品牌,持续探索交互技术在教育教学、产品展示、过程模拟、操作训练和数据可视化等场景中的应用。
我们不仅分享技术实现,也持续创作和沉淀交互动画、数字作品、开发教程、项目案例与行业解决方案,希望通过交互技术,让复杂事物变得更加容易理解、探索、操作和创造。
八荒启,专为交互动画而生
让复杂事物可探索、可操作、可反馈
如果你正在寻找交互作品、学习相关技术,或者希望把一个想法转化为可实际操作的交互项目,欢迎访问八荒启官网了解更多案例与服务。
- 官方网站:https://bahuangqi.com
- 主要内容:交互动画、3D 可视化、教育互动、仿真模拟与技术教程
- 定制服务:可通过官网提交需求或联系人工客服进行评估
感谢阅读。如果本文对你有所帮助,欢迎点赞、收藏和关注,我们会继续分享更多交互作品与项目实践。