絡(luò)平衡分析:從UE原理到Frank-Wolfe配流實(shí)現(xiàn))
簡(jiǎn)介黃海軍的《城市交通網(wǎng)絡(luò)平衡分析理論與實(shí)踐》是一本聚焦城市交通網(wǎng)絡(luò)建模與優(yōu)化的專業(yè)文獻(xiàn)面向交通工程、軌道交通及相關(guān)領(lǐng)域的研究者、規(guī)劃師和高校師生旨在幫助讀者理解交通網(wǎng)絡(luò)平衡原理并應(yīng)對(duì)擁堵、延誤等城市交通頑疾為城市交通治理提供系統(tǒng)方法論。資源為單個(gè)PDF文件大小約8.1MB內(nèi)容精煉完整已有608人學(xué)習(xí)關(guān)注。理論層面書(shū)中系統(tǒng)涵蓋網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)分析、交通流基本規(guī)律、出行時(shí)間與速度變化特征、通行能力約束等核心內(nèi)容實(shí)踐層面則介紹了交通監(jiān)控系統(tǒng)、模擬仿真、優(yōu)化算法、信息化平臺(tái)和交通規(guī)劃設(shè)計(jì)等落地手段。全書(shū)將理論推導(dǎo)與工程應(yīng)用緊密結(jié)合適合作為交通網(wǎng)絡(luò)平衡分析的參考書(shū)助力讀者構(gòu)建從基礎(chǔ)概念到實(shí)際評(píng)估的系統(tǒng)知識(shí)框架。1. 城市交通網(wǎng)絡(luò)平衡分析拿到 OD 矩陣后堵車(chē)是怎么算出來(lái)的如果你手里只有一張?jiān)绺叻?OD 矩陣和一套路網(wǎng)想提前知道明天哪個(gè)路口會(huì)排長(zhǎng)隊(duì)交通分配就是那個(gè)把需求壓到路網(wǎng)上、算出每條路流量的環(huán)節(jié)。這個(gè)環(huán)節(jié)在學(xué)術(shù)界有個(gè)更嚴(yán)格的名字城市交通網(wǎng)絡(luò)平衡分析。黃海軍的《城市交通網(wǎng)絡(luò)平衡分析理論與實(shí)踐》把這個(gè)環(huán)節(jié)的數(shù)學(xué)根子講透了用戶均衡UE假設(shè)每個(gè)人都只走當(dāng)前最快的路系統(tǒng)最優(yōu)SO假設(shè)有人統(tǒng)一調(diào)度兩者之間的差距就是交通治理的空間。這本書(shū)適合兩類人——一類是在 TransCAD、Emme 里點(diǎn)鼠標(biāo)點(diǎn)到心虛的模型師另一類是想把商業(yè)軟件當(dāng)黑匣子拆開(kāi)、自己寫(xiě)配流內(nèi)核的研究者。下面我按“先立住概念、再交出可復(fù)現(xiàn)代碼、最后補(bǔ)齊踩坑”的順序把這個(gè)方向講成一張能照著施工的圖紙。2. 從用戶均衡到系統(tǒng)最優(yōu)模型假設(shè)與適用邊界交通分配的外行版本是“哪條路短走哪條”聽(tīng)起來(lái)簡(jiǎn)單但一旦所有車(chē)都這么想最短的路會(huì)變堵然后有人改走次短路最后收斂到一個(gè)誰(shuí)都不愿意再換路的狀態(tài)。這個(gè)狀態(tài)才是平衡。黃海軍這套理論的核心是給這種“所有人互相博弈”的集體行為找一個(gè)嚴(yán)格數(shù)學(xué)定義。UE 和 SO 是這個(gè)定義的兩端UE 描述自由選擇下的現(xiàn)實(shí)SO 描述管制調(diào)度下的理想模型師必須在兩端之間反復(fù)切換才能回答不同層面的問(wèn)題。2.1 用戶均衡每個(gè)司機(jī)都以為自己在走最快路用戶均衡直接來(lái)自 Wardrop 第一原理同一 OD 對(duì)之間所有被使用的路徑其行程時(shí)間相等并且小于等于任何未被使用路徑的行程時(shí)間。翻譯成人話沒(méi)有一個(gè)人能靠單方面換路縮短行程時(shí)間大家誰(shuí)都沒(méi)動(dòng)力再動(dòng)系統(tǒng)就停了。光背定義不好消化我給個(gè)能用手算驗(yàn)證的例子。設(shè) OD 需求 3000 pcu/h有兩條平行路徑A 路自由流 10 分鐘、通行能力 1000 pcu/hB 路自由流 15 分鐘、通行能力 2000 pcu/h。路阻函數(shù)用工程里最常見(jiàn)的 BPR 形式t_a(x_a) t_a^0 * (1 alpha * (x_a / cap_a)^beta)alpha 取 0.15beta 取 4。假設(shè)流量 x 走 A剩余 3000 - x 走 B均衡條件是兩條路時(shí)間相等10 * (1 0.15 * (x / 1000)^4) 15 * (1 0.15 * ((3000 - x) / 2000)^4)這個(gè)方程沒(méi)有手算解析解但用二分法卡幾個(gè)值就能找到答案A 路約 1410 pcu/hB 路約 1590 pcu/h均衡行程時(shí)間約 15.9 分鐘。我列一張表方便你對(duì)照路徑自由流時(shí)間min通行能力pcu/h均衡流量pcu/h均衡時(shí)間minA101000141015.9B152000159015.9注意一個(gè)容易被質(zhì)疑的細(xì)節(jié)A 路自由流快但容量小最后只分到 47% 的流量B 路自由流慢但容量大反而承擔(dān)更多。這就是“個(gè)體理性導(dǎo)致整體次優(yōu)”的第一層體現(xiàn)。在項(xiàng)目匯報(bào)里這張表對(duì)應(yīng)的場(chǎng)景是平行通道分擔(dān)失衡領(lǐng)導(dǎo)會(huì)問(wèn)為什么快路沒(méi)被塞滿你把均衡時(shí)間算出來(lái)他才明白快路一旦塞滿就不再快。2.2 系統(tǒng)最優(yōu)為什么個(gè)體都理性路網(wǎng)反而沒(méi)在最優(yōu)如果把目標(biāo)從“每個(gè)司機(jī)自主選擇”換成“全網(wǎng)總行程時(shí)間最小”模型就切換到系統(tǒng)最優(yōu)。SO 的數(shù)學(xué)表達(dá)是min Z_SO Σ_a x_a * t_a(x_a)這個(gè)目標(biāo)函數(shù)直接用當(dāng)前行程時(shí)間做最短路是解錯(cuò)的因?yàn)槊總€(gè)司機(jī)換路時(shí)不會(huì)考慮自己加入后對(duì)后面所有車(chē)造成的額外延誤。正確的路阻應(yīng)該是邊際時(shí)間成本t_a(x_a) x_a * d t_a / d x_a也就是讓每個(gè)使用者承擔(dān)“我多走一個(gè)單位流量給全網(wǎng)帶來(lái)的額外延遲”。BPR 函數(shù)代入后邊際項(xiàng)比普通項(xiàng)多出一個(gè)大數(shù)所以 SO 分配出來(lái)的流量會(huì)往容量更寬的路段轉(zhuǎn)移犧牲部分個(gè)體的短時(shí)利益來(lái)?yè)Q整體效率。UE 和 SO 的總行程時(shí)間差距就是過(guò)度擁堵的量化值。拿上面兩條路徑繼續(xù)算UE 全網(wǎng)總時(shí)間約 3000 * 15.9 47700 分鐘SO 會(huì)引導(dǎo)更多車(chē)走 B 路總時(shí)間比 UE 低幾個(gè)百分點(diǎn)。真實(shí)路網(wǎng)中這個(gè)差距通常在 2% 到 15% 之間取決于網(wǎng)絡(luò)冗余度。做方案評(píng)估時(shí)如果你拿 UE 結(jié)果直接算“采取管理措施后節(jié)省了多少時(shí)間”等于假設(shè)司機(jī)真的服從調(diào)度。大多數(shù)項(xiàng)目里這個(gè)前提不成立所以評(píng)估限行、收費(fèi)、信號(hào)優(yōu)先這類政策時(shí)要么用 SO 做理想上界要么在 UE 基礎(chǔ)上疊加行為響應(yīng)模型。2.3 程序員的讀法把均衡翻譯成凸優(yōu)化和最短路徑迭代UE 不只是一個(gè)經(jīng)濟(jì)學(xué)假設(shè)它有一個(gè)漂亮的等價(jià)數(shù)學(xué)形式Beckmann 變換。找一個(gè)流量向量 x極小化Z(x) Σ_a ∫_0^{x_a} t_a(w) dw這個(gè)目標(biāo)函數(shù)沒(méi)有直觀物理含義但它的梯度恰好是路段行程時(shí)間 t_a(x_a)而 UE 條件正好是它的一階最優(yōu)性條件也就是 KKT 條件。這是整本書(shū)里最值得反復(fù)讀的一段。只要 t_a 是嚴(yán)格單調(diào)遞增函數(shù)這個(gè)凸問(wèn)題有唯一解Frank-Wolfe 算法就是順著這個(gè)性質(zhì)設(shè)計(jì)的。把理論翻譯成程序我習(xí)慣走三步把 Wardrop 條件寫(xiě)成 KKT對(duì)每個(gè) OD 對(duì)被使用路徑的時(shí)間等于最短 OD 時(shí)間未使用路徑的時(shí)間不低于它。把 BPR 路阻函數(shù)當(dāng)作目標(biāo)函數(shù)的梯度來(lái)源流量更新就等價(jià)于沿梯度方向逼近最優(yōu)。用“反復(fù)求最短路徑 沿方向走一小步”替代直接解非線性方程組因?yàn)檎鎸?shí)路網(wǎng)有幾千個(gè)節(jié)點(diǎn)純解析解法在工程上不可行。這三步做完你會(huì)明白為什么配流軟件的底層循環(huán)都長(zhǎng)一個(gè)樣算路段時(shí)間、全有全無(wú)分配、更新流量、檢查收斂。下一章就按這個(gè)循環(huán)把最小代碼寫(xiě)出來(lái)。3. 用 Frank-Wolfe 把理論跑成配流程序最小可復(fù)現(xiàn)實(shí)現(xiàn)很多模型師覺(jué)得 UE 配流是商業(yè)軟件的內(nèi)置黑盒其實(shí)它的迭代邏輯非常短。這一章給一個(gè)能直接在 Jupyter 里跑的最小實(shí)現(xiàn)NetworkX 負(fù)責(zé)最短路搜索SciPy 做一維線搜索剩下的路線邏輯全部手寫(xiě)方便你觀察中間過(guò)程、改參數(shù)、調(diào)試奇怪結(jié)果。3.1 數(shù)據(jù)準(zhǔn)備路網(wǎng)表、OD 表和 BPR 路阻參數(shù)先約定輸入格式。路網(wǎng)表至少要有四列起始節(jié)點(diǎn)、終到節(jié)點(diǎn)、自由流時(shí)間、通行能力。BPR 參數(shù) alpha 和 beta 可以放在邊屬性里方便按道路等級(jí)分開(kāi)設(shè)置。OD 表就是起始、終到、需求量單位我統(tǒng)一用 pcu/h否則后面所有對(duì)比都會(huì)亂。參數(shù)常見(jiàn)初值作用踩坑提示alpha0.15擁堵項(xiàng)系數(shù)不同道路類型應(yīng)分開(kāi)標(biāo)定beta4.0擁堵項(xiàng)指數(shù)beta 固定 4 時(shí)更容易擬合其他參數(shù)capacity實(shí)際通行能力決定曲線陡升位置雙車(chē)道不是單車(chē)道簡(jiǎn)單乘 2demand分時(shí)段 OD輸入規(guī)模全天揉成一張 OD 基本不能用gap 閾值1e-4收斂判據(jù)商業(yè)軟件常用 0.1% 到 1%讀 CSV 建立 NetworkX 有向圖的代碼import networkx as nx G nx.DiGraph() with open(network.csv, encodingutf-8) as f: for row in f: parts row.strip().split(,) if len(parts) 6: continue u, v parts[0], parts[1] t0, cap float(parts[2]), float(parts[3]) alpha, beta float(parts[4]), float(parts[5]) # flow 初始為 0后面每次迭代直接更新邊屬性 G.add_edge(u, v, t0t0, capcap, alphaalpha, betabeta, flow0.0)節(jié)點(diǎn) ID 可以是字符串也可以是整數(shù)但 CSV 和 OD 表必須保持一致。如果 OD 表里出現(xiàn)路網(wǎng)不存在的節(jié)點(diǎn)NetworkX 會(huì)在最短路搜索時(shí)報(bào) KeyError這個(gè)錯(cuò)誤信息對(duì)新手不太友好后面避坑章會(huì)專門(mén)講。3.2 UE 配流主循環(huán)從 AON 到收斂只需 60 行Frank-Wolfe 每次迭代做兩件事按當(dāng)前流量更新路段時(shí)間再做一次全有全無(wú)分配得到輔助流量然后沿兩者的差方向走一步。先寫(xiě)路阻函數(shù)、全有全無(wú)分配和目標(biāo)函數(shù)from scipy.optimize import minimize_scalar def edge_cost(e, flow): # BPR 路阻函數(shù)自由流時(shí)間 * (1 alpha * (流量/容量)^beta) return e[t0] * (1 e[alpha] * (flow / e[cap]) ** e[beta]) def set_times(G, flow): for u, v, e in G.edges(dataTrue): e[time] edge_cost(e, flow[(u, v)]) def all_or_nothing(G, flow, od): set_times(G, flow) aux {(u, v): 0.0 for u, v in G.edges()} shortest_total 0.0 for o, d, q in od: dist nx.dijkstra_path_length(G, o, d, weighttime) shortest_total q * dist path nx.dijkstra_path(G, o, d, weighttime) for a, b in zip(path[:-1], path[1:]): aux[(a, b)] q return aux, shortest_total def objective(G, flow): # Beckmann 目標(biāo)函數(shù)積分用解析式展開(kāi)beta1 出現(xiàn)在分母 z 0.0 for u, v, e in G.edges(dataTrue): cap, beta e[cap], e[beta] z e[t0] * (flow[(u, v)] e[alpha] * cap / (beta 1) * (flow[(u, v)] / cap) ** (beta 1)) return zedge_cost是 BPR 路阻all_or_nothing把每個(gè) OD 的需求全壓到當(dāng)前最短路徑上同時(shí)返回按最短路徑時(shí)間算出的理想總時(shí)間為后面計(jì)算 gap 做準(zhǔn)備objective是 Beckmann 目標(biāo)函數(shù)注意積分展開(kāi)后的系數(shù)beta 等于 4 時(shí)分母是 5。主循環(huán)如下flow {(u, v): 0.0 for u, v in G.edges()} aux, _ all_or_nothing(G, flow, od) for e in flow: flow[e] aux[e] for k in range(1, 200): aux, shortest_total all_or_nothing(G, flow, od) d {e: aux[e] - flow[e] for e in flow} # Frank-Wolfe 方向 def phi(lam): # 在當(dāng)前流與輔助流之間試一個(gè)步長(zhǎng)返回目標(biāo)函數(shù)值 trial {e: flow[e] lam * d[e] for e in flow} return objective(G, trial) res minimize_scalar(phi, bounds(0, 1), methodbounded) lam res.x flow {e: flow[e] lam * d[e] for e in flow} sys_total sum(edge_cost(e, flow[e]) * flow[e] for u, v, e in G.edges(dataTrue)) gap (sys_total - shortest_total) / sys_total print(k, round(lam, 4), round(gap, 6)) if gap 1e-4: breakd是 Frank-Wolfe 方向phi(lam)做一維線搜索找一個(gè)讓目標(biāo)函數(shù)最小的步長(zhǎng)。這里用scipy.optimize.minimize_scalar的有界搜索模式比手寫(xiě)黃金分割省事且更穩(wěn)。輸出里gap就是相對(duì)對(duì)偶間隙gap 降到 1e-4 以下認(rèn)為收斂。商業(yè)軟件默認(rèn)閾值經(jīng)常是 0.1% 到 1%自寫(xiě)程序建議至少放到 1e-4否則斷面流量會(huì)差幾個(gè)百分點(diǎn)。3.3 步長(zhǎng)與收斂判據(jù)MSA 和線搜索該選哪個(gè)Frank-Wolfe 最經(jīng)典的簡(jiǎn)化是固定步長(zhǎng) 1/k這個(gè)方案叫 MSA代碼只需替換一行l(wèi)am 1.0 / k不需要線搜索。它的優(yōu)點(diǎn)是穩(wěn)定不會(huì)在目標(biāo)函數(shù)不平滑時(shí)亂跳缺點(diǎn)是收斂慢。BPR 在容量附近很陡目標(biāo)函數(shù)接近但并不是嚴(yán)格的二次型1/k 的衰減速度在后期會(huì)讓 gap 卡在 1e-2 級(jí)別看起來(lái)很急。我的習(xí)慣是教學(xué)網(wǎng)絡(luò)和剛跑通的骨架用 MSA因?yàn)樾袨槿菀捉忉屨鎸?shí)路網(wǎng)第一版一定用線搜索等模型跑順后再評(píng)估要不要換投影類算法。線搜索還能順帶檢查目標(biāo)函數(shù)寫(xiě)沒(méi)寫(xiě)錯(cuò)——如果minimize_scalar返回的步長(zhǎng)長(zhǎng)期在 0.01 以下往往不是算法問(wèn)題而是某個(gè)邊容量小到離譜流量一上去路阻就爆了。相對(duì)對(duì)偶間隙的完整公式gap (Σ_a x_a * t_a(x_a) - Σ_rs q_rs * u_rs) / Σ_a x_a * t_a(x_a)其中 u_rs 是當(dāng)前路段時(shí)間下 OD 對(duì) rs 的最短路徑時(shí)間。分子是“全網(wǎng)因?yàn)槔@路多花的時(shí)間”分母是“當(dāng)前實(shí)際總行程時(shí)間”。gap 高說(shuō)明還有大量車(chē)沒(méi)找到更短路徑gap 低說(shuō)明再重新分配也榨不出多少收益。第五個(gè)避坑點(diǎn)里 gap 卡住不降的問(wèn)題就是從這個(gè)公式發(fā)端的。4. 把配流結(jié)果當(dāng)工具用五個(gè)關(guān)鍵參數(shù)與校正順序跑通最小實(shí)現(xiàn)后真正的工程挑戰(zhàn)是讓結(jié)果可信。這一章集中講五個(gè)關(guān)鍵參數(shù)alpha、beta、capacity、OD 需求、收斂閾值。前四個(gè)決定模型的骨架最后一個(gè)決定你信不信這個(gè)骨架。校準(zhǔn)時(shí)有個(gè)鐵律先修路網(wǎng)再調(diào)容量最后才動(dòng) OD順序亂了基本等于給自己挖坑。4.1 BPR 標(biāo)定別把 0.15/4 當(dāng)萬(wàn)能默認(rèn)值BPR 里的 0.15 和 4 是教材默認(rèn)值最初來(lái)自高速公路回歸經(jīng)驗(yàn)換到城市快速路、信號(hào)交叉口、匝道就不一定對(duì)。最務(wù)實(shí)的做法是找?guī)锥斡芯€圈檢測(cè)的路段拿到分時(shí)段流量和實(shí)際行程時(shí)間用最小二乘擬合 alpha 和 betafrom scipy.optimize import curve_fit def bpr(v, t0, cap, alpha, beta): return t0 * (1 alpha * (v / cap) ** beta) # volume 是觀測(cè)流量序列travel_time 是同一時(shí)段的實(shí)際行程時(shí)間 popt, _ curve_fit(bpr, volume, travel_time, p0[10, 1000, 0.15, 4.0])volume和travel_time來(lái)自同一路段的歷史檢測(cè)數(shù)據(jù)。這里有個(gè)統(tǒng)計(jì)陷阱beta 對(duì)曲線尾部形狀影響極大但日常觀測(cè)數(shù)據(jù)往往集中在低飽和度區(qū)間beta 的辨識(shí)度很差。所以我一般固定 beta 4只標(biāo)定 alpha、t0、cap。t0 用平峰自由流車(chē)速反推cap 用斷面飽和流率實(shí)測(cè)alpha 只用擁堵時(shí)段的流量時(shí)間對(duì)擬合這樣每個(gè)參數(shù)都有物理錨點(diǎn)不會(huì)出現(xiàn)“擬合出來(lái) alpha 是負(fù)數(shù)”這種玄學(xué)結(jié)果。4.2 OD 矩陣配流結(jié)果一半的誤差出在需求矩陣上很多第一次自建模型的人把注意力全放在算法上忽略配流對(duì) OD 矩陣的敏感程度。OD 需求差 10%擁堵路段流量可能差 20% 以上因?yàn)?BPR 曲線在容量附近是非線性的流量一過(guò) capacity 時(shí)間曲線直接抬頭。常見(jiàn)做法是先按早高峰 1 小時(shí)、平峰 1 小時(shí)、晚高峰 1 小時(shí)分別做時(shí)段 OD而不是把全天 24 小時(shí)揉成一張表。如果只有 24 小時(shí)總量就先按調(diào)查得到的時(shí)間分布系數(shù)拆成時(shí)段需求。OD 矩陣還需要平滑相鄰小區(qū)之間的需求往往會(huì)集中在少數(shù)幾條路徑原始 OD 直接分配會(huì)讓某些路段流量比實(shí)測(cè)高 30%另一些路段是 0。這時(shí)可以引入隨機(jī)項(xiàng)做 SUE 分配或者對(duì) OD 做空間平滑。但千萬(wàn)別為了擬合斷面流量而“拍腦袋調(diào) OD”那是在給自己埋雷后面第 4.3 節(jié)會(huì)解釋為什么。4.3 用 GEH 校驗(yàn)斷面流量先修路網(wǎng)再修 OD配流完成后的第一件事是把模型斷面流量和線圈檢測(cè)值對(duì)一對(duì)。工程界常用 GEH 指標(biāo)GEH sqrt( 2 * (q_est - q_obs)^2 / (q_est q_obs) )GEH 范圍建議 5可接受5 ~ 10需檢查周邊路網(wǎng)編碼與容量 10必須查 OD 或路網(wǎng)不能跳過(guò)調(diào)模型有個(gè)順序也是血淚經(jīng)驗(yàn)先查路網(wǎng)編碼錯(cuò)沒(méi)錯(cuò)再調(diào)通行能力和自由流時(shí)間最后才動(dòng) OD。很多人一上來(lái)就調(diào) OD結(jié)果斷面流量對(duì)上了但路徑結(jié)構(gòu)完全不合理回頭想改回真實(shí)需求難如登天。路網(wǎng)是底子OD 是需求事實(shí)兩者都不該為了擬合觀測(cè)隨意改動(dòng)。遇到 GEH 大于 10 的斷面我一般先打開(kāi)路網(wǎng)看上下游有沒(méi)有斷邊、方向反、節(jié)點(diǎn)拼錯(cuò)這類低級(jí)錯(cuò)誤在自建模型里能占到一半以上。另外收斂閾值不要一上來(lái)就要求 1e-6。真實(shí)路網(wǎng)的觀測(cè)誤差本身就大于 5%把 gap 壓到 1e-6 只會(huì)讓模型吞掉大量噪聲時(shí)間花在不必要的迭代上。我的做法是基礎(chǔ)方案用 1e-4方案對(duì)比時(shí)統(tǒng)一用 1e-4只有寫(xiě)論文時(shí)才跑 1e-6。5. 配流避坑指南求解器不收斂到反常識(shí)結(jié)果五個(gè)高頻翻車(chē)現(xiàn)場(chǎng)理論講得再漂亮一跑真實(shí)路網(wǎng)就露餡。下面五個(gè)問(wèn)題是我在項(xiàng)目里反復(fù)遇到的每個(gè)都按現(xiàn)象、原因、解決三段寫(xiě)你復(fù)現(xiàn)時(shí)大概率會(huì)撞上至少一個(gè)。5.1 現(xiàn)象迭代 200 次gap 還在 1% 附近鋸齒狀看起來(lái)結(jié)果已經(jīng)“差不多”但閾值降到 1e-4 就是達(dá)不到gap 曲線像鋸齒一樣上下跳。原因通常是步長(zhǎng)策略和路阻函數(shù)不匹配如果用 MSA1/k 衰減太慢后期每一步都在原地踏步如果用線搜索則可能是某個(gè)邊容量設(shè)得過(guò)大路阻函數(shù)太平滑方向向量 d 已經(jīng)接近零。解決方法是先改用線搜索再檢查有沒(méi)有容量小到離譜的邊如果問(wèn)題依舊就把 gap 定義里的最短路目標(biāo)項(xiàng)加上轉(zhuǎn)向懲罰不要讓模型利用不現(xiàn)實(shí)的繞行路徑。5.2 現(xiàn)象路段行程時(shí)間比自由流還低BPR 函數(shù)的輸出永遠(yuǎn)大于等于自由流時(shí)間所以出現(xiàn)比自由流還低的結(jié)果一定是公式實(shí)現(xiàn)錯(cuò)了。最常見(jiàn)原因是在代碼里寫(xiě)成了t0 * alpha * (flow / cap) ** beta少了括號(hào)里的1 其次是流量和容量單位不一致流量是 pcu/h容量標(biāo)成了 veh/s數(shù)值上看起來(lái)像負(fù)數(shù)校正。解決寫(xiě)一個(gè)單元測(cè)試構(gòu)造流量為 0 的邊斷言返回 t0構(gòu)造流量等于 capacity 的邊斷言返回 t0 * (1 alpha)。這兩行斷言能擋住一半以上的低級(jí)錯(cuò)誤。5.3 現(xiàn)象零流量路段永遠(yuǎn)是 0手算時(shí)明明該有車(chē)UE 的性質(zhì)決定“未使用路徑”流量就是 0但為什么一條和最短路徑只差 0.1 分鐘的平行邊沒(méi)車(chē)第一初始化全 0 后第一次 AON 選擇了最短邊之后線搜索步長(zhǎng)如果一直小于 1較慢邊始終分不到流量第二這其實(shí)是 UE 的角點(diǎn)解在真實(shí)世界很難出現(xiàn)因?yàn)闆](méi)人能掌握精確到 0.1 分鐘的路徑時(shí)間。解決如果關(guān)心這類接近并列的路徑就不要用純 UE改用 SUE 或者給路徑加一個(gè)感知誤差項(xiàng)。UE 適合看擁堵主骨架不適合回答“為什么這條次路一輛車(chē)都沒(méi)有”的微觀問(wèn)題。5.4 現(xiàn)象MSA 步長(zhǎng)讓流量像布朗運(yùn)動(dòng)收斂曲線拉不回來(lái)MSA 的 1/k 理論上能保證收斂但真實(shí)路網(wǎng)有大量非線性步長(zhǎng)序列會(huì)帶來(lái)長(zhǎng)時(shí)間震蕩。現(xiàn)象是前幾十次迭代流量大幅擺動(dòng)后面擺幅減小但 gap 紋絲不動(dòng)。解決把固定步長(zhǎng)換成線搜索后基本能恢復(fù)如果還想保留 MSA 的簡(jiǎn)潔可以用幾何平均步長(zhǎng)或?qū)ι洗蔚髁孔鲋笖?shù)平滑。注意震蕩本身是信息如果震蕩幅度大到 10%說(shuō)明路網(wǎng)存在兩條幾乎一樣短的路模型在它們之間來(lái)回跳這時(shí)候該去看路徑集而不是繼續(xù)調(diào)參數(shù)。5.5 現(xiàn)象商業(yè)軟件和自己寫(xiě)的程序結(jié)果差兩成懷疑書(shū)錯(cuò)了自寫(xiě) UE 收斂到 1e-4和商業(yè)軟件默認(rèn)結(jié)果差 20%第一反應(yīng)通常是理論有問(wèn)題。實(shí)際查下來(lái)大概率是三個(gè)差異疊加商業(yè)軟件默認(rèn)用 SUE 或帶路徑規(guī)模懲罰的模型BPR 參數(shù)和你設(shè)置的不一致收斂閾值不同。還有一個(gè)容易被忽略的因素是商業(yè)軟件對(duì)零點(diǎn)流量做了平滑處理。解決先把兩邊路網(wǎng)、OD、BPR 參數(shù)、收斂閾值統(tǒng)一成完全一樣的輸入再用一個(gè)只有兩條路徑的小網(wǎng)絡(luò)做對(duì)照實(shí)驗(yàn)。如果小網(wǎng)絡(luò)一致、大網(wǎng)絡(luò)不一致再去排查轉(zhuǎn)向阻抗、收費(fèi)、信號(hào)延誤等附加邏輯。6. 進(jìn)階玩法把平衡配流接到交通仿真之前先做三件事當(dāng)你把 UE 跑穩(wěn)下一步不是馬上接仿真軟件而是先做三件常規(guī)驗(yàn)證否則仿真階段翻車(chē)的代價(jià)會(huì)高出十倍。6.1 先做多模式均衡公交和小汽車(chē)的迭代反饋真實(shí)路網(wǎng)里公交和小汽車(chē)共享路權(quán)公交太慢會(huì)讓更多人開(kāi)小汽車(chē)小汽車(chē)變多又反過(guò)來(lái)讓公交延誤增加。常見(jiàn)做法是雙層迭代先跑小汽車(chē) UE得到各路段時(shí)間更新公交行程時(shí)間然后用 logit 模型調(diào)整模式分擔(dān)比例重新生成 OD再跑配流直到分擔(dān)比例變化小于 0.5%。注意 logit 的尺度參數(shù)需要單獨(dú)標(biāo)定否則分擔(dān)比例對(duì)時(shí)間差過(guò)于敏感一次小小的擁堵改善就可能讓公交分擔(dān)率暴漲 10 個(gè)百分點(diǎn)。6.2 用 KKT 條件給求解器做體檢路徑時(shí)間相等嗎UE 收斂后抽幾個(gè)需求大的 OD 對(duì)枚舉前若干條路徑檢查它們的時(shí)間是否真的相等。代碼很短for path in nx.shortest_simple_paths(G, o, d, weighttime, cutoff5): tt sum(G[a][b][time] for a, b in zip(path[:-1], path[1:])) print(round(tt, 4), path)只要被使用的路徑和枚舉出的最短路徑時(shí)間差超過(guò) 0.1%就說(shuō)明收斂判據(jù)或路徑加載邏輯還有問(wèn)題。大路網(wǎng)枚舉不了所有路徑就退回去看 gapgap 到 1e-4 之后路徑時(shí)間差通常在 0.05% 以內(nèi)。6.3 做一張敏感性參數(shù)表擾動(dòng) OD 和容量看流量變化范圍最后往 OD 需求上加 ±10% 擾動(dòng)再把關(guān)鍵路段容量調(diào)高調(diào)低 10%各跑二十次配流把重點(diǎn)斷面的流量變化范圍記成一張表。這個(gè)表才是給決策者的交付物比單點(diǎn)結(jié)果更接近真實(shí)不確定性。我現(xiàn)在每個(gè)項(xiàng)目交付前都會(huì)跑一遍這個(gè)敏感性矩陣花二十分鐘能擋掉后面一個(gè)月的扯皮。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取