MATLAB山地地形仿真与最高点到最低点最短路径(考虑地形高程)
注意:这里不是平面欧氏最短距离,是三维地形表面的最短路径(测地线),山地有高低起伏,路径要沿着山体表面走,不能直接穿山洞。

-
地形:用随机分形 / 高斯叠加模拟起伏不平山地曲面 z=f(x,y)z = f(x,y)z=f(x,y)
-
算法选用:Dijkstra 算法,把地形离散成网格图,每个网格点为图节点,相邻节点代价为地表实际行走距离,求解从最高海拔点到最低海拔点的最小代价路径。
完整 MATLAB 可直接运行代码
matlab
%% 1.生成起伏山地地形
clear; clc; close all;
n = 50; % 网格分辨率 n*n地形网格
[x,y] = meshgrid(linspace(0,20,n), linspace(0,20,n));
% 多个高斯山峰叠加模拟山地,制造高低起伏
z = zeros(size(x));
peak_num = 8;
rng(1); %固定随机种子,复现地形
for i = 1:peak_num
x0 = 20*rand();
y0 = 20*rand();
sigma = 1.5 + 3*rand();
amp = 2 + 6*rand();
z = z + amp*exp( -((x-x0).^2+(y-y0).^2)/(2*sigma^2) );
end
% 叠加小噪声模拟粗糙山地
z = z + 0.4*randn(size(z));
%% 找最高点、最低点的网格下标
[z_max,idx_max] = max(z(:));
[ix_start,iy_start] = ind2sub(size(z),idx_max);
[z_min,idx_min] = min(z(:));
[ix_end,iy_end] = ind2sub(size(z),idx_min);
fprintf("最高点(%d,%d),高程=%.2f \n",ix_start,iy_start,z_max);
fprintf("最低点(%d,%d),高程=%.2f \n",ix_end,iy_end,z_min);
%% 2.Dijkstra求解地形表面最短路径
% 将二维网格(i,j)映射成一维节点编号 node = (j-1)*n + i
idx2node = @(i,j) (j-1)*n + i;
node2idx = @(node) ind2sub([n,n],node);
N = n*n; %总节点数目
INF = 1e10;
dist = ones(N,1)*INF;
start_node = idx2node(ix_start, iy_start);
end_node = idx2node(ix_end, iy_end);
dist(start_node) = 0;
visited = false(N,1);
prev = zeros(N,1); %记录路径前驱,用于回溯路径
% 四邻域:上下左右;也可以改成8邻域允许斜向行走
neigh_d = [ -1,0; 1,0; 0,-1;0,1 ];
% neigh_d = [ -1,-1;-1,0;-1,1;0,-1;0,1;1,-1;1,0;1,1 ]; %8邻域
for iter = 1:N
% 选取未访问距离最小的节点u(Dijkstra核心)
minD = INF;
u = -1;
for v = 1:N
if(~visited(v) && dist(v) < minD)
minD = dist(v);
u = v;
end
end
if(u == -1) break;
if(u == end_node) break; %到达终点,提前退出
visited(u) = true;
[iu,ju] = node2idx(u);
for k = 1:size(neigh_d,1)
di = neigh_d(k,1);
dj = neigh_d(k,2);
iv = iu + di;
jv = ju + dj;
if(iv<1||iv>n||jv<1||jv>n) continue; %越界跳过
v = idx2node(iv,jv);
if(visited(v)), continue; end
% =========关键:计算山地地表实际行走距离=========
% 两点三维距离:dx,dy平面步长;dz高程差
dx = x(iu,ju)-x(iv,jv);
dy = y(iu,ju)-y(iv,jv);
dz = z(iu,ju)-z(iv,jv);
cost_uv = sqrt(dx^2 + dy^2 + dz^2); %地表欧式距离
if( dist(v) > dist(u)+cost_uv )
dist(v) = dist(u)+cost_uv;
prev(v) = u;
end
end
end
%% 回溯得到路径坐标
path_nodes = [];
cur = end_node;
while(cur ~= 0)
path_nodes = [cur; path_nodes];
cur = prev(cur);
end
%转成x,y,z坐标
px = []; py = []; pz = [];
for nd = path_nodes.'
[ii,jj] = node2idx(nd);
px = [px, x(ii,jj)];
py = [py, y(ii,jj)];
pz = [pz, z(ii,jj)];
end
fprintf("地形表面最短路径总长度:%.3f \n", dist(end_node));
%% 绘图可视化
figure('Color','w');
surf(x,y,z,'EdgeAlpha',0.1); hold on;
shading interp;
colormap jet;
%绘制路径
plot3(px,py,pz+0.15,'r-','LineWidth',2.5);
%标记起点最高点、终点最低点
plot3(x(ix_start,iy_start),y(ix_start,iy_start),z_max+0.2,'go','MarkerSize',8,'MarkerFaceColor','g');
plot3(x(ix_end,iy_end),y(ix_end,iy_end),z_min+0.2,'mo','MarkerSize',8,'MarkerFaceColor','m');
xlabel('X');ylabel('Y');zlabel('Z 高程');
title('山地地形 + 最高点→最低点地表最短路径(Dijkstra)');
view(3);
grid on;
运行说明
-
rng (1) 固定随机种子,每次生成同一片山地;注释掉可以生成不同起伏山地。
-
邻域:代码默认4 邻域(只能上下左右走);注释切换为 8 邻域可以斜向穿越。
-
cost_uv = Δx2+Δy2+Δz2\sqrt{\Delta x^2+\Delta y^2+\Delta z^2}Δx2+Δy2+Δz2 ,这是沿着山地表面行走的实际距离,不是平面距离,上坡下坡都会增加路径代价。
-
绿色圆点 = 地形最高点;品红色圆点 = 地形最低点;红色线条为算法算出来的地表最短路径。
算法原理详细说明
问题建模:山地→加权图
山地是连续曲面 z=f(x,y)z=f(x,y)z=f(x,y),计算机不能直接处理连续曲面,做离散网格化:
-
将 XY 平面切分成 n×nn\times nn×n 网格,每个网格交点 (i,j)(i,j)(i,j) 对应三维坐标 (xi,j,yi,j,zi,j)(x_{i,j},y_{i,j},z_{i,j})(xi,j,yi,j,zi,j),作为图的节点。
-
每个节点只和周围相邻节点连通(4 邻域 / 8 邻域)。
-
边的权重 cost:两个相邻节点之间沿着山体表面的三维欧氏距离:
关键点:高程差Δz\Delta zΔz会增加行走代价。爬上陡坡,两点平面很近,但地表路径代价会很大;下坡同样也要计入行走距离。
- 起点 S:网格上高程最大的点;终点 T:网格高程最小的点。
目标:求从 S 到 T,总边权之和最小的路径,也就是山地表面最短路径。
Dijkstra 算法原理(求解单源最短路径)
Dijkstra 适用于所有边权 ≥0 的图,本问题行走距离全部大于 0,满足条件。
核心变量
-
dist[]:dist(v)表示起点 S 到达节点 v 的当前已知最短距离;初始全部无穷大,仅起点 dist (S)=0。 -
visited[]:标记节点是否已经确定最短距离。 -
prev[]:记录路径前驱节点,算法结束后回溯,还原完整路径。
算法迭代步骤
-
在所有未被访问 的节点中,选出
dist值最小的节点 u。这个节点 u 的最短路径已经确定。 -
将 u 标记为已访问。
-
遍历 u 全部相邻节点 v:
尝试松弛:
if dist[v] > dist[u] + w(u→v)- 如果成立:说明经过 u 走到 v,距离更近。更新
dist[v]=dist[u]+w(u→v],记录prev[v]=u。
- 如果成立:说明经过 u 走到 v,距离更近。更新
-
重复 1‑3,直到终点 T 被选为最小节点,提前终止;或者全部节点遍历完毕。
-
路径回溯 :从终点 T 不断向前读取
prev[],反向得到完整路径点。
Dijkstra 数学保证
因为所有边权重非负,一旦节点 u 被选出(visited 标记),不可能后续再找到一条距离更小的路径到达 u,所以可以直接确定 u 的最短距离。
本模型局限
-
是网格离散近似,网格 n 越大结果越准,但计算速度变慢;n=50 兼顾效果与速度。
-
路径只能沿着网格点走,不是理论上连续曲面的精确测地线,属于工程数值近似。
-
本版本代价只考虑几何行走距离;如果你想模拟爬山体力消耗,可以修改 cost,上坡额外乘惩罚系数,模拟 "爬山比平路费力"。
拓展:A * 算法(加速版本)
Dijkstra 会盲目搜索整张图;A*加入启发函数加速,适合网格很大的场景。
A*估价函数:
-
g(n)g(n)g(n):起点到 n 的实际行走距离(等价 Dijkstra 的 dist)
-
h(n)h(n)h(n):启发估计,n 到终点的三维直线距离(不能大于真实最短距离,保证结果正确)
补充:如果想要真实连续曲面测地线
Dijkstra 网格是离散近似;MATLAB 自带 geodesic 可以计算三角网格曲面的严格测地线,需要把山地转成三角面片triangulation对象,计算量更大。Dijkstra 网格方法更容易理解,教学演示首选。
拓展修改示例:增加爬山体力惩罚
比如上坡增加代价:
matlab
dz = z(iu,ju)-z(iv,jv);
geo_dist = sqrt(dx^2+dy^2+dz^2);
if(dz < 0)
% dz<0:从u走到v高度上升(上坡)增加惩罚系数
cost_uv = geo_dist * 1.8;
else
cost_uv = geo_dist;
end
此时不再求几何最短,而是求体力消耗最小路径,路径会主动绕开陡峭山坡。