第三节:随机采样基础

好,咱们今天聊聊随机采样。说实话,这是整个基于采样路径规划的地基。地基打不牢,后面盖什么楼都得塌。我见过太多同学一上来就调RRT参数,结果路径奇形怪状,根源往往就是采样没搞明白。

3.1 均匀随机采样

均匀随机采样,说白了就是让采样点在整个空间里"雨露均沾"。每个位置被选中的概率都一样。你想想看,如果采样不均匀,有的地方密有的地方疏,那路径规划就容易漏掉关键通道。

在二维空间里,均匀采样很简单:

import random

def uniform_sample_2d(x_min, x_max, y_min, y_max):
    x = random.uniform(x_min, x_max)
    y = random.uniform(y_min, y_max)
    return (x, y)

但在高维空间,事情就没那么简单了。我记得有一次做机械臂的路径规划,7个自由度,采样空间是7维的。当时我直接用均匀采样,结果发现采样点全挤在边界附近。为什么会这样?因为高维空间的"体积"大部分集中在表面附近。这个现象叫"维度诅咒",后面我们会专门讲。

核心要点:均匀采样保证无偏性,但不保证覆盖率。在高维空间,均匀采样反而会让中心区域稀疏。

3.2 伪随机数生成器

计算机里其实没有真正的随机数。我们用的都是伪随机数生成器(PRNG)。它用一个种子(seed)初始化,然后通过数学公式生成一串看起来随机的数列。

我个人习惯用Python的random模块,或者NumPy的numpy.random。但要注意,不同PRNG的质量差别很大。

生成器 周期 质量 适用场景
Python random (Mersenne Twister) 2^19937 - 1 一般路径规划
NumPy default_rng (PCG64) 2^128 更高 大规模采样
os.urandom N/A 真随机 安全相关

小技巧:调试时固定种子,比如random.seed(42)。这样每次运行结果一样,方便找bug。我吃过这个亏——没固定种子,调试了一下午才发现是随机性导致的假bug。

3.3 采样密度与覆盖率

采样密度,就是单位空间里有多少个采样点。覆盖率,是采样点覆盖了多少空间区域。这两个概念容易混淆,我帮你理一理。

采样密度高,不一定覆盖率就好。举个例子:你把1000个点全撒在同一个角落,密度极高,但覆盖率极差。反过来,100个点均匀分布,密度低,但覆盖率可能更好。

在路径规划里,我们真正关心的是覆盖率。尤其是狭窄通道——那种刚好能通过机器人的缝隙。如果采样点没覆盖到那里,路径就找不到。

避坑指南:我曾经做一个狭窄通道的规划问题,通道宽度只有机器人直径的1.2倍。我用了10000个采样点,结果还是找不到路径。后来发现,采样点虽然多,但全落在开阔区域,狭窄通道里一个点都没有。这就是典型的"高密度低覆盖率"。

怎么解决?可以用启发式采样。比如在障碍物边界附近增加采样密度,或者在已找到路径的附近区域加密采样。这些方法我们后面会详细讲。

3.4 实践建议

嗯,这里给几条我踩坑踩出来的经验:

  • 先做小规模测试:别一上来就撒几万个点。先用几百个点看看覆盖率,调好了再放大。
  • 可视化采样分布:把采样点画出来,肉眼看看有没有明显空洞。我习惯用matplotlib的scatter图。
  • 注意边界处理:很多算法默认采样范围是[0,1],但实际空间可能有不同尺度。记得归一化。
  • 考虑确定性采样:如果空间不大,可以用网格采样或Halton序列,比纯随机更均匀。
# 一个简单的覆盖率检查函数
def check_coverage(samples, grid_resolution=0.1):
    """检查采样点对空间的覆盖情况"""
    covered_cells = set()
    for x, y in samples:
        cell_x = int(x / grid_resolution)
        cell_y = int(y / grid_resolution)
        covered_cells.add((cell_x, cell_y))
    coverage_ratio = len(covered_cells) / (1.0 / grid_resolution)**2
    return coverage_ratio

最后说一句:采样是门手艺活。别指望一次调好,多试几次,多看看分布图,慢慢就有感觉了。