搞综合管廊安全评估的同行,一定对LEC法不陌生。项目刚启动那阵,我直接拿传统LEC法给管廊基坑风险打分,现场七八个专家围着表格争论,同一个风险源打出完全相反的分值,取个平均硬算出来的D值,看上去精确到小数,实际上把所有判断里的不确定性都吞掉了。后来我把三角模糊数和云模型引进来,用matlab重写了整套评估流程,从打分表到风险云图一步到位,才算把这件事做顺。这篇文章就把完整的模型思路、代码实现和踩过的坑整理出来,给做地下工程、市政管廊安全评估的朋友做个参考。
1. 为什么综合管廊施工风险评估要换一套模型
1.1 综合管廊施工风险到底特殊在哪
综合管廊和普通房建工程不一样,它是把电力、通信、燃气、热力、给排水这些市政管线集中到一个地下隧道空间里。施工阶段开挖深度大、作业面狭窄、多工种交叉,再加上燃气舱、电力舱这类特殊功能舱室存在,风险链条比普通项目长得多。
我做过的一个项目里,管廊基坑深度超过12米,紧贴着既有市政道路,一侧还有运行中的高压燃气管。施工期间一旦发生基坑坍塌或者机械误伤管线,事故影响范围就远超现场本身,可能波及整条道路的交通和周边居民的供电供气。这种情况下,风险评估如果只靠拍脑袋定个“高风险”了事,根本没法支撑后续的安全管控决策,必须有一套能量化、可追溯、能对应到具体工序的评价方法。
1.2 传统LEC法的两个硬伤
LEC法的计算公式很简单:D = L × E × C,L是事故发生可能性,E是人员暴露频率,C是后果严重程度。上世纪八十年代引入国内后,因为简单实用,一直是安全评价的主流工具。
但用多了就会发现两个硬伤。
第一个硬伤是打分标准的边界切得太硬。LEC法把可能性分成“很不可能、可能、相当可能”等档位,每个档位对应一个固定分数。可实际上“可能性较大”和“可能性很大”之间根本没有清晰的分界线,让两个经验不同的专家给同一个场景打分,一个给3分一个给6分,背后的判断差异其实是模糊的,强行落到精确值就把模糊性丢了。
第二个硬伤是分值相乘的“一次定生死”。D值落在70以上就是显著风险,69以下就是一般风险,临界点两边一个天一个地。可施工现场的风险是动态波动的,今天支护没跟上就可能逼近临界值,明天加强监测又降回去了。传统LEC法给不出这种波动范围,只能给一个静态的数字,决策者看着这个数字,很难判断自己距离下一个风险等级还有多少安全裕度。
1.3 三角模糊数如何把“很危险”变成可计算的语言
三角模糊数解决的就是打分时的模糊性,它的表示形式是(a, b, c),a是判断的下限,b是最可能值,c是上限。专家打分的时候不需要纠结到底给3还是给4,直接说“这个风险的可能性在2到4之间,最可能是3”,就把主观判断的不确定性完整表达出来了。
判断起来也更自然,人脑本来就更擅长给范围而不是精确点值。比如我问你“明天出门带伞的概率”,你很容易说“大概七八成”,但要精确到73.5%就很难。三角模糊数符合人的表达习惯,又保留了足够的信息量去做后续计算。
从数学上说,三角模糊数的隶属度函数是分段线性的,区间中间数值隶属度最高,两端逐渐降到0,形式足够简单,后续做解模糊、模糊聚类、云变换都比较顺。这也是我最终选它而不是更高阶的梯形模糊数或者高斯模糊数的原因——够用,且不容易把模型复杂化到没法跟现场的人解释。
2. 模型落地的完整设计思路:从指标体系到云参数
2.1 风险评估指标体系怎么搭才不跑偏
管廊施工风险因素多,指标体系如果不加约束会越搭越臃肿。我的做法是采用“风险类型 × 施工阶段”的矩阵式思路,先把风险类型定住,再对照各施工阶段去筛选有效指标,这样专家打分行对各施工阶段去筛选有效指标,这样专家打分时能直接对应到具体作业场景,不至于泛泛而谈。
参考我实际项目里用得比较顺的一套指标,主要风险因素如下表:
| 序号 | 风险因素 | 主要关联施工阶段 |
|---|---|---|
| 1 | 基坑坍塌 | 基坑开挖、支护施工 |
| 2 | 高处坠落 | 主体结构施工、附属设施安装 |
| 3 | 地下管线破坏 | 基坑开挖、机械作业 |
| 4 | 机械伤害 | 吊装运输、预制拼装 |
| 5 | 触电伤害 | 机电安装、临时用电 |
| 6 | 中毒窒息 | 舱内作业、防水施工 |
| 7 | 火灾爆炸 | 燃气舱施工、焊接作业 |
指标数量控制在10个以内比较合适,超过15个打分时间成本就会明显上升,专家在后半程容易疲劳打分,数据质量反而下降。我一般建议优先保留后果严重度高的指标,比如燃气舱作业、深基坑开挖这些,这类风险即使发生概率低,一旦出事故就是大事,必须评进高风险区。
重点提一句,指标之间要避免语义重叠。比如“机械伤害”和“物体打击”放在一起,专家打分时很难区分边界,我遇到过好几个专家在类似指标上给出截然相反的分值。处理办法是合并相近指标,或者给每个指标补充明确的操作定义和典型事故场景,定义写清楚,打分噪音会小很多。
2.2 三角模糊LEC的语言变量与赋值表
确定了指标,下一步就是把L、E、C三个要素转换成三角模糊数。传统LEC法每个要素有标准赋值表,我在这套量表的基础上做了一层模糊化,让专家在打分时选择一个区间而不是孤零零的点。
这里给出我常用的三角模糊LEC赋值框架,基于传统LEC法档位做的扩展:
| 要素 | 语言描述 | 传统值 | 三角模糊区间 |
|---|---|---|---|
| L(可能性) | 很不可能 | 0.2 | (0.1, 0.2, 0.5) |
| L(可能性) | 可能 | 1 | (0.5, 1, 2) |
| L(可能性) | 相当可能 | 3 | (2, 3, 4) |
| L(可能性) | 完全可能 | 6 | (4, 6, 8) |
| E(暴露频率) | 偶尔暴露 | 1 | (0.5, 1, 2) |
| E(暴露频率) | 经常暴露 | 3 | (2, 3, 4) |
| E(暴露频率) | 持续暴露 | 6 | (4, 6, 10) |
| C(后果严重度) | 一般事故 | 7 | (3, 7, 10) |
| C(后果严重度) | 重大事故 | 15 | (10, 15, 20) |
| C(后果严重度) | 特别重大事故 | 100 | (80, 100, 100) |
这套赋值表在实际操作里让专家先选档位,再给一个上下浮动区间。比如某专家认为基坑坍塌的可能性属于“相当可能”,但开挖深度大、地质条件差,他愿意给到(3, 4, 6)而不是表里默认的(2, 3, 4),这种弹性正是模糊化带来的好处。
打分表建议做成Excel模板下发给专家,每位专家一列,每个指标三行,格式完全统一。第一轮打分完成后,我会把匿名汇总结果发回给所有专家,做一轮简单的德尔菲式反馈——看到别人的区间跟自己差异较大时,专家往往会主动修正自己的判断,这是控制打分发散度的最廉价手段。
2.3 云模型在这里解决的是什么问题
云模型是李德毅院士提出的一套定性定量转换工具,三个参数各有明确含义:期望Ex表示概念最典型的取值,熵En表示概念覆盖范围的模糊程度,超熵He表示熵本身的稳定程度,也就是“概念的厚度”。
说人话就是:Ex是风险评分最有可能落在的位置,En反映评分范围大小,He反映评分范围本身稳不稳定。如果多位专家对同一个风险共识度高,En和He都比较小,云看起来就是一条细窄的带;如果专家分歧很大,云的形状就会又宽又厚,这本身就是很重要的决策信息——分歧大的评估项需要在现场管理上多花精力去核实。
云模型接入这套评估体系解决的核心问题,是给LEC打分结果的每个D值都配上了一个“不确定度包络”。传统评估只给一个risk数字,配合云模型之后,我可以输出这个数字的置信带宽和专家共识程度,决策者看到的不再是孤零零一个点,而是一个风险分布。
2.4 整条流水线的逻辑链
这套三角模糊LEC云模型的核心流程可以整理成一条清晰的流水线:先把专家打分转换成三角模糊数,每个专家对某个指标在L、E、C三维度各给出一个模糊区间;然后对每个维度做三角模糊数解模糊,把每个专家的判断浓缩成一个代表性数值;接着把所有专家在该维度的代表性数值视为一组样本,用统计方法估计出云参数;最后通过云代数运算把L、E、C三朵云合成D值的云,再与标准风险等级云做隶属度比较,判定最终风险等级。
整条链路里最关键的决策点是“解模糊的时机”。我是先解模糊再统计云参数,而不是先聚合模糊数再做云计算,这样每个云参数都保留了专家个体判断的分布信息,改起来也更灵活。有人会问,直接解模糊不是又把模糊性丢了吗?注意这里丢的是“单个专家打分时的模糊区间”,保留下来的是“所有专家之间的意见分布”,后者才是更值得关注的不确定性来源,也是云模型能刻画的部分。
3. matlab代码实现:从打分表到云图的完整流程
3.1 代码框架与主脚本设计
我设计的代码框架包含一个主脚本和五个核心函数,分层清晰,方便替换模型参数。整个框架分四块:专家数据输入、云参数估计、云运算、风险等级判定与可视化。主脚本只需要控制数据流向,每个环节都是独立函数,调参不需要动主逻辑。
%% 三角模糊LEC云模型风险评估主脚本 % 功能:读取专家三角模糊打分,估计云参数,计算风险D值云,判定等级并绘图 % 依赖函数:tfn_defuzzify.m, estimate_cloud_params.m, forward_cloud.m, risk_level.m clear; clc; close all; rng(42); % 固定随机种子,保证结果可复现 % 风险因素名称 riskNames = {'基坑坍塌'; '高处坠落'; '地下管线破坏'; '机械伤害'; ... '触电伤害'; '中毒窒息'; '火灾爆炸'}; % 专家打分数据(cell数组,每个cell对应一个风险因素) % 每个矩阵:行 = 专家编号,列 = [L_a L_b L_c, E_a E_b E_c, C_a C_b C_c] scoresAll = cell(length(riskNames), 1); % 这里以“基坑坍塌”为例,展示5位专家的三角模糊打分 scoresAll{1} = [ 2 3 4 3 6 10 7 15 25; 2 3 3 3 6 6 7 15 15; 1 2 3 2 3 6 10 15 20; 3 4 5 3 6 10 7 10 15; 2 3 4 4 6 8 15 15 40; ]; % 其余风险因素的生产数据按同样格式填入,这里先初始化为示例矩阵 defaultScore = [ 1 2 3 2 3 6 3 7 10; 1 2 3 1 2 3 3 7 7; 1 1 2 2 3 6 3 7 10; 1 2 2 2 3 3 7 7 10; 2 3 4 3 6 6 7 7 15; ]; for k = 2:length(riskNames) scoresAll{k} = defaultScore; end注意matlab的随机数种子固定为42,不代表这是“官方标准”,只是让每次运行结果一致,方便编写文档时截图演示。真正做项目时我会跑多次蒙特卡洛,观察云参数在不同随机种子下的稳定性。
3.2 三角模糊数与云模型核心函数
核心函数五个,其中三角模糊数解模糊我用的期望值法,公式是(a + 4b + c) / 6,中间值权重是上下界的两倍,符合LEC打分时专家通常对“最可能值”更有把握的直觉。
function x = tfn_defuzzify(a, b, c) % 三角模糊数期望值法解模糊 % 输入:a-下限,b-最可能值,c-上限 % 输出:解模糊后的确定性代表值 x = (a + 4*b + c) / 6; end云参数估计函数里,Ex用样本均值,En用一阶绝对中心矩估算,He再根据样本方差和En的平方做非负修正。这段逻辑对应的是半经验估计法,操作上比最大似然估计更稳,不会因为样本量小就产生奇奇怪怪的参数值。
function [Ex, En, He] = estimate_cloud_params(x) % 根据样本数据估计云模型三参数 % 输入:x - 样本数据向量 % 输出:Ex-期望,En-熵,He-超熵 Ex = mean(x); % 一阶绝对中心矩估算熵 En = mean(abs(x - Ex)) * sqrt(pi / 2); % 用样本方差减En的平方估算超熵,避免负值 s2 = var(x); He = sqrt(max(0, s2 - En^2)); end正向云发生器是云模型可视化的核心,它做的事情是先给每个云滴加一个熵的随机扰动,再以扰动后的方差生成正态分布云滴,计算确定度。代码里直接用了randn,不依赖统计工具箱。
function [x, y] = forward_cloud(Ex, En, He, n) % 正向正态云发生器 % 输入:Ex-期望,En-熵,He-超熵,n-云滴数量 % 输出:x-云滴位置坐标,y-云滴确定度 Enn = En + He .* randn(1, n); x = Ex + Enn .* randn(1, n); y = exp(-(x - Ex).^2 ./ (2 .* Enn.^2)); end这三个函数加起来不到40行,但已经覆盖了模糊解算和云生成的完整链路。风险等级判定函数我会在3.3节跟可视化一起讲。
3.3 风险等级判定与可视化输出
主流程中,对每个风险因素要经历:聚合专家打分、逐维度解模糊、估计云参数、云代数运算、风险等级判定五个步骤。聚合时我直接对打分矩阵的每一列取平均,得到综合三角模糊数,这样专家权重均匀,简单直观,避免过度设计。
%% 主循环:逐风险因素计算云参数与风险等级 % 标准风险等级云参数:[Ex, En, He] stdLevels = [ 10 5 1; % 低风险 D: 0-20 45 8 1; % 一般风险 D: 20-70 115 20 2; % 显著风险 D: 70-160 240 30 3; % 高风险 D: 160-320 400 50 5; % 极高风险 D: >320 ]; levelNames = {'低风险'; '一般风险'; '显著风险'; '高风险'; '极高风险'}; numRisk = length(riskNames); D_clouds = zeros(numRisk, 3); D_levels = strings(numRisk, 1); L_clouds = zeros(numRisk, 3); E_clouds = zeros(numRisk, 3); C_clouds = zeros(numRisk, 3); for k = 1:numRisk scores = scoresAll{k}; numExpert = size(scores, 1); % 各维度逐专家解模糊 L_vals = zeros(numExpert, 1); E_vals = zeros(numExpert, 1); C_vals = zeros(numExpert, 1); for i = 1:numExpert L_vals(i) = tfn_defuzzify(scores(i,1), scores(i,2), scores(i,3)); E_vals(i) = tfn_defuzzify(scores(i,4), scores(i,5), scores(i,6)); C_vals(i) = tfn_defuzzify(scores(i,7), scores(i,8), scores(i,9)); end % 估计三维度云参数 [L_clouds(k,1), L_clouds(k,2), L_clouds(k,3)] = estimate_cloud_params(L_vals); [E_clouds(k,1), E_clouds(k,2), E_clouds(k,3)] = estimate_cloud_params(E_vals); [C_clouds(k,1), C_clouds(k,2), C_clouds(k,3)] = estimate_cloud_params(C_vals); % 云代数运算:D = L * E * C(一阶误差传播近似) ExL = L_clouds(k,1); EnL = L_clouds(k,2); HeL = L_clouds(k,3); ExE = E_clouds(k,1); EnE = E_clouds(k,2); HeE = E_clouds(k,3); ExC = C_clouds(k,1); EnC = C_clouds(k,2); HeC = C_clouds(k,3); ExD = ExL * ExE * ExC; EnD = abs(ExD) * sqrt((EnL/ExL)^2 + (EnE/ExE)^2 + (EnC/ExC)^2); HeD = abs(ExD) * sqrt((HeL/ExL)^2 + (HeE/ExE)^2 + (HeC/ExC)^2); D_clouds(k, :) = [ExD, EnD, HeD]; % 风险等级判定 D_levels(k) = risk_level(D_clouds(k,:), stdLevels, levelNames); end风险等级判定的核心思路是算综合确定度。对每个标准等级云,计算评估D值云期望落在该等级云期望曲线上的确定度,取确定度最大的那个等级作为判定结果。
function level = risk_level(D_cloud, stdLevels, levelNames) % 通过期望隶属度判定风险等级 % 输入:D_cloud-待判定的云参数向量,stdLevels-标准等级云参数矩阵,levelNames-等级名称cell numLevel = size(stdLevels, 1); membership = zeros(numLevel, 1); for i = 1:numLevel Ex_s = stdLevels(i, 1); En_s = stdLevels(i, 2); membership(i) = exp(-(D_cloud(1) - Ex_s)^2 / (2 * En_s^2)); end [~, idx] = max(membership); level = levelNames{idx}; end最后可视化输出三部分:每朵风险云的云滴图、全部风险因素的D值对比条形图、风险评估汇总表。云滴图用scatter加透明度,比plot更直观,因为大量云滴重叠在一起时,透明度能体现出密度分布差异。
%% 可视化:风险云图 + 等级对比条形图 figure('Position', [100 100 1200 900]); for k = 1:numRisk subplot(3, 3, k); [xd, yd] = forward_cloud(D_clouds(k,1), D_clouds(k,2), D_clouds(k,3), 3000); scatter(xd, yd, 6, 'filled', 'MarkerFaceAlpha', 0.25); hold on; % 叠加期望曲线 x_curve = 0:1:D_clouds(k,1) + 4*D_clouds(k,2); y_curve = exp(-(x_curve - D_clouds(k,1)).^2 / (2 * D_clouds(k,2)^2)); plot(x_curve, y_curve, 'r-', 'LineWidth', 1.5); xlabel('风险D值'); ylabel('确定度'); grid on; title(sprintf('%s | %s', riskNames{k}, D_levels(k))); end % 风险评估汇总条形图 figure('Position', [100 100 1000 500]); ExD_all = D_clouds(:,1); EnD_all = D_clouds(:,2); bar(ExD_all, 'FaceColor', [0.3 0.6 0.9]); hold on; errorbar(1:numRisk, ExD_all, EnD_all, 'k.', 'LineWidth', 1.2); set(gca, 'XTickLabel', riskNames); xtickangle(30); ylabel('风险D值期望'); title('综合管廊施工风险评估结果(含不确定性)'); grid on;运行这段代码,你会发现“基坑坍塌”的D值云可能由于C维度方差大,云团明显比“物体打击”更厚更宽。这个厚度差异本身就是风险预警信号——说明专家组对基坑坍塌后果严重度的认识很不一致,管理上需要先统一认识,再谈风险管控措施。
3.4 模拟数据验证与参数敏感性
用模拟数据跑通代码后,一定要做一步参数敏感性验证。我常用的方式是固定Ex集群不变,把En和He分别放大1.5倍和缩小一半,观察风险等级判定是否翻转。
比如某指标D值云参数是(95, 20, 4),En放大到30之后,期望隶属度在“显著风险”和“一般风险”之间的差值可能缩小到不足5%。这种情形说明风险正好处在等级边界附近,评估结果对专家认知波动非常敏感。我会把这个指标标记为“边界风险”,并要求现场加大监测频率,而不是为了让评估结果好看一点就去调整打分数据。
有一种反面做法是干脆不导入He,让云退化成带厚度的普通正态曲线。我用早期版本的时候试过,风险等级判定确实更“干脆”,但代价是抹掉了专家分歧度这个宝贵信息。从决策角度看,专家分歧很大本身就是风险,尤其管廊里涉及燃气舱、高压电力舱这类高风险源,分歧大就意味着管控预案需要覆盖更广的场景。
4. 实操中踩过的坑与排查技巧
4.1 云滴过聚或过散:En和He到底怎么来
第一次跑通代码时,我遇到了一个很尴尬的问题:云滴图看着像一条细线,完全没有云朵的厚度感。检查之后发现是En设得太小。当时为了演示方便,我拍脑袋给En设了0.5,结果云滴全挤在一个点上。
后来我改成用样本数据估计En和He,问题就解决了。重点在于,En不要从模糊区间里拉一个感觉值,而是通过一阶绝对中心矩从专家打分的离散度里去估计,He则通过样本方差与En平方的差值来反推。这样估计出来的云参数能真实反映专家分歧,不会犯“把不确定的东西输入确定值”的错。
如果你跑出来云滴整体形状没问题但局部太稀疏,说明云滴数不够。3000个云滴是个基础量,输出精细图的时候我会放到8000个以上,matlab运行也没压力。
4.2 专家打分离散度太高:一致性检查与异常值处理
有一回评估“火灾爆炸”风险,8个专家里有1个专家在L维度给出(8, 10, 10),其他专家基本都在(2, 4, 6)附近。均值一算,En被拉得很大,风险等级直接从“显著”跳到“极高”,整个结果都被这个异常值带跑了。
遇到这种情况要做的第一件事不是改数据,而是做专家一致性检查。简单的办法是计算专家打分两两之间的距离,设定一个相似度阈值,把明显偏离的专家打分标记出来。那个给高分的专家很可能掌握着你没注意到的重要信息,比如他知道施工班组曾在燃气舱附近违规动火。我处理的时候会找这位专家单独复核,确认理由属实就保留他的高分并提高权重,确认是误判就让他重新打。
不建议直接删除异常专家,因为真实项目里异常值往往对应着新的风险认知,直接删掉等于主动丢掉信息。流程上应该永远是“先沟通确认,再决定如何处理”。
4.3 风险等级边界卡住:不要硬切数据要算相似度
D值正好落在两个等级交界处的情况非常常见。比如某个指标D值期望是157,标准分级里“显著风险”上限是160,再往上就是“高风险”。传统做法会判定为“显著风险”,但这么处理的风险在于忽略了不确定性区间——如果En算出来是25,那么实际D值有一半概率超过160,风险提升到高等级是完全可能的。
我的做法是把判定结果改成输出“主风险等级 + 次风险等级”的形式。主等级由期望隶属度最大决定,次等级由隶属度第二大的等级给出。如果两个隶属度差值小于10%,评估结论会标注“处于等级边界,建议按较高等级管理”。这样做现场处理时有明确抓手,不会卡在数值边界上吵来吵去。
这个问题的根源在标准等级的云参数设置。每一档标准云不应该是一个点,而是一个带,我用(0-20)=低风险、(20-70)=一般风险这类区间时,会取区间中点作为Ex,区间半宽的两倍左右作为En,这样标准等级之间天然有交叠,评估结果不会因为一个临界点产生突变。
4.4 一个容易被忽略的细节:He出现零值
用最开始的版本跑数据时我遇到过He算出精确零的情况,查下来是样本方差比一阶绝对中心矩估算出的En平方还小。这说明专家打分高度一致,不确定度本身也极低,理论上说得通,但从云模型计算的角度,He为零会让正向云发生器里的Enn变成固定值,云滴厚度完全消失。
处理办法是给超熵设一个下限,比如默认0.5,防止云退化成普通正态分布。设置之后要留意,如果某个指标He一直压在底线上,说明该风险评估的共识度极高,可以适当降低云图展示的透明度,免得决策者误以为还有很大不确定性。
5. 最后再分享一点实际操作层面的体会
做完两个完整的综合管廊项目后,我最大的体会是:模型再好,专家打分环节的质量才是决定评估效果的因素。三角模糊LEC云模型的优势不是“算得准”,而是“算得明白”——准不准取决于输入,能不能解释清楚不确定性,才是模型本身的能力。
实际操作中,我建议打分表下发前先开半小时的评分培训会,用一两个示例场景现场带着专家走一遍打分流程,让大家明确“区间不是越宽越好”这个原则。有专家习惯性给(1, 5, 10)这种宽区间,看起来是表达模糊性,实质上是没有认真判断,这种数据对模型输出的En贡献极大,会把整体风险不确定性拉高到失真水平。
代码本身后续还有很多扩展方向。比如把云模型换成二维、把LEC维度和云相似度算法升级,或者对同一段管廊按不同施工阶段分别评估后做动态风险曲线,这些我都陆续在试。如果你也在做类似的安全风险评估,建议先用我这套模拟数据跑通流程,再替换成你项目的真实打分表,整个迁移过程会非常顺。