ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

路网拓扑驱动的交叉口进出方向判定与转向关系构建

路网拓扑驱动的交叉口进出方向判定与转向关系构建

道路交叉口出入口方向判定这件事,我最早接触的时候以为是个纯粹的几何题:拿到路口中心点坐标,算一下周围几条路的方位角,排个序,标上东南西北就完了。真正开始做项目才发现,几何只占三成,剩下七成全是数据组织和工程细节。一条路到底是进口道还是出口道,一条进口道到底对应几个可走的出口,转向关系怎么和流量矩阵对齐,这些问题在教科书里一句话带过,在实操中能把人卡两三天。

这篇内容讲的就是从路网拓扑结构出发,把交叉口的“进出方向”这件事判准、判全、判得能落地。适合正在做交通仿真建模、路网数据处理、导航路径计算、路口渠化分析的朋友,也适合刚接手路网数据、被一堆Link和Node搞得一头雾水的同学。我会把判定链条拆到每一步都能复现的程度,包括坐标系陷阱、双向道路的组织方式、转角公式的推导、特殊路口的修正策略,以及几条我吃过亏才总结出来的自动质检规则。

1. 先厘清“方向判定”到底在判什么

1.1 三个层次:拓扑归属、几何朝向、转向关系

“出入口方向判定”这个说法容易让人以为只是一件事,实际上它至少包含三个层次,很多项目做不下去就是因为把三层混在一起。

第一层是拓扑归属:这条Link相对于这个交叉口节点,是驶入还是驶出。这一层只看Link的起终点与节点的连接关系,不需要任何坐标。第二层是几何朝向:这条驶入的Link是从东边来的还是从北边来的,需要一个方位角,范围0到360度。第三层是转向关系:从这条进口道进来,能转到哪几条出口道上去,每条对应左转、直行还是右转。第三层是前两层的组合结果,但需要额外做配对和可达性过滤。

三层的难度是递增的。第一层大多数数据都能直接给出来,第二层需要坐标系和方向计算,第三层需要把进口集合和出口集合做笛卡尔积再筛选。我见过不少半成品工具,只做到第二层就交付了,结果用户拿到的是一堆方位角数字,根本没法用,因为他真正要的是“北进口左转对应哪条出口”。

1.2 一个反直觉的事实:Link的方向字段经常不可信

新手最容易犯的错,是默认路网数据里每条Link的起始节点就是“车辆驶来的方向”,终点节点就是“车辆驶去的方向”。在某些严格按行驶方向组织的数据里这是成立的,但在大量来源的路网数据里,一条Link的绘制方向只是制图时的顺手方向,和车行方向完全无关。

判断一个数据源属于哪种情况,有个很快的检验办法:找一个明确的单行道,看它的Link方向是否和实际车流方向一致;再找一个双向道路,看它是被拆成两条反向Link,还是只用一条Link加一个双向属性字段表示。这两种组织方式处理逻辑完全不同,后面第2章会展开。

拿到新数据源的第一件事,永远不是跑算法,而是抽十个路口手工核对Link方向的组织方式。这十分钟能省掉后面两天的返工。

1.3 为什么这件事值得认真做

方向判定的结果往下游走,会直接决定一堆东西的正确性:微观仿真的路网搭建、路口渠化方案的评估、转向流量矩阵的行列索引、导航的转向提示文本、路口事故黑点的归因分析。这些场景有一个共同点,就是错位是静默的。

举一个具体的例子。转向流量矩阵的常见排布是“北进口左转、直行、右转,东进口左转、直行、右转……”,这个顺序完全依赖进口道的方位角排序。如果某个路口的东进口和南进口方位角算反了,矩阵不会报错,仿真也能跑完,输出的排队长度看起来还挺合理,但整个结论是错的。这种错误如果没有专门的质检环节,可能几个月都发现不了。

2. 路网拓扑:判定链条的骨架

2.1 节点度先帮你筛掉一半的假路口

路网数据里的节点,绝大多数并不是交叉口。一条长路被切成若干段,中间会产生大量度为2的节点;一些用于表达道路形状的节点度甚至是1。真正需要做进出方向判定的,只有度大于等于3的节点。

度等于3是T型或Y型交叉口,度等于4是标准十字,度大于等于5是多岔口。度等于2的节点如果被当成交叉口处理,会得到“一条进口、一条出口、只能直行”的荒谬结果,而且会污染统计数据。度等于1的是断头路端点,在某些分析里需要单独标记,但不需要做转向配对。

实际操作中还有一个坑:有些路网因为数据切分的原因,一个真实十字路口被拆成了两个相邻的度为3的节点,两个节点相距可能只有十几米。这种情况如果不管,会得到两个“丁字路口”,转向关系全乱。处理办法是设定一个节点聚合半径,把距离小于该半径且度大于等于3的节点合并成一个虚拟交叉口节点,聚合半径一般取15到30米,主干路交叉口可以放宽到50米。

2.2 双向道路的两种组织方式,决定了完全不同的算法

这是整个判定流程里最重要的一个分岔点。

方式一:对向拆分。一条双向道路用两条Link表示,一条从A到B,一条从B到A,各自带单行属性。这种数据结构下,进出判定非常干净:终点落在交叉口节点上的Link就是进口道,起点在交叉口节点上的就是出口道。一个标准十字路口会得到4条进口Link和4条出口Link,数量平衡。

方式二:单Link双向。一条道路只用一条Link表示,附加一个方向属性(双向通行)。这种结构下,同一条Link相对于交叉口既是进口也是出口,方向相反,方位角相差180度。判定时需要把这条Link“虚拟拆成两条”,一条按“指向交叉口”的方向算方位角,一条按“背离交叉口”的方向算。

两种方式的对比:

对比项对向拆分单Link双向
存储量约为单Link的两倍较小
进出判定直接由起终点得出,无需额外逻辑需按方向虚拟拆分
转向配对进口集合与出口集合天然分离同一Link会同时出现在两个集合
单行道表达天然支持需额外属性区分
常见于仿真软件路网、导航路网部分基础测绘路网、简易示意图

如果数据是方式二而你按方式一处理,结果会是:一个十字路口只有两条进口道、两条出口道,另外两个方向的转向关系全部丢失,而且不会有任何报错。反过来如果数据是方式一而你按方式二处理,会得到双倍的进口道,每条路被判成两条进口,方位角还重复。

判断方法很简单:统计某个已知十字路口节点的关联Link数量。如果关联Link有4条且每条都只在一端连接该节点,基本是方式一;如果只有2条、每条都贯穿该节点,基本是方式二。

2.3 端点吸附:一个不显眼但会毁掉方位角的步骤

方位角计算依赖Link端点的坐标。如果同一个交叉口的多条Link端点坐标不完全重合(这在多源数据拼接时极其常见,偏差从几厘米到几米都有),算出来的方位角会带上额外噪声。

我遇到过一次典型情况:一个十字路口的四条Link端点分别落在四个不同位置,最大间距接近6米。结果北进口的方位角算出来是8度,东进口是96度,单看都还算正常,但做转向配对时,北进口到东出口的转角被算成88度,勉强还在右转的容差范围内;而南进口到西出口被算成了92度,被判成右转而不是直行。整个路口的转向关系错了两条。

处理办法是先做节点吸附再算角度:以交叉口节点坐标为中心,把所有落在容差半径内的Link端点坐标统一替换成节点坐标,然后再计算方位角。城市路网的容差半径取5米一般够用,郊区或数据质量差的地方可以放到10米。吸附之后,同一个交叉口所有Link的端点坐标完全一致,方位角的计算基准也就统一了。

3. 方位角:几何基座与坐标系陷阱

3.1 方位角的约定必须先统一

方位角的标准定义是从正北方向开始,顺时针旋转到目标方向的角度,取值范围0到360度。正北是0度,正东是90度,正南是180度,正西是270度。这个约定和数学里的极坐标(从正东开始逆时针)正好错开90度并且反向,是代码写错的高发区。

对应的计算公式要用atan2(dx, dy)而不是atan2(dy, dx),其中dx是东西向增量(东为正),dy是南北向增量(北为正)。我见过太多人第一次写的时候下意识按数学课本写成atan2(dy, dx),跑出来所有角度都旋转了90度并且左右镜像,但因为没有参照物,肉眼一时半会儿看不出来。

import math def azimuth(x1, y1, x2, y2): """平面投影坐标下的方位角,x为东向,y为北向,返回0-360度""" dx = x2 - x1 dy = y2 - y1 ang = math.degrees(math.atan2(dx, dy)) return (ang + 360) % 360

最后那一步归一化不能省。atan2返回的是负180到正180,不加这一步会出现负角度,后续做转角逐差时全部失效。

3.2 经纬度直接算角度,会偏出好几度

这是我最想强调的一个坑。很多数据的原始坐标就是经纬度,看起来直接代进公式就行,实际上经度和纬度在地球表面的实际长度不一样,一度纬度大约110.6公里,一度经度的长度是111.3公里乘以纬度的余弦。在中高纬度地区,直接用经纬度做平面计算会造成东西方向被拉伸。

量化一下:在北纬40度附近,余弦值约为0.766。一条真实的45度方位角道路,如果直接拿经纬度差值算,得到的角度约为52.5度,偏差7.5度。这个偏差听起来不大,但转向判定的容差通常设在正负15到22.5度之间,7.5度的系统性偏移会明显侵蚀容差空间,在斜交路口上直接导致误判。

修正方式有两种。轻量做法是在计算时把经度差值乘以中心纬度的余弦,相当于做一个局部等距圆柱近似,对于单个交叉口尺度(几百米内)精度完全够用:

def azimuth_lonlat(lon1, lat1, lon2, lat2): mid_lat = math.radians((lat1 + lat2) / 2.0) dx = (lon2 - lon1) * math.cos(mid_lat) dy = (lat2 - lat1) ang = math.degrees(math.atan2(dx, dy)) return (ang + 360) % 360

严谨做法是先把整个路网投影到平面坐标系再进行所有几何计算,高斯-克吕格投影或墨卡托类的等角投影都可以,选带号匹配当地中央经线的投影带。批量处理路网数据时我强烈建议走这条路,因为后续还会涉及距离计算、面积计算、缓冲区分析,统一在投影坐标系下做会少很多麻烦。

坐标处理方式适用场景精度遗留问题
经纬度直接算强烈不建议中高纬偏差可达数度距离、面积计算全部失真
乘余弦做局部近似单点、小范围快速验证交叉口尺度足够跨纬度批量处理误差累积
统一投影到平面坐标生产环境批量处理高需要管理投影参数

3.3 取哪一段算方向:直线近似会骗人

第三个容易忽略的点是:Link的端点连线方向,不等于车辆在路口附近的实际驶入方向。

很多进口道在接近路口的位置会有展宽段、渐变段、渠化岛,道路中线在那里会有一个明显的偏折。如果直接拿Link的两个端点算方位角,中间的形状点全部被忽略,算出来的方向可能偏离实际驶入方向十几度。一条本来该判成直行的进口道,可能因为这个偏差被推进左转或右转的容差区间。

正确做法是只取靠近交叉口的一小段来计算切线方向,通常是最后20到30米,或者取靠近交叉口方向的最后一个或两个形状点。这样得到的方位角才反映车辆真正进入路口时的朝向。

def azimuth_near_node(points, node_xy, window=25.0): """points为从远到近排列的坐标序列,最后一点已吸附到节点坐标""" # 从末端往回找,累计长度达到window即停 acc = 0.0 idx = len(points) - 1 while idx > 0 and acc < window: acc += math.hypot(points[idx][0] - points[idx-1][0], points[idx][1] - points[idx-1][1]) idx -= 1 return azimuth(points[idx][0], points[idx][1], points[-1][0], points[-1][1])

窗口取多大有个经验值:主干路交叉口进口道展宽段比较长,窗口取30米;支路和小路口取15到20米就够。窗口太大反而会把上游的弯道带进来。

3.4 角度归一化与容差设定

转向角计算出来之后必须做一次归一化,把所有角度映射到0到360区间。Python里用(ang % 360 + 360) % 360,注意不能只写ang % 360,因为Python的取模对负数返回正值,但在别的语言里不一定,写双重保险更省心。

容差设定直接决定判定的松紧。标准十字路口用正负15度足够,斜交路口建议放到正负22.5度,如果路口形态特别不规则,可以把容差做成按路口配置的参数。容差太小会把大量正常转向判成“未知”,容差太大会把斜交路口的所有方向都混成直行。

4. 从方位角到左直右:转向配对的完整链条

4.1 进口道的顺时针编号

进口道的排列顺序建议统一为从正北开始顺时针编号,这也是多数交通调查表和渠化图的惯例。排序键就是进口道的方位角(指向交叉口的方向),升序排列即为顺时针。

排序之后建议直接落一个seq_no字段,从1开始。这个序号往后会贯穿整个数据链路:转向矩阵的行索引、仿真软件里进口道的命名、可视化图上的标签。一旦定下来就不要在中途改排序规则,我吃过一次亏,中途把排序从“指向交叉口的方位角”改成“从交叉口指向外的方位角”,结果所有序号整体偏移了180度,历史数据和新数据对不上,排查了大半天。

4.2 转角公式:直行为什么是180度

这是整个算法里最容易绕晕的地方,我把推导过程写清楚。

设进口道的驶入方位角为 θ_in,定义为从Link远端指向交叉口中心的方向。设出口道的驶出方位角为 θ_out,定义为从交叉口中心指向出口Link远端的方向。

车辆沿进口道驶入时,车头朝向就是 θ_in。驶出时车头朝向变成 θ_out。那么车辆的实际转向角度就是两段车头方向的夹角。

关键在于:如果车辆直行穿过路口,进入时朝北(θ_in = 0度),离开时还是朝北,但 θ_out 是从交叉口指向外算的,所以也是0度。咦,这样算出来转角是0,显然不对。

问题出在这里:θ_in 的定义方向(指向路口)和 θ_out 的定义方向(背离路口)本身就是相反的。直行时,θ_in = 0(朝北驶入),θ_out 也等于0(朝北驶出),两者之差为0,但直觉上直行转角应该是0啊?不对,这里的“转角”指的是方向改变量,直行时方向确实没变,改变量就是0。

但问题是左转和右转就分不出来了:右转从朝北变成朝东,θ_in = 0,θ_out = 90,差值是90;左转从朝北变成朝西,θ_in = 0,θ_out = 270,差值是270。所以:

转角 Δ = (θ_out − θ_in) mod 360转向类型
0 附近掉头
90 附近右转
180 附近直行
270 附近左转

等一下,掉头和直行在这里反了。重新理一遍:如果车辆从北边驶入然后掉头往北驶出,θ_in 是从南指向北(朝北,0度),掉头后车头朝南,出口Link从路口指向南,θ_out = 180度。差值是180。所以180对应掉头,0对应直行。

我上面表格写错了,正确的关系是:

转角 Δ = (θ_out − θ_in + 360) mod 360转向类型说明
0 附近直行驶入驶出方向一致
90 附近右转顺时针偏转90度
180 附近掉头反向
270 附近左转逆时针偏转90度

这个结论和很多人的第一直觉相反,因为在方位角体系里角度增大是顺时针,右转正好对应角度增大。我建议第一次实现时拿一个标准十字路口手工验证一遍:北进口驶入,右转到东出口,θ_in=0,θ_out=90,Δ=90,判右转,正确。

4.3 可达性过滤:几何成立不等于实际能走

笛卡尔积配对会产生大量几何上成立但实际不可行的转向关系。一条进口道理论上能连到所有出口道,但真实路网里存在大量限制:

  • 单行道导致的单侧禁止,某条出口道只能进不能出
  • 主辅路分离,辅路进口道往往只允许右转
  • 中央隔离带导致的禁止左转和禁止掉头
  • 特定车型限制(大型车禁转)
  • 时段性禁转

几何配对做完只是初筛,必须叠加一层可达性过滤。数据源里如果带了转向限制表(turn restriction),直接用;如果没有,只能靠规则推断,比如“进口道与出口道之间隔着中央分隔带则禁止左转”。这类规则推断准确率有限,建议把推断结果标记来源字段,便于人工复核。

我在一个项目里遇到过更隐蔽的情况:两条Link在拓扑上连接,但中间隔着一道物理隔离护栏,数据里没有任何字段体现。这种情况只能靠实景核查,算法层面无解。所以交付前一定要留一个“抽样人工核验”的环节。

4.4 完整实现

import math def turn_type(theta_in, theta_out, tol=20.0): d = (theta_out - theta_in + 360.0) % 360.0 if d <= tol or d >= 360.0 - tol: return "直行" if abs(d - 90.0) <= tol: return "右转" if abs(d - 180.0) <= tol: return "掉头" if abs(d - 270.0) <= tol: return "左转" return "斜向未知" def build_movements(in_links, out_links, allowed=None): result = [] for i in in_links: for o in out_links: key = (i["link_id"], o["link_id"]) if allowed is not None and key not in allowed: continue t = turn_type(i["azimuth_in"], o["azimuth_out"]) result.append({ "in_link": i["link_id"], "out_link": o["link_id"], "turn": t, "delta": round((o["azimuth_out"] - i["azimuth_in"] + 360) % 360, 1), }) return result

对角色的字段说明要清楚:azimuth_in是进口道Link指向交叉口节点的方位角,azimuth_out是出口道Link从交叉口节点指向远端的方位角,两个角度都以交叉口节点为基准计算。

顺带提一下八方位命名的实现,用于生成“北进口”“东南进口”这类可读标签:

NAMES = ["北", "东北", "东", "东南", "南", "西南", "西", "西北"] def octant(ang): return NAMES[int((ang + 22.5) % 360 // 45)]

加22.5度是为了让每个扇区的中心对准正方向,边界落在22.5、67.5这样的位置。不加的话正北会被判成东北。

5. 特殊形态路口的判定修正

5.1 错位交叉与斜交路口

错位交叉是指两条道路在路口处错开一段距离,形成两个相邻的丁字路口。这种形态在旧城区路网里非常普遍。前面提到的节点聚合是标准解法,但聚合之后会产生一个新问题:虚拟交叉口中心到各条Link的方位角,和真实驾驶人的感知方向有偏差。

处理建议是聚合后重新计算每条Link指向虚拟中心的方位角,而不是保留原来的相对关系。同时把聚合半径做成可配置项,对错位距离特别大的路口(超过50米)做人工标记,不要强行合并,而是按两个独立交叉口处理并在数据里记录关联关系。

斜交路口的问题是转角不再分布在90、180、270这三个点上。一条以30度角斜穿的进口道,直行的转角可能只有150度,按正负20度的容差判,正好卡在边界。这时候有两种策略:把容差放宽到正负25度,或者改判定逻辑为“找最近的整数倍90度角”。后者更稳健:

def turn_type_nearest(theta_in, theta_out, tol=25.0): d = (theta_out - theta_in + 360.0) % 360.0 k = round(d / 90.0) % 4 names = ["直行", "右转", "掉头", "左转"] if abs(d - k * 90.0) > tol: return "斜向未知" return names[k]

5.2 环形交叉口不能用常规逻辑

环形交叉口的拓扑结构和其他路口完全不同。进口道接到环道上,出口道从环道引出,中间是一圈单向环道Link。如果按普通节点处理,会得到“车辆进入环道后可以沿环道任意方向行驶”的结果,转向关系彻底混乱。

正确做法是把环道作为一个特殊的连通体处理,判定逻辑变成:车辆从进口道进入环道后,沿环道单向行驶到目标出口,途经的环道弧长决定它实际转了多大角度。这个角度可以按环道上的累计转角来算,超过180度就认为是左转(绕行超过半圈)。

还有一点:环形交叉口的进口道通常只允许右转进入,不允许在入口处直接掉头,转向配对时要把这个约束加上。

5.3 主辅路与匝道

主辅路分离的道路,辅路上的进口道往往只允许右转,主路上的进口道可以左转直行右转。如果数据里主辅路的Link没有明确区分字段,算法会把辅路当成完整进口道,生成一堆不存在的左转关系。

区分方法有几个:看Link的车道数(辅路通常1到2车道),看与主路的横向距离(辅路一般距离主路中线10到20米),看是否有物理隔离。这几个指标都不绝对可靠,最稳的还是数据源自带的功能等级或道路类型字段。

匝道的问题更麻烦。匝道进出口的位置往往不在标准交叉口节点上,而是在主路的一个中间节点上,这个节点度可能是3但形态特殊。判定时要注意匝道的驶入驶出方向是单向的,不能生成反向movement。同时匝道的转弯半径通常很小,转角计算出来的结果会明显偏离90度的整数倍,容差要单独放宽。

5.4 多岔口和简易立交

五岔以上的路口,转向配对数量会快速上升,一个五岔口最多有20条movement。数量本身不是问题,问题是方位角间隔会变小,两条相邻进口道的方位角差可能只有30度,转向判定时容易互相干扰。

建议对多岔口做一层额外校验:检查相邻进口道的方位角间隔,如果小于30度就标记为待人工复核。同时检查每个进口道是否至少有一条对应的出口道,如果某条进口道完全没有可达出口,说明数据有问题。

简易立交(跨线桥、下穿隧道)的坑在于上下两层在平面投影上重合,拓扑上也可能被错误连接。如果数据里没有高程信息或分层字段,算法会把跨线桥的引道当成一个平面交叉口,生成一堆实际不存在的转向关系。这个只能靠数据源的层属性解决,算法补不了。

6. 批量落地的质检与踩坑清单

6.1 五条自动质检规则

算法跑完之后,绝对不能直接入库。下面这五条规则我每次都会跑,能拦掉九成以上的低级错误。

编号规则判定条件常见原因
Q1进出口数量失衡单交叉口的进口Link数与出口Link数差超过1双向道路组织方式判断错误
Q2方位角重复同一交叉口两条进口道方位角差小于5度数据重复、双向未拆分、端点未吸附
Q3孤立进口某进口道没有任何可达出口单向限制过严、拓扑断裂
Q4掉头占比异常某交叉口掉头movement占比超过30%方位角计算基准错误、坐标未投影
Q5斜向未知过多某交叉口“斜向未知”movement占比超过20%容差过小、未使用最近整数倍判定

Q4这条规则救过我一次。当时一批数据跑完,某些路口的掉头比例接近50%,肉眼检查路网图完全正常。最后定位到是坐标没有投影,直接用了经纬度算角度,中高纬度地区的经度拉伸导致大量直行被算成掉头。修完坐标系之后比例立刻回到正常水平。

6.2 三个隐蔽的事故

事故一:Link方向反转导致进口出口互换。有一批数据里,某条主干路的Link方向被整体反转了(可能是数据更新时出的问题)。单个路口看,进口出口数量依然平衡,方位角也正常,所有质检规则都能通过。唯一的破绽是转向关系的分布异常——某个进口道生成了大量左转,而且都指向同一条出口道,而对面进口道的转向关系又完全不匹配。最后是靠“对向进口道的movement应当大致对称”这条人工经验发现的。后来我把这条加进了质检规则,检查每个进口道movement的数量是否与其对向进口道相差过大。

事故二:节点聚合半径设太大,把相邻路口吃掉了。城市中心区路口密集,两条平行主干路间距可能只有100多米。节点聚合半径设成50米时,不小心把相邻的两个路口合并成了一个,导致一个本来只有4条进口的路口变成8条,方位角分布从90度间隔变成45度间隔,转向判定全部错乱。后来把聚合半径改成按路口密度动态调整,密集区不超过20米。

事故三:形状点丢失导致方位角跳变。数据格式转换过程中,Link的中间形状点被全部丢弃,只剩首尾两个端点。对于直路没影响,但凡是带弧线的进口道,方位角全部变成了弦方向,和实际驶入方向偏差很大。这个问题最阴险的地方在于,大部分路口是直的,只有少数几个弯道进口出错,整体错误率可能只有3%,抽样检查很容易漏过去。解决办法是把“Link形状点数量为0”作为一个独立的数据质量指标统计。

6.3 入库字段设计

判定结果怎么组织,直接影响下游好不好用。我的做法是分两张表存。

交叉口主表记录节点级别信息:交叉口ID、聚合后的中心坐标、节点度、进口道数量、出口道数量、方位角分布、质检状态。一张明细表记录每条关联Link的方向判定结果:

字段名类型说明
inter_id字符串交叉口唯一标识
link_id字符串关联Link标识
role枚举inlet(进口)/ outlet(出口)
azimuth浮点该角色对应的方位角,0到360
octant字符串八方位标签
seq_no整数顺时针序号,从正北开始
lane_cnt整数车道数,用于车道级扩展
src_flag枚举数据来源:原始属性/算法推断

role字段很关键。对向拆分的数据里一条Link只会出现一次,role是inlet或outlet其中之一;单Link双向的数据里同一条Link会出现两次,两条记录的role不同、azimuth相差180度。下游调用时按role取,不要按Link去重。

src_flag是我后来加的,用来区分哪些判定是数据直接给的、哪些是算法推的。人工复核时优先看算法推断的部分,效率高很多。

再往下还有一张movement表,记录进口道到出口道的转向关系,字段包括in_link、out_link、turn_type、delta_angle、allowed(是否可达)。这张表是仿真建模和流量矩阵对齐的直接输入,行数会比较多,一个标准十字路口大约12到16条记录(去掉部分掉头)。

6.4 验证环节怎么做才有效

自动化质检只能拦结构性问题,语义正确性还得靠人工。有效的抽样策略不是纯随机,而是分层抽样加异常优先:

先按节点度分层,度3、度4、度5以上各抽若干;再按方位角分布分层,间隔均匀的和间隔异常的分别抽;最后把质检规则命中的路口全部纳入复核,这部分通常只占总量的一小部分,但问题最集中。

复核时我习惯把判定结果直接画在路网上:进口道用一种颜色,出口道用另一种,方位角标签标在Link末端。肉眼扫一遍,哪里不对劲一眼就能看出来。纯看数字表格效率极低,可视化能省掉大量时间。

还有一个校验手段是和实测转向流量对照。如果有真实的转向流量数据,把movement表和流量表按“进口方向+转向类型”做关联,检查是否有流量落在了“不可达”的movement上。只要出现这种情况,要么是流量数据的方向标错了,要么是可达性判定漏了限制,两种都是必须查清的问题。


这套流程我从最早的手工标注,到现在基本能自动跑完加少量人工复核,中间迭代了大概三四版。最大的体会是:方位角公式本身五分钟就能写对,真正花时间的是数据组织方式的判断、坐标系的统一、特殊路口的兜底,以及那几条看起来不起眼但能救命的质量检查。如果只让我留一个建议,那就是在任何计算之前,先用十个已知路口把数据的Link方向组织方式彻底搞清楚,这一步做扎实,后面所有环节都会顺很多。

返回列表