ONE3 零基础复现指南(从"只会开 Excel"到交出一份报告)
0 / 0勾选进度

ONE3 零基础复现指南(从"只会开 Excel"到交出一份报告)

这份指南是一条线,从头到尾照着做就能出成果。不是参考手册,不需要跳着看。

你需要的基础:会双击文件、会在文件夹里找东西、会按 Ctrl+S。不需要:会 Python、会算法、会图论、会 Linux。

每一步都写了"你应该看到什么"。看到了就往下走,没看到就看那一步下面的"失败时多半是"。

一共 8 章 + 2 个附录。全部做完大约 2 小时(其中装环境 40 分钟,跑程序 1 分钟,剩下都是读和写)。

目录


0 你要做成什么样#

0.1 一句话版本#

给你两张 Excel 格子地图,让你在格子上画一条从起点到终点的路;小地图一程飞得完,大地图 47 km 一程飞不完,得在中间找几个"补给点"把它切开,每段不超过 15 km;最后把这些路画成一个网页、导成几张表,写进报告。

四件交付物:

交什么长什么样谁生成
① 成果网页out/small.html、out/large_length.html(双击用浏览器打开)程序自动生成
② 折点表out/small_waypoints.csv、out/large_waypoints_length.csv程序自动生成
③ 指标数据out/metrics_small.json、out/metrics_large.json(报告里的数字从这里抄)程序自动生成
④ 实验报告按 ONE3_无人机路径规划_实验报告模板.docx 填你自己写

现在先别跑任何命令。 仓库里已经有一份跑好的结果,你先看看"成品"长什么样。

0.2 打开 out/small.html 看成品#

在文件夹 out\ 里找到 small.html,双击(默认会用 Edge 或 Chrome 打开)。

你会看到(从上到下):

第一行:一个 22 号字的大标题

小地图 A→C 单程规划(A* + 视线平滑)

下面一行灰色的副标题:30×30 栅格,cell=0.5 km;红=辐射区(禁飞)、灰=废墟、深蓝=裂谷;热力=危险加权。

标题里的 "A*" 是什么,第 4 章才讲。现在你只要知道它是"一条线的名字"。

第二块:9 张指标卡,横着排开,每张卡上有一个灰小标题、一个大数字、一行更小的说明:

┌──────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
│ 路径总长      │ 直线距离      │ 绕行比        │ 风险分        │ 宏弯告警      │
│ 14.32 km     │ 12.09 km     │ 1.185        │ 0            │ 0 次         │
│ 平滑后折线长度 │ S→E 欧氏距离  │ 总长 / 直线   │ 沿途危险加权和 │ 偏转角 > 45°  │
├──────────────┼──────────────┼──────────────┼──────────────┼──────────────┤
│ 折点数        │ 链路损耗      │ 熔接损耗      │ 总损耗        │              │
│ 5            │ 2.86 dB      │ 0.00 dB      │ 2.86 dB      │              │
│ 逐格路径 26 格 │ 0.2 dB/km    │ 0 个熔接点    │ 链路 + 熔接   │              │
└──────────────┴──────────────┴──────────────┴──────────────┴──────────────┘

第三块:左边一张正方形地图(30×30 个彩色小格),右边一张表加一个黄色备注框。

地图上你能认出的东西:

颜色代表小地图上的位置
米白可达(普通空地)绝大多数格子
红色辐射区,禁飞中间偏左一团 26 格
灰色废墟,不可越上方 3×3 一小块 9 格
深蓝裂谷,不可越左下 6×2 长条 12 格
绿色起点 S左边中部 (2,12)
蓝色终点 E右边偏下 (26,15)
橙红晕危险热力围着红/灰/深蓝格往外散一圈
蓝色粗线规划出来的航线从 S 先向右,再斜着往上绕过红团,再下来到 E

地图下方有 4 个勾选框:风险热力、栅格线、中继点标注、搜索足迹。鼠标在地图上移动,会弹出小标签,写着这一格的 x / y / 编码 / 危险加权——这一条一定要试,后面核对全靠它。

右边那张表 5 行,列是:序号 | x | y | 累计(km) | 段长(km) | 风险 | 宏弯。黄色备注框里有 4 条建模说明(坐标约定、危险加权怎么算、路径长度怎么算、宏弯怎么判)。

0.3 大地图那个页面有什么不同#

打开 out/large_length.html:地图变成 100×100(格子很小,一格只有 6 像素),航线断成 4 段、每段一个颜色,中间标着 3 个橙色 R;右边那张表的标题从"折点表"变成"分段表",列是段 | 起 → 止 | 段长(km) | 累计(km) | ≤15km | 风险分 | 宏弯;指标卡里"折点数"那张换成了"分段 / 中继 = 4 段 / 中继点 3 个"。

0.4 什么时候算"你做对了"#

跑完之后,你的数字应该和第 6 章的基准表对得上:

  • 小地图:路径总长 14.32 km、直线 12.09 km、绕行比 1.185、风险分 0
  • 大地图:总长 47.16 km(4 段)、3 个中继点 R41 / R44 / R42、总损耗 10.33 dB

差一点点(比如 14.3 和 14.32)是四舍五入,没关系。差很多(比如 12.35 或 47.2)说明你漏了某一步,看第 4 章最后那个"反直觉对照"。

做完请勾选


1 先打开地图文件,把它看明白#

这一章完全不讲算法。你只需要会"看 Excel"。看不懂这一章,后面的代价、最短路全是空中楼阁——所以本章最慢。

1.1 文件在哪,有两份一样的#

ONE3无人机路径规划任务与数据\
├── one3_map_small_grid.xlsx        ← 根目录也有一份(和 data\ 里的完全一样)
├── one3_map_large_grid.xlsx        ← 同上
└── data\
    ├── one3_map_small_grid.xlsx    ← 程序实际读的是这一份
    ├── one3_map_large_grid.xlsx
    └── map_config.json             ← 约束参数(15 km、0.2 dB/km 那些)

两份是同一次拷贝(MD5 完全一致),所以你在 Excel 里打开哪一份看都一样;但程序只读 data\ 里的。你要改地图做实验,改 data\ 里的那份才生效。

文件很小:小地图 13 KB,大地图 39 KB。用 Excel / WPS / LibreOffice 都能打开,打不开也不影响——第 2 章跑起来之后,程序会用字符画把整张地图打印在屏幕上。

1.2 用 Excel 打开之后,你看到的是什么#

打开 one3_map_small_grid.xlsx,你会看到一个纯数字表格,没有图、没有颜色。左上角 6 行 6 列长这样(真实内容):

      A     B  C  D  E  F  ...
 1   y\x    0  1  2  3  4  ...
 2     0    0  0  0  0  0  ...
 3     1    0  0  0  0  0  ...
 4     2    0  0  0  0  0  ...
 5     3    0  0  0  0  0  ...
 6     4    0  0  0  0  0  ...
 7     5    0  0  0  0  0  ...

先记住三件事:

  1. A1 那一格写的是 y\x(字母 y、反斜杠、字母 x)。这是一句提示:"往右是 x,往下是 y"。它占掉了 A 列和第 1 行。
  2. 第 1 行(B1、C1……)是 x 的编号:0、1、2、3…… 一直到 29(小地图),到大 99(大地图)。
  3. A 列(A2、A3……)是 y 的编号:0、1、2、3…… 一直到 29(小地图),到大 99(大地图)。

所以:

  • 小地图的数据区是 B2 到 AE31,一共 30 列 × 30 行 = 900 个格子。
  • 大地图的数据区是 B2 到 CW101,一共 100 × 100 = 10000 个格子。

1.3 核心换算:Excel 地址 ↔ (x, y)#

这是整份指南最值钱的一张表。背下来。

x = 列号 - 2        (B 列是 x=0,所以列号减 2)
y = 行号 - 2        (第 2 行是 y=0,所以行号减 2)

反过来:
Excel 地址 = 字母(x + 2) + 数字(y + 2)

举三个例子,请你现在就拿笔算一遍:

你算的过程结果
第 14 行、D 列是哪个格子?D 是第 4 列 → x = 4-2 = 2;行 14 → y = 14-2 = 12(2, 12)
(11, 9) 在 Excel 哪儿?列 = 11+2 = 13 → 第 13 列是 M;行 = 9+2 = 11M11
(26, 15) 在 Excel 哪儿?列 = 26+2 = 28 → 第 28 列是 AB;行 = 15+2 = 17AB17

第 28 列为什么是 AB?因为 A–Z 是 26 列,第 27 列又是 A 开头 = AA,第 28 列 = AB。用 Excel 自己验证最快:在任意一格按 Ctrl+G(定位),输入 AB17 回车,看光标跳到哪。

1.4 五个必看的格子(小地图点名核对)#

下面这张表请一个一个在 Excel 里按地址找过去,并用眼睛确认那一格的数字。找到了,说明你看懂了坐标系;找不到,说明你算错了列。

Excel 地址坐标 (x, y)里面的数字是什么意思为什么挑这一格
D14(2, 12)5起点 S(旧机房 A)全部航线的出发点。注意它在第 13 行、靠左,不在左上角
AB17(26, 15)6终点 E(邻近驻扎点 C)终点几乎在正东,只比起点低 3 格
R11(16, 9)2废墟(不可越)3×3 废墟块的左上角,它右边和下面还有 8 个 2
N17(12, 15)1辐射区(禁飞)26 格红团的中心。它在起点的右下方,正好卡在 S→E 的直线上
J22(8, 20)3裂谷(不可越)6×2 深蓝长条的最左端,在左下角,离航线很远
L14(10, 12)0可达,但很贵数字是 0(能飞),可它右边就是辐射区,危险加权 2,代价乘 3
M14(11, 12)0可达,更贵危险加权 4,代价乘 5。看,编码 0 不等于"随便飞"
S10(17, 8)0可达,废墟正上方危险加权 3(左、右、下三面都是废墟),代价乘 4

顺手再验一个:B15 是 (0, 13),数字 0,危险加权 0——它是地图最西边的空地,这次航线根本不去那里,但你可以拿它当"干净格子"的参照。

1.5 方向:y = 0 在北,codes[y][x]#

任务书约定(也是全世界的图像坐标约定里最容易搞错的一条):

          x 向东增大(Excel 列往右)→
   y=0  ┌───────────────────────────┐
   北   │  y 小的行 = 北 = 屏幕上方   │
   ↓    │                           │
   南   │  y 大的行 = 南 = 屏幕下方   │
        └───────────────────────────┘
  • y 从北往南增大:第 2 行(y=0)是地图最北边,第 31 行(y=29)是最南边。
  • x 从西往东增大:B 列(x=0)是最西边,AE 列(x=29)是最东边。

所以第 y 行、第 x 列的编码要写成 codes[y][x](先 y 后 x),不是 codes[x][y]。写反了,地图会沿对角线翻个身——而它看起来"也能走",只是障碍位置全错,等于白做。

这一条是本项目第一大翻车点。检验方法:打印出来的字符画里,废墟块(###)必须在上方、裂谷条(////)必须在左下。如果废墟跑到下面去了,你翻错了。

1.6 编码表:0–7 每个数字是什么意思(人话版)#

任务书给了一张"表 1",但它只写了名字。下面这张是"能干什么"版本。

编码名字能不能飞上去?会不会给旁边加代价?假如你站在这个格子…
0可达能不加脚下是普通土地。但要看邻居:旁边有辐射,你这一格就变贵(例:M14 是 0,加权 4)
1辐射区不能(禁飞)给周围 8 格每格 +2站不上去,想都别想。紧挨它的格子代价 ×3(1+2)
2废墟不能(不可越)给周围 8 格每格 +1同上。旁边一格代价 ×2(1+1)
3裂谷不能(不可越)给周围 8 格每格 +1同上,代价 ×2
4(表 1 里没有这个值)——整张地图搜不到 4。程序遇到 4 会直接报错 出现未知编码 4——说明有人手改坏了地图
5起点 S能不给自己加,但吃邻居的加权你就是从这里出发。小地图在 D14
6终点 E能同上到这里就算完成。小地图在 AB17
7中继 R能同上充电 + 熔纤的候选停靠点。小地图 0 个,大地图 85 个

补充两条:

  • 危险格自己不算自己的加权(反正飞不进去),它只"污染"周围的可通行格。
  • 加权是累加的:一个格子如果同时挨着 1 个辐射(+2)和 1 个废墟(+1),它就是 3,代价乘 4。不是取最大值。

1.7 小地图完整字符画(对照 Excel 用)#

这张是程序算出来的(第 2 章你会在屏幕上看到一模一样的),X=辐射,#=废墟,/=裂谷,S=起点,E=终点,.=可达:

        x=0 向东 →
        000000000011111111112222222222
        012345678901234567890123456789
   y=0  ..............................
   y=1  ..............................
   y=2  ..............................
   y=3  ..............................
   y=4  ..............................
   y=5  ..............................
   y=6  ..............................
   y=7  ..............................
   y=8  ..............................
   y=9  ................###...........
  y=10  ................###...........   ← 废墟 9 格:x16–18, y9–11
  y=11  ................###...........
  y=12  ..S...........................   ← 起点 S 在 (2,12) = Excel D14
  y=13  ...........XXXX...............
  y=14  ..........XXXXXX..............   ← 辐射 26 格:x10–15, y13–17
  y=15  ..........XXXXXX..........E...   ← 终点 E 在 (26,15) = Excel AB17
  y=16  ..........XXXXXX..............
  y=17  ...........XXXX...............
  y=18  ..............................
  y=19  ..............................
  y=20  ........//////................   ← 裂谷 12 格:x8–13, y20–21
  y=21  ........//////................
  y=22  ..............................
   …    (y22–y29 全是点,没有障碍)

数一下:S 1 格 + E 1 格 + X 26 格 + # 9 格 + / 12 格 = 49 格,剩下 851 格是可达的。加起来 900 = 30×30 ✓。这个"加起来等于总数"的习惯要养成,它是发现读图错误最快的方法。

1.8 大地图:100×100,多了 85 个中继点和一个"洞"#

大地图和小地图规则完全一样,只是更大、更复杂:

小地图大地图
尺寸30 × 30 = 900 格100 × 100 = 10000 格
Excel 数据区B2:AE31B2:CW101
起点(2,12) = D14(3,50) = E52
终点(26,15) = AB17(97,50) = CU52
直线距离12.09 km47.00 km
辐射区26381 格 + 1 个空格(见下)
废墟9205
裂谷12139
中继候选 R085

中继点长什么样? 就是编码 7 的格子,在 Excel 里是孤零零的一个 7,在字符画里是 R。它们不连成片,散落在地图各处(y 从 2 到 97,x 从 3 到 98),因为它们是"巡逻驻扎点"——本来就该散布在荒原上。

程序给它们编号 R01…R85:从上往下、同一行从左往右。所以 R01 在最北边 (73,2),R85 在最南边 (96,97)。最常用的三个是(记住这张表,第 5 章要用):

编号坐标Excel 地址在哪儿
R41(27, 50)AC52裂谷带西口,正对着走廊入口
R44(49, 53)AY55裂谷带南侧,走廊正中间
R42(72, 51)BV53裂谷带东口,马上要回主方向的拐点

护着起点一带的放大图(x=24–39, y=45–52;. 可达,/ 裂谷,X 辐射,R 中继):

        x=24 →                      x=39
        4444444445555555
        4567890123456789
  y=45  ................
  y=46  ............../X     ← 这个 X 就是那个"洞",见 1.8.2
  y=47  .........../////
  y=48  ..........//////
  y=49  ..............//
  y=50  ...R............     ← R41 = (27,50):它西边是空走廊,东边就是裂谷北岸
  y=51  ................
  y=52  ................

看明白一件事:裂谷从 y=47 往南铺开,一路铺到 y=50。所以从西往东飞,必须在 y≈51–56 这条空走廊里走;R41 就卡在"必须往南拐"的那个点上。这就是第 5 章"为什么选中继点"的答案,现在先用眼睛记住地形。

1.8.1 地形大格局(为什么这条走廊能过)#

  • 裂谷主带:x≈34–66、y≈45–50 的一片"透镜形",把地图中间横向切开——你不能穿过去,只能从它南边(y≥51)或北边(y≤44)绕。
  • 辐射块 1:x≈29–40、y≈30–42,在走廊北边,堵死了"从北边绕"的近路。
  • 辐射块 2:x≈53–68、y≈57–69,在走廊南边,限制你往下绕的深度。
  • 裂谷墙:x=16 一条从 y=52 一直往南的单格竖线,把西半张图南北隔开。

于是 y≈51–56、x≈25–95 这段出现了一条连续 4–6 格宽、0 风险的干净走廊。最终航线就走在这条走廊里,风险分才能是 0。

1.8.2 空单元格 (39, 46) 是什么,怎么处理#

打开大地图 Excel,跳到 AO48(x = 41-2 = 39,y = 48-2 = 46),你会发现这一格是空的——不是 0,是彻底没填。

它的四周是:左 38,46 = 裂谷、下 39,47 = 裂谷、右 40,46 = 裂谷、上 39,45 = 可达。也就是说,它正好卡在裂谷带里,是裂谷中间一个 1 格宽的"洞"。

三种处理方式,后果完全不同:

你怎么处理后果能交吗
当成 0(可达)裂谷带上凭空出现一个缺口,算法完全可以从这里穿过裂谷。图上看着"通了",实际是作弊❌ 会被判"绕障不完整"
当成 1(辐射区/不可入) ← 本项目做法洞被补上,裂谷完整;这个格子记进 metrics_large.json 的 blank_cells 清单✅
让程序崩掉当然也不行❌

程序实际做法:读图时把这个空格记进 blank_cells 列表,按不可入处理,然后 run_large.py 在屏幕上打印一行告警:

⚠ 空单元格(已按不可入处理):[(39, 46)] ← 下发地图的一处数据缺口

说实话:这个洞在本次最优方案里没被用到(走廊在 y≈50–53,离它 4 格远,把它改成"可通行"算出来的方案一模一样,还是 47.157 km)。但你不能指望运气——换张地图、改个上限,它立刻就是一条非法捷径。正确做法是显式处理并记录下来,报告里要写一句"空单元格 AO48 按不可入处理"。

做完请勾选


2 装环境,跑通小地图#

这一章只做一件事:让 out/small.html 从"仓库里别人跑好的"变成"你自己跑出来的"。每一步都写了你应该看到什么。看不到就看下面那行"失败时多半是"。

2.1 第一步:先检查你电脑上的 Python 是不是"假的"#

按 Win + R,输入 cmd,回车,会弹出一个黑窗口。在黑窗口里敲:

where python

你可能看到两种结果:

✅ 正常的(装过官方 Python):

C:\Users\你的用户名\AppData\Local\Programs\Python\Python312\python.exe

❌ 假的(Windows 自带的"商店占位符"):

C:\Users\你的用户名\AppData\Local\Microsoft\WindowsApps\python.exe

看到 WindowsApps 就是假的。再敲一句验证:

python -c "print(1)"

假 Python 的表现很怪:没有输出,直接回到命令行(退出码 49)。它其实是个"广告",一点就把你导向 Microsoft Store。用它建环境、装包,全都会失败。

失败时多半是:'python' 不是内部或外部命令 → 说明根本没装,直接进 2.2。

2.2 第二步:装一个真的 Python(推荐 3.12)#

能上网的话,一条命令搞定(在同一个黑窗口里):

winget install -e --id Python.Python.3.12

装到一半会问你是否同意协议,输 Y 回车。装完关掉黑窗口再重开(否则 PATH 不生效)。

不能上网 / winget 不给用:去 python.org 下 Windows 版 python-3.12.x-amd64.exe,双击安装时务必勾上最下面那个 Add python.exe to PATH,然后 Install Now。

装完再验证一次:

where python
python -V

你应该看到:

C:\Users\你的用户名\AppData\Local\Programs\Python\Python312\python.exe
Python 3.12.10

失败时多半是:where python 还是指向 WindowsApps → 装了但没进 PATH,重新运行安装包选 Modify 勾上 PATH,或者干脆用 2.3 里的绝对路径。

2.3 第三步:进项目目录,建一个"独立小屋"(虚拟环境)#

虚拟环境(venv)的意思是:给这个项目单独一套 Python 包,不污染系统。

先 cd 到项目目录(路径里有中文和空格都没关系,但一定要加引号):

cd /d "C:\Users\limoon\Documents\ONE3无人机路径规划任务与数据"

换成你自己的路径。怎么知道对了?敲 dir,你应该看到 one3、scripts、data、out 这几个文件夹。

然后建环境:

python -m venv .venv

这一步没有任何输出,是正常的。 它要跑 10–30 秒。跑完检查:

dir .venv\Scripts\python.exe

你应该看到:

2025/10/20  11:42           274,424 python.exe

本项目仓库里已经有 .venv 了(就是上面这个文件)。所以你其实可以跳过这一步。但你要在别的电脑上/重新做一次,就得自己建。

失败时多半是:Unable to create process / 建到一半报错 → 你的 Python 是假的,回 2.1;或项目目录在 OneDrive 里被同步锁住,换个目录再试。

2.4 第四步:确认这个"小屋"里的 Python 能用#

从现在开始,不要再用 python 这个词,一律写全路径 .venv\Scripts\python.exe。这样永远不会用到那个假 Python。

.venv\Scripts\python.exe -V

你应该看到:

Python 3.12.10

(版本号可能不同,只要是 3.10–3.13 都行。)

失败时多半是:系统找不到指定的路径 → 你的 cd 不在项目根目录,或者 .venv 没建成功。

2.5 第五步(可选):装 matplotlib,只为了导出 PNG#

核心流程零第三方依赖(读 xlsx、算最短路、出 HTML/CSV/JSON 全用标准库)。只有想导出 out/small.png 这样的报告插图时才需要 matplotlib。装不装都能交作业。

.venv\Scripts\python.exe -m pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple

你应该看到最后一行类似:

Successfully installed contourpy-1.x cycler-0.12 ... matplotlib-3.9.x numpy-2.x ...

失败时多半是:卡在下载 / 超时 → 去掉 -i ... 换回默认源,或者干脆跳过(不影响交作业)。

2.6 第六步:跑小地图(关键一步)#

还在项目根目录的黑窗口里,敲:

.venv\Scripts\python.exe scripts\run_small.py

你应该看到(真实输出,前两段):

========================================================================
小地图建模检查
========================================================================
文件:one3_map_small_grid.xlsx
栅格:30 x 30,cell = 0.5 km
起点 S = (2, 12)  终点 E = (26, 15)  中继候选 = 0
编码统计:可达 851,废墟 9,起点S 1,辐射区 26,终点E 1,裂谷 12

ASCII 预览(行=y 向南递增,列=x 向东递增;与任务书方向一致)
    000000000011111111112222222222
    012345678901234567890123456789
  0 ..............................
  1 ..............................
  ...(中间 28 行,和 1.7 那张字符画一模一样)...
 29 ..............................

========================================================================
小地图求解结果
========================================================================
A* 扩张栅格 178 个,耗时 0.9 ms
逐格路径 26 格 → 平滑后 5 个折点
路径总长 14.325 km(逐格累加 14.985 km)
直线距离 12.093 km,绕行比 1.185
风险分 0,宏弯告警 0 次(内角口径 0 次)
对照:未平滑的逐格锯齿路径有 0 次宏弯告警 → 平滑后 0 次
最大偏转角 45.0°,折点 3 个
链路损耗 2.865 dB(无熔接)
对照(只按长度最优):12.354 km / 风险分 33 / 告警 0 次  → 多花 +1.971 km 换掉 +33 风险分

已生成:out/small.html、out/small_waypoints.csv、out/metrics_small.json

| 序号 | x | y | 累计(km) | 段长(km) | 宏弯 |
|---|---|---|---|---|---|
| 1 | 2 | 12 | 0.000 | 0.000 | - |  |
| 2 | 15 | 7 | 6.964 | 6.964 | - |  |
| 3 | 19 | 7 | 8.964 | 2.000 | - |  |
| 4 | 25 | 13 | 13.207 | 4.243 | - |  |
| 5 | 26 | 15 | 14.325 | 1.118 | - |  |

(耗时那行每次都不一样,0.5–3 ms 都正常,别纠结。)

同时,out\ 文件夹里多了 3 个文件:

文件大小量级是什么
out/small.html~18 KB成果网页,双击看
out/small_waypoints.csv< 1 KB折点表,Excel 能直接开
out/metrics_small.json~2 KB全部数字,报告从这里抄

这一步失败了?对照下表

屏幕上的红字多半是一行解决
系统找不到指定的路径你不在项目根目录cd /d "项目完整路径",然后 dir 确认能看到 scripts
can't open file ... run_small.py同上,或文件名打错用 Tab 补全,别手打
ModuleNotFoundError: No module named 'one3'你在别的地方运行了一个拷贝出来的脚本回项目根目录跑,别把脚本单独复制出去
FileNotFoundError: 找不到地图文件 ...data\ 里的 xlsx 丢了/被改名从根目录把 xlsx 拷回 data\
UnicodeEncodeError: 'gbk' codec你的黑窗口是 GBK,程序里的 ⚠ / → 打不出来先 chcp 65001 再跑;或改用 Windows Terminal
中文全是 锟斤拷 或乱码同上chcp 65001(本项目脚本已自带 UTF-8 处理,出现这行说明你改了脚本)

2.7 第七步:打开你自己的成果页#

start out\small.html

(或者在文件夹里双击 out\small.html。)

你应该看到:和第 0.2 节描述的一模一样——标题、9 张指标卡(路径总长 14.32 km)、彩色地图、5 行折点表。区别只有一个:这次是你自己跑出来的。

失败时多半是:浏览器一片空白 → 你用了 IE 或者某个老旧内核浏览器;用 Edge / Chrome 打开。文件被 Excel 抢了默认打开方式 → 右键 → 打开方式 → 浏览器。

2.8 顺手把大地图也跑了#

.venv\Scripts\python.exe scripts\run_large.py

第一次跑会花 2–4 秒(要算 87 个点两两之间的最短路),屏幕上会滚动中继层连边 200/799、400/799……这是正常进度。

你应该看到(结尾部分):

中继层有效边 688 条(其余点对超出单段上限或无通路),用时 1.7s

========================================================================
第二级:中继层选链(三种目标)
========================================================================
  length   段数 4  中继 ['R41', 'R44', 'R42']  总长 47.16 km  风险 0  告警 0  总损耗 10.33 dB
  splices  段数 4  中继 ['R41', 'R44', 'R42']  总长 47.16 km  风险 0  告警 0  总损耗 10.33 dB
  loss     段数 4  中继 ['R41', 'R44', 'R42']  总长 47.16 km  风险 0  告警 0  总损耗 10.33 dB

主方案 中继点:[('R41', (27, 50)), ('R44', (49, 53)), ('R42', (72, 51))]
已生成 out/large_*.html、out/large_segments_*.csv、out/metrics_large.json

并在最前面看到这一行告警(它是对的,别慌):

⚠ 空单元格(已按不可入处理):[(39, 46)] ← 下发地图的一处数据缺口

out\ 里新增:large_length.html(主方案)、large_splices.html、large_loss.html、large_segments_length.csv、large_segments_splices.csv、large_segments_loss.csv、large_waypoints_*.csv(3 份)、metrics_large.json。

只想跑一次全部实验?

.venv\Scripts\python.exe scripts\run_all.py

它会依次跑 7 个脚本 + 28 项自检,约 1 分钟,屏幕上最后应该是总耗时 ... s;产出目录:...\out,且中间没有 ✗。

做完请勾选


3 地图怎么变成"代价"#

到这里你还没看见一个算法。这一章讲"给格子标价"。标完价,"最短路"才有意义——因为最短路找的是"最便宜",不是"最短"。

3.1 一格的尺寸:横竖 0.5 km,斜着 0.7071 km#

data/map_config.json 里写着 "cell_km": 0.5:一格边长 0.5 km。

从一格的中心走到相邻一格的中心,距离是:

怎么走距离怎么算出来的
上 / 下 / 左 / 右(正方向)0.500 km1 格 × 0.5
四个斜角(东北/东南/西南/西北)0.7071 km√2 × 0.5 ≈ 1.4142 × 0.5

为什么允许斜着走?因为把 8 个邻居都算进来(叫"8 邻域"),航线才不会被逼成"先横再竖"的直角台阶。只有 4 个邻居的话,路径会是方块状的,实际飞不出来。

长度就这样累加:小地图那条路一共 26 个格点,走 25 步——其中 13 步直行 + 12 步斜行(后面 4.5 节给你逐段算清):13 × 0.5 + 12 × 0.7071 = 6.5 + 8.485 = 14.985 km。这个数你在屏幕上见过:逐格累加 14.985 km。

3.2 危险加权:给"危险旁边的格子"标价#

规则只有三行:

  1. 看一个能飞的格子,数它周围 8 个邻居里有几个危险格。
  2. 每个辐射区邻居 +2,每个废墟或裂谷邻居 +1。
  3. 全部加起来,就是这一格的"危险加权 w"。

然后:

走进这一格的代价 = 距离 × (1 + w)

画成图是这样(小地图 x=0–20、y=8–21 那一片,数字就是 w,X/#// 是危险格本身):

        x=0 向东 →
        000000000011111111112222222222
        012345678901234567890123456789
   y=8  000000000000000123210.........   ← 废墟正上方:1 2 3 2 1
   y=9  0000000000000002###20.........   ← 废墟本身(不可入)
  y=10  0000000000000003###30.........
  y=11  0000000000000002###20.........
  y=12  00S000000024664323210.........   ← 起点的正东方向,越靠近红团越贵
  y=13  00000000026XXXX620000.........   ← 辐射区(不可入)
  y=14  0000000004XXXXXX40000.........
  y=15  0000000006XXXXXX60000.........
  y=16  0000000004XXXXXX40000.........
  y=17  00000000026XXXX620000.........
  y=18  000000000024664200000.........
  y=19  000000012333321000000.........   ← 裂谷上方
  y=20  00000002//////2000000.........   ← 裂谷本身(不可入)

三条能直接看出来的规律(这几条是本章的精华):

  1. 贴着危险格最贵:y=15 行、红团正左方那格 (9,15) 是 6——它同时挨着 3 个辐射格(+2×3)。系数 1+6 = 7 倍代价。而绕一格才多花 0.5 km,绝对值。
  2. 对称会破:y=12 这一行,x=9 是 0(免费),x=10 是 2(×3),x=11 是 4(×5),x=12 是 6(×7)。所以"沿 y=12 一直往东"在第 10 列就开始变贵。
  3. 往北爬一格就免费了:x=13 在 y=12 是 6(挨着 3 个辐射格),往上挪到 y=11 立刻变成 0(完全干净)。于是最优解会斜着往北爬,爬到 y=7 那条干净走廊——这正是 0.2 节那张图里航线的形状。

3.3 手算三个格子(拿笔跟着算)#

站在哪去哪个邻居距离目标格 w系数 1+w这一步代价
(9, 12) 空地→ (10, 12) 正东0.500231.500 km
(9, 12) 空地→ (10, 11) 东北斜0.7071010.707 km
(9, 12) 空地→ (10, 13) 东南斜—目标格是辐射区—不能进(硬禁飞)

同样的"向东一格",走上面(斜)比走中间(直)便宜一半还多:0.707 对 1.500。这就是为什么路径会往上拐。

再看一个"编码 0 但很贵"的:

站在哪去哪个邻居距离目标格w代价
(10, 11)→ (11, 12) 东南斜0.70710(能飞!)40.7071 × 5 = 3.536 km

绕一格 0.5 km 就能省下 2.8 km 的代价。所以别拿编码 0 当"免费"。

3.4 四条硬约束(不是"贵",是"不许")#

约束在哪看违反了会怎样
危险格完全不能进编码 1/2/3搜索直接跳过,永远走不到
不许斜穿夹角:两个正交邻格都不能进时,不能从中间斜切过去"allow_corner_cut": false线缆现实中会蹭到障碍角,所以要禁
单段 ≤ 15 km"max_range_km": 15、"drum_km": 15电池飞不到 / 一盘光纤不够长。小地图总长 14.3 km 刚好一程;大地图 47 km 必须分段
偏转角 > 45° 记一次宏弯告警"turn_warn_deg": 45不阻止路径,但会计数进报告(光纤弯太急会损耗)

最后一条要解释一句:45° 恰好不算告警(判据是"大于")。小地图最大偏转角就是 45.0°,所以宏弯告警 = 0 次。这不是 bug。

做完请勾选


4 最短路在干什么(手算 3 步)#

4.1 Dijkstra:往水里扔石头#

想象地图是一池水。你在起点丢一颗石头,涟漪一圈一圈往外扩:

  • 每一圈只扩到"到目前为止最便宜"的那些格子。
  • 每个格子第一次被涟漪碰到时,记下的就是"到它为止的最小代价"。
  • 涟漪扩到终点,就收工。因为涟漪总是先到便宜的地方,所以第一次碰到终点时走的就是最便宜的路线。

这就是 Dijkstra。它老实、保证正确、但会把整个池子都淹一遍。小地图上它扩张了 741 格(全图才 900 格,等于淹了 82%)。

4.2 A*:涟漪 + 一个指南针#

A* 和 Dijkstra 一模一样,只改了一件事:给每个格子加一个"离终点还有多远"的估计。

优先级 = 已经花掉的代价 g  +  到终点的直线距离 h

h 就是指南针:它不告诉你路怎么走,只告诉你"往这个方向大概还有多远"。于是涟漪不再是圆的,而是被拉成一根朝终点指的雪茄形。

小地图实测:同样的路线,A* 只扩张 178 格(Dijkstra 的 24%),快了 3 倍多;大地图差距更夸张:405 格 vs 8140 格(5%)。两者算出来的总代价完全相同(14.985 对小图、47.828 对大图),因为 h 用的是欧氏距离——它是真实代价的下界,绝不会高估,所以不会带错路。

一句话记住:Dijkstra 保证最优但很笨,A* 一样最优但会看方向。本项目默认用 A*,报告 2.1 那张表就照这个填。

4.3 手算:从 S 出发的头三步#

起点 S 在 (2,12)。它周围 8 个邻居全是空地(w=0),代价只按距离算。

步从 → 到这一步的距离目标格 w这一步代价累计代价
1(2,12) → (3,12) 正东0.50000.5 × 1 = 0.5000.500
2(3,12) → (4,12) 正东0.50000.5001.000
3(4,12) → (5,12) 正东0.50000.5001.500

为什么前几步都往东?因为往东每格 0.5,往东北斜每格 0.7071——都一样免费的时候,正东更便宜。而终点在东边,所以正东又便宜又对方向。

一直正东走到 (9,12),走了 7 步,累计 3.500 km。

4.4 关键岔口:在 (9,12) 买哪一张票#

这里第一次出现"便宜但绕"和"贵但直"的对抗。把三个选项摆出来:

选项目标格距离目标格 w系数这一步代价走到这里累计
正东(10,12)0.500231.5005.000
东北斜(10,11)0.7071010.7074.207
东南斜(10,13)—辐射区—不允许—

机器选 东北斜:花 0.707 而不是 1.500。于是航线在这里开始往北爬。

注意一个细节:(10,12) 本身能飞,编码是 0,只是它右下方那一格 (11,13) 是辐射区,所以它被标价 2(系数 3)。"能飞"和"划算"是两件事。

继续爬:从 (10,11) 往东北斜着走 4 步到 (15,7),一路都是 0(免费)。

4.5 全程账本:14.985 km 是怎么加出来的#

段从 → 到走法单步小计累计
1(2,12) → (9,12)7 步直行0.50003.5003.500
2(9,12) → (10,11)1 步斜行0.70710.7074.207
3(10,11) → (11,11)1 步直行0.50000.5004.707
4(11,11) → (15,7)4 步斜行0.70712.8287.535
5(15,7) → (19,7)4 步直行0.50002.0009.535
6(19,7) → (25,13)6 步斜行0.70714.24313.778
7(25,13) → (25,14)1 步直行0.50000.50014.278
8(25,14) → (26,15)1 步斜行0.70710.70714.985

整条路一共 26 格(起点算一格)。这就是屏幕上那行 逐格路径 26 格。因为全程走的都是 w=0 的格子,代价等于长度,所以 14.985 既是长度也是代价。(只要有一步踩到 w=2 的格子,这两个数就会分家——这就是"加权最短路"的意思。)

4.6 平滑:14.985 → 14.325,为什么变短了#

逐格路径是台阶状的(东-东北-东北-东-东…),像楼梯。但无人机不用走楼梯,它可以沿一条斜直线飞过一串格子,只要这条线不碰障碍。

于是程序做"视线拉直":从当前折点出发,尽量往远处看,能看到多远就连到多远。结果 26 个格点被压成 5 个折点:

段起 → 止计算段长累计
1(2,12) → (15,7)√(13²+5²) × 0.5 = √194 × 0.56.9646.964
2(15,7) → (19,7)4 × 0.52.0008.964
3(19,7) → (25,13)√(6²+6²) × 0.5 = 6√2 × 0.54.24313.207
4(25,13) → (26,15)√(1²+2²) × 0.5 = √5 × 0.51.11814.325

省了 0.66 km(4.4%)。这 4 段加起来 14.325,就是报告里要填的"路径总长"。

两个长度别搞混:

  • 14.985 km = 逐格锯齿路径(搜索时算的账,是"代价")
  • 14.325 km = 拉直后的折线(报告要填的"路径总长")

报告 3.1 填 14.325,然后在"反思"里提一句"逐格 14.985,平滑后 14.325,省 4.4%",这一句能体现你真懂了两者的区别。

4.7 反直觉对照:几何最短反而"不是最优"#

把危险加权关掉,只按长度找最短路,程序会给你另一条路:(2,12) → (20,12) → (26,15),长度 12.354 km——比 14.325 短了 1.97 km!

听起来更好?看风险分:33。

因为它沿 y=12 一路往东,那是辐射区北岸的"毒边",一路贴着 w=2/4/6 的格子走。

方案长度风险分说明
代价最优(默认)14.325 km0往北绕到干净走廊,多飞 1.97 km
纯几何最短12.354 km33贴着辐射区边缘,每格代价 ×3~×7

这就是报告 3.4「高风险段处理说明」的全部素材:

"按任务书的代价模型(距离 ×(1+危险加权)),最优解不是几何最短。本方案多飞 1.97 km,把风险分从 33 压到 0,约合 0.06 km/风险分,工程上远优于在辐射区边缘施工。"

4.8 程序到底怎么跑的(一句话)#

scripts/run_small.py 做了 5 件事,顺序不能反:

  1. 读 xlsx → 得到 30×30 编码 + 起点/终点
  2. 算危险加权 w(第 3 章)
  3. 从 S 到 E 跑 A*(本章 4.1–4.2)
  4. 拉直 + 算指标(本章 4.6)
  5. 写 small.html / small_waypoints.csv / metrics_small.json

做完请勾选


5 大地图为什么要分段#

5.1 一次性飞不到:47 km 对 15 km#

大地图起点 (3,50)、终点 (97,50),直线距离:

94 格 × 0.5 km = 47.00 km

但单段上限是 15 km(电池航程和光纤盘长取小)。47 ÷ 15 = 3.13,所以最少要切成 4 段,也就是需要 3 个中继点:

起点 ──12.00 km── R41 ──11.10 km── R44 ──11.54 km── R42 ──12.51 km── 终点
      段1             段2             段3             段4           Σ 47.16 km

中继点干两件事:换电池(充电补给)+ 熔纤续接(一盘光纤只有 15 km)。所以"中继点"和"熔接点"在这里是同一个东西,数量 = 段数 − 1 = 3。

注意:段数不是"随便切 4 段"。中继点只能选地图上编码 7 的那 85 个格子,你不能把补给点放在沙漠里。

5.2 两级规划:先选落脚点,再选每段怎么走#

这是本章唯一的新概念,但很好理解——先定城市,再修路。

第一级(选点)第二级(选路)
干什么从 85 个候选里挑 3 个当落脚点每一段内部怎么绕障
图上有多少点87 个:S + 85 个 R + E100×100 = 10000 个格子
边怎么来两点之间距离 ≤15 km 才连一条边(边权 = 该段实际长度)8 邻域,每步有代价
用谁算DijkstraA*(每一段都要重跑一次)
花多久计算 87 个点两两最短路,本项目实测 1.7 秒每段几毫秒

具体跑了多少:87 个点两两配对 = 87×86/2 = 3741 对,其中距离 ≤15 km 的 799 对(其余直接丢掉,连算都不算);这 799 对里真正有可行通路的 688 条边。屏幕上那行中继层有效边 688 条 就是这个数。

为什么要剪枝?因为 47 km 的地图上"相距 20 km 的两个中继点"根本连不起来(超限),提前丢掉它们,能省掉一大半时间。不剪枝也能跑,就是慢十倍。

5.3 选出来的三个点,为什么是它们#

程序给出:R41(27,50) → R44(49,53) → R42(72,51),4 段长度12.000 / 11.104 / 11.543 / 12.510 km,全部 ≤15 km,且余量很均匀(没有哪段贴着上限)。

为什么是这三点?回看 1.8 节的地形:

  • 裂谷主带在 x≈34–66、y≈45–50,横切地图中部,不能穿;
  • 北边 x≈29–40、y≈30–42 是辐射块,堵死了往北绕的近路;
  • 南边 x≈53–68、y≈57–69 也是辐射块,限制你往下绕的深度;
  • 于是只有 y≈51–56 这条 4–6 格宽的干净走廊能过。

三个点正好卡住这条走廊的入口、中段、出口:

   y=44  ................  ← 裂谷北岸(不能走)
   y=47  .........../////
   y=48  ..........//////
   y=50  ...R............  ← R41 (27,50):走廊西口,必须在这里往南拐
   y=51  ................  ← ┐
   y=52  ................  ← │ 干净走廊(w=0)
   y=53  .....R..........  ← ┘ R44 (49,53):走廊正中,最长一段的中点
  ...                     ← R42 (72,51):走廊东口,拐回 y≈50 的地方

这三句话可以直接写进报告 3.2 的"选点决策理由":R41 在裂谷带西端、R44 在绕行段中点、R42 在裂谷带东端,正好把"必须下绕"的那 20 多公里切成四段长度均匀的短段。

5.4 三种目标算出来是同一个答案#

程序在"选点"这一级试了三种目标:

目标想优化什么结果
length总长最短(省电、省光纤)4 段 / 47.16 km / 3 个熔接
splices熔接点最少(省现场工时)同上
loss总光损耗最小(0.2 dB/km + 0.3 dB/熔接)同上

三个都是 R41/R44/R42。这说明这张图上"少熔接"和"短线路"不冲突,报告里写一句"三目标同解,说明 15 km 上限是个合理折中"就够了。

5.5 如果改上限,会发生什么(报告思考题④的答案)#

把单段上限从 8 km 一路扫到 25 km(out/sweep.md,程序 scripts/sweep_range.py 生成):

单段上限段数中继点总长(km)绕行比熔接点总损耗(dB)
8 km9848.821.039812.16
10 km9848.821.039812.16
12 km5447.241.005410.65
15 km(默认)4347.161.003310.33
18 km4347.161.003310.33
20 km4347.161.003310.33
25 km3247.081.002210.02

三条结论(照抄进报告):

  1. 上限收紧,中继点阶梯式增加,不是线性的:8 km 和 10 km 落在同一档(因为候选点位置是固定的,你必须迁就它们的分布)。
  2. 每多一个熔接点,多 0.3 dB:8 个熔接点比 3 个多 2.4 dB,等于白扔 12 km 的功率预算(0.3 ÷ 0.2 = 1.5 km 光纤/熔接点)。
  3. 放宽到 25 km 收益就饱和了:总长只降 0.08 km,损耗只降 0.31 dB。所以 15 km 是个合理折中。

自己试一次:

.venv\Scripts\python.exe scripts\run_large.py --cap 12

屏幕上"段数"会从 4 变成 5,中继点变成 4 个。

5.6 一个容易被当成 bug 的细节:47.23 和 47.16#

你会看到两个"大地图总长":

数字是什么从哪来
47.232 km不分段、一口气 A* 跑通全图再拉直out/compare.md、out/compare_large.html
47.157 km分成 4 段、每段各自拉直后相加out/large_segments_length.csv、out/metrics_large.json

分段(有约束)反而更短? 这不是 bug。原因是"拉直"这一步是贪心的:它每个折点只往后看一次,看到挡就停。分段后每段起点被钉死成折点,等于给它多插了几个锚点,贪心反而拉得更狠。

证据:不分段时贪心拉直得到 47.232,而"理论最优拉直"能到 47.045——说明贪心本身就有 0.19 km 的损失。三个数都合法,报告里统一用分段的 47.157。

报告里别写 47.23。47.23 那条路是违规的:47 km 一口气飞完,没有中继,单段超了 3 倍多。它只能用来做"算法效率对比"(看扩张格数),不能当方案。

做完请勾选


6 读懂成果页与 CSV#

6.1 out\ 里的文件,谁用在哪#

文件是什么报告哪一节用它
small.html小地图成果页4.1 说明、4.2 图 1(截图)
small_waypoints.csv小地图折点表(5 行)3.3 折点表
metrics_small.json小地图全部指标3.1 填空、3.3 指标汇总
large_length.html大地图主方案(4 段 4 色)4.2 图 2(截图)
large_segments_length.csv大地图分段表(4 行)3.2 分段表
large_waypoints_length.csv大地图折点表(6 行)3.3 折点表(放大图用)
large_splices.html、large_loss.html另两种目标(本图结果一样)3.4 或 5.2 对比
metrics_large.json大地图全部指标(含 3 种方案)3.2、3.3
compare_small.html、compare_large.htmlDijkstra / A* 搜索足迹叠加2.1、思考题②
compare.md、compare.csv算法对比表2.1、思考题②(直接抄)
sweep*.html、sweep.md、sweep.csv单段上限扫描思考题④
maps.md、maps.csv自造地图(尺寸/密度)扫描思考题⑤
threed.md、threed_*.html三维(可飞越)拓展思考题⑥
small.png、large_length.png位图版(要装 matplotlib)4.2 贴图(也可以直接截 HTML)

你没跑过的那些(sweep、maps、threed、compare)是 run_all.py 产出的,仓库里已经有一份完整结果,可以直接引用;想自己生成就跑 run_all.py。

6.2 成果页怎么读(以 small.html 为例,五个区域)#

区域位置你要核对什么
① 标题 + 副标题最上面标题是"小地图 A→C 单程规划";副标题里有 30×30、cell=0.5 km
② 指标卡(9 张)标题下方路径总长 14.32 / 直线 12.09 / 绕行比 1.185 / 风险 0 / 宏弯 0 / 折点 5 / 链路 2.86 / 熔接 0.00 / 总损耗 2.86
③ 地图画布左边绿色 S 在左中、蓝色 E 在右偏下、航线从 S 往右再往上绕
④ 切换开关 + 图例画布下方勾上"栅格线"能看到 30×30 网格;勾上"搜索足迹"会盖一层蓝雾(那是 A* 走过的格子)
⑤ 右侧表格 + 黄色备注右边表格标题是"折点表"、5 行;备注框 4 条(坐标约定/危险加权/长度算法/宏弯口径)

怎么用悬停核对坐标:鼠标移到绿 S 上,标签应显示 x=2 y=12 编码=5 危险加权=0。再移到 (11,12) 那格(S 右边 9 格、下方 0 格),应该看到 编码=0 危险加权=4。对上了,说明你的坐标观是对的。

6.3 large_length.html 怎么读(三处不同)#

  1. 地图 100×100,一格 6 像素,中间能看到深蓝色的裂谷带横着切过地图中部。
  2. 航线是 4 段 4 色(蓝、橙、青、紫),中间 3 个橙色方块标着 R41/R44/R42。
  3. 右侧表格标题是"分段表",4 行,最后一列"≤15km"全是 ✓。

自己检查一件事:把"中继点标注"关掉再打开,看那 3 个橙色方块是不是正好在航线的段与段的接头上。是,就说明"中继点 = 分段点"这个关系你理解了。

6.4 CSV 怎么打开、每列什么意思#

打开方式:双击就能用 Excel 打开(文件是 utf-8-sig 编码,专门为了 Excel 双击不乱码)。如果打开后中文变成乱码,用 Excel → 数据 → 从文本/CSV 导入 → 文件原始格式选 65001 : Unicode (UTF-8)。

small_waypoints.csv(折点表)7 列:

列名意思小地图第 2 行
序号第几个折点(从 1 开始)2
x格子的 x 坐标(0 在西)15
y格子的 y 坐标(0 在北)7
累计(km)从起点沿折线走到这里的长度6.964
段长(km)上一个折点到这个折点的长度6.964
风险该折点的危险加权-(表示 0)
宏弯该折点是否宏弯告警空(表示否;有告警是 ⚠)

注意 风险 列是 - 不是 0。这是生成 CSV 时"有值才写"的写法,报告里照实填 0 就行(- 就是 0 的意思)。

large_segments_length.csv(分段表)7 列:

列名意思第 2 行
段第几段2
起 → 止R41(27,50)→R44(49,53) 这种格式,括号里是坐标R41(27,50)→R44(49,53)
段长(km)这一段的折线长度11.104
累计(km)走到这段末尾的总长23.104
≤15km是否满足单段上限✓
风险分这一段沿途的危险加权之和0
宏弯这一段宏弯告警次数0

large_waypoints_length.csv(大地图折点表)6 行,但只有 3 个是中继点:

1  (3,50)    0.000      ← 起点 A
2  (27,50)  12.000      ← R41(中继点)
3  (43,52)  20.062      ← 只是拉直时的拐点!不是中继点
4  (49,53)  23.104      ← R44(中继点)
5  (72,51)  34.647      ← R42(中继点)
6  (97,50)  47.157      ← 终点 B

第 3 行 (43,52) 是"拉直算法在那儿不得不拐一下"产生的折点,那里没有中继点、没有熔接、飞机不停地飞过去。报告 3.2 写"选中 3 个中继点",不要写成 4 个。

6.5 metrics_*.json 怎么快速看#

用记事本打开会看到一堆缩进的大括号。别慌,它就是个"树",你要的数值都在下面这些路径上("→"表示一层层往下点):

你要的数字文件路径(键名)
小图路径总长 14.3249metrics_small.jsonsolution → length_km
小图逐格长度 14.9853同上solution → raster_length_km
小图直线 12.0934同上solution → straight_km
小图绕行比 1.1845同上solution → detour_ratio
小图风险分 / 宏弯同上solution → risk / macro_warnings
小图 A* 扩张 178 格同上solution → expanded
小图三种损耗同上solution → loss → fiber_loss_db / splice_loss_db / total_loss_db
大图 4 段明细metrics_large.jsonplans → length → legs(数组,每段一个对象)
大图中继点坐标同上plans → length → relay_points
大图总长 47.157同上plans → length → total_length_km
大图熔接数 3同上plans → length → splices
大图空单元格同上blank_cells → [[39, 46]]

更省事的办法:这些数字在成果页的指标卡上已经排好了,直接对着卡片抄,再用 JSON 核对一遍(防止抄错)。

6.6 基准数字表(跑完必须对上)#

指标小地图 A→C大地图 A→B
栅格30 × 30100 × 100
起点 → 终点(2,12) → (26,15)(3,50) → (97,50)
障碍(辐射/废墟/裂谷)26 / 9 / 12381(+1 空格) / 205 / 139
中继候选085
路径总长(km)14.32547.157
直线距离(km)12.09347.000
绕行比1.1851.003
风险分00
宏弯告警(次)00
分段 / 中继点1 段 / 0 个4 段 / 3 个(R41、R44、R42)
链路损耗(dB)2.8659.431
熔接损耗(dB)0.0000.900(3 × 0.3)
总损耗(dB)2.86510.331
算法扩张格数(A*)178405(Dijkstra 8140)

做完请勾选


7 按报告模板填数#

模板:ONE3_无人机路径规划_实验报告模板.docx。这一节是"填空答案纸":每一块该从哪个文件的哪一列抄。

7.1 总映射表(先看这张)#

报告小节要填什么从哪儿抄
2.1 调研表Dijkstra/A* 的复杂度、选型直接照模板给的文字勾选,本指南 4.1/4.2 有依据;数字用 out/compare.md
2.2 原理与选型代价怎么加权、怎么避障本指南第 3 章;口径与 data/map_config.json 一致
3.1 小地图栅格尺寸、障碍数、5 个指标run_small.py 的屏幕输出 + metrics_small.json → solution
3.2 大地图驻扎点、分段表metrics_large.json → plans.length + out/large_segments_length.csv
3.3 折点表序号/x/y/累计/段长/风险/宏弯out/small_waypoints.csv、out/large_waypoints_length.csv
3.3 指标汇总9 行指标(小图/大图)6.6 基准表,或两个 metrics_*.json
3.4 高风险段怎么压低风险分本指南 4.7(12.354 km / 风险 33 的对照)
4.1 成果页说明展示了哪些要素本指南 6.2/6.3
4.2 图 1/2/3三张截图small.html、large_length.html,图 3 用 small.html 的风险热力(或 compare_large.html 的搜索足迹)
5.1 学习复盘你的过程本指南目录就是一条现成的叙事线
5.2 思考题①–⑥六道见 7.3 的对照表
5.3 物理组网设想1–2 条优选线路47.16 km / 3 中继 / 10.33 dB 那套数字

7.2 逐块填写(含现成答案)#

3.1 小地图练手(模板填空 → 答案)#

建模要点:栅格尺寸 30×30;cell = 0.5 km;障碍类型与数量:辐射区 26、废墟 9、裂谷 12。

求解结果:路径总长 14.325 km;直线距离 12.093 km;绕行比 1.185;风险分 0;宏弯告警 0 次。

反思怎么写(模板问"小地图是否需要绕行"):

需要绕行。S→E 的直连线在 x≈10–15 处正好穿过辐射区(x10–15, y13–17,26 格禁飞),北边 x16–18 又有一块 3×3 废墟形成"上下夹击"。由于辐射区邻格的代价系数为 3,最优解从北侧 y=7 的干净走廊通过,代价是多飞 2.23 km(绕行比 1.185)。另外,若只按几何最短(不计危险加权)可做到 12.354 km,但风险分高达 33,按任务书代价模型反而不是最优——这是本次实验最反直觉的一点。

3.2 大地图(模板填空 → 答案)#

你选中的驻扎点:R41(27,50)、R44(49,53)、R42(72,51)(共 3 个)。

分段表(模板只有 3 行,我们的方案是 4 段,请自己加一行;"途经驻扎点"全填"—(直达)",因为每段都是两个停靠点之间的直达航线,中间不再停):

分段起点 → 终点途经驻扎点段长(km)是否≤15km风险分
1A(3,50) → R41(27,50)—(直达)12.000☑0
2R41 → R44(49,53)—(直达)11.104☑0
3R44 → R42(72,51)—(直达)11.543☑0
4R42 → B(97,50)—(直达)12.510☑0
Σ3 个中继47.157全部 ✓0

选点理由怎么写:

采用两级规划。第一级把 87 个节点(起点 + 85 个巡逻驻扎点 + 终点)两两求最短路,仅保留长度 ≤15 km 的点对,得到 688 条有效边(约 1.7 s);第二级在中继层图上用 Dijkstra 选链。地图中部 x≈34–66、y≈45–50 有一条裂谷带横切,北侧又被辐射块堵死,因此只有 y≈51–56 的走廊可通行。三个驻扎点分别卡在走廊的入口(R41)、中点(R44)和出口(R42),把 47 km 切成 4 段、每段 11–13 km,余量均匀,不会出现"最后一段差 0.1 km 超限"的返工。「总长最短 / 熔接最少 / 总损耗最小」三种目标在本图同解,说明 15 km 上限是个合理折中。

3.3 折点表 + 指标汇总#

折点表直接抄 out/small_waypoints.csv(5 行)或 out/large_waypoints_length.csv(6 行),列名一模一样,复制粘贴即可。模板给了 10 行,用不完就空着。

指标汇总(模板 9 行,答案):

指标小地图 A→C大地图 A→B
路径总长(km)14.32547.157
直线距离(km)12.09347.000
绕行比1.1851.003
风险分00
宏弯告警(次)00
选中继/熔纤点(个)03
链路损耗(dB)2.8659.431
熔接损耗(dB)0.0000.900
总损耗(dB)2.86510.331

三个损耗的算式(模板没写,你要写出来才显得懂):

链路损耗 = 总长 × 0.20 dB/km        小图 14.325 × 0.20 = 2.865
熔接损耗 = 熔接点数 × 0.3 dB        大图 3 × 0.3 = 0.900
熔接点数 = 段数 − 1                 大图 4 段 → 3 个
总损耗   = 链路 + 熔接              大图 9.431 + 0.900 = 10.331

3.4 高风险段处理说明(直接可用)#

本方案风险分为 0,因为路径全程走在 y≈51–56 的 0 加权走廊里,与裂谷带南岸留出 2 格以上缓冲,不与任何危险格相邻。代价模型下"能远离就远离"是最优选择:贴边飞行每格代价 ×3~×7,而绕一格的代价只有 0.5 km。

定量对照(关闭危险加权、只按长度最优):小地图可缩短到 12.354 km,但风险分升到 33,即"用 1.97 km 换掉 33 风险分"(约 0.06 km/风险分)。工程上绕行、降速、加保护的成本远低于辐射区施工风险,故取代价最优方案。若任务要求穿越裂谷带,可在该段降速通过并加保护套管,并在报告中单独标注该段长度与风险分。

4.2 三张截图怎么截#

图打开哪个文件截什么命名
图 1out/small.html整页(标题 + 指标卡 + 地图 + 折点表)图1_小地图_A到C.png
图 2out/large_length.html整页(4 段 4 色 + 3 个 R)图2_大地图_A到B分段.png
图 3out/small.html 勾掉"栅格线"、只留"风险热力",放大红团周围局部放大,能看清热力图3_风险热力.png

截图方法(Windows 自带,不用装软件):

  1. 浏览器按 F11 全屏 → 再按 Ctrl + + 把页面缩放到"指标卡和表格都在一屏内"。
  2. 按 Win + Shift + S,屏幕变暗,用鼠标框选整个页面。
  3. 图片自动进剪贴板,直接在 Word 里 Ctrl + V 粘贴。
  4. 粘完在 Word 里右键 → 大小和位置 → 宽度设 16 cm,图就整齐了。

想让图更清楚:浏览器缩放到 125%–150% 再截;或者干脆用 out/small.png(需要装 matplotlib,见 2.5)。

7.3 思考题 ①–⑥ 各自用哪个文件#

题问什么抄哪里
①最短路怎么给 ONE1 熔接/功率预算提供依据7.2 里的损耗算式 + 6.6 表
②Dijkstra vs A* 效率差异out/compare.md(扩张 8140 vs 405、47.828 同代价)
③物理组网还需补什么实地参数docs/实验报告数据.md 5.2③(DEM、剂量、续航、接头盒…)
④改约束(单段航程)的影响out/sweep.md(8→25 km 那张表)+ 本指南 5.5
⑤自定义更大更难地图的影响out/maps.md(尺寸 40→140、密度 5%→25%)
⑥引入高度维度out/threed.md(小图 14.99→13.85 等效里程,大图不变)

① 的定量关系(背下来能直接写):

总损耗(dB) = L × 0.20 + N × 0.3      L=总长(km),N=熔接点数=段数−1
1 个熔接点 ≡ 1.5 km 光纤长度(0.3 ÷ 0.2)
本方案:47.157 × 0.20 + 3 × 0.3 = 9.431 + 0.900 = 10.331 dB
若 ONE1 的预算只留 10 dB → 本方案刚好不够,必须减一个中继或换短线路

7.4 提交前跑一遍自检#

.venv\Scripts\python.exe scripts\selfcheck.py

它会把任务书"四卡"(可行 / 最短 / 绕障 / 文档)拆成 28 项检查。你应该看到全绿、结尾没有 ✗,退出码 0。任何一项 ✗ 都别提交。

做完请勾选


8 自审:一个只会开 Excel 的大一学生还会卡在哪#

我把这份指南假想给一个"只会双击 Excel、没写过代码"的大一学生读了一遍,标出三处仍然会卡住的地方,并且已经把补写内容放进了正文(不是堆在这里)。

坑 1:在 Excel 里改地图不生效(改错了文件)#

症状:学生想试试"把辐射区改成可达会怎样",打开根目录那份 one3_map_small_grid.xlsx把 1 改成 0,保存,重跑,结果一模一样。然后怀疑人生。

为什么会卡:根目录有同名副本,程序只读 data\ 那份。学生根本没有"文件路径"的概念。

已补写在 1.1:明确写了"两份 MD5 完全一致,但程序只读 data\ 里的,你要改地图做实验,改 data\ 里那份才生效"。

坑 2:CSV 用 Excel 打开后,累计(km) 这种列名变成了奇怪的东西#

症状:双击 CSV 打开,列名显示 累计(km) 全角括号,或者中文字符串成乱码;再或者把 - 当成"未定义"而不是 0,报告里填了个 -。

为什么会卡:学生不知道 CSV 是纯文本、编码会影响中文;也不知道 - 是程序"有值才写"的省略。

已补写在 6.4:给了"乱码时用 数据 → 从文本/CSV → 65001 (UTF-8)"的完整路径,以及一句"风险 列是 - 不是 0,报告里照实填 0"。

坑 3:不知道"跑一下"是在哪里敲命令,也不知道截图怎么弄#

症状:拿到 .venv\Scripts\python.exe scripts\run_small.py 这行命令,在桌面按 Win+R 输入、在 Excel 的宏里输、在 VS Code 里乱点,都不对。

为什么会卡:没人告诉过他"命令行有个当前目录",也没人告诉过他截图快捷键。

已补写在 2.3 和 7.2:2.3 那句"先 cd 到项目目录,怎么知道对了?敲 dir,你应该看到 one3、scripts、data、out",以及 7.2 的四步截图法(F11 → Win+Shift+S → Ctrl+V → 宽度 16 cm)。这两处是"零基础"和"会一点"的分水岭,必须显式写出来。


附录 A 和现有图文手册的关系#

本项目已经有几份成品文档。它们是成品,不要改、不要删。本指南是新增的"从零跟做"线,两者是"教程"和"参考书"的关系。

文件是什么什么时候看
docs/指导手册.html完整图文手册:12 张 SVG 图 + 10 个可交互实验台 + 7 段动画想看图/动手拖滑杆时;投屏讲解时
docs/指导手册.pdf上面那份的 A4 打印版(33 页)想打印/在平板上看时
docs/指导手册-阶段版.html按"六个阶段"拆的设计决策手册:每段产出、4 种实现方案及代价、5 个口径决策想做设计取舍(换算法/换口径)时
docs/卡点与教学指引.md22 个实测卡点 + 课堂节奏建议你卡住了,想看"别人卡在哪"时
docs/实验报告数据.md报告数据包(含六道思考题的完整答案)写报告 5.2 时直接查
docs/算法调研小结.mdDijkstra/A*/栅格法调研写报告 2.1 时
README.md项目总览 + 目录结构 + 一键命令想跑别的实验(3D/扫描/自造地图)时

推荐阅读顺序(本指南读完之后):

  1. 先看 out/compare_large.html——把"搜索足迹"勾上,看 Dijkstra 像水波铺满全图、A* 只沿一条走廊钻。这是第 4 章最好的插图。
  2. 再开 docs/指导手册.html,跳到第 3 章(栅格→图、Dijkstra、A*)看动画。
  3. 最后读 docs/卡点与教学指引.md 的"建模卡点"一节,回头检查自己报告里的口径写清楚没有(危险加权归属、叠加方式、宏弯定义、"最短"的定义——这四条最容易失分)。

本指南和它们的分工:

  • 本指南 = 线性跟做,一个命令接一个命令,不跳步,每一步有验收。
  • 那几份 = 查阅,想深挖某个点(比如"三维拓展怎么做")时翻。
  • 数字全部一致(都来自同一套 out/ 产出),可以互相引用。

附录 B 常见报错速查#

屏幕上的字真正的原因一行修法
'python' 不是内部或外部命令没装 Python,或没进 PATH装官方 3.12,勾 Add to PATH;或直接用 .venv\Scripts\python.exe
python -c "print(1)" 没输出、退出码 49撞上 WindowsApps 假 Pythonwhere python 确认;装真 Python(2.2)
系统找不到指定的路径当前目录不在项目里cd /d "项目完整路径",再 dir 确认看到 scripts
can't open file 'scripts\run_small.py'同上,或文件名打错用 Tab 补全
ModuleNotFoundError: No module named 'one3'把脚本复制到别处单独跑了回项目根目录运行
FileNotFoundError: 找不到地图文件:...data\ 里 xlsx 丢了/被改名从根目录拷回 data\
MapFormatError: y 行号从 1 开始编号...在 Excel 里插了行/删了行/改了 A 列用原始 xlsx 覆盖回去(别手改首列和表头)
MapFormatError: (x=.., y=..) 出现未知编码 4你手改地图时写了表 1 没有的值改回 0–3/5/6/7 之一
MapFormatError: 出现多个起点 5复制粘贴时多了一个 5全图只能有 1 个 5、1 个 6
UnicodeEncodeError: 'gbk' codec ...cmd 是 GBK,打不出 ⚠ / →chcp 65001 再跑;或改用 Windows Terminal
屏幕上中文是 锟斤拷 / 乱码同上(编码问题)chcp 65001
ModuleNotFoundError: No module named 'matplotlib'没装画图库不影响交作业;要 PNG 就 pip install matplotlib
终端提示 (未安装 matplotlib,跳过 PNG 导出)同上,程序自己降级了同上,可忽略
浏览器打开 small.html 一片空白用了 IE / 老旧内核右键 → 打开方式 → Edge 或 Chrome
CSV 双击后中文乱码编码选错Excel → 数据 → 从文本/CSV → 选 65001 (UTF-8)
run_large.py 卡在 中继层连边 xxx/799没卡,正在算 87 个点两两最短路等 2–4 秒,别关窗口
✗ 目标 xxx:无可行链--cap 给小了(比如 5 km)把 --cap 调回 15 或更大
大地图段数和我不一样(4 vs 5)上限不同默认 15 km 是 4 段;--cap 12 会变 5 段

自检清单(对照项目验收标准)#

这一节是写给批改者/上级看的:逐条对照项目 AGENTS.md 的 10 条验收标准。学生可以跳过,但可以拿它当"我这份指南读全了没有"的清单。

逐条自检结果(点开/收起)
#验收标准落在哪状态
1先数据后算法;前两章只讲打开并看懂 xlsx,禁止一上来讲 Dijkstra第 0 章只描述"成品长什么样"(连 A* 都只标注"第 4 章才讲");第 1 章全是 Excel 地址换算、编码、字符画、空单元格。第一次出现"最短路/代价"在第 3 章✅
2格子级对照:小图 ≥5 个具体单元格(Excel 地址 + 坐标 + 含义);说明 y=0 在北、codes[y][x];大图说明中继点长什么样 + 空单元格 (39,46) 怎么处理1.4 给了 8 个格子:D14=(2,12)=5、AB17=(26,15)=6、R11=(16,9)=2、N17=(12,15)=1、J22=(8,20)=3、L14=(10,12)=0 但 w=2、M14=(11,12)=0 但 w=4、S10=(17,8)=0 但 w=3;1.5 专讲 y=0 在北与 codes[y][x](含"翻错会怎样");1.8 讲 85 个 R 的外观/编号规则 + AO48 空单元格(含"当成 0 会打洞"的后果与实测说明)✅
3编码表 0–7 用人话:能不能飞 / 旁边会不会加代价 / "假如你站在这个格子"1.6 三列表格逐行 0/1/2/3/4/5/6/7;编码 4 在表 1 中不存在,已显式说明(地图里没有、程序遇到会报错),没有编造含义✅
4环境逐步验收:假 Python(WindowsApps)→ venv → run_small.py,每步写期望输出/文件,失败给一句原因2.1–2.7 七步,每步都有"你应该看到"+ 一段"失败时多半是";2.6 贴了 run_small.py 的真实屏幕输出(含 路径总长 14.325 km)与新增的 3 个文件;2.7 给 start out\small.html 的验收;2.6 附失败对照表 6 行✅
5算法用类比 + 手算:Dijkstra=水波、A*=带指南针;小图从 S 走出的前 2–3 步手算代价(具体数字)4.1 水波涟漪、4.2 涟漪+指南针 h;4.3 前 3 步 0.5/1.0/1.5 km 逐行算;4.4 在 (9,12) 岔口算 1.500 vs 0.707 并解释为何选东北斜;4.5 把 14.985 拆成 8 段账本;4.6 再拆 14.325 的 4 段✅
6对基准表:小/大关键数字(14.32 / 47.16 等),告诉读者跑完打开哪个 out/ 文件对哪一列6.6 基准表(14.325 / 47.157 / 1.185 / 1.003 / 2.865 / 10.331 / 178 vs 741 / 405 vs 8140);6.1 文件↔报告章节对照;6.4 逐列解释 3 个 CSV;6.5 逐键解释 2 个 JSON✅
7交作业映射:报告模板每一块"数字从哪个文件哪一列抄"7.1 总映射表(13 行);7.2 逐块填答案(3.1/3.2/3.3/3.4 含现成文字);7.3 六道思考题 ↔ 文件;7.4 提交前 selfcheck.py✅
8不删改现有 docs/指导手册.html 等成品;可引用为进阶阅读附录 A 明确"它们是成品,不要改",并给了 7 个现有文档的定位与推荐阅读顺序✅
9全文中文、句子短、每节末尾有"做完请勾选"每章末尾都有勾选清单(0/1/2/3/4/5/6/7 共 8 处,53 个勾选项);正文以短句为主✅
10终末自我审查:找一个"只会开 Excel 的大一学生",标出仍会卡的 3 处并补写第 8 章列 3 个坑(改错文件、CSV 列名/-、不知道在哪敲命令与截图),并逐条说明补写已落到哪一节(1.1 / 6.4 / 2.3+7.2)✅

额外做的(不在 10 条内,但补上了原文的空白)

补充为什么
5.6「47.23 与 47.16 为什么不相等」两个数都出现在产出里,学生极易当成 bug。已用"贪心拉直 + 锚点更多"解释,并给出 47.045 的实测上界作证据
6.4「大地图折点表第 3 行 (43,52) 不是中继点」折点表 6 行、中继点 3 个,学生极可能写成 4 个中继点
1.8.2「空单元格改成可通行后方案不变」避免把"这次没影响"吹成"必须修否则必错";如实说明风险
3.4「45° 恰好不告警」解释 最大偏转角 45.0° 与 宏弯 0 次 并列出现时不矛盾

已知弱项(如实记录)

  1. 本指南以这台机器(Windows 11 + Python 3.12.10 + 项目内已有 .venv)的实测输出为准;macOS / Linux 的命令(source .venv/bin/activate、路径分隔符)只提了一句,没有展开。
  2. 2.2 的 winget install 与官网下载两条路都写了,但没有覆盖"学校机房无管理员权限"的场景。
  3. 第 8 章的"大一学生"是假设的读者,不是真实用户测试(没有真人试读记录)。
  4. 数据里 (39,46) 这个空格是否属于下发文件的有意设计(而非导出 bug)无法从仓库判断,本指南按"数据缺口"处理,并把两种后果都写了。