第三节:随机采样基础
好,咱们今天聊聊随机采样。说实话,这是整个基于采样路径规划的地基。地基打不牢,后面盖什么楼都得塌。我见过太多同学一上来就调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
最后说一句:采样是门手艺活。别指望一次调好,多试几次,多看看分布图,慢慢就有感觉了。