空间与极端环境机器人 空间与极端环境机器人把自主性推向极限:在那里,通信延迟、辐射和结构化程度极低的地形要求机器人能独立思考。本文件涵盖行星探测车、在轨服务、通信受限下的自主、抗辐射计算、水下机器人、搜索与救援、群体机器人与人机交互。 在本章中,我们一直研究的自主系统都运行在相对温和的环境里:有车道标线的道路、地板平坦的仓库、物体类别已知的厨房。但机器人最有影响力的一些应用恰恰在人去不了、或者人存在的代价极高的环境里:火星表面、深海海底、核灾难现场、燃烧的建筑。 这些极端环境共享一些共同挑战:通信受限或延迟、地形结构化程度低且不可预测、硬件必须在恶劣条件下存活,而且出问题时附近没人能修理。机器人必须是真正自主的,而不只是"有个看着屏幕的人盯着的那种自主"。 空间机器人 空间是终极的极端环境。
空间与极端环境机器人把自主性推向极限:在那里,通信延迟、辐射和结构化程度极低的地形要求机器人能独立思考。本文件涵盖行星探测车、在轨服务、通信受限下的自主、抗辐射计算、水下机器人、搜索与救援、群体机器人与人机交互。
在本章中,我们一直研究的自主系统都运行在相对温和的环境里:有车道标线的道路、地板平坦的仓库、物体类别已知的厨房。但机器人最有影响力的一些应用恰恰在人去不了、或者人存在的代价极高的环境里:火星表面、深海海底、核灾难现场、燃烧的建筑。
这些极端环境共享一些共同挑战:通信受限或延迟、地形结构化程度低且不可预测、硬件必须在恶劣条件下存活,而且出问题时附近没人能修理。机器人必须是真正自主的,而不只是"有个看着屏幕的人盯着的那种自主"。
空间是终极的极端环境。那里没有空气,温度在 -170°C 到 +120°C 之间剧烈波动,辐射不断轰击电子器件,而最近的援助在数百万公里之外。空间机器人必须极其可靠、节能、自主。
行星探测车是探索其他星球表面的移动机器人。NASA 的火星车(Spirit、Opportunity、Curiosity、Perseverance)是最著名的例子。每一代都比上一代更自主。
根本的约束是通信延迟。火星与地球之间的无线电单程需要 4-24 分钟(取决于轨道位置),因此往返通信需要 8-48 分钟。探测车无法被实时手柄操控。如果它遇到一块岩石,它无法向地球求助并等待回应。它必须自己做决定。
早期火星车(Spirit、Opportunity)严重依赖"人在回路"规划:人类研究图像、规划路径、上传命令,探测车再执行。一个完整的驾驶周期要花整整一个火星日(sol)。探测车每个 sol 大约只能前进 50-100 米。
Curiosity 和 Perseverance 上的 AutoNav(自主导航)大幅提升了自主性。探测车用双目摄像头构建局部 3D 地图(回顾第 8 章的双目深度),评估地形可通过性(坡度、粗糙度、岩石大小),并用一个基于网格、带可通过性代价图的规划器规划安全路径。探测车在人类团队睡觉时自主驾驶,把每日前进距离提升到 100 米以上。
火星车上的感知流水线受限于抗辐射处理器——它们比消费级硬件慢好几个数量级(下文详述)。算法必须在计算上非常节省:经典的双目匹配而非深度神经网络,简单的代价图规划器而非学习型策略。
在轨服务涉及在轨道上检查、修理、加注或让卫星脱轨的机器人。随着太空日益拥挤,这是一个不断增长的领域。像 OSAM-1(NASA)以及商业项目(Astroscale、Northrop Grumman MEV)这样的任务,使用机械臂和对接机构来服务卫星。
挑战在于近距操作:服务航天器必须接近目标卫星(后者可能在翻滚、不配合、且没有对接接口),并在微重力下完成精确操作。基于视觉的位姿估计(从摄像头图像确定目标的 3D 位置和朝向)至关重要。这用到了第 8 章的技术:特征检测、PnP(Perspective-n-Point)求解,以及更近期的基于深度学习的位姿估计器。
卫星巡检使用小型航天器对其他卫星进行视觉检查,以发现损伤或异常。巡检器必须自主地在目标周围导航、避免碰撞,并从最佳视角捕获高分辨率图像。这是一个规划问题:在满足燃料约束、光照条件和避障的前提下,找到覆盖所有检查点的轨迹。
在太空中,通信受限于光速、可用带宽和轨道几何(火星背面的探测车在没有中继卫星的情况下根本无法与地球通信)。
这些约束从根本上改变了自主性架构。在地球上,机器人可以把高清视频流到云服务器、在 GPU 集群上做推理、并在几毫秒内收到命令。在太空中,机器人必须在星上完成一切。
高延迟意味着机器人必须在无实时人类指导下规划和行动。自主软件必须处理常规操作、检测异常、并对危险做出响应,而不能等待人类输入。这需要鲁棒的车载状态估计、故障检测和应急预案。
有限带宽意味着机器人无法传输原始传感器数据。一张高分辨率图像可能有几兆字节,但火星到地球的直连数据速率只有每秒几千比特(通过轨道中继会高一些,但仍有限)。机器人必须大幅压缩数据、优先选择要发送的数据,并把大部分决策放在本地完成。
通信窗口是间歇性的。火星车只能在特定轨道几何下与地球通信,通常每个 sol 通过中继卫星几个小时。在这些窗口之外,探测车完全靠自己。
对 AI 的含义是:车载自主必须高度可靠。系统需要检测哪里出了问题(一个轮子卡住了、一个传感器坏了、前方地形不可通行),决定一个安全的应对,并继续运行直到下一个通信窗口,届时它可以汇报并接收更新的指令。
太空充斥着电离辐射:宇宙射线、太阳粒子事件、以及被行星磁场捕获的辐射。高能粒子可以在存储器中翻转比特(单粒子翻转,single-event upset,SEU),永久损坏晶体管(总电离剂量,total ionising dose,TID),或在电路中引起破坏性闩锁。
**抗辐射(radiation-hardened,rad-hard)**处理器设计用来抵御这种环境。它们使用更大的晶体管几何结构、冗余逻辑(三模冗余:每条电路做三份,对输出投票)以及专门的制造工艺。代价是性能:一颗最先进的抗辐射处理器可能只能提供 200 MIPS,而消费级 GPU 每秒能做数十亿次运算。
RAD750(BAE Systems)驱动了 Curiosity 和许多其他航天器。它运行在 200 MHz,处理能力约 400 MIPS,大致相当于 1990 年代中期的台式机。Perseverance 使用同级别的处理器。在这样硬件上运行一个现代神经网络(数百万参数、数十亿次乘加运算)是不可行的。
模型压缩变得至关重要。第 6 章的技术(量化、剪枝、知识蒸馏)被用来把神经网络压缩到极限计算预算之内。一个在笔记本 GPU 上几毫秒就能跑完的模型,在抗辐射处理器上可能需要几分钟,甚至根本装不进内存。
一种替代方案使用**商用现成(commercial off-the-shelf,COTS)**处理器,并通过软件手段进行辐射缓解:纠错码、看门狗定时器、周期性内存清洗和优雅降级策略。一些现代任务采用这种方式,以软件复杂性和风险的增加为代价换取更强的算力。
未来的行星任务正在探索 FPGA 和专门的 AI 加速器,它们可以在抗辐射的同时提供远超传统抗辐射 CPU 的算力,有望首次实现车载深度学习。
在地球上,道路平坦、标线清晰、且已建图。在火星、月球或灾难现场,没有道路。地形是结构化程度很低的:岩石、坡度、沙地、裂缝,以及可能撑不住机器人重量的表面。
地形分类评估每一小块地面是否可以安全通过。特征包括坡度(来自 3D 重建)、粗糙度(表面法线的方差)、岩石密度和土壤类型。经典方法从双目深度图计算这些特征;现代方法在视觉和几何特征上使用学习型分类器。
**视觉-惯性里程计(visual-inertial odometry,VIO)**通过跨摄像头帧跟踪视觉特征并与 IMU 测量融合,来估计机器人的运动。这是 SLAM 的核心组件(第 8 章),针对极端条件做了适配。在火星上,VIO 必须应对:缺乏特征的沙地(可跟踪的视觉特征很少)、严酷光照(极端阴影)以及有限的算力。
估计过程使用**扩展卡尔曼滤波(Extended Kalman Filter,EKF)**或因子图优化来融合视觉和惯性数据。状态向量包括位置、速度、朝向以及 IMU 偏置。预测步用 IMU 积分:
其中 \mathbf{u}_t 是 IMU 测量(加速度和角速度)。更新步用视觉特征观测来修正预测。这是贝叶斯估计(第 5 章):IMU 提供先验,视觉观测更新信念。
危险规避在行星着陆时至关重要。当航天器下降到表面时,它必须用车载摄像头或 LiDAR 实时识别安全着陆区。NASA Perseverance 上的**地形相对导航(Terrain Relative Navigation,TRN)**系统把车载摄像头图像与预装的轨道地图进行比对,在下降过程中确定自身位置,然后避开危险地形。这使得在 Jezero 陨石坑着陆成为可能——这是一个科学价值丰富但地形危险的地点,对以往任务来说风险太高。
深海和外星一样陌生: crushing 般的压力(全海深处可达 1000+ 个大气压)、几乎为零的能见度、没有 GPS、通信受限。水下机器人对海洋科学、海上基础设施巡检、深海采矿和搜索行动都至关重要。
AUV(Autonomous Underwater Vehicle,自主水下航行器)无缆运行,自带电源和计算。它们沿预编程的测量航线航行,或用车载智能根据发现进行调整。AUV 用于海底测绘、管道巡检和环境监测。
ROV(Remotely Operated Vehicle,遥控潜水器)通过一根电缆与水面船相连,由电缆提供电源和通信。它们用于需要实时人控的任务:深海操作、施工和修理。缆绳解除了通信约束,但限制了航程并增加了操作复杂度。
声学通信是主要的水下通信方式(无线电波在水中迅速衰减)。声学调制解调器在几公里距离上达到的数据速率为每秒 1-10 千比特,而陆地上的无线电可达每秒数千兆比特。这甚至比火星通信还要受限,迫使 AUV 高度自主。
水下 SLAM 尤为困难。声呐提供距离测量但角度分辨率差、噪声大(来自海底和海面的多径反射)。摄像头只在极短距离内有效(清水中几米,浑浊水中更短)。基于特征的水下视觉 SLAM(第 8 章)必须针对水下场景独特的视觉特性做适配:色彩衰减(红光最先被吸收)、后向散射,以及会产生亮斑和深阴影的人工照明。
没有 GPS 的导航使用航位推算(dead reckoning)(对来自多普勒速度计 DVL 的速度积分,DVL 利用声学多普勒频移测量相对于海底的速度),偶尔上浮获取 GPS 定位,或借助水面应答器的声学定位。这与仅用 IMU 导航面临同样的漂移问题:长任务中微小的速度误差会累积。
地震、建筑倒塌或工业事故之后,机器人可以进入对人类救援者来说过于危险的空间:结构不稳定的建筑、有毒环境、火灾或密闭空间。
要求包括:快速部署(几分钟而非几小时)、在无 GPS 的环境中运行(建筑内部、地下)、能穿透墙壁和瓦砾进行可靠通信,以及在高度杂乱、部分坍塌、充满碎片、灰尘和光线差的空间中导航。
多机器人协同在搜救中很有价值,因为一队机器人能比单台机器人更快覆盖大面积区域。挑战在于协调:机器人必须划分搜索区域、避免重复劳动,并共享发现。
**基于前沿的探索(frontier-based exploration)**把机器人分配到已探索和未探索空间的边界("前沿")。每台机器人导航到最近的未探索前沿,绘制它,然后继续前进。一个集中式或分布式规划器把前沿分配给各机器人,以最小化总探索时间。这是一个覆盖优化问题。
穿过瓦砾的通信不可靠。机器人可能与操作员及彼此失去联系。系统必须对间歇性通信鲁棒:每台机器人应能独立运行,构建自己的局部地图并做出自己的决策,然后在通信恢复时合并信息。
**群体机器人(swarm robotics)**使用大量简单、低成本的机器人,通过局部交互产生复杂的集体行为。没有任何单个机器人具备能力,但群体整体能完成任何个体都做不到的任务。
灵感来自生物群体:用身体搭桥的蚂蚁、关于巢址做集体决策的蜜蜂、通过协同移动躲避捕食者的鱼群。在每种情形下,简单的局部规则(跟随邻居、避免碰撞、朝食物移动)产生出复杂的全局行为。
去中心化控制(decentralised control)意味着没有中央指挥。每台机器人遵循相同的局部规则,只对自己的邻居和直接环境做出反应。全局行为从这些局部交互中涌现。这使得群体天然鲁棒:一台机器人失效,群体继续运转。不存在单点失效。
**共识算法(consensus algorithms)**让群体仅通过局部通信就某项集体决策达成一致(例如朝哪个方向移动、优先做哪个任务)。一个简单的共识协议让每台机器人把自己的值与邻居平均:
集群算法(flocking algorithms)(Reynolds 规则)用每台机器人三条简单规则产生协调的群体运动:
每条规则都对机器人速度贡献一个向量。这些向量的加权和产生自然的集群行为。这是向量的线性组合(第 1 章),权重控制每种行为的相对重要性。
群体机器人的应用包括环境监测(在大范围内分布传感器)、精准农业(协调无人机进行作物喷洒)、建造(机器人协同组装结构)和搜索行动(高效覆盖大区域)。
**共享自主(shared autonomy)**融合人与机器人的控制。它不是完全遥操作(人控制一切)或完全自主(机器人控制一切),而是让人提供高层意图、机器人负责低层执行。例如,人可能指向一个物体说"把那个拿起来",机器人则自主规划抓取和手臂运动。
在数学上,共享自主可以建模为人的输入 \mathbf{u}_h 与机器人自主动作 \mathbf{u}_r 的混合:
其中 \alpha \in [0, 1] 是混合参数。当 \alpha = 1 时,人完全控制(遥操作)。当 \alpha = 0 时,机器人完全自主。自适应共享自主根据情境调整 \alpha:机器人有把握时多接管,不确定或遇到新情况时把控制权交还给人。
遥操作(teleoperation)对超出当前自主能力的任务仍然重要。人类操作员远程控制机器人,通过机器人的摄像头观察场景。挑战在于延迟:哪怕 100ms 的延迟都会让遥操作变得困难,而空间中的多秒级延迟几乎让精细操作不可能。预测性显示(展示机器人预测的未来状态)和虚拟夹具(阻止操作员下达危险运动的软件导引)能有所帮助。
**信任校准(trust calibration)**是确保人对机器人有恰当信任的问题:不能太多(过度信任会导致松懈,该介入时不介入),也不能太少(信任不足会导致不必要的介入和资源浪费)。信任应与机器人的实际能力相匹配:在它擅长的情境信任它,在接近它能力边缘的情境保持怀疑。
研究表明,信任受以下因素影响:机器人的透明度(它会解释自己的决策吗?)、可靠性(它的失效是可预测的还是随机的?)以及沟通方式(它会表达不确定性吗?)。一个会说"我只有 40% 的把握这条路是安全的,要继续吗?"的机器人,能比一个默默往前开的机器人让人做出更好的决策。
机器人运动中的**可读性(legibility)**意味着机器人以向附近的人传达其意图的方式运动。如果机器人伸手去够一个物体,它的路径应该在它到达之前就让人一眼看出它瞄准的是哪个物体。这涉及规划能最大化观察者早期推断目标能力的轨迹,可以形式化为在给定已观测到的部分轨迹下,最大化真实目标的后验概率:
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], ["Initial", "Step 10", "Final"]): 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("Swarm Consensus: Robots Converge to Agreement") plt.tight_layout() plt.show()
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], ["Start", "Step 50", "Step 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' Flocking: Separation + Alignment + Cohesion") plt.tight_layout() plt.show()
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} ({'human' if alpha==1 else 'robot' if alpha==0 else 'shared'})") ax.set_xlim(-1, 12); ax.set_ylim(-3, 8) ax.set_aspect("equal"); ax.grid(True) plt.suptitle("Shared Autonomy: Blending Human and Robot Control") plt.tight_layout() plt.show()