4、C空间与障碍物建模:构型空间(C-Space)概念、障碍物在C空间中的表示、自由空间与障碍空间

各位同学,欢迎来到第四章。

前面几章我们聊了轨迹规划的基本概念和数学基础。说实话,那些内容比较抽象。但这一章不一样,这一章讲的是「空间」——机器人到底在什么「空间」里运动?

你可能觉得这问题很简单:机器人当然在物理世界里运动啊。但做轨迹规划时,我们很少直接在物理空间里算。为什么?因为机器人的形状、关节限制、障碍物的形状……这些因素搅在一起,直接算会非常复杂。

所以,我们需要一个更聪明的办法——构型空间,也就是 C-Space。

4.1 从物理空间到构型空间

先问一个问题:一个六轴机械臂,它的「位置」怎么描述?

在物理世界里,我们可能说「末端在 (x, y, z) 点」。但机械臂的每个关节都有一个角度值,六个关节角度组合起来,才真正决定了机械臂的完整状态。这个状态,就是构型。

所有可能的构型组成的空间,就是构型空间(Configuration Space),简称 C-Space。

核心理解:

  • 物理空间(Work Space):机器人实际存在的三维世界。我们看得见、摸得着。
  • 构型空间(C-Space):用机器人的所有自由度参数构成的一个抽象空间。每个点代表机器人的一个完整状态。

举个例子。一个在平面上移动的小车,它的位置可以用 (x, y, θ) 三个参数描述。那么它的 C-Space 就是一个三维空间。你想想看,物理世界是二维的,但 C-Space 是三维的——因为多了朝向这个维度。

对于六轴机械臂,C-Space 是六维的。我们没法直接画出来,但数学上可以处理。

我个人习惯:刚开始接触 C-Space 时,别被高维吓到。先从 2D 平面机器人的 C-Space 入手,理解清楚概念,再推广到高维。我在项目里带新人时,都是让他们先画一个 2D 圆盘机器人的 C-Space 图,画明白了再碰机械臂。

4.2 障碍物在 C-Space 中的表示

好,现在有了 C-Space 的概念。下一个问题是:物理世界里的障碍物,在 C-Space 里长什么样?

这其实是整个轨迹规划里最核心的转化之一。说白了,就是把「机器人不能碰障碍物」这个约束,从物理空间映射到 C-Space 里。

4.2.1 映射的基本思路

给定一个障碍物 O(在物理空间里),C-Space 中的障碍物 C-O 定义为:所有会导致机器人与 O 发生碰撞的构型的集合。

用数学语言说:

C-O = { q ∈ C-Space | 机器人在构型 q 时与障碍物 O 发生碰撞 }

这个映射过程,我们称之为「障碍物膨胀」或「C-Space 障碍物构建」。

4.2.2 一个简单的例子:圆形机器人与多边形障碍物

假设机器人在平面上是一个半径为 r 的圆盘。障碍物是一个多边形。

在 C-Space 里,障碍物会「膨胀」一圈——膨胀的半径就是机器人的半径 r。为什么?因为只要机器人的中心点距离障碍物边界小于 r,就会发生碰撞。

所以,C-Space 中的障碍物就是:将原始障碍物的每条边向外平移 r,然后取这些平移后的边围成的区域。

我曾经踩过的一个坑:做项目时,我直接拿原始障碍物的多边形去做碰撞检测,结果规划出来的轨迹,机器人明明没碰到障碍物,但实际运行时却撞了。查了半天才发现,我忘了把机器人自身的半径算进去。嗯,这个错误我只犯过一次。

4.2.3 非圆形机器人的情况

如果机器人不是圆形,事情就复杂了。比如一个矩形机器人,它的 C-Space 障碍物不再是简单的膨胀,而是需要做 Minkowski 和运算。

Minkowski 和的定义很简单:

A ⊕ B = { a + b | a ∈ A, b ∈ B }

对于障碍物 O 和机器人 R(以参考点表示),C-Space 障碍物为:

C-O = O ⊕ (-R)

其中 -R 是机器人绕参考点旋转 180 度后的形状。

机器人形状 C-Space 障碍物构建方法 计算复杂度
圆形(半径 r) 障碍物向外膨胀 r 低
凸多边形 Minkowski 和运算 中
凹多边形 先分解为凸多边形,再分别做 Minkowski 和 高
任意形状 离散采样 + 碰撞检测 很高

4.3 自由空间与障碍空间

有了 C-Space 障碍物,我们就可以把整个 C-Space 分成两部分:

  • 障碍空间(C-Obs):所有会导致碰撞的构型的集合。
  • 自由空间(C-Free):所有不会导致碰撞的构型的集合。即 C-Free = C-Space \ C-Obs。

轨迹规划的任务,就是在 C-Free 中找一条从起始构型到目标构型的连续路径。

记住这个核心逻辑:

物理空间中的障碍物 → 映射到 C-Space 中的障碍空间 → 剩下的就是自由空间 → 在自由空间里规划路径。

这个流程,是所有基于 C-Space 的轨迹规划方法的基础。

3.3.1 自由空间的特性

C-Free 不一定是连通的。它可能被 C-Obs 分割成多个连通分量。如果起始点和目标点不在同一个连通分量里,那就不存在可行路径。

这也是为什么有些时候,规划算法会告诉你「无解」——不是算法不行,而是物理上就不可能。

3.3.2 显式 vs 隐式表示

在实际工程中,我们很少显式地构建整个 C-Space 地图。为什么?因为维度太高,存不下。

对于 2D 机器人,C-Space 是 2D 或 3D 的,我们可以用栅格地图显式表示。但对于六轴机械臂,C-Space 是 6D 的,显式表示需要天文数字的存储量。

所以,实际做法是:

  • 显式表示:适用于低维 C-Space(≤ 3D)。直接构建栅格或 Voronoi 图。
  • 隐式表示:适用于高维 C-Space。不预先构建,而是在规划过程中通过碰撞检测来判断一个构型是否在 C-Free 中。

我建议:做项目时,先评估你的机器人自由度。如果是 2D 移动机器人,大胆用显式 C-Space 栅格。如果是机械臂,老老实实用隐式表示 + 碰撞检测。别试图把 6D C-Space 存下来,你会把内存撑爆的。

4.4 本章知识体系总览

下面这张图,把本章的核心逻辑串起来了。我建议你多看几遍,把每个节点的关系理清楚。

C-Space 与障碍物建模知识体系 物理世界 机器人 + 障碍物 映射 构型空间 (C-Space) 所有可能构型的集合 障碍空间 C-Obs 自由空间 C-Free 障碍物构建方法 Minkowski 和 / 膨胀 表示方式 显式 vs 隐式 碰撞检测 判断构型是否安全 轨迹规划:在 C-Free 中搜索路径 从起始构型到目标构型,避开所有 C-Obs 💡 核心思想:将物理空间中的几何碰撞问题,转化为 C-Space 中的点是否在自由空间中的问题。

4.5 工程中的实用技巧

最后,分享几个我在实际项目中积累的经验。

4.5.1 碰撞检测的加速

隐式表示依赖碰撞检测。如果每次判断都要做精确的几何计算,速度会很慢。

常用的加速方法:

  • 包围盒层级(BVH):先用粗粒度的包围盒做快速排除,再对可能碰撞的做精细检测。
  • 距离场:预计算障碍物的距离场,碰撞检测退化为查表 + 插值。
  • GPU 并行:对于大量构型的批量碰撞检测,用 GPU 可以大幅加速。

4.5.2 C-Space 的维度灾难

随着自由度增加,C-Space 的体积呈指数增长。这就是「维度灾难」。

对于高维 C-Space,显式表示基本不可行。隐式表示 + 采样规划(如 RRT、PRM)是主流方案。

注意:不要试图在 6D C-Space 上用栅格做 BFS 或 Dijkstra。栅格数量会多到离谱。我见过有人这么干,结果程序跑了三天还没初始化完。

4.5.3 特殊约束的处理

有些机器人有额外的约束,比如:

  • 关节限位:某些关节不能转到特定角度。这相当于在 C-Space 中切掉一部分区域。
  • 自碰撞:机器人的不同部分不能互相穿透。这也会在 C-Space 中产生障碍区域。
  • 动力学约束:机器人不能瞬间改变速度或方向。这限制了 C-Free 中路径的可行性。

处理这些约束时,我通常的做法是:先把几何碰撞的 C-Obs 建好,再把约束条件作为额外的「虚拟障碍物」加进去。这样,规划算法只需要处理一个统一的 C-Obs 集合,逻辑更清晰。

好了,这一章的内容就到这里。C-Space 是轨迹规划的基石,理解透彻了,后面学各种规划算法会轻松很多。