做人群仿真的项目,最累的不是建模,也不是调参,而是跑完那一刻对着满屏结果文件发呆。SimWalk 算完一个场景,会给你几十种输出:密度图、轨迹、疏散时间、服务水平统计……每一样都像是答案的一部分,但又都不是现成的答案。这篇是 SimWalk 系列教程的第九篇,专门讲结果分析与可视化。不管你是做地铁站疏散评估、大型活动应急预案,还是商场动线优化,只要你的工作流里用到了 SimWalk,这篇都能帮上忙,核心就一件事:把跑出来的原始结果,变成能拍板的结论,也让外行一眼看懂仿真到底说了什么。我自己用 SimWalk 做了好几个实际项目,踩过的坑和沉淀下来的方法,下面一次性讲透。
1. 结果分析之前,先搞清楚SimWalk到底输出了什么
1.1 五大类结果文件,别拿到数据就晕
SimWalk 不像普通计算软件只给一个汇总表,它默认会输出一堆结果视图和数据文件。我用的是 SimWalk 4.x 版本,界面功能名在不同版本里叫法可能不一样,但原理大同小异。从实际项目经验来看,最常用的输出可以分成五类,每一类都有自己的使用场景和读取方法。
首先是密度图(Density Diagram)。SimWalk 把场景网格化,每个格子里统计行人存在时间的占比,平均之后得到密度值,用颜色体现。网格大小非常关键,我通常设置 0.5 米乘 0.5 米,太大会把瓶颈细节抹平,太小则噪声偏大,热力图会花成一片。密度图的颜色默认从蓝绿渐变到黄红,蓝色代表几乎没人,红色代表密度非常高。有一个小技巧:在结果显示设置里把颜色范围手动固定,不要用自动范围。否则你对比两个场景的时候会误读,一个最大密度只有 1.0 的场景,和一个最大密度到 3.0 的场景,自动范围会让前者也红得不轻,但实际上两码事。
其次是流量图(Flow Diagram)。SimWalk 里可以画测量截面(Section line),统计通过这个截面的双向流量和总流量。流量图上的箭头方向是统计时间内行人移动的方向,注意,箭头是聚合结果,不是单个行人。如果箭头在某个区域交叉打结,说明动线冲突明显,这就是需要重点优化的地方。流量数据是判断“通行能力够不够”的第一手证据,比单纯看密度图要更接近问题本质。
第三类是轨迹图(Trajectories / Route Lines)。每条线代表一个代理的路径,可以按速度着色。轨迹图最容易暴露“绕路”问题:在开阔区域出现密集的弧线,说明人们没走最短路径,很可能被障碍物、排队区或引导标识影响。轨迹线太多时画面会很乱,我一般只显示 20% 左右的代理,看起来干净,趋势也清晰。
第四类是疏散/行走时间结果。疏散场景里我最关心的是“最晚离开时间”和“95 百分位离场时间”。规范一般要求所有人离开时间,但最后一个人的离场时间受随机因素影响很大,所以我更爱看 95 分位,更稳定。SimWalk 可以直接在结果面板里看,也可以导出 CSV 自己统计。
第五类是服务水平统计(LOS Table)。软件会按 Fruin 标准,把每片区域的服务等级划分成 A 到 F,用色块展示。密度阈值通常是:A 级小于 0.31 人/平方米,B 级 0.31 到 0.43,C 级 0.43 到 0.72,D 级 0.72 到 1.08,E 级 1.08 到 2.17,F 级大于 2.17。这个标准做报告非常好用,直接说“某区域服务水平为 E,人流量已经超过舒适范围”,业主马上能理解。
1.2 从数据到结论的分析逻辑:把数字翻译成决策
拿到这些输出之后,怎么用?我经常跟项目组说,结果分析就做三件事:找瓶颈、验状态、给结论。这三件事,各有各的对应数据。
找瓶颈,主要看密度。密度超过 1.08 人/平方米时,已经到 D 级,人开始频繁调整速度,走起来很不舒服;超过 2.17 就是 F 级,几乎堵死。SimWalk 密度图上这些区域会呈现红褐色,一眼就能看到。但光看颜色还不够,我一般会把最大密度对应的时间步长也找出来,再配合流量图看这段时间通过瓶颈截面的通行量有没有断崖式下跌。如果密度高而流量也高,那只是拥挤;如果密度高而流量掉下去了,那就是真堵车式停滞。
验状态,看速度和排队。SimWalk 能统计平均行走速度。如果某段通道平均速度低于每秒 0.5 米,大概率已经形成拥堵。排队长度可以看轨迹是否在某处大量重合并长时间停滞。再把服务水平统计加上,一个区域的舒适度就能量化成“能在报告里写数字”的结论。比如“候车厅入口处平均密度 1.30,LOS 为 E,平均步行速度下降到 0.42 米/秒,队伍长度约 12 米”。
给结论,就是把疏散时间、密度、LOS、最大排队长度这些指标汇总成一张表,对应到业主关心的合规性和体验问题。这个过程必须结合场景,同样的密度在楼梯口和候车厅,含义完全不同。所以我做分析时,习惯把 SimWalk 结果分成两半:左边是数值摘要,右边是对应视图截图,在报告里直接引用视图位置来定位问题。这个习惯救了我很多次,因为很多业主只看图,数字说再多也不如一张热力图直观。
| 指标 | 数据特征 | 让我做的决策 |
|---|---|---|
| 密度 1.5 人/㎡ | 颜色偏红,LOS E | 加宽通道、调整栏杆位置 |
| 平均速度 0.4 m/s | 低于 0.5 | 检查是否形成排队、出口是否阻塞 |
| 轨迹严重重叠 | 弧线集中在某一区域 | 调整动线、增加引导标识 |
| 疏散时间超标 | 超过规范或甲方要求 | 扩大出口宽度、增加疏散门 |
| 两个出口流量差过大 | 一侧 70%,一侧 30% | 调整导向设施,均衡出口利用 |
2. SimWalk自带的可视化,怎么用才最出效果
2.1 密度热力图的正确打开方式
SimWalk 自带的密度热力图是最常用的可视化工具,但不是打开就完事。很多人直接把默认视图截图放进报告,结果问题一大堆:颜色范围没固定、网格太粗、时间段不对、没有叠加平面底图。做热力图,我建议按这个顺序调:先设网格尺寸,再固定颜色标尺,然后选择统计时间段,最后叠加底图。
网格尺寸前面提到了,0.5 米左右比较合适。固定颜色标尺可以在结果属性里手工输入最大值,比如把全局颜色标尺定为 0 到 3.0 人/平方米,这样不同方案之间可以公平对比。时间段选择上,SimWalk 默认做整个模拟周期的平均密度,但瓶颈往往只出现在高峰期那几分钟,所以我会额外打开逐时间步长的密度动画,把红色区域出现的时间段记下来,再单独导出那个时间段的数据。
叠加底图也很关键。SimWalk 支持把 CAD 平面图或位图作为背景,调成半透明再叠加热力图层。这样一来,业主看报告时能对号入座,知道哪片红色是安检区、哪片是楼梯口,而不是对着一个抽象网格发呆。透明度我一般设置在 50% 左右,太实会挡住底图,太虚颜色又看不清楚。
2.2 动态轨迹与流量箭头:还原人群运动过程
动态轨迹是我向甲方演示时最讨喜的功能。SimWalk 里可以播放整个模拟过程,能看到行人从生成到离开的完整运动轨迹。播放速度建议从 1 倍开始,把注意力放在容易出现冲突的区域。有些现象慢速播放时才能对得上时序,比如闸机前排队,你会看到轨迹在一个截面处逐渐重叠,队伍长度随时间变长。这时候光靠一张静态热力图是讲不清楚“怎么堵的”,只能靠动画。
流量箭头更适合做“分时段分析”。我一般每 5 分钟截一张图,看高峰人流方向的演变。比如地铁换乘通道,早高峰和晚高峰的主导方向正好相反,如果把整个时间段混在一起,箭头会互相抵消,得出的方向一点也不明确。分开截屏后才能看出原来的动线设计是否照顾了两个方向。
轨迹图还有个用法是看速度分布。给轨迹按速度着色,慢速区域自然显示为冷色调,和密度图互相印证。如果密度图显示红色而轨迹速度并不低,说明人流还在动,只是拥挤;如果轨迹速度也低,那就是停滞排队,问题更严重。
2.3 结果报告与图表输出:软件自带功能的上限
SimWalk 自带报告功能,可以生成 HTML 报告,包含基本统计和图表。用来快速给同事看可以,但正式交付给甲方时,样式和深度都不够。我通常把 SimWalk 的报告当“过程文件”,最终结论图表还是要自己加工。
导出方面,SimWalk 可以把密度云图作为图片导出,也可以把数据导出为文本或 CSV,再导入其他工具。我个人推荐至少保留三类导出物:密度矩阵 CSV、轨迹 CSV、截面流量时间序列 CSV。这三样数据在手,后续用 Python、Excel,甚至前端图表库都能做很多事。
如果你需要把 SimWalk 结果叠加到 CAD 或 GIS 里,也可以把密度云图作为光栅图导出,再对齐坐标放入 CAD。这样做疏散指示图、安全评估图非常方便。注意在导出前把不需要显示的图层全部关掉,只保留密度和底图,否则叠到 CAD 里会有一堆乱线。
3. 把SimWalk结果接入外部可视化工具,我的一套完整流程
3.1 导出字段、时间步长与数据格式
SimWalk 的导出选项比较多,用之前一定要确认字段含义。最常见的是密度矩阵,字段大致是时间步、网格 X、网格 Y、密度值。轨迹数据则是每个代理在不同时间的坐标、速度和状态。截面流量数据则是某个截面在连续时间步里的通过人数。
时间步长要提前想好。SimWalk 内部计算步长可能很细,但导出时为了控制文件体积,可以每 2 秒采样一次。注意,采样间隔太大会丢峰值,我一般是先看模拟总时长,如果模拟 5 分钟,用 1 秒或 2 秒采样足够;如果模拟半小时,就按 5 秒采样。密度矩阵如果网格很多,单次导出会非常大,建议按区域导出,不要整场景一次导完,否则轻则文件几百兆,重则直接卡死。
我给一个典型的密度导出文件示例:
TimeStep,GridX,GridY,Density 0,1,1,0.00 2,1,1,0.12 4,1,1,0.35这个格式就是给 Python 用的。拿到的第一件事,先看文件行数。如果行数和预期差距太大,回模型里检查测量区是否覆盖了目标范围,别急着往下算。
3.2 用Python把密集数据变成决策表
SimWalk 自带图表够用,但要做定制分析、批量处理多工况,Python 几乎是必须的。我一般用 Pandas 读 CSV,然后算几个核心指标:全局平均密度、最大密度时间点和位置、超过阈值的时间占比;轨迹数据则用来算平均移动速度、95 分位离场时间、最大排队时间。
下面这段是我常用的一段密度分析代码,以“抄作业”的标准给你,直接改文件名就能用:
import pandas as pd df = pd.read_csv("density_matrix.csv") # 全局平均密度 avg_density = df["Density"].mean() print(f"平均密度: {avg_density:.2f} 人/m²") # 最大密度及对应时间、网格位置 peak = df.loc[df["Density"].idxmax()] print(f"最大密度: {peak['Density']:.2f} 人/m² @ TimeStep={peak['TimeStep']} Grid=({peak['GridX']}, {peak['GridY']})") # 超过 1.08 人/m²(LOS D)的时间步占比 threshold = 1.08 ratio = (df["Density"] > threshold).mean() print(f"超过 {threshold} 人/m² 的时间步占比: {ratio:.1%}")注意idxmax()只能找出第一个最大值。如果场景有多个疏散高峰,我建议改成按时间步聚合,看每个时间步的平均密度,再画一条时间曲线。
轨迹数据的分析类似,主要看离场时间和速度分布:
import pandas as pd import numpy as np traj = pd.read_csv("trajectories.csv") # 只统计移动状态,剔除停留 moving = traj[traj["Speed"] > 0.1] avg_speed = moving["Speed"].mean() print(f"平均移动速度: {avg_speed:.2f} m/s") # 每个代理最后出现时间 agent_last_time = traj.groupby("AgentID")["Time"].max() p95_time = np.percentile(agent_last_time, 95) print(f"95% 人群离场时间: {p95_time:.1f} s")一个容易忽略的点:如果轨迹 CSV 里同一个 AgentID 的最后一条记录并不代表“离场”,而是被截断在测量边界外,那这个 95 分位就是假的。所以导出轨迹数据时,要确认场景边界完整,代理全部走到出口之外。
3.3 用Matplotlib、Plotly甚至ECharts做汇报级图表
有了 DataFrame,画图就是顺手的事。密度云图可以用 Matplotlib 的imshow画,也可以用 Plotly 做成交互式热力图,鼠标移上去能看具体数值。我项目汇报时更倾向 Plotly,因为甲方会让我现场放大某个区域看细节,交互式图表比静态图片好用得多。
下面是一个密度云图的快速绘制代码,把密度矩阵变成二维透视表:
import matplotlib.pyplot as plt pivot = df.pivot_table(index="GridY", columns="GridX", values="Density", aggfunc="mean") pivot = pivot.sort_index(ascending=False) # 让 Y 轴正向朝上 fig, ax = plt.subplots(figsize=(10, 7)) im = ax.imshow(pivot, cmap="jet", origin="upper") plt.colorbar(im, label="Density (persons/m²)") ax.set_xlabel("X grid") ax.set_ylabel("Y grid") plt.tight_layout() plt.savefig("density_map.png", dpi=200)如果想对接 Web 可视化大屏,把结果整理成 JSON 交给 ECharts 很顺手。比如按时间步聚合成平均密度序列,前端用折线图或热力图展示。我一般先在 Pandas 里算好,再导出 JSON:
import json time_series = df.groupby("TimeStep")["Density"].mean().reset_index() chart_data = [ {"time": int(row["TimeStep"]), "avg_density": round(row["Density"], 3)} for _, row in time_series.iterrows() ] with open("chart_data.json", "w", encoding="utf-8") as f: json.dump(chart_data, f, ensure_ascii=False, indent=2)为什么要把 SimWalk 结果搬出来画图?因为自带的视图只是一个“快照”,没法联动筛选,也没法挂到大屏上实时展示多方案对比。外部工具灵活得多,颜色、注释、柱状图、雷达图都能做,关键是能跟其他专业数据合并,形成一份统一风格的交付图纸。
4. 一次真实项目的完整结果分析与优化闭环
4.1 场景设定与仿真配置
拿一个高铁站候车厅疏散模拟项目举例。这个项目的核心要求是:把候车厅内 1200 人在突发情况下全部疏散到室外安全区域,疏散时间不得高于 6 分钟。候车厅面积大概 40 米乘 60 米,四个出口,其中两个常用出口宽度 3 米,一个应急出口宽度 2.5 米,还有一个员工通道不参与疏散。
SimWalk 里建模时,我按设计图把柱子、座椅、售货亭都建了进去。人群构成按常见比例设置:普通成年人 70%,老人 15%,儿童 10%,行动不便者 5%。行走速度在每秒 1.0 到 1.5 米之间随机分布。网格尺寸 0.5 米,仿真步长 0.25 秒,统计间隔 2 秒。为了降低随机性,同一个方案跑了 5 个随机种子,取平均结果。
这里要说一句:随机种子非常重要。SimWalk 的人群生成和路径选择带随机性,只跑一遍可能碰上偏激场景。5 个种子都跑完,疏散时间的最大值和最小值差距在 10% 以内,结果才算稳定。如果一个种子的结果明显异常,要回去查模型,而不是草率删除。
4.2 结果解读:疏散时间、瓶颈、密度分布
第一次跑完,SimWalk 显示平均疏散时间是 384 秒,约 6 分 24 秒,超标了 24 秒。这就意味着方案不合格,必须优化。密度热力图上,问题很清楚:在西侧安检区到楼梯口的连接通道,出现一片红色区域,最高密度达到 3.4 人/平方米,远超 F 级线。轨迹动画则显示大量人流绕过中央座椅区后,同时涌向闸机方向,有一个出口承担了 65% 的人流,而东侧应急出口只走了 15% 的人。
用 Python 导出的轨迹 CSV 进一步算了一下,95 分位离场时间是 402 秒,最后一个人在第 412 秒才出去。平均移动速度在红色区域降到 0.3 米/秒,属于明显停滞。流量数据也印证了:楼梯口截面的通过流量在 90 秒到 150 秒之间出现下跌,说明拥堵已经反噬了通行能力。
这个案例里,最大瓶颈不在出口本身,而在出口前的汇聚段。很多项目都是这样:你把四个出口的宽度数字摆出来,每个看起来都够,但人流到出口前挤成一团,通行能力照样上不去。只看出口宽度不看上游动线,是初做疏散分析最容易犯的错。
4.3 优化措施与前后对比
基于分析,我提了三个优化方向。第一,把西侧楼梯口到出口之间的隔墙后退 1 米,扩大缓冲空间;第二,在东侧应急出口前增设发光引导标志,并通过座椅重排形成一条通往东出口的“引流道”;第三,把一个主要出口的前置闸机从 6 台增加到 8 台,减少闸机处的排队时间。
改完模型后重新跑 5 个种子,平均疏散时间降到 326 秒,约 5 分 26 秒,满足 6 分钟要求。95 分位离场时间降到 341 秒。楼梯口处最大密度从 3.4 降到 1.9,虽然还是拥挤,但不至于停滞。出口流量比例从原来的 65:15 变成 48:32,两个出口的利用率均衡了不少。
| 指标 | 优化前 | 优化后 | 判断 |
|---|---|---|---|
| 平均疏散时间(秒) | 384 | 326 | 达标 |
| 95% 离场时间(秒) | 402 | 341 | 改善 |
| 最大密度(人/㎡) | 3.4 | 1.9 | 大幅改善 |
| 西侧出口分担率 | 65% | 48% | 均衡 |
| 高风险区数量 | 4 个 | 2 个 | 减少 |
这个案例想说明的是:结果分析和可视化不是跑完就结束,它们是优化的依据。每一次改动是否有效,都得回到同一套结果指标里去看,不然就是凭感觉拍脑袋。
5. 结果可信度校验与常见排查技巧
5.1 先别画图,做这四步校验
拿到结果不要急着做图,先验证可信度。我做项目时固定有四步校验,少了哪一步心里都不踏实。
第一步是网格无关性检查。把网格分别设成 0.25 米、0.5 米、1 米,跑同一个场景对比疏散时间。如果三者的结果差距在 5% 以内,说明网格尺寸不是主导因素,可以用最省计算量的 0.5 米。如果差距超过了 10%,说明你的模型对网格敏感,必须用更细的网格。
第二步是随机种子检查。同一模型至少跑 3 到 5 个随机种子,看疏散时间和最大密度的离散程度。单一结果不能代表真实情况,因为在现实里同样条件下每次疏散也会有波动。模拟结果的置信区间比绝对精确值更有参考价值。
第三步是流量守恒检查。统计进入场景的总人数、离开场景的总人数和模拟结束时留在场景里的人数,三者应该满足:进入人数减去离开人数等于系统内人数。如果对不上,可能是代理卡在障碍物里没出来,或者出口定义有问题。SimWalk 的日志里能看到具体报告,别跳过日志直接看图形。
第四步是经验公式对照。疏散时间可以用 SFPE 手册里的一些手算公式估个数量级。比如已知楼梯通行能力,算出理论通过时间,再和 SimWalk 的结果比较。如果模拟比手算快了 3 倍以上,多半是模型边界条件设置太宽松。这个对照不需要追求精确,但能挡住明显离谱的结果。
5.2 常见结果异常及其排查思路
实际项目里,很容易遇到几个固定异常。我整理了一个速查表,方便大家对照。
| 异常现象 | 可能原因 | 排查方法 |
|---|---|---|
| 密度图满屏红色 | 网格太细或聚合了全部时间段 | 调粗网格,按时间段分别查看 |
| 疏散时间突然异常大 | 代理被卡在墙角或障碍物附近 | 检查障碍物是否连续、墙线是否闭合 |
| 轨迹穿墙 | 碰撞模型参数不当或墙线未闭合 | 检查墙体实体,增大代理半径 |
| 导出 CSV 为空 | 测量区域未覆盖、导出范围不对 | 确认测量区与结果图层,重新导出 |
| 动画回放卡顿 | 轨迹显示数量过多 | 只显示 10%-20% 代理,关闭阴影 |
| 两个种子结果差太多 | 随机性过大或模型边界敏感 | 增加种子数,查看是否为个别人群路径异常 |
| 某个出口流量为零 | 代理无法到达该出口 | 检查该出口是否被障碍物遮挡、是否有入口连接 |
每次都仔细看日志。SimWalk 的日志会记录没有到达目的地的代理数量,如果这个数量不是零,说明模型里有地方让人走不出去。别等到最后发现热力图上有人一直在原地打转,那整场仿真基本可以作废。
5.3 我常用的几个小技巧
最后分享几个私藏技巧。第一个是固定颜色范围,这条前面提过,但值得再强调。固定颜色标尺后,做多方案对比时,颜色深浅差异才是“真差异”,否则两张图根本不是同一把尺子量出来的。
第二个是善用 SimWalk 的区域统计功能。在模型里预先画好“测量多边形”,软件会自动统计进出该区域的人数、平均停留时间和最大排队人数。这个功能比我自己写 Python 还准,因为它是基于代理的状态数据算的,不用我从坐标里猜目的地。
第三个是多方案对比时,导出数据的时间轴要保持一致。我习惯以模拟开始时间为零点,截取前 300 秒数据。如果某个方案只有 280 秒就结束了,也要把时间轴补满 300 秒,那么对比曲线才公平。补的时间段可以看作是“所有人都已经离场”,密度记 0,速度不计算。
第四个是不要只导一次数据。SimWalk 的结果在每次跑完后都可能变化,尤其是我改过模型参数之后。导出数据前,确认当前激活的结果集是你想分析的那次,而不是上一次的残留结果。我曾经因为没注意结果集被覆盖,导出了一份旧的密度数据,整个上午白干,这是真实踩过的坑。
写在最后
自己做仿真项目做到现在,最大的体会是:结果分析和可视化几乎就是仿真本身的价值所在。模型建得再用心,参数配得再准,最后表达不出来,或者表达出来别人看不懂,那前面的工作就打了折扣。我始终建议刚开始用 SimWalk 的朋友,宁可前期在测量区、导出格式、颜色标尺这些“不起眼”的地方多花半小时,也别等到跑完后对着自动生成的彩虹图硬编故事。
另外一个实用经验是:SimWalk 自带的视图适合自己人看,外部工具做出来的图表才适合交付。现在可视化工具的生态很成熟,Pandas 加 Plotly,或者导出 JSON 接 ECharts,都能把仿真数据变成领导、甲方一眼能看懂的汇报材料。把 SimWalk、数据导出、外部可视化这条链路跑通之后,后面每个项目都会越来越顺。希望这篇能帮你少走点弯路。