1. 校园导航被低估的难度:室内外无缝切换才是真正的痛点
1.1 为什么室外导航那一套在校园里不灵
先说我做这个项目的起因。我带过一个新学期的迎新系统,当时很多家长和学生都在问同一个问题:"商学院304怎么走?"打开地图App,定位倒是能定到教学楼门口,可进了大厅就抓瞎——地图上只有一个楼块,里面哪条走廊通向304、要不要上二楼,完全看不出来。最后还是靠志愿者站在楼道里人工引导。
这件事让我意识到,校园导航和城市导航完全是两个物种。城市导航解决的是"从A道路到B道路"的路径问题,路网数据由专业测绘提供,主路、辅路、高架桥都有严格拓扑。而校园场景要解决的是"从楼下到楼内某个具体房间"的问题,这中间至少跨越两个完全不同定位环境:室外有GPS信号,室内基本没有;室外道路是线性拓扑,室内走廊是复杂网络;室外导航精度差个几十米无所谓,室内你差三米就可能把用户导进隔壁办公室。
所以做智慧校园定位与导航系统,第一步不是写代码,而是重新定义问题:你要导航的对象,到底是"一栋楼"还是"一间教室"?如果定位目标只到楼,那直接套高德地图就行;可一旦目标细化到教室、工位、洗手间、自助打印机,就必须自建室内路网和室内定位方案。这也是这个项目和其他导航类课设最本质的差别。
1.2 项目边界与需求拆解:给什么人、解决什么事
在定技术方案之前,我花了两天时间做需求梳理,把用户角色和核心场景拆开看:
- 新生与访客:最典型的用户,对校园完全陌生,需要"从校门口/宿舍/停车场到我所在的教室"这种跨楼层的连续导航。
- 教职工与管理员:更多是寻找特定办公室、会议室,或者查看某个楼栋的空间分布。
- 迎新与家长开放日组织者:需要把大量人群从集合点分流到不同教室,相当于临时性的群体路径引导。
由此得出三个核心功能点:室外到室内无缝切换的定位、跨楼层路径规划、目标房间的精准到达提醒。其他的都属于加分项,比如反向寻车、无障碍导航、公共设施检索。
我特别建议做课设或毕设的同学先把这个需求分析章节写扎实,因为它直接决定了你后面系统的复杂度。如果你的选题只要求"室外校园导航",那完全没必要做室内定位和指纹库,高德SDK加上校园建筑的点位就能交差。但如果你想让系统有差异化亮点,室内外一体化才是值得投入的方向。本项目的定位是后者,所以项目标题里才强调"设计与实现",整个系统的重心也放在室内定位与跨楼层导航上。
这里补充一个实用的需求评估方法:把所有功能按"必做/应做/可做"分三层,必做是基础导航框架,应做是室内定位与跨楼层路径,可做是语音播报、考勤联动、后台路网管理。项目答辩时老师最爱问"你这个系统的创新点在哪",提前把这三层划分理清楚,就能答得很有条理。
1.3 一个容易被忽略的指标:定位误差的可接受范围
做定位系统,绕不开一个指标——误差。但"误差多少算合格"完全取决于场景。城市道路导航,误差20米也能用,因为路网稀疏,判断你在哪条路上就够了;教学楼走廊通常只有两三米宽,房间门宽度不到一米,误差超过三米就很容易把用户导进错误房间。
我在设计时把误差要求定成这样:
| 场景 | 要求误差 | 实现方式 |
|---|---|---|
| 室外道路 | ≤10米 | GPS/北斗+基站辅助 |
| 楼栋入口附近 | ≤5米 | GPS与室内定位切换缓冲 |
| 室内走廊 | ≤3米 | iBeacon指纹+地磁辅助定位 |
| 房间门口(最后3米) | ≤1米 | 地图匹配+到达判断逻辑 |
最后一档"房间门口精度"不是靠定位硬件做出来的,而是靠路径终点的判断逻辑:当用户距离目标房间所属的节点小于阈值且停驻时间超过2秒时,判定为"已到达"。这个思路很多导航系统都在用,本质是用后验逻辑弥补定位硬件的物理极限。
2. 整体架构与核心技术选型:为什么是"高德底图+自建路网+多源定位"
2.1 系统总体架构
整个系统我分成了三端:Android客户端、后端服务、管理后台。这三者的职责必须划清楚,不然后面扩展和答辩都会乱。
- Android客户端:负责定位采集、地图渲染、路径规划展示、语音播报。所有计算量大的活尽量放在本地,减少对网络的依赖,尤其是室内环境可能出现信号不稳定的情况。
- 后端服务:负责用户认证、楼栋/教室/路网数据的下发、定位指纹库的更新、路径离线包的管理。用Spring Boot实现,数据存MySQL,部署简单、资料多、遇到问题好查。
- 管理后台:以Web形式支持路网编辑器的可视化操作,管理员在后台拖拽点位、连接走廊、编辑教室信息、导入测绘底图。这一步是很多导航项目里非常容易偷懒的地方,但如果没有它,你的路网数据就只能靠手工写JSON,维护成本高到根本没法演示。
地图层面我采用"高德SDK做底图+自建TransparentOverlay绘制室内图"的组合:室外直接用高德地图的底图和定位能力,进入楼栋后切换成自绘室内地图图层,高德的底图只作为背景参考。这么做的好处是省去了自己维护公开地图(室外)的成本,同时保留了室内部分完全可控的灵活性。
2.2 室内定位选型:蓝牙指纹为主、地磁/计步为辅
室内定位的技术路线挺多,UWB精度最高但需要铺硬件,WiFi指纹部署方便但受环境变动影响大,基站定位精度又不够。我最终选了"iBeacon蓝牙指纹为主、地磁匹配+惯性计步为辅"的混合方案,理由有三:
- 成本可接受:iBeacon信标几十块钱一个,一栋五层的教学楼布置三十到四十个就能跑通演示。
- 指纹方案对设备要求低:普通手机就能采集RSSI信号,不需要专用硬件。
- 有冗余度:光线变化大、走廊人流量不均匀的时候,仅靠WiFi指纹很容易跳变,加上地磁特征可以做二次校验。
实现思路是:在每条走廊的重点点位(岔路口、教室门口、电梯口)采集连续20秒的蓝牙信号,记录每个信标的MAC地址和RSSI值,建立指纹点表。定位时,客户端扫描周围信标,取信号最强的4到6个信标的RSSI值,用加权K近邻算法匹配指纹库,得出粗略坐标,再用加速度计推算的步数做一步航位推算,最后用地磁特征对结果做修正。
这样做完的实测效果是:在一条40米长的走廊上,纯指纹定位误差大概在5到8米,加上步数约束后能压到3米左右。注意这个实验数据是在人少、信号稳定的条件下获得的,人群密集时误差会大一些,所以又要人工加入地图匹配规则。
2.3 路径规划算法的选择:A*的工程化改造
路网建好之后,路径规划本身反而没那么难,A算法足够用,关键是在A之上做了几点工程化改造:
- 启发函数用欧氏距离:走廊网络虽然是网格状,但教学楼里的走廊不一定横平竖直,欧氏距离比曼哈顿距离更符合实际尺度。
- 跨楼层建模为特殊节点对:电梯、楼梯间是连接不同楼层路网的特殊边,我在路网模型里给它们加了
floorChange属性。计算代价时给楼梯设一个偏高的权重,这样规划默认优先走电梯,除非用户选了"少等电梯"模式。 - 限制搜索范围:每个楼栋的每层路网节点一般不超过一百个,全部参与搜索也没问题。但如果后台要支持整个校园几十栋楼,就必须把搜索限制在"同一楼栋+相邻楼栋"范围内,否则性能会明显下降。
一个更能体现工程化的细节是转弯惩罚。如果不做处理,A*规划出来的路径可能频繁地在岔路口左右转弯,体验很差。我在边与边的代价计算里,当方向夹角大于45度时额外加一个5米的惩罚代价,这样规划结果天然倾向于"少转弯、走长直走廊"。这个改动很小,但对最终体验的提升非常明显。
2.4 技术栈与关键依赖清单
| 模块 | 选型 | 说明 |
|---|---|---|
| Android客户端 | Kotlin + Jetpack Compose | Compose写动态地图UI比XML更顺手,但注意和地图SDK的SurfaceView冲突 |
| 地图SDK | 高德地图Android SDK | 提供底图、GPS定位、POI检索,室外部分直接复用 |
| 室内地图 | 自建矢量图层 + Canvas绘制 | 基于路网节点数据动态绘制走廊/房间/楼层切换按钮 |
| 后端 | Spring Boot + MyBatis-Plus | 提供数据接口,管理用户和路网数据 |
| 数据库 | MySQL 8.0 | 存储指纹点表、路网节点、用户信息等 |
| 定位 | 自研定位融合模块 | 聚合GPS、蓝牙指纹、计步、地磁多源数据 |
强调一个细节:地图SDK和自绘图层叠加的时候,最难处理的是坐标投影。高德底图用的是GCJ-02坐标系,你自建的室内地图如果也用了GCJ-02坐标,那么叠加就没问题;但如果你的测绘底图是WGS-84或者独立平面坐标,叠加之后所有Marker会全部偏移。这个坑我在后面踩坑部分会详细展开。
3. 核心模块实现细节:路网模型、A*规划与定位融合
3.1 路网数据模型是怎么设计的
路网是整个导航系统的心脏,它的数据结构设计直接决定后面所有功能的实现难度。我用的是最经典的节点-边模型,但针对楼层和室内场景做了一些扩展:
node { id: String // 节点唯一标识,如 B3F2-N14 floor: Int // 楼层号,1、2、3... 或 -1、-2... 表示地下 type: ENUM // NORMAL、DOOR、STAIR、ELEVATOR、CORNER x, y: Double // 平面坐标(GCJ-02系或独立工程坐标) poiId: Long // 关联的POI信息,如教室、洗手间、打印机 neighbors: List[Edge] // 从当前节点出发的边 } edge { from, to: String // 两端节点ID cost: Double // 步行成本(米) direction: ENUM // BIDIRECTIONAL、ONE_WAY floorChange: Boolean // 是否跨楼层(楼梯/电梯) }核心设计思想是"节点即决策点"。在实际环境里,只有当用户走到走廊尽头、三岔路口或者教室门口时,才需要做方向决策,所以节点应该设在这些位置,而不是平均分布在走廊上。走廊中段如果很长且没有岔路,可以用中间节点来表示路径走向,但不需要每个几米就布一个,否则路径规划的搜索空间会被无限放大。
教室门口的节点需要关联到教室ID,定位融合模块会把"当前用户是否在教室门节点附近"作为到达判断的依据。洗手间、电梯、楼梯这些公共设施点也做成了独立节点,这样用户搜索"最近的洗手间"时会非常自然,直接在节点集合里做一次最短路径搜索就行。
每个楼栋的每层路网单独一张表,表结构里增加了building_id和floor字段,查询时用联合索引快速过滤。跨楼栋之间通过室外路网连接,而室内外衔接通过楼栋出入口节点(大厅、侧门)来桥接。
3.2 路径规划算法的实现与调优
我把路径规划模块拆成了三个接口:findPath(start, end)、findNearestFacility(position, type)、getRouteInfo(routeId)。核心的findPath实现了一个简化的A*:
public Route findPath(String startNodeId, String endNodeId) { PriorityQueue<NodeRecord> openList = new PriorityQueue<>(Comparator.comparingDouble(r -> r.fScore)); Map<String, Double> gScore = new HashMap<>(); Map<String, String> cameFrom = new HashMap<>(); gScore.put(startNodeId, 0D); openList.add(new NodeRecord(startNodeId, 0D, heuristic(startNodeId, endNodeId))); while (!openList.isEmpty()) { NodeRecord current = openList.poll(); if (current.nodeId.equals(endNodeId)) break; if (current.fScore > gScore.getOrDefault(current.nodeId, Double.MAX_VALUE)) continue; for (Edge edge : nodeMap.get(current.nodeId).neighbors) { double tentativeG = gScore.get(current.nodeId) + edge.cost; // 转弯惩罚处理 if (hasTurn(cameFrom.get(current.nodeId), current.nodeId, edge.to)) { tentativeG += TURN_PENALTY; } if (tentativeG < gScore.getOrDefault(edge.to, Double.MAX_VALUE)) { gScore.put(edge.to, tentativeG); cameFrom.put(edge.to, current.nodeId); openList.add(new NodeRecord(edge.to, tentativeG, tentativeG + heuristic(edge.to, endNodeId))); } } } return reconstructPath(cameFrom, startNodeId, endNodeId); }A*在这种数百节点规模的图里跑起来非常快,一次规划通常不到10毫秒,完全不需要引入辅助索引或者预处理。真正花费时间调的是转弯惩罚系数:系数太大,路径会绕远走直线走廊;太小则会出现频繁左右转。我试了几个值,最终把45度夹角的惩罚定在5米,90度定在8米,效果比较接近人在真实环境里的步行直觉。
跨楼层路径的实现是这样的:搜索时允许经过楼梯/电梯特殊节点,但给这些边增加一个额外的"楼层切换代价"(比如坐电梯加10米,走楼梯加20米)。当起点和终点在不同楼层时,A*会自然找到一条经过电梯或楼梯的最优路径。在此基础上,我还会在返回路径时增加一个节点列表,专门标注出"请乘坐电梯至3楼"这种跨楼层指令。
3.3 多源定位融合与地图匹配
定位融合模块是整个系统里debug最久的模块。简单说,它要把GPS、蓝牙指纹、计步、地磁这些数据源揉成一个最终坐标,只靠简单的加权平均是不够的,数据来源之间经常互相矛盾。
我的融合逻辑是一个简化的无迹卡尔曼滤波:观测量来自GPS坐标或蓝牙指纹坐标,控制量来自计步器(每帧推算位移增量),更新时把地图约束也考虑进去。关键的一点是,为了让滤波结果在走廊内移动时平滑,我加了一层约束:每次更新坐标后,将坐标投影到最近的路网边上,限制垂直偏移。这个"投影到路网"的动作是地图匹配的雏形,它能有效消除定位点飘进房间内或者飘出楼外的异常情况。
实现地图匹配最省事的方法是在融合模块后面加一个snapToGraph(lat, lng, floor)函数:
- 找到距离当前坐标最近的路网边。
- 将当前坐标投影到该边上,投影点即修正后的最终坐标。
- 如果最近边的投影距离超过5米,判定为信号异常,保留原始坐标并上报"信号弱"状态。
这个机制对室内场景尤其重要。教学楼走廊很窄,定位跳变点容易落在隔壁教室里,如果不做图形投影修正,用户看着自己在墙里面走,会直接判定系统不可用。
3.4 路径导航中的语音播报与到达判定
路径规划出来之后,导航界面要做的不是画一条线就结束,而是要模拟"有人带路"的体验。我实现了三个子模块:
- 分段指令生成:遍历路径节点,按转向角度生成"前方直行30米""左转进入走廊""乘坐电梯到3楼""前方到达目的地"等指令。
- 语音播报触发:用距离判断触发时机,距下一个转向点剩15米时播报一次;如果用户走错方向偏离路径超过5米,重新规划路径并播报"已为您重新规划路线"。
- 到达判断:当用户距离终点节点小于3米且GPS定位连续3次都稳定在附近时,弹窗提示并自动播放到达语音。
这里有个容易踩的坑:语音播报的触发不能只依赖一次定位结果,因为定位点本身有抖动。我加了"连续3帧定位点都处于转向点半径内"的过滤逻辑,相当于一个简单的去抖器,效果很明显。
4. 实测踩坑记录:坐标偏移、楼层跳变与SDK限制
4.1 坐标系的坑:GCJ-02、WGS-84和自建平面坐标
这个坑几乎所有做过地图开发的人都遇到过,但我还是想再强调一遍,因为它的破坏力实在太隐蔽。在我第一次把高德底图和自建室内路网叠加的时候,室内路网整体向东偏了大约100多米,所有走廊Marker都画到了楼外的马路上,整个界面看起来完全没法用。
排查过程花了很久,最后发现是底图坐标系不一致:高德SDK使用GCJ-02坐标,而我从校园测绘图纸里获取到的室内平面图用的是独立工程坐标(通常基于WGS-84投影)。两种坐标系之间存在一个非线性的偏移,华东地区偏移量大约在几百米量级,肉眼可见地离谱。
解决方案是增加一个坐标转换工具类,把自建路网的所有原始测绘坐标先做一步投影转换,转成GCJ-02再存储。转换公式本身不复杂,但要注意:如果原始测绘坐标是经纬度(WGS-84),需要用标准算法转GCJ-02;如果原始坐标是平面投影坐标(比如从CAD图纸里量出来的毫米坐标),则需要先做仿射变换对齐到高德地图上若干已知参考点。我最终用的是"选取校园内5个对齐点+最小二乘法算仿射变换参数"的方式,把CAD平面坐标转成了高德可用的经纬度,误差在1米以内,完全满足室内导航需求。
这个坑我建议所有做地图类项目的同学都要写进论文的"系统测试与问题分析"章节,因为它是绝大多数同类系统都会踩到、但又不适合在琐碎细节里占篇幅的问题,评委看到你能清晰地分析坐标系转换原理,本身就是加分项。
4.2 楼层切换时的定位跳变与信号弱问题
第二个让我头疼的坑是楼层切换时的定位跳变。用户从一楼坐电梯上到三楼,GPS信号被屏蔽了大半,蓝牙指纹识别需要几秒收敛,这时候系统可能给出一个漂移到二楼甚至室外停车场的坐标,导航界面直接乱掉。
我最后总结出两条经验:
识别电梯场景并主动重置定位状态。通过加速度计检测到持续的垂直加速度(电梯启动和停止瞬间会有明显的z轴加速度脉冲),触发一次定位状态重置,清除之前的定位历史,并切换到"等待新楼层蓝牙指纹匹配"的模式。垂直加速度的时间窗口设定为1.5秒,实测能识别大部分电梯场景。
楼层切换按钮手动兜底。技术再准也挡不住用户不坐电梯走楼梯,楼梯间往往信号更差。所以我在UI上保留了一个手动切换楼层的引导按钮,用户可以在界面上主动切换当前楼层视图,切换后定位模块会重新回到"保守模式"——直到指纹匹配到足够数量的信标,才恢复自动定位。
第三个和信号弱相关的问题是室内人流遮挡。比如下课高峰期,走廊里挤满学生,身体对蓝牙信号会形成遮挡,导致指纹匹配结果在小范围内来回跳。这个问题的根源是RSSI的时变性,纯靠滤波并不稳定,我最终对指纹匹配结果做了一级"稳定性约束":如果最近三秒的定位结果都落在同一个路网边上,就锁定该边,除非新的定位点连续五秒偏离该边才允许切换。
4.3 Android定位权限与前台服务限制
Android系统对定位权限和后台服务的限制,是很多学生在做完功能后才发现的问题。Android 12及以上版本对精确定位权限要求更严格,如果应用没有声明并使用ACCESS_FINE_LOCATION,定位结果会被系统限制到城市级精度,完全不可用。
需要特别注意的是,如果你在室内持续导航,就得用前台服务或者后台定位类型的特殊声明,否则系统会在应用切到后台几秒内杀掉定位请求。我的做法是:
- 在Manifest中声明
FOREGROUND_SERVICE、FOREGROUND_SERVICE_LOCATION两个权限,针对API 34及以上还需要声明FOREGROUND_SERVICE_TYPE的值。 - 开启导航时启动一个前台服务,通知栏常驻"正在导航"。
- 在
onStop时暂停定位更新而非直接关闭,避免用户短暂切回桌面再回来时定位冷启动。
还有一个很容易被忽略的点,就是模拟定位在调试阶段很有用,但演示时务必关掉。曾经我在演示过程中开着模拟定位去演示,结果系统以为我在几百公里外的另一个城市,整个人当场社死。建议在客户端加一个"是否启用模拟定位"的开关,并打上明显的调试标记。
4.4 指纹库采集耗时长?试试先稀疏后加密
室内定位要建指纹库,但一次性把所有点位全采一遍非常耗时。我实测采一个10米的走廊密集点(每隔1米采10秒)大概需要5分钟,一栋楼下来得折腾一整天。对于课设或毕设的时间节奏,这样做压力很大。
我的改进思路是"先稀疏建库,再按需加密":
- 第一轮只在走廊拐角、三岔口、电梯口等关键决策点采集,每栋楼大概10到15个点。
- 用路径规划和地图匹配做辅助修正,把稀疏指纹库的定位结果约束到走廊路网上,粗定位就能达到可用的程度。
- 对经常出现定位误差超过5米的区域,再针对性加密采集,而不是全部重采。
最终效果是:完成一栋教学楼的基本定位能力,时间从一整天压缩到两个小时左右,误差也能维持在3到5米范围。这个策略既保证了演示效果,又不会让项目周期失控。
5. 从源码到一套完整交付:报告组织、演示设计与功能扩展
5.1 万字报告怎么写才不虚、不凑字
项目标题里强调"万字报告",很多同学一听到这个要求就想开始堆代码截图,这是最常见的误区。一份好的课程设计或毕业设计报告,重点不是贴了多少代码,而是把"需求→设计→实现→测试"这条链路讲清楚,尤其是逻辑推导过程。
我组织这份报告时用的结构是:
- 绪论:背景与意义,重点写实际痛点,比如迎新引导成本高、平面地图App交互差等。
- 相关技术综述:定位技术对比(GPS、蓝牙指纹、UWB、地磁)、地图SDK选型、路径规划算法比较。
- 需求分析:用例图、功能需求与性能需求表格、核心场景描述。
- 系统设计:总体架构图、数据库设计、核心模块接口定义、路网数据模型设计。
- 系统实现:每项核心功能对应实现流程、核心代码片段与分析,不要整段贴,要挑有代表性的代码讲解意图。
- 系统测试:功能测试用例表、定位精度实测数据分析、坐标系转换验证。
- 总结与展望:主要工作回顾,以及系统可以继续扩展的方向。
报告的字数重点是"设计与测试"两部分,这是评委最关注的部分。如果能把每个模块为什么这么设计的理由写清楚,比空写一万个字有价值得多。对了,每个图表都要有图号和表号,答辩演示时直接引用这些编号,能明显提升专业感。
5.2 演示讲解怎么设计才不会被问倒
有了源码和报告,最后一步是讲解演示。我强烈建议提前准备一个"演示脚本",因为演示现场容易紧张,脚本可以保证流程不散。我的脚本是这样设计的:
- 开场30秒讲清楚系统解决什么问题:用一句"这个系统解决的是从校门口到教室内房间的全流程导航"作为主线。
- 室外场景演示:进入校园,定位显示当前位置,搜索目标教室,规划出路径,展示跨楼栋的路线。
- 室内场景演示:走进楼内,地图自动切换到室内图层,展示楼层切换、走廊转向指令和到达判定。
- 后台管理页面演示:展示路网编辑器如何新增一个教室节点、如何编辑路径。
- 总结价值:从定位、路径、体验三个维度简单收尾。
答辩环节老师最爱问的无非就是这几个问题:精度为什么不够高?指纹库能不能更新?为什么不用UWB?路径规划算法为什么选A*?回答的关键是"承认物理限制,强调工程权衡":比如提到UWB精度高但部署成本太大,对于校园场景性价比低;A*在数百节点的路网里表现足够好,如果扩展到全市级路网,可以考虑换成收缩层次图。
我也建议准备一把尺子或者现场测量工具,演示时实际测一下走廊宽度、教室门宽度,说明"误差3米在这里意味着什么",这种具体场景化解释比空谈精度数字更有说服力。
5.3 哪些扩展方向最有价值
从交付角度看,如果时间还有富余,我建议优先扩展这三个方向,它们对答辩加分非常明显:
- 小程序端:校园场景里用户不愿意专门下载一个App,小程序是天然入口。技术上有两条路,一是用uni-app封装现有API,二是单独做一套H5定位版本。工作量不小,但做出来之后整套系统的应用价值会明显提升。
- 无障碍导航:针对轮椅、视障人群设计无障碍优先的路径规划,通过加入无障碍坡道、电梯优先、避开楼梯等属性,与"智慧校园"理念高度契合。这是比较容易成为论文亮点的方向,因为很多同题项目没有考虑这个维度。
- 通过课表联动:对接教务系统的课表后,上课前十分钟自动提醒用户出发,并直接导航到下一堂课所在教室。这种从"被动导航"到"主动服务"的转变,才是智慧校园真正的魅力所在。
另外还有停车场反向寻车、会议室预订联动、校园活动临时导流等功能,都属于按需添加的场景化模块,核心的路网与定位引擎是不变的,扩展时只加数据节点和业务逻辑就行。
最后说一点个人体会。做这个项目,最有价值的收获并不是会用A*算法、会调地图SDK,而是学会了"在复杂的物理环境和技术限制之间做取舍"。采集指纹库的时候要决定采多少点才够,画路网的时候要决定节点放在哪里最合理,定位融合的时候要在实时性和稳定性之间找平衡……这些决策能力,是单纯看源码、背概念学不来的。如果让我重做一次,我会在项目初期就把路网编辑器做出来,先解决数据生产的问题,再去优化定位算法——因为数据才是制约体验的瓶颈,这个问题想明白了,整个项目的推进节奏会顺很多。