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 ...先记住三件事:
- A1 那一格写的是
y\x(字母 y、反斜杠、字母 x)。这是一句提示:"往右是 x,往下是 y"。它占掉了 A 列和第 1 行。 - 第 1 行(B1、C1……)是 x 的编号:0、1、2、3…… 一直到 29(小地图),到大 99(大地图)。
- 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 = 11 | M11 |
| (26, 15) 在 Excel 哪儿? | 列 = 26+2 = 28 → 第 28 列是 AB;行 = 15+2 = 17 | AB17 |
第 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:AE31 | B2:CW101 |
| 起点 | (2,12) = D14 | (3,50) = E52 |
| 终点 | (26,15) = AB17 | (97,50) = CU52 |
| 直线距离 | 12.09 km | 47.00 km |
| 辐射区 | 26 | 381 格 + 1 个空格(见下) |
| 废墟 | 9 | 205 |
| 裂谷 | 12 | 139 |
| 中继候选 R | 0 | 85 |
中继点长什么样? 就是编码 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 km | 1 格 × 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 危险加权:给"危险旁边的格子"标价#
规则只有三行:
- 看一个能飞的格子,数它周围 8 个邻居里有几个危险格。
- 每个辐射区邻居 +2,每个废墟或裂谷邻居 +1。
- 全部加起来,就是这一格的"危险加权 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......... ← 裂谷本身(不可入)三条能直接看出来的规律(这几条是本章的精华):
- 贴着危险格最贵:
y=15行、红团正左方那格 (9,15) 是6——它同时挨着 3 个辐射格(+2×3)。系数 1+6 = 7 倍代价。而绕一格才多花 0.5 km,绝对值。 - 对称会破:
y=12这一行,x=9 是0(免费),x=10 是2(×3),x=11 是4(×5),x=12 是6(×7)。所以"沿 y=12 一直往东"在第 10 列就开始变贵。 - 往北爬一格就免费了:
x=13在y=12是6(挨着 3 个辐射格),往上挪到y=11立刻变成0(完全干净)。于是最优解会斜着往北爬,爬到 y=7 那条干净走廊——这正是 0.2 节那张图里航线的形状。
3.3 手算三个格子(拿笔跟着算)#
| 站在哪 | 去哪个邻居 | 距离 | 目标格 w | 系数 1+w | 这一步代价 |
|---|---|---|---|---|---|
| (9, 12) 空地 | → (10, 12) 正东 | 0.500 | 2 | 3 | 1.500 km |
| (9, 12) 空地 | → (10, 11) 东北斜 | 0.7071 | 0 | 1 | 0.707 km |
| (9, 12) 空地 | → (10, 13) 东南斜 | — | 目标格是辐射区 | — | 不能进(硬禁飞) |
同样的"向东一格",走上面(斜)比走中间(直)便宜一半还多:0.707 对 1.500。这就是为什么路径会往上拐。
再看一个"编码 0 但很贵"的:
| 站在哪 | 去哪个邻居 | 距离 | 目标格 | w | 代价 |
|---|---|---|---|---|---|
| (10, 11) | → (11, 12) 东南斜 | 0.7071 | 0(能飞!) | 4 | 0.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 + 到终点的直线距离 hh 就是指南针:它不告诉你路怎么走,只告诉你"往这个方向大概还有多远"。于是涟漪不再是圆的,而是被拉成一根朝终点指的雪茄形。
小地图实测:同样的路线,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.500 | 0 | 0.5 × 1 = 0.500 | 0.500 |
| 2 | (3,12) → (4,12) 正东 | 0.500 | 0 | 0.500 | 1.000 |
| 3 | (4,12) → (5,12) 正东 | 0.500 | 0 | 0.500 | 1.500 |
为什么前几步都往东?因为往东每格 0.5,往东北斜每格 0.7071——都一样免费的时候,正东更便宜。而终点在东边,所以正东又便宜又对方向。
一直正东走到 (9,12),走了 7 步,累计 3.500 km。
4.4 关键岔口:在 (9,12) 买哪一张票#
这里第一次出现"便宜但绕"和"贵但直"的对抗。把三个选项摆出来:
| 选项 | 目标格 | 距离 | 目标格 w | 系数 | 这一步代价 | 走到这里累计 |
|---|---|---|---|---|---|---|
| 正东 | (10,12) | 0.500 | 2 | 3 | 1.500 | 5.000 |
| 东北斜 | (10,11) | 0.7071 | 0 | 1 | 0.707 | 4.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.5000 | 3.500 | 3.500 |
| 2 | (9,12) → (10,11) | 1 步斜行 | 0.7071 | 0.707 | 4.207 |
| 3 | (10,11) → (11,11) | 1 步直行 | 0.5000 | 0.500 | 4.707 |
| 4 | (11,11) → (15,7) | 4 步斜行 | 0.7071 | 2.828 | 7.535 |
| 5 | (15,7) → (19,7) | 4 步直行 | 0.5000 | 2.000 | 9.535 |
| 6 | (19,7) → (25,13) | 6 步斜行 | 0.7071 | 4.243 | 13.778 |
| 7 | (25,13) → (25,14) | 1 步直行 | 0.5000 | 0.500 | 14.278 |
| 8 | (25,14) → (26,15) | 1 步斜行 | 0.7071 | 0.707 | 14.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.5 | 6.964 | 6.964 |
| 2 | (15,7) → (19,7) | 4 × 0.5 | 2.000 | 8.964 |
| 3 | (19,7) → (25,13) | √(6²+6²) × 0.5 = 6√2 × 0.5 | 4.243 | 13.207 |
| 4 | (25,13) → (26,15) | √(1²+2²) × 0.5 = √5 × 0.5 | 1.118 | 14.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 km | 0 | 往北绕到干净走廊,多飞 1.97 km |
| 纯几何最短 | 12.354 km | 33 | 贴着辐射区边缘,每格代价 ×3~×7 |
这就是报告 3.4「高风险段处理说明」的全部素材:
"按任务书的代价模型(距离 ×(1+危险加权)),最优解不是几何最短。本方案多飞 1.97 km,把风险分从 33 压到 0,约合 0.06 km/风险分,工程上远优于在辐射区边缘施工。"
4.8 程序到底怎么跑的(一句话)#
scripts/run_small.py 做了 5 件事,顺序不能反:
- 读 xlsx → 得到 30×30 编码 + 起点/终点
- 算危险加权 w(第 3 章)
- 从 S 到 E 跑 A*(本章 4.1–4.2)
- 拉直 + 算指标(本章 4.6)
- 写
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 + E | 100×100 = 10000 个格子 |
| 边怎么来 | 两点之间距离 ≤15 km 才连一条边(边权 = 该段实际长度) | 8 邻域,每步有代价 |
| 用谁算 | Dijkstra | A*(每一段都要重跑一次) |
| 花多久 | 计算 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 km | 9 | 8 | 48.82 | 1.039 | 8 | 12.16 |
| 10 km | 9 | 8 | 48.82 | 1.039 | 8 | 12.16 |
| 12 km | 5 | 4 | 47.24 | 1.005 | 4 | 10.65 |
| 15 km(默认) | 4 | 3 | 47.16 | 1.003 | 3 | 10.33 |
| 18 km | 4 | 3 | 47.16 | 1.003 | 3 | 10.33 |
| 20 km | 4 | 3 | 47.16 | 1.003 | 3 | 10.33 |
| 25 km | 3 | 2 | 47.08 | 1.002 | 2 | 10.02 |
三条结论(照抄进报告):
- 上限收紧,中继点阶梯式增加,不是线性的:8 km 和 10 km 落在同一档(因为候选点位置是固定的,你必须迁就它们的分布)。
- 每多一个熔接点,多 0.3 dB:8 个熔接点比 3 个多 2.4 dB,等于白扔 12 km 的功率预算(0.3 ÷ 0.2 = 1.5 km 光纤/熔接点)。
- 放宽到 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.html | Dijkstra / 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 怎么读(三处不同)#
- 地图 100×100,一格 6 像素,中间能看到深蓝色的裂谷带横着切过地图中部。
- 航线是 4 段 4 色(蓝、橙、青、紫),中间 3 个橙色方块标着
R41/R44/R42。 - 右侧表格标题是"分段表",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.3249 | metrics_small.json | solution → 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.json | plans → 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 × 30 | 100 × 100 |
| 起点 → 终点 | (2,12) → (26,15) | (3,50) → (97,50) |
| 障碍(辐射/废墟/裂谷) | 26 / 9 / 12 | 381(+1 空格) / 205 / 139 |
| 中继候选 | 0 | 85 |
| 路径总长(km) | 14.325 | 47.157 |
| 直线距离(km) | 12.093 | 47.000 |
| 绕行比 | 1.185 | 1.003 |
| 风险分 | 0 | 0 |
| 宏弯告警(次) | 0 | 0 |
| 分段 / 中继点 | 1 段 / 0 个 | 4 段 / 3 个(R41、R44、R42) |
| 链路损耗(dB) | 2.865 | 9.431 |
| 熔接损耗(dB) | 0.000 | 0.900(3 × 0.3) |
| 总损耗(dB) | 2.865 | 10.331 |
| 算法扩张格数(A*) | 178 | 405(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 | 风险分 |
|---|---|---|---|---|---|
| 1 | A(3,50) → R41(27,50) | —(直达) | 12.000 | ☑ | 0 |
| 2 | R41 → R44(49,53) | —(直达) | 11.104 | ☑ | 0 |
| 3 | R44 → R42(72,51) | —(直达) | 11.543 | ☑ | 0 |
| 4 | R42 → 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.325 | 47.157 |
| 直线距离(km) | 12.093 | 47.000 |
| 绕行比 | 1.185 | 1.003 |
| 风险分 | 0 | 0 |
| 宏弯告警(次) | 0 | 0 |
| 选中继/熔纤点(个) | 0 | 3 |
| 链路损耗(dB) | 2.865 | 9.431 |
| 熔接损耗(dB) | 0.000 | 0.900 |
| 总损耗(dB) | 2.865 | 10.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.3313.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 三张截图怎么截#
| 图 | 打开哪个文件 | 截什么 | 命名 |
|---|---|---|---|
| 图 1 | out/small.html | 整页(标题 + 指标卡 + 地图 + 折点表) | 图1_小地图_A到C.png |
| 图 2 | out/large_length.html | 整页(4 段 4 色 + 3 个 R) | 图2_大地图_A到B分段.png |
| 图 3 | out/small.html 勾掉"栅格线"、只留"风险热力",放大红团周围 | 局部放大,能看清热力 | 图3_风险热力.png |
截图方法(Windows 自带,不用装软件):
- 浏览器按
F11全屏 → 再按Ctrl++把页面缩放到"指标卡和表格都在一屏内"。 - 按
Win + Shift + S,屏幕变暗,用鼠标框选整个页面。 - 图片自动进剪贴板,直接在 Word 里
Ctrl + V粘贴。 - 粘完在 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/卡点与教学指引.md | 22 个实测卡点 + 课堂节奏建议 | 你卡住了,想看"别人卡在哪"时 |
docs/实验报告数据.md | 报告数据包(含六道思考题的完整答案) | 写报告 5.2 时直接查 |
docs/算法调研小结.md | Dijkstra/A*/栅格法调研 | 写报告 2.1 时 |
README.md | 项目总览 + 目录结构 + 一键命令 | 想跑别的实验(3D/扫描/自造地图)时 |
推荐阅读顺序(本指南读完之后):
- 先看
out/compare_large.html——把"搜索足迹"勾上,看 Dijkstra 像水波铺满全图、A* 只沿一条走廊钻。这是第 4 章最好的插图。 - 再开
docs/指导手册.html,跳到第 3 章(栅格→图、Dijkstra、A*)看动画。 - 最后读
docs/卡点与教学指引.md的"建模卡点"一节,回头检查自己报告里的口径写清楚没有(危险加权归属、叠加方式、宏弯定义、"最短"的定义——这四条最容易失分)。
本指南和它们的分工:
- 本指南 = 线性跟做,一个命令接一个命令,不跳步,每一步有验收。
- 那几份 = 查阅,想深挖某个点(比如"三维拓展怎么做")时翻。
- 数字全部一致(都来自同一套
out/产出),可以互相引用。
附录 B 常见报错速查#
| 屏幕上的字 | 真正的原因 | 一行修法 |
|---|---|---|
'python' 不是内部或外部命令 | 没装 Python,或没进 PATH | 装官方 3.12,勾 Add to PATH;或直接用 .venv\Scripts\python.exe |
python -c "print(1)" 没输出、退出码 49 | 撞上 WindowsApps 假 Python | where 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 次 并列出现时不矛盾 |
已知弱项(如实记录)
- 本指南以这台机器(Windows 11 + Python 3.12.10 + 项目内已有
.venv)的实测输出为准;macOS / Linux 的命令(source .venv/bin/activate、路径分隔符)只提了一句,没有展开。 - 2.2 的
winget install与官网下载两条路都写了,但没有覆盖"学校机房无管理员权限"的场景。 - 第 8 章的"大一学生"是假设的读者,不是真实用户测试(没有真人试读记录)。
- 数据里
(39,46)这个空格是否属于下发文件的有意设计(而非导出 bug)无法从仓库判断,本指南按"数据缺口"处理,并把两种后果都写了。