4、C空间与构型空间:机器人规划的“隐形战场”

各位工程师朋友,欢迎来到第四章。今天聊的话题,是轨迹规划里最基础、也最容易被忽视的概念——构型空间,也就是我们常说的 C 空间。

说实话,我刚开始做机器人规划时,根本没把 C 空间当回事。觉得不就是把机器人关节角度画个坐标系嘛,有什么好学的?直到有一次,我在做七轴机械臂的避障规划,算法怎么跑都撞墙,调试了整整三天。最后才发现,我一直在笛卡尔空间里做规划,完全忽略了 C 空间的特性。嗯,从那以后,我再也不敢跳过 C 空间了。

4.1 什么是构型空间?

先问大家一个问题:机器人的“位置”到底怎么描述?

如果你问一个移动机器人,它可能会说“我在 (x, y, θ)”。但如果你问一个六轴机械臂,它得告诉你六个关节的角度值 (θ₁, θ₂, …, θ₆)。

这六个角度值,就构成了一个构型。所有可能的构型组成的集合,就是构型空间,简称 C 空间。

核心定义:构型空间 = 机器人所有可能状态的集合。每个状态对应 C 空间中的一个点。

举个例子。一个在平面上移动的小车,它的构型是 (x, y, θ)。x 和 y 是位置,θ 是朝向。那么它的 C 空间就是 R² × S¹——两个直线维度加一个旋转维度。说白了,就是所有可能的位置和朝向的组合。

我个人习惯把 C 空间想象成一个“地图”。地图上的每个点,都对应机器人真实世界中的一个姿态。规划问题,就变成了在这个地图上找一条从起点到终点的路径。

4.2 障碍物在 C 空间中的表示

好,现在问题来了:真实世界里有障碍物,它们在 C 空间里长什么样?

这其实是整个 C 空间理论最精妙的地方。真实世界中的障碍物,经过机器人的运动学映射,会变成 C 空间中的禁区——也就是机器人不能进入的区域。

我给大家画个图,帮助理解这个映射过程。

障碍物从真实空间到 C 空间的映射 真实空间 (Work Space) 机器人 障碍物 映射 构型空间 (C-Space) C_free (自由空间) C_obs (障碍物) 起点 终点 规划路径 关键理解:真实空间中的障碍物,经过机器人运动学映射后,在 C 空间中变成不规则形状的禁区

你看,真实空间里一个方方正正的障碍物,到了 C 空间就变成了一个扭曲的形状。为什么会这样?因为机器人的关节运动是非线性的。一个关节转动,可能让末端执行器走出一条弧线,这个弧线扫过的区域,在 C 空间里就是一块禁区。

我记得在做一个协作机器人项目时,遇到过一个问题:桌面上放了一个杯子,机器人要绕过它抓取后面的零件。在笛卡尔空间里看,杯子只是一个小圆柱。但在 C 空间里,这个杯子对应的禁区却非常大——因为机器人的大臂和小臂都可能撞到它。这就是 C 空间障碍物“膨胀”的现象。

实战技巧:在做碰撞检测时,不要只看末端是否碰到障碍物。要把整个机器人的连杆都考虑进去。我习惯用“扫掠体”的方法,把每个连杆在运动过程中扫过的体积都算进 C 空间障碍物里。

4.3 C 空间维度的概念

聊完了障碍物,咱们说说维度。C 空间的维度,说白了就是机器人自由度的数量。

来看个表格,一目了然:

机器人类型 自由度 C 空间维度 典型构型表示
平面移动小车 3 3 维 (x, y, θ)
差分驱动小车 3 3 维 (x, y, θ)
四轴机械臂 4 4 维 (θ₁, θ₂, θ₃, θ₄)
六轴工业机械臂 6 6 维 (θ₁, θ₂, …, θ₆)
七轴协作机器人 7 7 维 (θ₁, θ₂, …, θ₇)
四旋翼无人机 6 6 维 (x, y, z, roll, pitch, yaw)

这里有个关键点:维度越高,规划越难。你想想看,2 维空间里找路径,就是在一张纸上画线。3 维空间里找路径,是在一个立方体里穿行。到了 6 维、7 维,人脑根本没法想象——这就是所谓的“维度诅咒”。

我曾经做过一个项目,用 RRT 算法给七轴机械臂做规划。在 7 维 C 空间里,随机采样的点大部分都落在障碍物区域里。算法的效率低得令人发指。后来我换了一种采样策略,才勉强跑通。

避坑指南:我曾经犯过一个错误——在 C 空间维度很高时,还使用均匀随机采样。结果算法几乎找不到可行路径。后来我改用“目标偏置采样”,让采样点更倾向于朝着终点方向生成,效率提升了 10 倍以上。

4.4 C 空间的拓扑结构

说到维度,就不得不提 C 空间的拓扑结构。不是所有维度都是直线型的。比如旋转关节,它的角度是循环的——0° 和 360° 其实是同一个位置。

这就引出了 C 空间的几种基本拓扑:

  • Rⁿ(欧几里得空间):平移关节、移动机器人的位置部分。直线维度,没有边界循环。
  • S¹(圆环):单个旋转关节。0° 和 360° 是同一个点。
  • Tⁿ(n 维环面):多个旋转关节的组合。比如六轴机械臂的 C 空间就是 T⁶。
  • SE(2) / SE(3):移动机器人的位姿空间。SE(2) = R² × S¹,SE(3) = R³ × SO(3)。

你可能会问:知道拓扑有什么用?用处大了。比如在 S¹ 上做规划时,你不能用普通的欧几里得距离——因为 350° 到 10° 的直线距离是 340°,但实际最短路径只有 20°(绕一圈)。

我习惯在代码里专门处理这种循环维度。用一个 wrap_angle 函数,把角度差值归一化到 [-π, π] 之间。

def wrap_angle(angle):
    """将角度归一化到 [-π, π]"""
    while angle > math.pi:
        angle -= 2 * math.pi
    while angle < -math.pi:
        angle += 2 * math.pi
    return angle

def angular_distance(a1, a2):
    """计算两个角度之间的最短距离"""
    diff = wrap_angle(a1 - a2)
    return abs(diff)

核心要点:C 空间的拓扑结构决定了距离度量的方式。用错了距离,规划出来的路径可能绕远路,甚至根本找不到路。

4.5 从 C 空间到实际规划

好了,讲了这么多理论,咱们回到实际。C 空间到底怎么用在轨迹规划里?

流程其实很清晰:

  1. 建立 C 空间模型:确定机器人的自由度,定义每个维度的范围。
  2. 构建障碍物映射:把真实世界的障碍物,通过运动学映射到 C 空间中,标记出 C_obs 区域。
  3. 定义起点和终点:把真实世界的起始姿态和目标姿态,转换成 C 空间中的两个点。
  4. 在 C 空间中规划:在 C_free(自由空间)中找一条连接起点和终点的连续路径。
  5. 映射回真实空间:把 C 空间中的路径点,通过正运动学转换回关节角度或末端位姿。

这个流程看起来简单,但每一步都有坑。比如第二步,构建障碍物映射时,如果机器人有多个连杆,你需要对每个连杆都做碰撞检测。我见过有人只检测末端,结果大臂撞墙了都不知道。

再比如第四步,在 C 空间中规划时,路径的平滑性很重要。C 空间中的直线,映射回真实空间可能是一条很扭曲的轨迹。所以我在规划完成后,通常会加一个平滑后处理步骤。

我的习惯:在 C 空间规划完成后,我会用 B 样条曲线对路径进行拟合。这样既能保证路径在 C 空间中的连续性,又能让真实空间的运动更平滑。具体做法是:把 C 空间路径点作为控制点,生成一条 3 阶 B 样条,然后重新采样。

最后,我想强调一点:C 空间不是万能的。对于高自由度机器人,C 空间的维度太高,直接规划可能很慢。这时候可以考虑混合规划——在 C 空间中做全局规划,在笛卡尔空间中做局部优化。这个我们后面章节会详细讲。