feat: 完整中文翻译 maths-cs-ai-compendium(数学·计算机科学·AI 知识大全)

翻译自英文原版 maths-cs-ai-compendium,共 20 章全部完成。

第01章 向量 | 第02章 矩阵 | 第03章 微积分
第04章 统计学 | 第05章 概率论 | 第06章 机器学习
第07章 计算语言学 | 第08章 计算机视觉 | 第09章 音频与语音
第10章 多模态学习 | 第11章 自主系统 | 第12章 图神经网络
第13章 计算与操作系统 | 第14章 数据结构与算法
第15章 生产级软件工程 | 第16章 SIMD与GPU编程
第17章 AI推理 | 第18章 ML系统设计
第19章 应用人工智能 | 第20章 前沿人工智能

翻译说明:
- 所有数学公式 $...$ / $$...$$、代码块、图片引用完整保留
- mkdocs.yml 配置中文导航 + language: zh
- README.md 已翻译为中文(兼 docs/index.md)
- docs/ 目录包含指向各章文件的 symlink
- 约 29,000 行中文内容,排除 .cache/ 构建缓存
This commit is contained in:
2026-05-03 10:23:20 +08:00
commit 2536c937e3
400 changed files with 49040 additions and 0 deletions
@@ -0,0 +1,287 @@
# 感知
*感知是自主系统感知和解释物理世界的方式。本章涵盖传感器模态、标定、传感器融合、3D目标检测、深度估计、占据网络、车道检测和语义建图——这是每个机器人、无人机和自动驾驶汽车赖以构建的感知基础。*
- 对人类而言,感知世界毫不费力:你看到一辆车驶近,听到引擎声,感受到脚下的地面,瞬间在脑海中构建出周围环境的心智模型。自主系统也必须做到同样的事,但它使用的是电子传感器和算法,而非眼睛和耳朵。
- 根本性挑战在于:传感器提供的是原始数字(像素强度、点云、信号反射),系统必须将这些数字转化为结构化的理解:"前方12米处有一个行人,以1.5米/秒的速度向左移动。"这就是感知问题。
- 下游的所有任务(预测、规划、控制)都依赖于感知。一个拥有完美规划器但感知能力差的自动驾驶汽车仍然会撞车。感知是瓶颈。
## 传感器模态
- 自主系统使用多种传感器类型,各有其优势与失效模式。没有哪种传感器能单独胜任。
![传感器比较:相机、LiDAR、雷达和IMU在分辨率、深度、天气鲁棒性、速度测量和成本方面的评分](../images/sensor_comparison.svg)
- **相机**以高分辨率捕获密集的颜色信息。单张图像包含数百万像素,每个像素记录RGB值(如我们在第8章所见)。相机价格低廉、重量轻,提供丰富的纹理和颜色信息,这对识别标牌、检测交通信号灯和识别物体至关重要。
- 相机类型包括**单目**(单个镜头,无原生深度)、**立体**(两个镜头相隔一个基线,通过视差计算深度,详见第8章)和**鱼眼**(超宽视场角,180°以上,具有严重径向畸变,用于环绕视图泊车系统)。
- 相机的主要弱点是投影过程中丢失深度信息。3D场景通过针孔相机模型映射到2D图像平面(回顾第8章的内参矩阵$K$):
$$\\begin{bmatrix} u \\\\ v \\\\ 1 \\end{bmatrix} = \\frac{1}{Z} K \\begin{bmatrix} X \\\\ Y \\\\ Z \\end{bmatrix}$$
- 除以$Z$的过程丢弃了绝对深度。两个不同大小、不同距离的物体可能产生完全相同的投影。从单张图像恢复深度是病态问题,这就是为什么需要立体相机或学习型单目深度模型。
- 相机在恶劣条件下也会受到影响:直射阳光产生眩光,黑暗降低信号,雨雾散射光线。
- **LiDAR**(光探测与测距)发射激光脉冲并测量每个脉冲返回的时间。由于光速已知($c \\approx 3 \\times 10^8$ m/s),每个反射点的距离为:
$$d = \\frac{c \\cdot \\Delta t}{2}$$
![LiDAR飞行时间:激光脉冲从物体上弹回,距离由往返时间计算得出](../images/lidar_time_of_flight.svg)
- 因子2考虑了往返行程(去程和回程)。通过将激光扫过场景,LiDAR构建了一个**点云**:一组3D坐标$(x, y, z)$,通常带有强度(反射率)值。
- **旋转式LiDAR**(如Velodyne)旋转激光阵列360°以提供完整的环视视图。典型设备每秒生成超过30万个点,覆盖64-128个垂直通道。结果是场景的稀疏但几何精度高的3D表示。
- **固态LiDAR**没有运动部件,使用光学相控阵或MEMS镜面。这使得它们更便宜、更紧凑、更可靠,但通常视场角较窄(120° vs 360°)。
- LiDAR提供精确的深度,但生成的是稀疏数据("像素"数量远少于相机),没有颜色信息,且价格昂贵。在大雨、雪或灰尘中,粒子会散射激光脉冲,其性能也会下降。
- **雷达**(无线电探测与测距)基于与LiDAR相同的飞行时间原理,但使用无线电波(毫米波,汽车领域通常为77 GHz)。无线电波穿透雨、雾、尘和雪的能力远优于光,使雷达成为最耐天气的传感器。
- 雷达还能通过**多普勒效应**直接测量**速度**。当物体向传感器移动时,反射波被压缩(频率升高);当远离时,反射波被拉伸(频率降低)。速度公式为:
$$v = \\frac{\\Delta f \\cdot c}{2 f_0}$$
- 其中$\\Delta f$是频移,$f_0$是发射频率。这提供了瞬时径向速度,无需任何跟踪或帧间计算。
- 折中是分辨率:雷达的角分辨率远低于相机或LiDAR,难以区分邻近物体或检测精细细节。但它在任何天气条件下都能出色地探测远距离(200米以上)的车辆。
- **超声波传感器**发射高频声脉冲(40-70 kHz)并测量回波返回时间。它们工作在极短距离(0.2-5米),主要用于泊车辅助。其物理原理与LiDAR相同,只是用声音代替光,因此$d = \\frac{v_{\\text{声}} \\cdot \\Delta t}{2}$,其中$v_{\\text{声}} \\approx 343$ m/s。
- **IMU**(惯性测量单元)包含加速度计和陀螺仪,分别测量线加速度和角速度。IMU提供高频运动数据(通常200-1000 Hz),填补了较慢传感器更新之间的空白。它们不直接感知环境,而是跟踪机器人自身的运动,因此对航位推算和状态估计至关重要。
- IMU存在**漂移**问题:小的测量误差随时间累积,导致估计位置偏离真实位置。这就是为什么IMU几乎总是与其他传感器(相机、GPS、LiDAR)融合使用,而非单独使用。
- **GNSS**(全球导航卫星系统,包括GPS)通过三角测量来自多颗卫星的信号,提供地球表面的绝对位置。标准GPS精度为2-5米,不足以进行车道级驾驶。**RTK-GPS**(实时动态差分GPS)使用固定基站校正误差,达到厘米级精度,但需要清晰的天空视野和基站基础设施。
## 传感器标定
- 在传感器协同工作之前,必须进行**标定**:将每个传感器的测量值与共同坐标系关联起来。
- **内参标定**确定传感器的内部参数。对于相机,这意味着焦距、主点和畸变系数(如第8章所述)。对于LiDAR,这意味着激光束之间的精确角度偏移。常用的方法是张氏棋盘格标定法,即从多个角度观察已知平面图案,求解内参矩阵。
- **外参标定**确定两个传感器之间的刚体变换(旋转$R$和平移$\\mathbf{t}$)。如果相机和LiDAR安装在同一辆车上,外参标定会找到一个$4 \\times 4$变换矩阵,将LiDAR坐标中的点映射到相机坐标:
$$\\mathbf{p}_{\\text{相}} = \\begin{bmatrix} R & \\mathbf{t} \\\\ \\mathbf{0}^T & 1 \\end{bmatrix} \\mathbf{p}_{\\text{激}}$$
- 这是齐次坐标中的仿射变换,正是我们在第2章(线性变换)中研究过的那种。如果这个矩阵出错,LiDAR点将投影到错误的像素上,整个融合流程就会崩溃。
- **时间标定**同步传感器时钟。以30 Hz工作的相机和以10 Hz工作的LiDAR在不同时间戳产生数据。如果汽车以30 m/s(高速公路速度)行驶,10毫秒的时间误差对应30厘米的空间误差。硬件触发(共享时钟脉冲)或软件同步(时间戳之间的插值)至关重要。
## 传感器融合
- 没有哪种传感器能覆盖所有条件。相机看得到颜色和纹理但丢失深度。LiDAR精确测量深度但稀疏且色盲。雷达在任何天气下都能工作但分辨率低。**传感器融合**结合各自的优势,弥补各自的弱点。
- **早期融合**(数据级融合)在进行任何处理之前合并原始传感器数据。例如,将LiDAR点投影到相机图像上,创建RGBD表示(每像素的颜色+深度),或者为每个LiDAR点赋予其投影到的相机像素的颜色。这种方式保留了最多的信息,但需要精确的标定,且对未对准非常敏感。
- **后期融合**(决策级融合)每个传感器独立运行各自的检测流程,然后合并最终输出(边界框、类别标签、置信度分数)。每个传感器投票,融合模块协调分歧。这种方式更简单、更模块化,但每个流程无法从其他传感器的原始数据中获益。
- **中级融合**在中间特征表示层面进行操作。每个传感器的原始数据被编码到学习到的特征空间(使用CNN或transformer),然后特征被合并。这是现代系统的主流方法,因为它让网络学习从每种模态中提取什么。
![BEV融合流程:相机和LiDAR被独立编码,然后投影到共享的鸟瞰图网格](../images/bev_fusion_pipeline.svg)
- **BEVFusion**是一种代表性的中级融合架构。它将相机特征和LiDAR特征投影到一个共同的**鸟瞰图(BEV)**表示中,即场景的俯视网格。相机特征通过预测的深度分布被"提升"到3D,然后散布到BEV网格上。LiDAR特征已经是3D的,直接被体素化到同一个网格上。融合后的BEV特征随后由检测头处理。
- BEV表示之所以强大,是因为它提供了一个统一的、度量尺度的坐标框架,空间推理(距离、尺寸、重叠)在其中变得直观。在相机图像中,一辆近处的自行车和一辆远处的卡车可能占据相同数量的像素。而在BEV中,它们的真实大小和位置一目了然。
## 3D目标检测
- 感知的核心任务是在3D空间中检测物体:它们在哪里,有多大,是什么,朝向何方?每次检测得到一个**3D边界框**,包括位置$(x, y, z)$、尺寸$(l, w, h)$、航向角$\\theta$、类别标签和置信度分数。
- **基于LiDAR的检测**直接在点云上操作。挑战在于点云是无序的、不规则的,且密度变化大(近处物体有数千个点,远处物体只有几个)。回顾第8章,PointNet通过共享MLP和置换不变的聚合(最大池化)来处理这一问题。
- **PointPillars**通过将地面平面离散化为垂直柱("pillars")的网格,将点云转化为结构化表示。每个pillar内的所有点由一个小型PointNet编码为固定大小的特征向量。结果是一个2D伪图像,可以由标准的2D CNN主干网络处理,然后是检测头(如第8章中的SSD架构)。这种方法快速且有效。
- **CenterPoint**将物体检测为点而非边界框。它在BEV中预测物体中心的热图,然后在每个峰值处回归边界框属性(尺寸、高度、航向、速度)。这是CenterNet(第8章)的3D类比:无锚点,训练时无需NMS,并通过跨帧关联中心点自然扩展到跟踪。
- **纯相机3D检测**必须从2D图像推断深度,这从根本上更难。现代方法如**BEVDet**和**BEVFormer**使用transformer架构将2D图像特征"提升"到3D。BEVFormer使用空间交叉注意力:BEV查询关注投影到每个相机图像上的特定3D参考点,从相关位置提取特征。
- 基于LiDAR和基于相机的3D检测之间的精度差距正在迅速缩小,这得益于更好的深度估计、更大的模型和时间融合(利用多帧累积深度线索,类似立体匹配但跨时间进行)。
## 深度估计
- 深度估计是为每个像素或点分配距离值的问题。
- **立体匹配**使用两个相隔已知基线$b$的相机。同一个3D点在两幅图像中的水平位置略有不同,形成**视差**$d$。深度计算公式为(来自第8章):
$$Z = \\frac{f \\cdot b}{d}$$
- 其中$f$是焦距。挑战在于找到两幅图像之间的正确对应关系,特别是在无纹理区域、遮挡区域和重复图案中。现代立体网络(如RAFT-Stereo)使用带有相关体积的迭代精化方法。
- **单目深度估计**从单张图像预测深度。由于这是病态问题(无限多个3D场景可以产生相同的图像),网络必须学习统计先验:"地面是平的"、"物体随距离增大而变小"、"纹理梯度表明表面在后退"。
- **Depth Anything**(第8章中介绍过)通过在大规模无标注数据集上进行自监督训练,然后在标注数据上进行微调,实现了强大的单目深度估计。关键洞察在于尺度不变损失处理了固有不明确性:模型预测的是相对深度(排序)而非绝对米数。
- **LiDAR-相机深度融合**将稀疏的LiDAR深度测量值投影到相机图像上作为监督信号。网络学习"填充"稀疏点之间的空白,生成结合LiDAR精度和相机分辨率的密集深度图。
## 占据网络
- 传统感知输出一个边界框列表,每个检测到的物体一个框。但现实世界中许多事物并不适合用框来整齐地表示:形状不规则的碎片、施工围挡、悬垂的树枝、部分坍塌的墙壁。
![边界框在不规则形状上浪费空间;占据网格遵循实际几何形状](../images/occupancy_vs_bbox.svg)
- **占据网络**将场景表示为密集的3D体素网格。每个体素(一个小立方体空间,例如0.2m × 0.2m × 0.2m)被分类为空闲、占据或未知,并可选择赋予语义标签(道路、人行道、车辆、植被等)。
- 这意味着从以物体为中心的感知("检测汽车")转向以场景为中心的感知("3D空间的哪些部分被占据?")。优势在于通用性:系统不需要预定义对象类别列表来避免与任意障碍物碰撞。
- 从架构上看,占据网络接收传感器输入(相机、LiDAR或两者),将其编码为3D特征体积,并预测每个体素的标签。3D特征体积通常通过将2D特征提升到3D(类似于BEV构建但扩展到垂直方向)来构建,然后使用3D卷积或稀疏卷积进行处理。
- **TPVFormer**(三视角)通过将3D体积分解为三个正交平面(俯视图、前视图、侧视图)来避免完整3D注意力的立方级计算成本。每个平面使用2D注意力,其特征在每个体素处组合。这让人想起SVD如何将矩阵分解为更简单的因子(第2章):将一个困难的3D问题分解为可管理的2D部分。
- 输出的体素网格直接告诉规划器哪些空间区域是安全的、哪些不是,使其成为感知和规划之间的自然接口。
## 车道检测与道路拓扑
- 对于在结构化道路上行驶的车辆,理解**车道几何**至关重要。系统必须知道车道在哪里、如何弯曲、在哪里合并和分叉,以及车辆处于哪条车道。
- 经典方法将参数曲线拟合到检测到的车道标线上。常用的模型是三次多项式:
$$x(y) = a_0 + a_1 y + a_2 y^2 + a_3 y^3$$
- 其中$y$是前方纵向距离,$x$是横向偏移。这是多项式近似(回顾第3章的泰勒级数),选择它是因为道路是平滑曲线,很好地由低次多项式描述。系数通过检测到的车道点上的最小二乘回归估计得出。
- 现代方法使用神经网络直接检测车道。**LaneNet**将每条车道视为一个实例,使用嵌入分支对属于同一条车道的像素进行分组,然后进行曲线拟合。**GANet**使用基于图的方法,将车道拓扑表示为有向图,其中节点是车道点,边编码连接关系(哪些车道在交叉口合并、分叉或连接)。
- **道路拓扑**超越了单个车道曲线,捕获完整结构:车道之间如何连接,哪些车道允许左转,高速公路入口匝道在哪里汇入。这被建模为有向图,交叉口是节点,车道段是带有属性(限速、车道类型、转弯限制)的边。
- 图结构对路线规划至关重要:规划器需要知道的不仅是"车道在哪里",还有"哪条车道序列能到达目的地"。
## 语义建图
- 感知并不止于检测单帧中的物体。随着时间的推移,自主系统构建一个**语义地图**:对其环境的持久化、结构化表示,积累来自多次观测的信息。
- 最简单的情况下,语义地图是一个2D网格(**占据网格**),每个单元存储被占据的概率。随着机器人移动并通过传感器扫描,它使用贝叶斯更新来更新这些概率:
$$P(\\text{占据} \\mid z_{1:t}) = \\frac{P(z_t \\mid \\text{占据}) \\cdot P(\\text{占据} \\mid z_{1:t-1})}{P(z_t)}$$
- 这就是贝叶斯定理的实际应用(来自第5章):每次新的测量$z_t$更新对每个单元的先验信念。通常使用**对数几率**表示来避免乘以许多小概率带来的数值问题:
$$l_t = l_{t-1} + \\log \\frac{P(z_t \\mid \\text{占据})}{P(z_t \\mid \\text{空闲})}$$
- 对数几率相加等价于概率相乘(回顾$\\log(ab) = \\log a + \\log b$),累加和自然地随时间积累证据。
- 更丰富的地图为每个单元分配语义标签(道路、人行道、建筑、植被),并可扩展到3D。这与占据网络密切相关,但强调持久性和时间聚合而非单帧预测。
- **SLAM**(同时定位与建图),在第8章中介绍过,是在构建地图的同时跟踪机器人在地图中的位置的算法。视觉-惯性SLAM融合相机和IMU数据;LiDAR SLAM使用点云配准。感知流程将检测结果和深度估计输入SLAM系统,由后者维护全局地图。
- 现代方法越来越多地使用神经隐式表示(如第8章中的NeRF)来构建可在任何3D点查询的密集、逼真地图。这些神经地图将整个场景的压缩表示存储在网络权重中,支持新视角合成和详细空间查询等任务。
## 编程任务(使用CoLab或notebook
1. 使用投影矩阵将3D LiDAR点投影到2D相机图像上。可视化哪些点落在图像边界内。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 模拟3D LiDAR点(x=向前,y=向左,z=向上)
rng = jax.random.PRNGKey(0)
points_3d = jax.random.uniform(rng, (200, 3), minval=jnp.array([5, -10, -2]),
maxval=jnp.array([50, 10, 3]))
# 相机内参矩阵(焦距500,图像中心320x240)
K = jnp.array([[500, 0, 320],
[0, 500, 240],
[0, 0, 1.0]])
# 外参:LiDAR到相机(单位旋转,小平移)
R = jnp.eye(3)
t = jnp.array([0.0, 0.0, -0.5])
# 投影:p_cam = K @ (R @ p_lidar + t)
p_cam = (R @ points_3d.T).T + t
p_img = (K @ p_cam.T).T
p_img = p_img[:, :2] / p_img[:, 2:3] # 除以Z
# 过滤相机前方且在图像内的点
mask = (p_cam[:, 2] > 0) & (p_img[:, 0] > 0) & (p_img[:, 0] < 640) & \
(p_img[:, 1] > 0) & (p_img[:, 1] < 480)
depth = p_cam[mask, 2]
plt.figure(figsize=(8, 5))
plt.scatter(p_img[mask, 0], p_img[mask, 1], c=depth, cmap="viridis", s=5)
plt.colorbar(label="深度 (米)")
plt.xlim(0, 640); plt.ylim(480, 0)
plt.title("投影到相机图像上的LiDAR点")
plt.xlabel("u (像素)"); plt.ylabel("v (像素)")
plt.show()
```
2. 使用贝叶斯对数几率更新构建一个简单的2D占据网格。模拟一个距离传感器扫描环境,观察地图的生成过程。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 网格设置:50x50个单元,每个0.2米
grid_size = 50
log_odds = jnp.zeros((grid_size, grid_size))
# 传感器模型:对数几率更新值
l_occ = 0.85 # 命中意味着占据的置信度
l_free = -0.4 # 穿过意味着空闲的置信度
# 模拟障碍物:从(5,20)到(5,30)的墙(网格坐标)
wall_y = jnp.arange(20, 30)
# 机器人在(25, 25),向外扫描
robot = jnp.array([25, 25])
for angle_deg in range(0, 360, 5):
angle = jnp.radians(angle_deg)
direction = jnp.array([jnp.cos(angle), jnp.sin(angle)])
for step in range(1, 25):
cell = (robot + direction * step).astype(int)
r, c = int(cell[0]), int(cell[1])
if r < 0 or r >= grid_size or c < 0 or c >= grid_size:
break
# 检查此单元是否为墙
is_wall = (r == 5) and (c >= 20) and (c < 30)
if is_wall:
log_odds = log_odds.at[r, c].add(l_occ)
break
else:
log_odds = log_odds.at[r, c].add(l_free)
# 将对数几率转换为概率
prob = 1.0 / (1.0 + jnp.exp(-log_odds))
plt.figure(figsize=(6, 6))
plt.imshow(prob.T, origin="lower", cmap="RdYlGn_r", vmin=0, vmax=1)
plt.colorbar(label="P(被占据)")
plt.plot(25, 25, "b*", markersize=10, label="机器人")
plt.legend()
plt.title("贝叶斯更新生成的2D占据网格")
plt.show()
```
3. 使用视差从立体图像对计算深度。模拟两个相机视角下的3D点,计算视差并恢复深度。
```python
import jax
import jax.numpy as jnp
# 相机参数
f = 500.0 # 焦距(像素)
b = 0.12 # 基线(米,12厘米)
# 已知深度的3D点
depths_true = jnp.array([5.0, 10.0, 20.0, 50.0, 100.0])
# 视差 = f * b / Z
disparities = f * b / depths_true
# 从视差恢复深度
depths_recovered = f * b / disparities
for z, d, z_r in zip(depths_true, disparities, depths_recovered):
print(f"真实深度: {z:6.1f}米 视差: {d:6.2f}像素 恢复值: {z_r:6.1f}")
# 注意:视差与深度成反比
# 近处物体视差大,远处物体视差小
# 这就是为什么立体视觉在近距离最准确
```
@@ -0,0 +1,298 @@
# 机器人学习
*机器人学习弥合了算法与物理行动之间的鸿沟。本章涵盖运动学、动力学、经典控制、模仿学习、仿真到现实迁移、操作、移动和安全——这些技术赋予机器人在现实世界中移动、抓取、行走和交互的能力。*
- 在前面的章节中,我们研究了如何感知世界(第8章,第11章文件1)以及如何从数据中学习(第6章)。但感知和学习还不够。机器人必须**行动**:移动手臂抓取杯子、在不平坦的地形上行走、或在仓库中导航。这就是机器人学习的用武之地。
- 核心挑战在于物理世界是连续的、高维的、接触丰富的且不宽容的。图像识别中的分类错误只是标签错误,而机器人学中的控制错误则意味着机器人损坏或物体掉落。两者的代价截然不同。
## 机器人运动学
- **运动学**描述运动的几何关系,不考虑力。机器人手臂是由关节连接的刚性连杆组成的链条。每个关节有一个自由度(DoF):要么旋转(旋转关节),要么滑动(棱柱关节)。
- 机器人的**构型**是所有关节角度(或位移)的集合 $\\mathbf{q} = [q_1, q_2, \\ldots, q_n]^T$。这个向量位于**关节空间**(或构型空间)中,这是一个$n$维空间,每个轴对应一个关节。一个6自由度机器人手臂有一个6维构型空间。
![2连杆机器人手臂:关节角度q1和q2通过正向运动学确定末端执行器位置](../images/robot_arm_fk.svg)
- **正向运动学(FK)**根据给定的关节角度计算末端执行器("手")的位置和姿态。这是一个从关节空间映射到**任务空间**(末端执行器的3D位置和姿态,也称为笛卡尔空间)的函数 $\\mathbf{x} = f(\\mathbf{q})$。
- 每个关节由一个$4 \\times 4$齐次变换矩阵描述(回顾第2章的仿射变换)。**Denavit-HartenbergDH)约定**用四个参数参数化每个关节:连杆长度$a$、连杆扭转角$\\alpha$、连杆偏移$d$和关节角度$\\theta$。关节$i$的变换为:
$$T_i = \\begin{bmatrix} \\cos\\theta_i & -\\sin\\theta_i \\cos\\alpha_i & \\sin\\theta_i \\sin\\alpha_i & a_i \\cos\\theta_i \\\\ \\sin\\theta_i & \\cos\\theta_i \\cos\\alpha_i & -\\cos\\theta_i \\sin\\alpha_i & a_i \\sin\\theta_i \\\\ 0 & \\sin\\alpha_i & \\cos\\alpha_i & d_i \\\\ 0 & 0 & 0 & 1 \\end{bmatrix}$$
- 完整的正向运动学是所有关节变换的乘积:$T_{0 \\to n} = T_1 T_2 \\cdots T_n$。这是矩阵乘法链式变换(第2章):每个关节的变换依次应用,将坐标系从基座旋转和平移到末端执行器。
- **逆向运动学(IK)**是反向问题:给定期望的末端执行器姿态$\\mathbf{x}^*$,求关节角度$\\mathbf{q}$使得$f(\\mathbf{q}) = \\mathbf{x}^*$。这要难得多,因为:
- 映射是非线性的(涉及正弦和余弦)。
- 可能有多个解(不同的手臂构型可以到达同一点)。
- 可能没有解(目标超出可达范围)。
- 解析解只存在于特定的机器人几何构型中。对于通用机器人,IK使用**雅可比矩阵**迭代求解。雅可比矩阵$J(\\mathbf{q})$将关节角度的微小变化与末端执行器位置的微小变化联系起来(回顾第3章的雅可比矩阵):
$$\\dot{\\mathbf{x}} = J(\\mathbf{q}) \\dot{\\mathbf{q}}$$
- 要将末端执行器移动一个小的量$\\Delta \\mathbf{x}$,我们需要$\\Delta \\mathbf{q} = J^{-1} \\Delta \\mathbf{x}$(当$J$不是方阵时使用伪逆$J^+ \\Delta \\mathbf{x}$)。这个过程迭代进行,直到末端执行器到达目标,本质上就是将牛顿法(第3章)应用于运动学方程。
- 在**奇异点**附近,雅可比矩阵的秩下降(某些列变得线性相关,如我们在第2章中研究的)。物理上这意味着机器人失去一个自由度:无论关节移动多快,末端执行器都无法在某些方向上移动。伪逆在奇异点附近会爆炸,因此使用阻尼最小二乘法(加入正则化项$\\lambda^2 I$):
$$\\Delta \\mathbf{q} = J^T(JJ^T + \\lambda^2 I)^{-1} \\Delta \\mathbf{x}$$
## 动力学与控制
- **动力学**将力引入画面。机器人手臂的运动方程遵循**操作臂方程**:
$$M(\\mathbf{q})\\ddot{\\mathbf{q}} + C(\\mathbf{q}, \\dot{\\mathbf{q}})\\dot{\\mathbf{q}} + \\mathbf{g}(\\mathbf{q}) = \\boldsymbol{\\tau}$$
- 其中$M(\\mathbf{q})$是质量(惯性)矩阵,$C(\\mathbf{q}, \\dot{\\mathbf{q}})$捕获科里奥利力和离心力效应,$\\mathbf{g}(\\mathbf{q})$是重力向量,$\\boldsymbol{\\tau}$是关节力矩向量(控制输入)。这是一个二阶微分方程组,每个关节一个方程。
- 质量矩阵$M$总是对称正定的(回顾第2章,正定矩阵保证唯一最小值,在这里它确保系统对施加的力矩有可预测的响应)。
- **PID控制**是机器人学中使用最广泛的控制器。对于每个关节,它根据误差$e(t) = q_{\\text{期望}}(t) - q_{\\text{实际}}(t)$计算力矩:
$$\\tau(t) = K_p e(t) + K_i \\int_0^t e(s) \\, ds + K_d \\dot{e}(t)$$
- 三个项有直观的作用:
- **比例项**($K_p$):与当前误差成比例地校正。误差越大 → 校正越大。就像弹簧将关节拉向目标。
- **积分项**($K_i$):累积过去的误差以消除稳态偏差。如果关节持续欠调,积分项会积累并提供额外的推力。
- **微分项**($K_d$):对误差变化率作出反应,提供阻尼。它随着误差减小而减缓响应,防止过冲和震荡。
![PID控制器调参:高Kp震荡,高Kd迟钝,调谐好的PID快速到达目标](../images/pid_response.svg)
- 调整$K_p, K_i, K_d$是一种平衡:$K_p$太大会引起震荡,$K_d$太大会使系统反应迟钝,$K_i$太大会导致积分饱和(在持续误差期间积分无限增长)。
- **模型预测控制(MPC)**具有前瞻性。在每个时间步,它求解一个优化问题:找到未来控制序列,在有限时域内最小化代价函数(例如,跟踪误差+控制能量),并满足动力学模型和约束条件。只应用第一个控制量,然后在下一个时间步重复该过程。
$$\\min_{\\mathbf{u}_{0:T}} \\sum_{t=0}^{T} \\left[ \\|\\mathbf{x}_t - \\mathbf{x}_t^*\\|_Q^2 + \\|\\mathbf{u}_t\\|_R^2 \\right] \\quad \\text{subject to} \\quad \\mathbf{x}_{t+1} = f(\\mathbf{x}_t, \\mathbf{u}_t)$$
- 这里$\\|\\mathbf{x}\\|_Q^2 = \\mathbf{x}^T Q \\mathbf{x}$是使用正定矩阵$Q$(第2章)的加权范数,允许对不同状态误差进行不同惩罚。MPC自然地处理约束(关节限位、力矩限位、避障),因为它们被显式地包含在优化中。
- **阻抗控制**调节力与运动之间的关系,而不是跟踪刚性轨迹。它不命令"到达位置$x$",而是命令"表现得像一个以$x$为中心的弹簧-阻尼系统":
$$F = K_s(\\mathbf{x}^* - \\mathbf{x}) + D(\\dot{\\mathbf{x}}^* - \\dot{\\mathbf{x}})$$
- 其中$K_s$是刚度矩阵,$D$是阻尼矩阵。这使得机器人具有柔顺性:如果它接触到障碍物,它会退让而不是强行通过。阻抗控制对于接触密集型任务(如将销钉插入孔中或将物体递给人类)至关重要。
## 模仿学习
- 我们可以从示范中学习控制策略,而不是手工设计控制器。人类执行任务,机器人观察,学习算法提取策略。这就是**模仿学习**(或从示范中学习)。
- **行为克隆(BC)**是最简单的方法:将示范视为监督学习数据集。给定来自专家的观测-动作对$\\{(\\mathbf{o}_t, \\mathbf{a}_t)\\}$,训练策略$\\pi_\\theta(\\mathbf{a} \\mid \\mathbf{o})$从观测中预测专家的动作。这是标准的监督学习(第6章):最小化损失:
$$\\mathcal{L}(\\theta) = \\mathbb{E}_{(\\mathbf{o}, \\mathbf{a}) \\sim \\mathcal{D}} \\left[ \\| \\pi_\\theta(\\mathbf{o}) - \\mathbf{a} \\|^2 \\right]$$
![行为克隆中的分布偏移:微小误差不断累积,导致学习到的策略远离专家轨迹](../images/distribution_shift_bc.svg)
- 问题是**分布偏移**(也称为**复合误差问题**)。在训练期间,策略看到的是专家的状态。在部署期间,策略自身的小误差将其推入专家从未访问过的状态。这些不熟悉的状态导致更差的动作,进而导致更不熟悉的状态,误差迅速累积放大。
- 想象一下通过观看完美驾驶员来学习开车。你从未见过小幅偏移后会发生什么,因为专家从未偏移过。第一次你稍为偏离时,你完全不知道如何恢复。
- **DAgger**(数据集聚合)通过迭代解决这个问题:
1. 在当前数据上训练策略。
2. 在环境中运行策略,收集新状态。
3. 请专家用正确的动作标注这些新状态。
4. 将新数据添加到数据集并重新训练。
- 经过多次迭代,数据集覆盖了学习策略实际访问的状态,而不仅仅是专家的轨迹。策略得到了改善,因为它已经看到并学会了从自己的错误中恢复。
- **使用Transformer的动作分块(ACT)**是一种现代方法,策略预测一系列未来动作(一个"块"),而不是一次预测一个动作。它使用带有transformer主干的 conditional VAE 实现。预测动作块更鲁棒,因为它捕获了时间相关性:伸手动作的平滑性编码在块中,而不是依赖于可能漂移的自回归单步预测。
- **扩散策略**将扩散模型(第8章)应用于动作生成。它不预测单个动作,而是建模以观测为条件的完整动作分布。从噪声开始,它迭代地去噪以生成动作序列。这自然地处理了**多模态性**:当有多个有效方式完成任务时(从左边或右边伸手),扩散模型可以表示两种模式,而回归策略会平均它们(到达中间某处,可能两种都无效)。
## 仿真到现实迁移
- 在现实世界中训练机器人是昂贵、缓慢且危险的。一个通过试错学习抓取的机器人可能需要数千次尝试,在这个过程中损坏物体和自身。**仿真**提供了无限、安全、快速的体验。但仿真器并非完美:物理近似、视觉合成、接触简化。
- **仿真到现实差距**是仿真性能与真实性能之间的差异。在仿真中完美运行的策略可能在真实机器人上完全失败,因为它过度拟合了仿真器的特定细节。
![通过域随机化实现仿真到现实迁移:在大量随机化仿真上训练,使现实世界只是另一种变体](../images/sim_to_real.svg)
- **域随机化**通过在广泛的仿真器设置上进行训练来应对这一问题。不是使用一种仿真,而是使用数千种具有随机化参数的仿真:
- 物理:摩擦系数、质量、阻尼
- 视觉:光照、纹理、颜色、相机位置
- 动力学:电机延迟、噪声水平
- 其思想是,如果策略在所有这些变化下都能工作,那么现实世界只是分布中的"另一种变化"。策略学习对随机化属性不变的特征,这些不变特征能够迁移。
- **系统辨识**采取相反的方法:不是随机化所有内容,而是仔细测量真实系统的物理参数并将仿真器调谐到匹配。这提供了更精确的仿真,但也更脆弱(任何未建模的效应都会导致差距)。
- 在实践中,最好的结果是将两者结合:使用系统辨识使仿真器合理接近,然后使用域随机化覆盖剩余的不确定性。
- **通过微调的仿真到现实迁移**主要在仿真中训练,然后进行少量的真实世界微调。仿真提供了良好的初始化,真实世界数据纠正了仿真器特定的偏差。这需要的真实世界数据远少于从头训练。
## 机器人世界模型
- 上述所有强化学习和模仿学习方法都是**无模型**的:策略通过直接交互(或示范)学习行动,而不显式建模世界如何运作。另一种是**基于模型**的学习:首先学习环境动力学模型,然后使用该模型进行规划或生成合成经验。
- **世界模型**学习转移函数$p(s_{t+1} \\mid s_t, a_t)$:给定当前状态和动作,预测下一状态(如第10章所述)。在机器人学中,这意味着预测如果机器人采取特定动作会发生什么:"如果我向左推这个方块,它会滑动3厘米,它后面的杯子会倒下。"
- 其吸引力在于**样本效率**。现实世界的机器人交互成本高昂。如果机器人能从适量的真实数据中学习一个世界模型,它就可以通过在大脑中滚动模型来"想象"数千条轨迹,在不动用物理世界的情况下规划和完善策略。这类似于棋手通过在脑海中模拟走棋来思考。
- **DreamerV3**是一个通用的基于模型的强化学习智能体。它联合学习三个组件:
- **表示模型**:将观测编码为紧凑的潜在状态。
- **转移模型**(世界模型):根据当前状态和动作预测下一潜在状态。
- **奖励模型**:从潜在状态预测奖励。
- 然后智能体通过在潜在空间中展开转移模型多步来进行"做梦",在这些想象的轨迹上训练策略,并将策略转移到真实环境。关键创新在于所有想象都在潜在空间(紧凑的学习表示)中进行,而不是在像素空间中,使其计算可行。
$$\\hat{s}_{t+1} = f_\\theta(s_t, a_t), \\quad \\hat{r}_t = g_\\theta(s_t)$$
- 转移模型$f_\\theta$和奖励模型$g_\\theta$在真实经验上训练,策略在想象的展开上训练。这将数据收集与策略优化解耦。
- 对于机器人操作,世界模型实现了**心理排练**。在尝试抓取之前,机器人可以在其学习模型上模拟多种方法,并选择最可能成功的一种。这对于接触密集型任务尤其有价值,因为在这些任务中现实世界的试错既慢又危险。
- 世界模型也自然地与**仿真到现实迁移**相关联:在真实数据上训练的世界模型实际上是一个自动捕获真实世界物理的学习型仿真器,完全绕过了仿真到现实差距。对于理解良好的场景,它可能不如手工构建的仿真器精确,但它捕获了手工仿真器常常出错的效果(摩擦、形变、接触动力学)。
- **JEPA**(联合嵌入预测架构,在第10章中介绍)提供了像素级预测的替代方案。JEPA不在像素空间预测精确的未来观测,而是在嵌入空间中预测:"下一状态的潜在表示将接近该向量。"这避免了预测像素级完美未来的困难(既无必要又计算浪费),并专注于预测对决策重要的未来方面。
- 世界模型的局限性在于**复合预测误差**。转移模型中的微小不准确性在长程展开中积累,导致想象的轨迹偏离现实。缓解措施包括:短想象时域、集成模型(使用不确定性检测预测何时变得不可靠)、以及定期用新的真实世界数据校准模型。
## 操作
- **操作**是使用机器人末端执行器与物体交互的艺术:抓取、放置、推、插入、组装。
- **抓取**是基础的操作技能。目标是找到一个稳定的抓取姿态:夹爪的位置和方向,能够牢固地抓住物体。
- **解析抓取规划**使用物理学。如果接触力能够抵抗外部扳手(力和力矩),则抓取是稳定的。对于平行夹爪,最简单的标准是**力闭合**条件:接触法线必须跨越所有力的方向,使抓取能够抵抗任何扰动。这涉及检查抓取扳手矩阵的秩,是第2章秩概念的直接应用。
- **数据驱动的抓取**学习从感官输入预测抓取成功。给定桌子上物体的深度图像,网络预测每个候选夹爪姿态的抓取质量分数。**GraspNet**和类似架构使用点云编码器(PointNet风格,第8章)来预测带有置信度分数的6自由度抓取姿态(位置+方向)。
- **灵巧操作**超越了简单的抓取和放置。多指手具有20+自由度,可以执行手中旋转(在手指间旋转笔)、工具使用和精细组装等任务。状态空间巨大且接触复杂,使其成为机器人学中最困难的问题之一。
- 学习灵巧操作通常使用带有大量域随机化的仿真中的强化学习(第6章)。OpenAI用Shadow手解决魔方的工作就是在仿真中使用随机化物理训练PPO策略,最终实现了向真实机器人手的迁移。
- **接触密集型任务**如销钉入孔或擦拭表面,要求机器人与环境保持受控接触。这些任务需要力传感和柔顺控制(阻抗控制),并且难以准确仿真,因为接触物理众所周知地难以建模。
## 移动
- 移动是让机器人的身体在世界中移动:行走、奔跑、攀爬、游泳。与操作的关键区别在于机器人必须在移动时保持平衡,并且与地面的接触点随时间变化。
- **腿式移动**具有挑战性,因为它本质上是不稳定的。单步站立的双足机器人(类人机器人)就像一个倒立摆。质心必须保持在支撑多边形(与地面接触的脚的凸包)上方,否则机器人会摔倒。
- **零力矩点(ZMP)**是地面上重力和惯性力产生的净力矩为零的点。如果ZMP保持在支撑多边形内,机器人就不会翻倒。传统的人形机器人控制器(如本田ASIMO)规划使ZMP保持在边界内的轨迹。
- **中央模式发生器(CPG)**是受生物学启发的基于振荡器的控制器。动物使用脊髓中的神经回路产生有节奏的移动模式(行走、小跑、奔跑),无需大脑持续参与。CPG模型使用耦合微分方程:
$$\\dot{\\phi}_i = \\omega_i + \\sum_j w_{ij} \\sin(\\phi_j - \\phi_i - \\psi_{ij})$$
- 其中$\\phi_i$是振荡器$i$的相位,$\\omega_i$是自然频率,$w_{ij}$是耦合强度,$\\psi_{ij}$是期望的相位偏移。不同的相位关系产生不同的步态:所有腿同步(跳跃)、交替配对(小跑)、顺序(行走)。正弦耦合自然地同步振荡器,类似于傅里叶级数(第3章)如何将运动分解为频率分量。
- **用于移动的强化学习**已成为敏捷四足和类人机器人的主要方法。机器人在仿真中通过试错学习策略$\\pi(\\mathbf{a} \\mid \\mathbf{o})$(第6章),奖励包括前进速度、稳定性和能效,惩罚包括摔倒、关节限位违规和抖动运动。
- 近期工作(如Agility Robotics、Boston Dynamics和学术实验室)的关键洞见是,RL训练的移动策略远优于手工设计的控制器。它们自然学会从推动中恢复、适应地形变化,并处理没有工程师能预料到的情况。训练通常使用PPO(第6章)结合域随机化。
- **四足机器人**(如Boston Dynamics Spot或Unitree Go2)已成为腿式机器人的主力。四条腿提供固有稳定性(三条腿的三角支撑总能在一条腿移动时支撑身体)。四足机器人的RL策略实现了令人印象深刻的结果:以3+米/秒奔跑、爬楼梯、在岩石地形上导航以及从踢击中恢复。
- **类人机器人移动**更难,因为双足机器人有更小的支撑多边形和更高的质心。最近的进展(Tesla Optimus、Figure、Unitree H1)使用在仿真中训练的RL,配以精心的奖励塑造。类人机器人必须学会的不仅仅是行走,还要协调手臂摆动以保持平衡、在不平坦表面上导航以及从扰动中恢复。
## 机器人学习中的安全性
- 一个为了学习而随机探索的机器人(如在RL中)可能会损坏自身、环境或附近的人类。**安全的机器人学习**约束探索以避免灾难性后果。
- **约束RL**向MDP(第6章)添加安全约束。目标变为:在满足$J_c(\\pi) \\leq d$的条件下最大化奖励,其中$J_c$是期望的累积代价(如碰撞事件),$d$是最大允许代价。像约束策略优化(CPO)这样的算法扩展了PPO以处理这些约束。
- **安全包络**定义了机器人绝不能越过的硬边界,无论学习策略如何输出。一个安全控制器监控机器人状态,并在即将违反约束时覆盖学习策略(例如,接近关节限位、在人类附近移动过快、或超过力阈值)。这是一种分层架构:学习算法处理性能,安全层处理约束。
- **风险感知规划**显式地建模环境和机器人自身状态估计中的不确定性。它不是为最可能的结果进行规划,而是在置信区间内为最坏情况进行规划。这与条件数概念(第2章)相关:良态系统对扰动具有鲁棒性,风险感知规划寻求在扰动下仍保持安全的控制策略。
## 编程任务(使用CoLab或notebook
1. 实现一个简单2连杆平面机器人手臂的正向运动学。计算并可视化不同关节角度下的末端执行器位置。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
def forward_kinematics(q1, q2, l1=1.0, l2=0.8):
"""计算2连杆手臂的关节和末端执行器位置。"""
x1 = l1 * jnp.cos(q1)
y1 = l1 * jnp.sin(q1)
x2 = x1 + l2 * jnp.cos(q1 + q2)
y2 = y1 + l2 * jnp.sin(q1 + q2)
return jnp.array([0, x1, x2]), jnp.array([0, y1, y2])
fig, ax = plt.subplots(figsize=(6, 6))
configs = [(0.5, 0.3), (1.0, -0.5), (1.5, 1.0), (2.0, -1.5)]
colors = ["#e74c3c", "#3498db", "#27ae60", "#9b59b6"]
for (q1, q2), c in zip(configs, colors):
xs, ys = forward_kinematics(q1, q2)
ax.plot(xs, ys, "o-", color=c, linewidth=2, markersize=6,
label=f"q=({q1:.1f}, {q2:.1f})")
ax.set_xlim(-2, 2); ax.set_ylim(-2, 2)
ax.set_aspect("equal"); ax.grid(True); ax.legend()
ax.set_title("2连杆机器人手臂:正向运动学")
plt.show()
```
2. 使用雅可比伪逆实现逆向运动学。从随机构型开始,迭代地将末端执行器移动到目标。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
l1, l2 = 1.0, 0.8
def end_effector(q):
x = l1 * jnp.cos(q[0]) + l2 * jnp.cos(q[0] + q[1])
y = l1 * jnp.sin(q[0]) + l2 * jnp.sin(q[0] + q[1])
return jnp.array([x, y])
jacobian_fn = jax.jacobian(end_effector)
target = jnp.array([0.5, 1.2])
q = jnp.array([0.1, 0.1])
trajectory = [end_effector(q)]
for _ in range(50):
pos = end_effector(q)
error = target - pos
if jnp.linalg.norm(error) < 1e-4:
break
J = jacobian_fn(q)
# 阻尼伪逆处理接近奇异点的情况
dq = J.T @ jnp.linalg.solve(J @ J.T + 0.01 * jnp.eye(2), error)
q = q + dq
trajectory.append(end_effector(q))
traj = jnp.stack(trajectory)
plt.plot(traj[:, 0], traj[:, 1], "b.-", label="末端执行器路径")
plt.plot(*target, "r*", markersize=15, label="目标点")
plt.gca().set_aspect("equal"); plt.grid(True); plt.legend()
plt.title(f"IK在{len(trajectory)-1}步内收敛")
plt.show()
```
3. 模拟一个简单的PID控制器跟踪期望的关节轨迹。观察调参对增益的影响。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 期望轨迹:平滑正弦运动
dt = 0.01
t = jnp.arange(0, 5, dt)
q_desired = jnp.sin(2 * t)
# 模拟二阶动力学:m * q_ddot + b * q_dot = tau
m, b_damp = 1.0, 0.5
for Kp, Kd, Ki, label in [(10, 5, 0, "仅PD"), (10, 5, 2, "PID"), (50, 10, 2, "激进PID")]:
q, q_dot, integral = 0.0, 0.0, 0.0
qs = []
for i in range(len(t)):
error = q_desired[i] - q
integral += error * dt
d_error = -q_dot # 误差导数(此处简化,已知期望速度)
tau = Kp * error + Kd * d_error + Ki * integral
q_ddot = (tau - b_damp * q_dot) / m
q_dot += q_ddot * dt
q += q_dot * dt
qs.append(float(q))
plt.plot(t, qs, label=label)
plt.plot(t, q_desired, "k--", label="期望值", linewidth=2)
plt.xlabel("时间 (秒)"); plt.ylabel("关节角度")
plt.legend(); plt.title("PID控制器跟踪")
plt.show()
```
@@ -0,0 +1,217 @@
# 视觉-语言-动作模型
*视觉-语言-动作模型(VLA)将视觉理解、语言理解和行动控制统一到单个神经网络中。本章涵盖VLA架构、动作标记化、RT-2、Octo、OpenVLA、预训练策略、泛化能力、与具体形态无关的模型以及基准测试。*
- 在前面的文件中,我们涵盖了感知(感知世界)和机器人学习(控制身体)。传统上,这些是独立的流程:感知模块检测物体,语言模块解释指令,控制模块生成动作。每个模块独立设计、训练和调试。
- **视觉-语言-动作模型(VLA)**将这一流程压缩为单个神经网络。模型接收图像(视觉)和自然语言指令(语言),并输出电机命令(动作)。一个模型,端到端。
- 这沿袭了我们在第10章看到的统一趋势:正如多模态模型将视觉和语言理解合并到一个架构中一样,VLA将这一趋势扩展到物理行动。关键洞见在于,语言为指定任务提供了自然、灵活的接口("拿起红色杯子放到架子上"),而大规模预训练的视觉-语言模型已经理解图像和指令。
## 从视觉-语言到行动
- 回顾第10章,**视觉-语言模型(VLM**如LLaVA和Flamingo接收图像和文本作为输入,并生成文本作为输出。它们理解场景、回答问题、遵循指令——全部通过语言完成。
- VLA提出的问题是:如果输出不是文本而是**机器人动作**呢?模型不再生成"红色杯子在桌子的左侧",而是生成一系列电机命令,驱动手臂去抓取那个杯子。
- 关键的架构洞见是,动作可以像单词一样表示为标记。如果VLM使用下一个标记预测逐个生成语言标记,那么VLA以同样的方式生成动作标记。Transformer从根本上并不关心输出标记表示"杯子"还是"将夹爪向前移动2厘米"。
- 这重新定义了机器人控制为序列建模问题,这正是transformer擅长的(第7章)。模型学习映射:(图像观测,语言指令)$\\to$(动作标记序列)。
## VLA架构
![VLA架构:相机图像和语言指令被编码为标记,由LLM主干网络处理,并解码为机器人动作](../images/vla_architecture.svg)
- 典型的VLA有三个组件:
- **视觉编码器**:将相机图像处理为视觉标记。通常是预训练的ViT(第8章)或SigLIP编码器(第10章)。图像被分割成块,每个块嵌入为一个标记,与标准视觉transformer完全一样。
- **语言模型主干网络**:一个预训练的LLM(例如LLaMA、PaLM),处理交错的视觉标记和语言标记序列。这就是推理发生的地方:模型通过同时关注指令和视觉特征来理解"拿起**红色**杯子"。
- **动作头**:将LLM的输出映射到机器人动作。可以是一个简单的MLP,将最后的隐藏状态映射到连续动作值,或者是一种将动作转换为离散标记的方案,由LLM的现有词汇表来预测。
- 架构看起来像:
$$\\text{图像} \\xrightarrow{\\text{ViT}} \\text{视觉标记} \\quad + \\quad \\text{指令} \\xrightarrow{\\text{分词器}} \\text{语言标记} \\quad \\xrightarrow{\\text{LLM}} \\quad \\text{动作标记}$$
- 视觉标记和语言标记被拼接(或交错)并输入到transformer主干网络,后者自回归地生成动作标记。这与VLM(第10章)的架构相同,但输出模态是动作而非文本。
## 动作标记化
- 机器人动作是连续的:关节速度、末端执行器位置、夹爪宽度。这些必须转换为离散标记才能让LLM生成。
![动作标记化:连续动作值被分箱为离散索引,LLM将其作为标记生成](../images/action_tokenisation.svg)
- 最简单的方法是**均匀离散化**。每个动作维度被划分为$N$个箱,覆盖有效值范围。例如,如果x方向速度范围从-0.1到0.1米/秒,使用256个箱,每个箱代表$\\frac{0.2}{256} \\approx 0.8$毫米/秒。动作值被映射到最近的箱索引,该索引成为一个标记。
- 对于7个动作维度(6自由度+夹爪)和每个维度256个箱,动作词汇表有$7 \\times 256 = 1792$个标记。这些被添加到LLM现有的文本词汇表中。模型每个维度生成一个动作标记,自回归地,就像生成单词一样。
- **动作分块**一次预测多个未来时间步,而不是单个动作。如果块大小为$H$,模型输出$H \\times d$个标记(其中$d$是动作维度)。这对于平滑、时间连贯的运动至关重要。一次预测一步会产生抖动行为,因为每次预测都是独立的。分块迫使模型规划一个短轨迹,捕获时间结构。
- 更复杂的方法使用**学习型标记化**,通过VQ-VAE(第10章)。VQ-VAE编码器将连续动作序列映射到离散码本索引序列,解码器从这些索引重建连续动作。LLM然后生成码本索引,而不是均匀分箱的值。这类似于图像分词器(第10章)如何将视觉信息压缩为紧凑的离散编码。
## 关键VLA模型
- **RT-2**(机器人Transformer 2Google DeepMind)是第一个大规模VLA。它使用预训练的VLM(PaLM-E或PaLI-X,参数高达55B)并在机器人示范数据上微调。动作表示为文本字符串:标记序列"1 128 91 241 5 101 127"编码了一个7维动作(每个数字是箱索引)。
- RT-2展示了一个显著特性:来自VLM主干网络的**涌现能力**迁移到了机器人领域。模型可以遵循涉及从未在机器人数据中见过的概念的指令(例如,"将香蕉移动到以A开头的国家"需要视觉物体识别+世界知识+行动)。VLM的语言理解和视觉推理"免费"获得。
- RT-2的局限性在于它是在单个机器人形态(特定的手臂和特定的夹爪)的数据上训练的。它不能泛化到不同的机器人。
- **Octo**(加州大学伯克利分校)是一个开源的、**与具体形态无关**的VLA,设计用于跨不同机器人平台工作。关键创新包括:
- **扩散动作头**,而不是自回归标记预测。动作头获取transformer的输出,并通过去噪扩散过程(第8章)生成动作。这自然地处理了多模态动作分布(见下图),即存在多个有效的任务完成方式。
![多模态动作分布:回归将两条有效路径平均为一条穿过障碍物的无效路径](../images/multimodal_action_distribution.svg)
- **灵活的观测和动作空间**:Octo为不同的机器人配置使用特定于任务的标记化器。它在Open X-Embodiment数据集上预训练,该数据集包含来自22种不同机器人形态的示范。
- **高效微调**:Octo只需100个示范就可以微调到新机器人,使其适用于数据有限的实验室。
- **OpenVLA**(斯坦福大学、加州大学伯克利分校)采用微调现有开源VLM(基于Llama)用于机器人技术的方法。它使用7B参数主干网络、均匀动作标记化(每个维度256个箱),并在Open X-Embodiment数据上训练。其优势在于简单性:架构是标准的VLM,动作标记被附加到词汇表中,使其易于使用现有的LLM基础设施进行训练和部署。
- **$\\pi_0$**Physical Intelligence)代表了当前最高水平。它使用预训练的VLM主干网络和**流匹配**动作头(第8章)。流匹配通过学习一个速度场将噪声传输到动作分布来生成动作,产生平滑、时间连贯的动作轨迹。$\\pi_0$展示了卓越的通用性,在多种机器人形态(包括双臂操作和灵巧手控制)上执行任务。
## 预训练配方
- VLA极大地受益于预训练的VLM主干网络,这些网络已经理解视觉场景和语言。训练流程通常分为几个阶段:
1. **VLM预训练**:在数十亿来自互联网的图像-文本对(CLIP、SigLIP、LLaVA风格的训练,如第10章所述)上训练(或使用现成的)视觉-语言模型。
2. **机器人数据协同训练**:在互联网数据和机器人示范数据的混合上微调VLM。互联网数据防止视觉和语言理解的灾难性遗忘,而机器人数据教授动作生成。混合比例很重要:机器人数据过多会降低语言理解,过少则无法学习动作。
3. **特定任务微调**:可选地在特定任务或机器人的示范上进行微调,通常使用LoRA(第10章)保持可训练参数数量较少。
- 机器人数据的数量比互联网数据少数个数量级。VLM可能在上数十亿张图像上预训练,但最大的机器人数据集(Open X-Embodiment)在所有形态上只有数百万帧。这种数据稀缺性正是从预训练VLM开始至关重要的原因:视觉和语言表示可以迁移,只有动作映射需要从有限的机器人数据中学习。
## 泛化能力
- VLA的承诺是**泛化**:执行训练中未见的任务,使用未见过的物体,在未见过的环境中,遵循未见过的指令。
- VLA沿多个轴进行泛化:
- **新颖物体**:VLM主干网络从互联网预训练中识别物体。如果模型从网络图像中知道"螺丝刀"长什么样,即使没有机器人示范涉及螺丝刀,它也能操作螺丝刀。
- **新颖指令**:组合语言理解使模型能够遵循已知概念的新组合。"将蓝色方块堆叠在绿色方块上"即使训练只展示了堆叠红色方块也能工作,因为模型从语言预训练中理解了颜色形容词。
- **新颖环境**:在一定程度上,VLA跨视觉域(不同的桌子、光照、背景)迁移,因为视觉编码器在多样化的网络图像上预训练。但这有局限性:在实验室训练的机器人可能在杂乱厨房中遇到困难。
- **新颖形态**:这是最难的轴。不同机器人有不同的动作空间(关节角度 vs. 末端执行器速度)、不同的传感器(腕部相机 vs. 头顶相机)和不同的物理能力。与形态无关的模型如Octo和$\\pi_0$通过灵活的标记化器和跨多种机器人类型的预训练来解决这一问题。
- 泛化能力通过**保留任务**进行评估:机器人被要求执行从未训练过的任务。在新颖任务上50-80%的成功率被认为是强劲的结果,而在分布内任务上成功率通常>90%。随着模型规模扩大和机器人数据集增长,这一差距正在缩小。
## 与形态无关的模型
- 该领域正朝着"一个模型,多种机器人"的方向发展。不再为每个机器人训练单独的策略,而是单个VLA处理多种形态。
- 这需要解决**动作空间不匹配**问题。一个7自由度手臂带平行夹爪有7个动作维度。双臂设置是14个。四足机器人有12个。类人机器人有30个以上。动作标记化必须足够灵活以处理所有这些。
- 解决方案包括:
- **填充动作向量**:使用最大的动作空间,较小的用零填充。
- **每种形态的动作头**:共享的transformer主干网络,每种机器人类型有单独的小型MLP。
- **归一化动作表示**:在共同框架中表示所有动作(如世界坐标系中的末端执行器速度),使产生类似末端执行器运动的不同机器人共享相同的动作标记。
- 共享主干网络学习通用的视觉和语言理解,加上通用的操作策略(从上方接近、对齐物体、闭合夹爪)。特定于形态的组件只需要将这些高层策略转化为具体的电机命令。
## 基准测试与评估
- 评估VLA具有独特的挑战性,因为它需要物理机器人实验(或高保真仿真)。
- **SIMPLER**(机器人学习模拟操作策略评估)提供了标准化的仿真环境,无需物理硬件即可比较VLA性能。它与现实世界的成功率相关性良好,并实现了可复现的基准测试。
- **现实世界评估**仍然是金标准。典型协议:
1. 定义一组具有明确成功标准的任务(物体到达目标位置、选择正确物体、在时限内完成任务)。
2. 每次任务运行$N$次试验(通常10-50次)。
3. 报告成功率及置信区间。
4. 包括保留任务(从未训练过的)以衡量泛化能力。
- **Open X-Embodiment**数据集和基准测试汇总了来自22个机构、跨越多个机器人平台的机器人数据。它提供了共享示范的标准格式和用于跨形态迁移的通用评估套件。
## 编程任务(使用CoLab或notebook
1. 实现动作标记化:将连续动作离散化为箱并重建。观察量化误差随箱数量的变化。
```python
import jax.numpy as jnp
# 连续动作:7个维度(6自由度+夹爪)
action_true = jnp.array([0.023, -0.051, 0.012, 0.1, -0.03, 0.005, 0.8])
action_min = jnp.array([-0.1, -0.1, -0.1, -0.5, -0.5, -0.5, 0.0])
action_max = jnp.array([ 0.1, 0.1, 0.1, 0.5, 0.5, 0.5, 1.0])
for n_bins in [16, 64, 256, 1024]:
# 标记化:将连续值映射为箱索引
normalised = (action_true - action_min) / (action_max - action_min)
tokens = jnp.clip((normalised * n_bins).astype(int), 0, n_bins - 1)
# 去标记化:将箱索引映射回连续值
reconstructed = (tokens + 0.5) / n_bins * (action_max - action_min) + action_min
error = jnp.linalg.norm(action_true - reconstructed)
print(f"箱数={n_bins:4d} 标记={tokens} 误差={error:.6f}")
```
2. 模拟动作分块与单步预测的比较。生成平滑轨迹,向单步预测添加噪声,并与分块预测比较。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 真实平滑轨迹(例如,伸手动作)
t = jnp.linspace(0, 2 * jnp.pi, 100)
gt_x = jnp.sin(t)
gt_y = 1 - jnp.cos(t)
# 单步:每次预测有独立噪声
rng = jax.random.PRNGKey(42)
noise_ss = jax.random.normal(rng, (100, 2)) * 0.05
single_step = jnp.stack([gt_x, gt_y], axis=1) + noise_ss
# 单步误差累积漂移
single_step_cumulative = jnp.cumsum(noise_ss, axis=0) * 0.3 + jnp.stack([gt_x, gt_y], axis=1)
# 分块(块大小=10):块内噪声关联,更平滑
chunk_size = 10
rng2 = jax.random.PRNGKey(7)
chunks = []
for i in range(0, 100, chunk_size):
chunk_noise = jax.random.normal(jax.random.fold_in(rng2, i), (2,)) * 0.05
chunk = jnp.stack([gt_x[i:i+chunk_size], gt_y[i:i+chunk_size]], axis=1)
chunks.append(chunk + chunk_noise)
chunked = jnp.concatenate(chunks, axis=0)
plt.figure(figsize=(8, 4))
plt.plot(gt_x, gt_y, "k-", linewidth=2, label="真实轨迹")
plt.plot(single_step_cumulative[:, 0], single_step_cumulative[:, 1],
"r-", alpha=0.7, label="单步(漂移)")
plt.plot(chunked[:, 0], chunked[:, 1], "b-", alpha=0.7, label="分块(稳定)")
plt.legend(); plt.axis("equal"); plt.grid(True)
plt.title("动作分块 vs 单步预测")
plt.show()
```
3. 可视化VLA动作分布如何是多模态的。使用简单的2D高斯混合来展示为什么扩散/流匹配动作头优于回归。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 绕过障碍物的两种有效方式:左边或右边
rng = jax.random.PRNGKey(0)
k1, k2 = jax.random.split(rng)
mode1 = jax.random.normal(k1, (200, 2)) * 0.15 + jnp.array([-1.0, 0.5])
mode2 = jax.random.normal(k2, (200, 2)) * 0.15 + jnp.array([ 1.0, 0.5])
samples = jnp.concatenate([mode1, mode2])
# 回归预测均值 = 模态的均值(无效!)
mean_pred = samples.mean(axis=0)
plt.figure(figsize=(6, 5))
plt.scatter(samples[:, 0], samples[:, 1], s=5, alpha=0.5, label="真实动作分布")
plt.plot(*mean_pred, "rx", markersize=15, markeredgewidth=3, label="回归均值(无效!)")
plt.plot(-1, 0.5, "g^", markersize=12, label="模态1(向左)")
plt.plot(1, 0.5, "b^", markersize=12, label="模态2(向右)")
plt.legend(); plt.grid(True)
plt.title("多模态动作:为什么回归失败")
plt.xlabel("动作维度1"); plt.ylabel("动作维度2")
plt.show()
```
@@ -0,0 +1,312 @@
# 自动驾驶汽车
*自动驾驶汽车是商业上最先进的自主系统,将感知、预测、规划和控制集成到单个车辆中。本章涵盖自动驾驶堆栈、高精地图、运动预测、规划、端到端驾驶、仿真、安全标准和自主等级。*
- 自动驾驶汽车可以说是正在大规模尝试的最困难的机器人问题。与在受控环境中运行的工厂机器人不同,自动驾驶汽车必须处理一个开放世界:不可预测的人类驾驶员、乱穿马路的行人、一夜之间出现的施工区域以及每分钟都在变化的天气。
- 其风险也异常之高。自动驾驶汽车在高速公路上行驶,周围是脆弱的道路使用者。对于安全关键的故障,误差容限几乎为零。
## 自动驾驶堆栈
- 经典的自动驾驶架构是一个**模块化流水线**,包含四个阶段,每个阶段作为下一个阶段的输入:
$$\\text{感知} \\to \\text{预测} \\to \\text{规划} \\to \\text{控制}$$
![自动驾驶堆栈:传感器流入感知,感知流入预测、规划,最后流入控制](../images/autonomous_driving_stack.svg)
- **感知**(本章文件1中已介绍)将原始传感器数据处理为结构化的场景表示:带有3D位置、速度和类别标签的检测物体;车道标线;交通信号灯;可行驶表面边界。
- **预测**预测其他交通参与者(车辆、行人、骑行者)未来将如何移动。给定场景的当前状态,预测模块为每个交通参与者输出未来一段时间(通常3-8秒)的轨迹。
- **规划**决定主车应该做什么:走哪条路径、何时变道、何时让行、何时加速或刹车。它接收预测的场景,为主车生成一条安全、舒适且向目的地前进的轨迹。
- **控制**将规划的轨迹转化为执行器命令:转向角、油门和刹车。这是最底层,将抽象轨迹转化为物理运动。
- 模块化设计有明确的工程优势:每个模块可以独立开发、测试和改进。但它也有弱点:误差向下游传播(漏检对规划器是不可见的),并且信息在每个接口处丢失(规划器看到的是边界框,而不是产生它们的丰富传感器数据)。
## 高精地图
- **高精(HD)地图**是详细、厘米级精度的数字地图,编码道路结构:车道边界、车道连通性(哪个车道在交叉口连接到哪个)、交通标志位置、限速、人行横道位置和路面高程。
- 高精地图为驾驶任务提供了强有力的先验。感知模块不需要每帧从头发现车道边界;它只需要将车辆在地图中进行定位,并验证现实是否与存储的结构匹配。这极大地简化了规划。
- 构建高精地图需要配备高端LiDAR、相机和RTK-GPS的专业测量车辆。地图必须随着道路变化而维护和更新。这很昂贵,且不容易扩展到地球上的每条道路。
- **无图驾驶**(也称为"在线地图构建")旨在消除对预建高精地图的依赖。车辆从传感器实时构建局部地图。像**MapTR**和**MapTRv2**这样的模型使用transformer架构直接从相机图像预测矢量化地图元素(车道中心线、道路边界、人行横道),将多段线输出为有序点序列。
- 无图方法用地图精度换取可扩展性:任何汽车能行驶的道路,它都能建图。但它要求感知系统足够鲁棒,能够实时检测所有相关的道路结构,包括在复杂交叉口、高速公路匝道和施工区域中。
- 在实践中,许多系统采用混合方法:轻量级地图包含粗略的道路拓扑(来自现有地图提供商),并通过车辆的传感器实时丰富。
## 运动预测
- 预测其他道路使用者将去哪里是自动驾驶中最困难的子问题之一。人类不可预测,意图是隐藏的,未来可能性的空间迅速分叉。
- 预测模型的输入是**场景上下文**:所有检测到的参与者在近期过去(通常1-2秒的历史)的位置和速度,加上静态上下文(车道几何、交通信号、道路边界)。
- 输出是每个参与者的一组**预测轨迹**,通常覆盖未来3-8秒。由于未来是不确定的,好的预测模型输出多条可能的轨迹及其相关概率,而不是单一的点估计。
- **轨迹预测**作为一个回归问题:预测每个参与者在离散未来时间步的$(x, y)$坐标。损失通常是$K$条预测轨迹上的最小平均位移误差(minADE):
$$\\text{minADE}_K = \\min_{k \\in \\{1, \\ldots, K\\}} \\frac{1}{T} \\sum_{t=1}^{T} \\| \\hat{\\mathbf{p}}_t^{(k)} - \\mathbf{p}_t \\|_2$$
- 这是一个"最佳$K$个"指标:如果模型的$K$个预测中有一个接近真实值,模型就得分。这鼓励多样化的多模态预测。
- **社会力模型**将行人行为建模为动力系统,其中每个人受到吸引力(朝向目标)和排斥力(远离其他行人和障碍物)。行人$i$的加速度为:
$$\\mathbf{a}_i = \\frac{\\mathbf{v}_i^{\\text{期望}} - \\mathbf{v}_i}{\\tau} + \\sum_{j \\neq i} \\mathbf{f}_{ij}^{\\text{排斥}} + \\sum_{\\text{墙壁}} \\mathbf{f}_{\\text{墙壁}}$$
- 这是一个与本章文件2中的机器人动力学方程类似的微分方程组。该模型优雅但依赖于手工调谐的力参数,并且在复杂多智能体交互中表现不佳。
- **图神经网络(GNN)**用于预测时将场景建模为图:每个参与者是一个节点,边表示空间关系(邻近度、共享车道)。节点之间的消息传递捕获交互:"这辆车正在给那个行人让行"或"这两辆车正在汇入同一条车道。"
- 现代预测架构(例如**MTR**、**QCNet**)使用基于transformer的模型,联合推理参与者历史、地图上下文和参与者之间的交互。参与者通过交叉注意力关注相关的地图特征(当前车道、即将到来的交叉口)和其他参与者(前车、人行横道上的行人)。输出是一组通过自回归生成或混合模型产生的轨迹假设。
- **目标条件预测**首先预测参与者可能去哪里(一组候选目标点,如车道端点或交叉口出口),然后预测到达每个目标的轨迹。这将问题分解为"去哪里"(离散的、可管理的)和"怎么去"(给定目标的连续路径),使多模态预测问题更加可解。
## 规划
- 给定预测的场景,规划器必须为主车生成一条轨迹。这是一个约束优化问题:找到一条安全、舒适、高效且合法的轨迹。
- **基于规则的规划器**将驾驶行为编码为一组if-then规则:"如果行人在人行横道上,让行"、"如果与前车距离小于2秒,不变道"、"如果接近红灯,减速停在停止线处。"这些规则是可解释和可审计的,但对于复杂场景(数千条规则、许多边缘情况、规则间的交互),它们变得难以管理。
- **基于优化的规划器**将驾驶形式化为轨迹优化。主车轨迹被参数化(例如,作为未来时间步的$(x, y, \\theta, v)$状态序列),并最小化一个目标函数:
$$\\min_{\\boldsymbol{\\xi}} \\underbrace{w_1 \\cdot J_{\\text{进度}}(\\boldsymbol{\\xi})}_{\\text{到达目的地}} + \\underbrace{w_2 \\cdot J_{\\text{舒适}}(\\boldsymbol{\\xi})}_{\\text{平稳行驶}} + \\underbrace{w_3 \\cdot J_{\\text{安全}}(\\boldsymbol{\\xi})}_{\\text{避免碰撞}}$$
$$\\text{约束条件:运动学约束、限速、车道边界}$$
- 进度项惩罚偏离期望路线。舒适项惩罚高横向加速度、加加速度(加速度的导数)和突然转向,因为乘客能感受到这些。安全项惩罚与其他交通参与者的接近程度,使用预测轨迹评估碰撞风险。
- 这是约束优化(第3章):在不等式约束下最小化代价函数。权重$w_1, w_2, w_3$权衡竞争目标(激进驾驶更快但更不舒适且更不安全)。
- **基于学习的规划器**使用在人类驾驶数据上训练的神经网络生成轨迹。模型观察场景并直接输出规划的轨迹,从专家人类驾驶示例中隐式学习复杂的权衡。
- 优势在于人类驾驶行为被整体捕获,包括那些微妙且难以形式化的方面:何时激进地合流、何时在交叉口前微微前移、给骑行者留出多少空间。缺点是来自模仿学习(文件2)的相同分布偏移问题:模型在训练数据中未充分代表的情况下可能表现不可预测。
## 端到端驾驶
- **端到端驾驶**完全消除了模块边界。单个神经网络接收原始传感器输入(相机图像、LiDAR点云)并直接输出驾驶命令(转向、油门、刹车)或规划轨迹。没有独立的感知、预测或规划模块。
- 其吸引力在于整个系统针对最终任务(安全驾驶)进行联合优化,因此没有信息在模块边界丢失。感知模块学习精确提取规划器所需的特征,而不是通用的目标检测结果,后者可能不捕获任务相关的细节。
- **UniAD**(统一自动驾驶)是一个里程碑式的端到端架构。它通过BEV编码器处理多相机图像,然后应用一系列基于transformer的模块:跟踪、在线建图、运动预测、占据预测和规划。虽然它有内部模块,但它们都是可微的,并端到端联合训练,规划损失通过整个网络反向传播。
- UniAD中的规划模块通过关注预测的BEV特征、预测的参与者轨迹和预测的占据来生成未来主车路径点。这就是多元链式法则(第3章)的实际应用:梯度从规划损失一直流回图像编码器,告诉感知特征如何对规划更有用。
- 更近期端到端方法使用VLA风格的架构(本章文件3)。像**DriveVLM**这样的模型接收相机图像和导航指令(或路线),并使用VLM主干网络产生驾驶动作。这带来了大规模预训练(视觉理解、推理)的好处,直接融入驾驶堆栈。
- 端到端驾驶中的张力是**可解释性**。模块化系统可以报告"我检测到行人在(x,y)处,预测他们会横穿"——故障模式是可诊断的。端到端系统是一个产生转向角的黑盒。当它失败时,诊断原因很困难,这对安全认证是一个严重问题。
## 驾驶世界模型
- **世界模型**学习在给定当前状态和主车动作的情况下预测驾驶场景的未来状态:$p(s_{t+1} \\mid s_t, a_t)$(如第10章所述)。在驾驶中,这意味着生成逼真的未来帧或BEV布局:"如果我加速并左转,3秒后的场景会是这样。"
- 世界模型为自动驾驶提供了两种强大能力:
- **基于想象的规划**:规划器不是先执行一个动作再看结果,而是可以通过世界模型"想象"多条候选轨迹,评估每条的安全性和舒适性,然后选择最佳的一条。这是基于模型的RL(本章文件2中介绍)应用于驾驶。
- **学习型仿真**:在真实驾驶数据上训练的世界模型实际上是一个数据驱动的仿真器。它生成逼真的场景(包括罕见的边缘情况),无需手工构建仿真器的工作。关键是,它捕获了真实驾驶的统计模式:其他驾驶员实际如何表现、光照如何变化、雨水如何影响可见度。
- **GAIA-1**(Wayve)是一个用于驾驶的生成式世界模型。给定过去相机帧和主车动作的序列,它自回归地生成未来视频帧。它使用以动作为条件的视频扩散架构。模型学习生成合理的未来:遵守交通规则的车辆、在人行道上行走的行人以及正确变化的交通信号灯——都从训练数据中涌现,而非编程规则。
- **DriveDreamer**和**GenAD**采取类似方法,但在BEV空间而非像素空间中操作。预测未来BEV布局比生成完整视频帧更紧凑(类似于机器人学中的DreamerV3在潜在空间而非像素空间中进行预测,如文件2所述)。BEV世界模型预测所有参与者的位置、道路结构的样子以及自由空间的位置,规划器直接使用这些信息。
- **神经闭环仿真**使用世界模型替代手工构建的仿真器进行测试。给定真实驾驶日志作为起点,世界模型生成如果主车采取了不同动作会发生什么。这使得反事实评估成为可能:"如果我刹车晚了0.5秒会怎样?"而无需实际重现场景。
- 与**JEPA**框架(第10章)的联系在这里很自然。驾驶世界模型不需要预测像素级完美的未来(每个像素的精确RGB值)。它们需要预测对规划重要的方面:参与者在哪、移动速度多快、自由空间在哪。嵌入空间预测(JEPA风格)捕获这些语义上有意义的属性,而无需浪费容量在无关的视觉细节上,如确切的云纹理。
- 主要挑战是**长时程保真度**。世界模型随时间累积误差:第2帧的一个小错误会偏移所有后续帧。对于驾驶,3秒的预测时域对战术决策有用(我应该现在合流吗?),但30秒的时域(用于路线规划等战略决策所需)仍然不可靠。当前工作通过重新锚定(定期用真实观测重置模型)和不确定性估计(在预测变得不可靠时标记)来缓解这一问题。
## 仿真
- 通过在真实道路上驾驶来测试自动驾驶汽车是必要的,但还不够。危险场景(近碰撞、边缘情况)很少见,因此通过行驶里程来测试效率低下。一辆车需要行驶数亿英里才能以统计学方式证明安全性,这是不可行的。
- **仿真**提供了无限、可控且安全的测试。在现实世界中罕见的场景(一个孩子跑上马路、轮胎爆胎、突然的障碍物)可以在仿真中测试数百万次。
- **CARLA**是一个基于Unreal Engine构建的开源驾驶仿真器。它提供逼真的城市环境、动态天气、交通参与者以及传感器仿真(相机、LiDAR、雷达)。研究人员使用CARLA训练基于RL的驾驶智能体并评估感知算法。
- **nuPlan**Motional)是一个闭环规划基准测试。与开环评估(重放记录数据,比较规划器的输出与人类驾驶员的实际轨迹)不同,闭环评估允许规划器的决策影响仿真:如果规划器决定变道,仿真会相应地演变。这测试了反应性行为,而不仅仅是轨迹相似性。
![开环重放日志,无交互;闭环让模型的动作改变仿真状态](../images/open_vs_closed_loop.svg)
- **开环**和**闭环**评估之间的区别至关重要:
- 开环:重放记录的场景,计算模型输出与人类驾驶员动作的相似度。这容易设置但具有误导性:一个总是预测"直行"的模型在高速公路上误差可能很低,但在第一个转弯处就会撞车。
- 闭环:模型的动作改变仿真状态,仿真相应地演变。这测试了模型从自身错误中恢复和响应动态情况的能力。它昂贵得多,但更有意义。
- **场景生成**创建对系统进行压力测试的测试用例。对抗性场景(车辆突然刹车、行人隐藏在停放的汽车后面)通过优化使自动驾驶系统表现最差的情况来生成。这与ML中的对抗训练(第6章)有关:寻找最大化损失的输入。
## 安全性
- 自动驾驶中的安全性由工程标准而非仅ML指标来管理。
- **ISO 26262**(功能安全)是安全关键电子系统的汽车标准。它根据潜在危害的严重性、暴露度和可控性定义了**汽车安全完整性等级(ASIL)**,从A(最低)到D(最高)。自动驾驶系统的感知和规划组件通常为ASIL-D,即最高等级,需要广泛的验证、冗余和故障安全设计。
- **SOTIF**(预期功能安全,ISO 21448)处理另一类危害:不是硬件故障(ISO 26262覆盖的),而是系统按设计工作但仍产生不安全结果的情况。一个将白色卡车误分类为天空的感知模型(真实事件)是SOTIF问题:硬件工作正常,但算法的局限性导致了危害。
- **运行设计域(ODD)**定义了自动驾驶系统设计用于运行的条件:特定的地理区域、道路类型(仅高速、城市道路、两者兼有)、天气条件(无大雪)、速度范围和时间段。不允许在ODD之外运行:如果系统不能处理雪,就不能在雪中驾驶。
- **故障安全** vs **故障可操作**设计:
- 故障安全:检测到故障时,系统过渡到安全状态(例如,靠边停车)。这是最低要求。
- 故障可操作:系统在故障情况下仍能安全运行,使用冗余组件。具有冗余转向、制动和计算的自动驾驶汽车可以在单个组件故障后存活并仍然行驶到安全位置。
- **冗余**是基础。关键感知传感器被复制:多个相机覆盖重叠视场、LiDAR和雷达同时提供独立的深度测量、双计算平台运行相同的软件。如果任何单个组件发生故障,其他组件提供足够的信息来安全驾驶。
## 自动驾驶等级
![SAE自动驾驶等级从L0(无自动化)到L5(完全自动化),显示责任从人类转移到系统的过程](../images/sae_autonomy_levels.svg)
- **SAE J3016**标准定义了六个驾驶自动化等级,从0(无自动化)到5(完全自动化):
- **等级0(无自动化)**:人类做所有事情。系统可能提供警告(车道偏离警报)但不控制车辆。
- **等级1(驾驶辅助)**:系统控制转向或速度,但不能同时控制两者。自适应巡航控制(保持速度和跟车距离)或车道保持辅助(使车辆保持在车道中央)属于等级1。
- **等级2(部分自动化)**:系统同时控制转向和速度,但人类必须时刻监控并准备接管。特斯拉Autopilot、GM Super Cruise和大多数当前的"自动驾驶"功能属于等级2。人类仍然是负责的驾驶员。
- **等级3(条件自动化)**:系统驾驶并监控环境,但仅在特定条件下(ODD内)。人类可以脱离关注,但必须准备好在系统请求时接管(有时间缓冲,通常10秒以上)。Mercedes Drive Pilot(特定高速公路上,低于60公里/小时)是第一个经认证的等级3系统。
- **等级4(高度自动化)**:系统在ODD内驾驶并处理所有情况,无需人类干预。如果遇到ODD之外的情况,它可以安全地自己停车。Waymo的机器人出租车服务在特定地理区域内以等级4运行。
- **等级5(完全自动化)**:系统能在人类能去的一切地方、一切条件下驾驶。无需方向盘或踏板。这目前还不存在。
- 关键区别在于**谁对安全负责**。在等级0-2,人类负责。在等级3-5,系统负责(在其ODD内)。这具有深远的法律、保险和伦理影响。
- 当前行业状态是等级2(广泛部署)、等级3(开始部署)和等级4(有限地理部署)的混合。等级5仍然是一个长期研究目标。
## 编程任务(使用CoLab或notebook
1. 实现一个简单的轨迹优化规划器。给定起始位置、目标和障碍物,使用梯度下降找到最平滑的无碰撞路径。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 轨迹:N个路径点,每个(x, y)
N = 20
start = jnp.array([0.0, 0.0])
goal = jnp.array([10.0, 0.0])
obstacle = jnp.array([5.0, 0.0])
obs_radius = 1.5
# 初始化:从起点到终点的直线
waypoints_init = jnp.linspace(start, goal, N)
def cost(waypoints):
wp = jnp.concatenate([start[None], waypoints, goal[None]], axis=0)
# 平滑度:惩罚加速度(二阶差分)
accel = wp[2:] - 2 * wp[1:-1] + wp[:-2]
smooth_cost = jnp.sum(accel ** 2)
# 避障:惩罚接近度
dists = jnp.linalg.norm(wp - obstacle, axis=1)
collision_cost = jnp.sum(jnp.maximum(0, obs_radius + 0.5 - dists) ** 2)
return 10 * smooth_cost + 100 * collision_cost
grad_cost = jax.grad(cost)
# 优化内部路径点
waypoints = waypoints_init[1:-1]
lr = 0.01
for _ in range(500):
g = grad_cost(waypoints)
waypoints = waypoints - lr * g
# 绘图
full_path = jnp.concatenate([start[None], waypoints, goal[None]], axis=0)
theta = jnp.linspace(0, 2 * jnp.pi, 100)
plt.figure(figsize=(10, 4))
plt.plot(full_path[:, 0], full_path[:, 1], "b.-", label="优化后路径")
plt.plot(waypoints_init[:, 0], waypoints_init[:, 1], "r--", alpha=0.5, label="初始(直线)")
plt.fill(obstacle[0] + obs_radius * jnp.cos(theta),
obstacle[1] + obs_radius * jnp.sin(theta), alpha=0.3, color="red", label="障碍物")
plt.plot(*start, "go", markersize=10); plt.plot(*goal, "g*", markersize=15)
plt.legend(); plt.axis("equal"); plt.grid(True)
plt.title("轨迹优化:平滑无碰撞路径")
plt.show()
```
2. 模拟一个匀速运动预测模型,并与转弯车辆的真实值比较。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 真实值:车辆右转
dt = 0.1
T = 40 # 4秒
v = 10.0 # 米/秒
omega = 0.3 # 弧度/秒(转弯速率)
# 真实轨迹(恒定转弯速率)
t = jnp.arange(T) * dt
theta = omega * t
gt_x = (v / omega) * jnp.sin(theta)
gt_y = (v / omega) * (1 - jnp.cos(theta))
# 从t=0开始的匀速预测
# 假设车辆沿当前航向继续直行
obs_steps = 10 # 观察前1秒
vx0 = v * jnp.cos(theta[obs_steps - 1])
vy0 = v * jnp.sin(theta[obs_steps - 1])
pred_t = jnp.arange(T - obs_steps) * dt
pred_x = gt_x[obs_steps - 1] + vx0 * pred_t
pred_y = gt_y[obs_steps - 1] + vy0 * pred_t
plt.figure(figsize=(8, 6))
plt.plot(gt_x[:obs_steps], gt_y[:obs_steps], "ko-", label="已观测")
plt.plot(gt_x[obs_steps:], gt_y[obs_steps:], "g-", linewidth=2, label="真实未来")
plt.plot(pred_x, pred_y, "r--", linewidth=2, label="匀速预测")
plt.legend(); plt.axis("equal"); plt.grid(True)
plt.xlabel("x (米)"); plt.ylabel("y (米)")
plt.title("匀速预测 vs 转弯车辆")
plt.show()
```
3. 实现一个简单的基于规则的规划器,根据检测到的障碍物决定保持车道还是停车。
```python
import jax.numpy as jnp
def rule_based_planner(ego_speed, obstacles, speed_limit=13.9):
"""
简单的基于规则的规划器。
ego_speed: 当前速度(米/秒)
obstacles: 前方车辆的(距离,速度)元组列表
speed_limit: 最高允许速度(米/秒),默认约50公里/小时
返回:(目标速度,动作标签)
"""
min_following_distance = 2.0 * ego_speed # 2秒规则
emergency_distance = 5.0 # 米
if not obstacles:
return speed_limit, "巡航"
# 找到最近的前方障碍物
closest_dist, closest_speed = min(obstacles, key=lambda o: o[0])
if closest_dist < emergency_distance:
return 0.0, "紧急停车"
elif closest_dist < min_following_distance:
# 匹配前车速度
target = min(closest_speed, speed_limit)
return target, "跟随"
else:
return speed_limit, "巡航"
# 测试场景
scenarios = [
(13.9, [], "空旷道路"),
(13.9, [(30.0, 10.0)], "前方有较慢车辆"),
(13.9, [(3.0, 0.0)], "前方有停靠车辆,距离极近"),
(13.9, [(50.0, 13.9)], "前方车辆同速行驶"),
]
for speed, obs, desc in scenarios:
target, action = rule_based_planner(speed, obs)
print(f"{desc:30s}{action:15s} 目标速度={target:.1f} 米/秒 ({target*3.6:.0f} 公里/小时)")
```
@@ -0,0 +1,311 @@
# 太空与极端环境机器人
*太空和极端环境机器人将自主性推向极限——通信延迟、辐射和非结构化地形要求机器人自己思考。本章涵盖行星漫游车、在轨服务、通信受限自主性、抗辐射计算、水下机器人、搜索救援、群体机器人和人机交互。*
- 在本章中,我们研究了在相对温和环境中运行的自主系统:有车道标线的道路、有平坦地板的地板、有已知物体类别的厨房。但机器人技术的一些最具影响力的应用是在人类无法到达的环境,或者人类存在的成本极高的环境:火星表面、深海海底、核灾难现场和燃烧的建筑。
- 这些**极端环境**面临着共同的挑战:通信受限或有延迟、地形非结构化且不可预测、硬件必须在恶劣条件下生存、而且附近没有人能在出现问题时修理。机器人必须真正自主,而不仅仅是"有人在屏幕前监控的自主"。
## 太空机器人
- 太空是终极的极端环境。没有空气,温度在-170°C到+120°C之间摆动,辐射轰击电子设备,而援助在数百万公里之外。太空机器人必须异常可靠、节能且自主。
- **行星漫游车**是在其他世界表面探索的移动机器人。NASA的火星漫游车(勇气号、机遇号、好奇号、毅力号)是最著名的例子。每一代都比上一代更加自主。
![地球-火星通信延迟:单向4-24分钟,往返8-48分钟,使得实时控制不可能](../images/earth_mars_delay.svg)
- 根本限制是**通信延迟**。火星距地球4-24分钟的无线电距离(取决于轨道位置),因此往返通信需要8-48分钟。漫游车不能实时操控。如果遇到岩石,它不能向地球求助并等待回应。它必须自己决定。
- 早期的漫游车(勇气号、机遇号)严重依赖地面参与的规划:人类研究图像、规划路径、上传命令,漫游车执行命令。一个驾驶周期需要一个完整的火星日。漫游车每天大约能行进50-100米。
- 好奇号和毅力号上的**AutoNav**(自主导航)极大地提高了自主性。漫游车使用立体相机构建局部3D地图(回顾第8章的立体深度),评估地形可通过性(坡度、粗糙度、岩石大小),并使用基于网格的规划器和可通过性代价图规划安全路径。漫游车在人类团队睡眠时自主行驶,将每日行进距离提高到100米以上。
- 火星漫游车上的感知流程受到抗辐射处理器的限制,这些处理器比消费级硬件慢几个数量级(下文讨论)。算法必须计算节俭:经典的立体匹配而非深度神经网络,简单的代价图规划器而非学习型策略。
- **在轨服务**涉及在轨道上检查、修理、加油或使卫星脱离轨道的机器人。随着太空变得更加拥挤,这是一个不断增长的领域。**OSAM-1**NASA)和商业企业(Astroscale、Northrop Grumman MEV)等任务使用机械臂和对接机构来服务卫星。
- 挑战在于**近距离操作**:服务航天器必须接近目标卫星(可能正在翻滚、不合作且缺乏对接接口),并在微重力下执行精确操作。基于视觉的位姿估计(从相机图像确定目标的3D位置和方向)至关重要。这使用了第8章的技术:特征检测、PnP(透视n点)求解,以及最近基于深度学习的位姿估计器。
- **卫星检查**使用小型航天器目视检查其他卫星是否有损坏或异常。检查者必须自主绕目标导航、避免碰撞并从最佳视角捕获高分辨率图像。这是一个规划问题:找到覆盖所有检查点且满足燃料约束、光照条件和避碰要求的轨迹。
## 通信约束
- 在太空中,通信受到光速、可用带宽和轨道几何的限制(火星背面的漫游车在没有中继卫星的情况下根本无法与地球通信)。
- 这些限制从根本上改变了自主性架构。在地球上,机器人可以将高清视频流传输到云服务器,在GPU集群上运行推理,并在毫秒内接收指令。在太空中,机器人必须在飞行器上完成所有工作。
- **高延迟**意味着机器人必须在没有实时人类指导的情况下规划和行动。自主软件必须处理常规操作、检测异常并响应危险,而无需等待人类输入。这需要鲁棒的板载状态估计、故障检测和应急规划。
- **有限带宽**意味着机器人无法传输原始传感器数据。一张高分辨率图像可能有几兆字节,但火星到地球的数据速率通过直接对地链路只有每秒几千比特(通过轨道中继更高,但仍然有限)。机器人必须积极压缩数据、优先决定发送哪些数据,并在本地做出大部分决策。
- **通信窗口**是间歇性的。火星漫游车只能在特定轨道几何形状期间与地球通信,通常每个火星日通过中继卫星只有几小时。在这些窗口之外,漫游车完全靠自己。
- 对AI的影响是**板载自主性**必须非常可靠。系统需要检测是否出了问题(轮子卡住了、传感器故障了、前方地形无法通行),决定安全响应,并继续运行直到下一个通信窗口,届时它可以报告并接收更新指令。
## 抗辐射计算
- 太空中充满了电离辐射:宇宙射线、太阳粒子事件以及行星磁场中的捕获辐射。高能粒子可以翻转存储器中的比特(**单粒子翻转,SEU**),永久损坏晶体管(**总电离剂量,TID**),或在电路中引起破坏性闩锁。
- **抗辐射处理器**被设计为承受这种环境。它们使用更大的晶体管几何尺寸、冗余逻辑(三模冗余:每个电路有三个副本对输出进行投票)和专门的制造工艺。代价是性能:最先进的抗辐射处理器可能提供200 MIPS,而消费级GPU每秒可执行数十亿次操作。
- **RAD750**BAE Systems)为好奇号和许多其他航天器提供动力。它以200 MHz运行,约400 MIPS的处理能力,相当于1990年代中期的台式电脑。毅力号使用类似等级的处理器。在现代神经网络上运行(数百万参数、数十亿次乘加运算)在这样的硬件上是不可行的。
- **模型压缩**变得至关重要。第6章的技术(量化、剪枝、知识蒸馏)用于缩小神经网络以适应极端的计算预算。在笔记本电脑GPU上毫秒级运行的模型可能在抗辐射处理器上需要数分钟,或者根本无法装入内存。
- 另一种方法使用**商用现货**处理器,配合软件中的辐射缓解措施:纠错码、看门狗定时器、定期内存清理和优雅降级策略。一些现代任务使用这种方法以获得更强大的计算能力,代价是增加了软件复杂性和风险。
- 未来的行星任务正在探索**FPGA**和专门的AI加速器,它们可以具有耐辐射性,同时提供比传统抗辐射CPU多得多的计算能力,可能首次实现板载深度学习。
## 非结构化地形中的自主导航
- 在地球上,道路平坦、标记清晰且有地图。在火星、月球或灾难现场,没有道路。地形是非结构化的:岩石、斜坡、沙地、裂缝和可能无法支撑机器人重量的表面。
- **地形分类**评估每块地面是否安全通行。特征包括坡度(来自3D重建)、粗糙度(表面法线的方差)、岩石密度和土壤类型。经典方法从立体深度图计算这些特征;现代方法在视觉和几何特征上使用学习型分类器。
- **视觉-惯性里程计(VIO)**通过跟踪跨相机帧的视觉特征并与IMU测量融合来估计机器人的运动。这是SLAM的核心组件(第8章),针对极端条件进行了调整。在火星上,VIO必须处理:无特征的沙地地形(几乎没有可跟踪的视觉特征)、强烈的光照(极端阴影)和有限的计算能力。
- 估计过程使用**扩展卡尔曼滤波(EKF)**或因子图优化融合视觉和惯性数据。状态向量包括位置、速度、方向和IMU偏差。预测步骤使用IMU积分:
$$\\mathbf{x}_{t+1} = f(\\mathbf{x}_t, \\mathbf{u}_t)$$
- 其中$\\mathbf{u}_t$是IMU测量值(加速度和角速度)。更新步骤使用视觉特征观测来校正预测。这是贝叶斯估计(第5章):IMU提供先验,视觉观测更新信念。
- **危险规避**在行星着陆过程中至关重要。当航天器下降向表面时,它必须使用板载相机或LiDAR实时识别安全的着陆区。NASA毅力号上的**地形相对导航(TRN)**系统将板载相机图像与预加载的轨道地图进行比较,以确定下降过程中的位置,然后避开危险地形。这使得在Jezero陨石坑着陆成为可能——一个科学丰富但地形危险的站点,对于以前的 missions 来说风险太大。
## 水下机器人
- 深海与太空一样陌生:压碎性压力(全海深1000+个大气压)、接近零能见度、无GPS和有限的通信。水下机器人对海洋科学、近海基础设施检查、深海采矿和搜索操作至关重要。
- **AUV**(自主水下航行器)无缆运行,携带自己的电力和计算资源。它们遵循预设的测量模式或使用板载智能来适应发现。AUV用于海底测绘、管道检查和环境监测。
- **ROV**(遥控水下航行器)通过电缆连接到水面船只,提供电力和通信。它们用于需要实时人类控制的任务:深海操作、建造和修理。缆线消除了通信限制,但限制了范围并增加了操作复杂性。
- **声学通信**是主要的水下通信方法(无线电波在水中迅速衰减)。声学调制解调器在几公里范围内达到1-10 kbps的数据速率,而陆地上无线电可达吉比特每秒。这甚至比火星通信更加受限,迫使AUV高度自主。
- **水下SLAM**尤其具有挑战性。声纳提供距离测量,但角分辨率差且噪声大(来自海底和水面的多径反射)。相机只能在非常短的距离内工作(清澈水中几米,浑浊条件下更短)。基于特征的可视SLAM(第8章)必须针对水下场景的独特视觉特征进行调整:颜色衰减(红光首先被吸收)、反向散射以及产生亮斑和深影的人工照明。
- 无GPS导航使用**航位推算**(积分来自多普勒测速仪DVL的速度,该仪器利用声学多普勒频移测量相对于海底的速度),辅以偶尔浮出水面获取GPS定位或来自水面应答器的声学定位。这与仅IMU导航相同的漂移问题:小的速度误差在长任务中累积。
## 搜索救援机器人
- 在地震、建筑物倒塌或工业事故后,机器人可以进入对人类救援人员太危险的区域:结构不稳定的建筑物、有毒环境、火场或密闭空间。
- 需求是:快速部署(几分钟而非几小时)、在GPS受限环境中运行(建筑物内部、地下)、通过墙壁和瓦砾的鲁棒通信,以及导航高度杂乱、部分坍塌空间的能力,这些空间充满碎片、灰尘和不良照明。
- **多机器人协调**在搜索救援中很有价值,因为一支机器人团队可以比单个机器人更快地覆盖大面积。挑战在于协调:机器人必须划分搜索区域、避免重复工作并共享发现。
- **前沿探索**将机器人分配到已探索和未探索空间之间的边界("前沿")。每个机器人导航到最近的未探索前沿、绘制地图并继续前进。中央或分布式规划器将前沿分配给机器人以最小化总探索时间。这是一个覆盖优化问题。
- 通过瓦砾的通信不可靠。机器人可能失去与控制台和彼此的联系。系统必须对间歇通信具有鲁棒性:每个机器人应能独立运行,构建自己的局部地图并做出自己的决策,然后在通信恢复时合并信息。
## 群体机器人
- **群体机器人**使用大量简单、低成本的机器人,通过局部交互实现复杂的集体行为。没有单个机器人单独具备能力,但整个群体可以执行单个机器人无法完成的任务。
- 灵感来自生物群体:蚂蚁用身体搭桥、蜜蜂集体决定巢穴位置、鱼群通过协调运动躲避捕食者。在每种情况下,简单的局部规则(跟随邻居、避免碰撞、向食物移动)产生复杂的全局行为。
- **去中心化控制**意味着没有中央指挥官。每个机器人遵循相同的局部规则,仅对其邻居和即时环境作出反应。全局行为从这些局部交互中**涌现**。这使得群体具有固有的鲁棒性:如果一个机器人失效,群体继续运行。没有单点故障。
- **共识算法**使群体能够仅通过局部通信就集体决策达成一致(例如,向哪个方向移动、优先处理哪个任务)。一个简单的共识协议让每个机器人与其邻居平均其值:
$$x_i(t+1) = \\frac{1}{|N_i| + 1} \\left( x_i(t) + \\sum_{j \\in N_i} x_j(t) \\right)$$
![群体共识:机器人从分散开始,迭代地与邻居平均,收敛到共享位置](../images/swarm_consensus.svg)
- 其中$N_i$是机器人$i$的邻居集合。这一过程迭代直到所有机器人收敛到相同的值(全局平均值)。收敛速度取决于通信图的拓扑结构,特别是其代数连通性(图拉普拉斯矩阵的第二小特征值,与第2章的特征值相关)。
![Reynolds三个群集规则:分离避免碰撞,对齐匹配航向,内聚保持群体](../images/reynolds_flocking.svg)
- **群集算法**(Reynolds规则)通过每个机器人的三个简单规则产生协调的群体运动:
- **分离**:远离太近的邻居(避免碰撞)。
- **对齐**:朝向邻居的平均航向(朝相同方向移动)。
- **内聚**:朝向邻居的平均位置(与群体在一起)。
- 每个规则是机器人速度的一个向量贡献。这些向量的加权和产生自然主义的群集行为。这是一个向量的线性组合(第1章),其中权重控制每个行为的相对重要性。
- 群体机器人的应用包括环境监测(在大范围内分布传感器)、精准农业(协调无人机进行作物喷洒)、建造(机器人集体组装结构)和搜索操作(高效覆盖大面积)。
## 人机交互
- 大多数真实的自主系统是与人类并肩运行,而非孤立运行。人与机器人之间的交互——他们如何沟通、共享控制和建立信任——与机器人的技术能力同样重要。
![共享自主光谱:从完全人类遥控操作(alpha=1)经混合控制到完全机器人自主(alpha=0)](../images/shared_autonomy_spectrum.svg)
- **共享自主**混合了人和机器人的控制。不是完全遥控操作(人类控制一切)或完全自主(机器人控制一切),而是共享自主让人类提供高层意图,同时机器人处理底层执行。例如,人类可能指向一个物体说"捡起来",然后机器人自主规划抓取和手臂运动。
- 数学上,共享自主可以建模为人类输入$\\mathbf{u}_h$和机器人自主动作$\\mathbf{u}_r$的混合:
$$\\mathbf{u} = \\alpha \\mathbf{u}_h + (1 - \\alpha) \\mathbf{u}_r$$
- 其中$\\alpha \\in [0, 1]$是混合参数。当$\\alpha = 1$时,人类完全控制(遥控操作)。当$\\alpha = 0$时,机器人完全自主。自适应共享自主根据情况调整$\\alpha$:机器人在自信时接管更多控制,在不确定或情况新颖时让出控制。
- **遥控操作**对于超出当前自主能力的任务仍然很重要。人类操作员通过机器人的相机远程查看场景并控制机器人。挑战是**延迟**:即使100毫秒的延迟也会使遥控操作变得困难,而太空中的多秒延迟使其对精细操作几乎不可能。预测显示(显示机器人预测的未来状态)和虚拟夹具(防止操作员命令危险运动的软件引导)有助于弥补。
- **信任校准**是确保人类对机器人有适当信任的问题:不要太多(过度信任导致自满,在需要时未能干预),也不要太少(信任不足导致不必要干预和利用不足)。信任应该校准到机器人的实际能力:在它处理得好的情况下信任它,在接近其能力边缘的情况下保持怀疑。
- 研究表明,信任受以下因素的影响:机器人的透明度(它是否解释其决策?)、可靠性(它是可预测地失败还是随机地失败?)以及沟通(它是否表达不确定性?)。一个说"我对此路径只有40%的置信度,是否继续?"的机器人比一个默默向前驾驶的机器人能做出更好的人类决策。
- 机器人运动中的**可读性**意味着机器人以传达其意图的方式运动给附近的人类。如果机器人伸手去拿一个物体,它的路径应该使其目标对象显而易见,即使它还未到达。这涉及规划最大化观察者早期推断目标的轨迹,可以形式化为给定观察到的部分轨迹时真实目标的后验概率最大化:
$$\\pi^* = \\arg\\max_\\pi P(G \\mid \\xi_{0:t})$$
- 其中$G$是目标,$\\xi_{0:t}$是到目前为止观察到的轨迹。这使用了贝叶斯推理(第5章):观察者对可能的目标有先验,机器人的轨迹提供了更新此信念的证据。
## 编程任务(使用CoLab或notebook
1. 模拟机器人群体就目标位置达成一致的共识算法。从随机初始位置开始,观察收敛过程。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
n_robots = 10
rng = jax.random.PRNGKey(0)
positions = jax.random.uniform(rng, (n_robots, 2), minval=-5, maxval=5)
# 通信图:每个机器人与最近的3个邻居通信
def get_neighbours(positions, k=3):
dists = jnp.linalg.norm(positions[:, None] - positions[None, :], axis=-1)
# 对每个机器人,找最近的k个(排除自身)
neighbours = jnp.argsort(dists, axis=1)[:, 1:k+1]
return neighbours
history = [positions.copy()]
for step in range(30):
neighbours = get_neighbours(positions)
new_positions = jnp.zeros_like(positions)
for i in range(n_robots):
nbr_pos = positions[neighbours[i]]
new_positions = new_positions.at[i].set(
(positions[i] + nbr_pos.sum(axis=0)) / (len(neighbours[i]) + 1)
)
positions = new_positions
history.append(positions.copy())
# 绘制收敛过程
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, step_idx, title in zip(axes, [0, 10, 29], ["初始", "第10步", "最终"]):
h = history[step_idx]
ax.scatter(h[:, 0], h[:, 1], s=50)
ax.set_xlim(-6, 6); ax.set_ylim(-6, 6)
ax.set_aspect("equal"); ax.grid(True); ax.set_title(title)
plt.suptitle("群体共识:机器人收敛到一致性")
plt.tight_layout()
plt.show()
```
2. 实现Reynolds群集规则(分离、对齐、内聚)并模拟一个群体一起移动。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
n = 30
rng = jax.random.PRNGKey(1)
k1, k2 = jax.random.split(rng)
pos = jax.random.uniform(k1, (n, 2), minval=-5, maxval=5)
vel = jax.random.uniform(k2, (n, 2), minval=-0.5, maxval=0.5)
dt = 0.1
separation_radius = 1.0
neighbour_radius = 3.0
trajectories = [pos.copy()]
for _ in range(200):
new_vel = jnp.zeros_like(vel)
for i in range(n):
diffs = pos - pos[i]
dists = jnp.linalg.norm(diffs, axis=1)
# 半径内的邻居(排除自身)
nbr_mask = (dists < neighbour_radius) & (dists > 0)
sep_mask = (dists < separation_radius) & (dists > 0)
# 分离:远离非常近的邻居
if sep_mask.any():
sep = -diffs[sep_mask].sum(axis=0)
else:
sep = jnp.zeros(2)
# 对齐:匹配邻居的平均速度
if nbr_mask.any():
align = vel[nbr_mask].mean(axis=0) - vel[i]
else:
align = jnp.zeros(2)
# 内聚:朝向邻居的平均位置
if nbr_mask.any():
cohesion = pos[nbr_mask].mean(axis=0) - pos[i]
else:
cohesion = jnp.zeros(2)
new_vel = new_vel.at[i].set(vel[i] + 1.5 * sep + 0.5 * align + 0.3 * cohesion)
# 限制速度
speeds = jnp.linalg.norm(new_vel, axis=1, keepdims=True)
vel = jnp.where(speeds > 2.0, new_vel / speeds * 2.0, new_vel)
pos = pos + vel * dt
trajectories.append(pos.copy())
# 绘制快照
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, idx, title in zip(axes, [0, 50, 199], ["开始", "第50步", "第200步"]):
p = trajectories[idx]
v = vel if idx == 199 else jnp.zeros_like(vel)
ax.scatter(p[:, 0], p[:, 1], s=20, c="blue")
ax.set_aspect("equal"); ax.grid(True); ax.set_title(title)
lim = max(abs(p).max() + 1, 6)
ax.set_xlim(-lim, lim); ax.set_ylim(-lim, lim)
plt.suptitle("Reynolds群集:分离+对齐+内聚")
plt.tight_layout()
plt.show()
```
3. 模拟共享自主混合:人类提供带噪声的方向输入,机器人的自主系统提供到目标的平滑路径。用不同的alpha值进行混合。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
goal = jnp.array([10.0, 5.0])
pos = jnp.array([0.0, 0.0])
dt = 0.1
rng = jax.random.PRNGKey(3)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, alpha in zip(axes, [1.0, 0.5, 0.0]):
pos = jnp.array([0.0, 0.0])
path = [pos.copy()]
for step in range(150):
# 机器人自主:到目标的平滑路径
direction = goal - pos
u_robot = direction / (jnp.linalg.norm(direction) + 1e-6) * 1.0
# 人类输入:大致正确的方向但有噪声
noise = jax.random.normal(jax.random.fold_in(rng, step), (2,)) * 0.5
u_human = u_robot + noise
# 混合
u = alpha * u_human + (1 - alpha) * u_robot
pos = pos + u * dt
path.append(pos.copy())
if jnp.linalg.norm(pos - goal) < 0.3:
break
path = jnp.stack(path)
ax.plot(path[:, 0], path[:, 1], "b-", alpha=0.7)
ax.plot(*goal, "r*", markersize=15)
ax.plot(0, 0, "go", markersize=10)
ax.set_title(f"α={alpha:.1f} ({'人类' if alpha==1 else '机器人' if alpha==0 else '共享'})")
ax.set_xlim(-1, 12); ax.set_ylim(-3, 8)
ax.set_aspect("equal"); ax.grid(True)
plt.suptitle("共享自主:混合人类与机器人控制")
plt.tight_layout()
plt.show()
```