数学建模基础:栅格地图构建、障碍物表示、起点终点定义

做路径规划,第一步不是写算法,而是把现实世界翻译成计算机能懂的语言。这个过程,就是数学建模。

我个人习惯把这一步叫做「搭舞台」。舞台搭不好,再好的算法也白搭。今天我们就聊聊这个舞台怎么搭——栅格地图、障碍物表示、还有起点终点的定义。

1. 栅格地图:把连续世界变成离散格子

现实世界是连续的。机器人可以走任意角度,任意距离。但计算机处理连续数据太费劲了。怎么办?

说白了,就是「切格子」。把环境切成一个个小方格,每个格子要么能走,要么不能走。这就是栅格地图的核心思想。

核心概念:栅格地图(Grid Map)将环境离散化为 M×N 的网格单元。每个网格单元称为一个栅格(Cell),用坐标 (i, j) 唯一标识。

我在项目中遇到过一个问题:栅格大小怎么定?切太细,地图大、计算慢;切太粗,路径不精确。我的经验是——

  • 栅格尺寸 = 机器人直径 × 0.5,这是经验值
  • 如果环境复杂,可以缩小到机器人直径 × 0.3
  • 千万别小于机器人直径 × 0.2,否则计算量爆炸

举个例子。假设机器人直径 0.5 米,环境是 10m × 10m 的房间。栅格尺寸取 0.25 米,那么地图就是 40 × 40 = 1600 个格子。嗯,这个量级对蚁群算法来说很舒服。

2. 障碍物表示:哪些格子不能走?

地图建好了,接下来要告诉算法:哪些地方不能去。

障碍物表示其实就两种思路:

  1. 二值法:0 表示空闲,1 表示障碍。简单粗暴。
  2. 概率法:0~1 之间的值,表示该格子被占用的概率。更精细,但复杂。

做课程项目,我建议用二值法。为什么?因为蚁群算法本身已经够复杂了,别在输入数据上给自己加戏。

我的小技巧:障碍物周围做一层「膨胀处理」。就是把障碍物边缘的格子也标记为障碍。这样机器人不会贴着墙走,安全系数高很多。膨胀半径取机器人半径对应的栅格数就行。

我曾经在一个仓库项目里吃过亏。障碍物没做膨胀,结果规划的路径贴着货架走,机器人稍微偏一点就撞上了。后来加了 2 格膨胀,再也没出过问题。

代码实现很简单:

# 栅格地图初始化
import numpy as np

# 地图尺寸:10m x 10m,栅格尺寸0.25m
grid_size = 0.25
width = int(10 / grid_size)   # 40
height = int(10 / grid_size)  # 40

# 0=空闲, 1=障碍
grid_map = np.zeros((height, width), dtype=int)

# 手动设置障碍物(矩形区域)
grid_map[10:15, 20:25] = 1   # 一个5x5的障碍物

# 膨胀处理(简单实现)
def inflate_obstacles(map, radius=2):
    inflated = map.copy()
    obstacles = np.argwhere(map == 1)
    for (y, x) in obstacles:
        for dy in range(-radius, radius+1):
            for dx in range(-radius, radius+1):
                ny, nx = y+dy, x+dx
                if 0 <= ny < map.shape[0] and 0 <= nx < map.shape[1]:
                    inflated[ny, nx] = 1
    return inflated

grid_map = inflate_obstacles(grid_map, radius=2)

3. 起点终点定义:给算法一个目标

地图有了,障碍物标好了。接下来就是告诉算法:从哪出发,到哪去。

起点和终点在栅格地图里就是两个坐标 (sx, sy) 和 (ex, ey)。听起来简单,但有几个坑要注意。

注意:起点和终点不能落在障碍物上!这听起来是废话,但我见过太多人犯这个错。尤其是终点,有时候贴着墙放,膨胀后就被吞掉了。

我建议的做法:

  • 起点选在空旷区域,周围至少 3 格没有障碍
  • 终点同样要留出空间,方便机器人到达后调整姿态
  • 如果终点在狭窄通道里,考虑放宽终点判定条件

代码里怎么定义?

# 起点和终点定义
start = (5, 5)      # 第5行第5列
goal = (35, 35)     # 第35行第35列

# 检查合法性
def check_valid(point, map):
    y, x = point
    if map[y, x] == 1:
        print(f"错误:点 {point} 在障碍物上!")
        return False
    return True

assert check_valid(start, grid_map), "起点无效"
assert check_valid(goal, grid_map), "终点无效"

4. 知识体系总览

说了这么多,我把整个流程画了张图。你一看就明白了。

栅格地图建模知识体系 栅格地图构建 障碍物表示 起点终点定义 离散化:M×N 网格 栅格尺寸 = 机器人直径×0.5 坐标映射:现实↔栅格 二值法:0=空闲,1=障碍 概率法:0~1 占用概率 膨胀处理:安全边界 坐标 (sx, sy) 和 (ex, ey) 合法性检查:不在障碍上 周围留空:至少3格 输出:栅格地图矩阵 + 起点终点坐标 作为蚁群算法的输入数据

5. 避坑指南

最后,分享几个我踩过的坑。你遇到了可以直接跳过。

我曾经犯过的错:

  • 坐标搞反:栅格地图的行列和笛卡尔坐标的 xy 是反的。行对应 y,列对应 x。我一开始没注意,路径规划出来全是斜线。
  • 障碍物太密:如果障碍物占地图面积超过 60%,蚁群算法基本找不到路径。这时候要重新考虑环境布局。
  • 起点终点太近:距离小于 5 格时,算法可能直接「瞬移」过去,路径看起来很奇怪。建议至少保持 10 格以上距离。

嗯,数学建模这块就这些。说白了就是把现实问题翻译成矩阵和坐标。翻译对了,后面的算法才能跑得顺。翻译错了,debug 能让你怀疑人生。

我个人习惯是先把地图画出来可视化看看,确认没问题再往下走。你也不妨试试。


专注资料整理