汽车乘员热舒适测评的预测模型

这是一个非常完整的汽车乘员热舒适预测模型可视化分析平台,让我从多个维度进行深度解读:
系统架构设计
1. 核心类设计 - ThermalComfortModels
class ThermalComfortModels:"""热舒适预测模型实现类"""
这个类封装了四种主流热舒适模型,采用静态方法设计,体现了良好的模块化思想。
2. 四种模型的技术实现
2.1 PMV-PPD模型 (Fanger, 1970)
def pmv_ppd(T_a, T_r, v_air, RH, M, I_cl):
- •
原理:基于热平衡方程的稳态模型
- •
关键技术点:
- •
服装热阻单位转换:
clo → m²·K/W - •
对流换热系数动态计算:根据风速选择公式
- •
使用海豹运算符简化赋值:
(T_cl := ...)
- •
2.2 等效温度模型 (Nilsson)
def equivalent_temperature(T_local, v_local, RH, body_part):
- •
创新点:考虑身体各部位权重差异
- •
应用场景:非均匀热环境分析
2.3 DTS动态模型 (Fiala, 1998)
def dts_model(t, T_env, M, I_cl, T_skin_initial=33.0, T_core_initial=37.0):
- •
核心技术:使用
scipy.integrate.odeint求解微分方程组 - •
模拟人体热生理动态响应
- •
热感觉计算结合了皮肤温度和变化率
2.4 UCB模型 (Zhang Hui, 2003)
def ucb_model(T_local_dict, v_local_dict, M, I_cl, personal_factor=1.0):
- •
特点:局部热感觉整合 + 个性化因子
- •
体现了从整体到局部的研究趋势
可视化
1. 多标签页界面设计
tab1, tab2, tab3, tab4 = st.tabs(["模型结果", "公式展示", "对比分析", "数据详情"])
这种设计提供了清晰的信息层级和用户体验。
2. 智能布局管理
cols = st.columns(2)
model_count = 0
# 动态分配显示位置
with cols[model_count % 2]:
实现了自适应布局,无论选择几个模型都能合理排列。
3. 丰富的可视化类型
- •
曲线图:PMV-PPD关系、DTS动态响应
- •
柱状图:部位权重分布、局部热感觉
- •
指标卡:关键数值直观显示
- •
数据表格:详细参数展示
工程化
1. 用户体验优化
with st.spinner("计算PMV-PPD模型..."):# 计算代码time.sleep(0.5) # 人为延迟,增强体验
- •
加载状态提示
- •
适度延迟营造计算感
- •
进度反馈机制
2. 错误处理与边界条件
f_cl = 1.0 + 0.3 * I_cl if I_cl > 0 else 1.0
h_c = 2.38 * abs(T_sk - T_a) ** 0.25 if v_air < 0.1 else 12.1 * np.sqrt(v_air)
考虑了各种边界情况的鲁棒性设计。
3. 数据流管理
models_data = {'pmv': {'pmv': pmv, 'ppd': ppd},'equivalent_temp': teq_results,'dts_data': {'time': t, 'tsv': tsv, ...},'ucb_data': {'overall_tsv': overall_tsv, ...}
}
统一的数据容器设计,便于扩展和维护。
科学计算
1. 微分方程求解
def thermal_model(y, t):T_skin, T_core = y# 热生理微分方程dT_skin_dt = 0.1 * (T_env_t - T_skin) - 0.05 * (T_skin - T_core)dT_core_dt = 0.02 * (M / 58.15 - 1) - 0.01 * (T_core - 37) + 0.005 * (T_skin - T_core)return [dT_skin_dt, dT_core_dt]solution = odeint(thermal_model, y0, t)
体现了数值计算在工程中的应用。
2. 物理单位处理
I_cl_m2 = I_cl * 0.155 # clo to m²·K/W
T_a_k = T_a + 273.15 # °C to K
严格的单位换算确保模型准确性。
代码
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib import rcParams
import sympy as sp
from scipy.integrate import odeint
import time# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
rcParams['font.size'] = 12class ThermalComfortModels:"""热舒适预测模型实现类"""@staticmethoddef pmv_ppd(T_a, T_r, v_air, RH, M, I_cl):"""PMV-PPD模型 (Fanger, 1970)参数:T_a: 空气温度(°C), T_r: 平均辐射温度(°C)v_air: 空气流速(m/s), RH: 相对湿度(%)M: 代谢率(W/m²), I_cl: 服装热阻(clo)"""# 服装热阻转换 (clo to m²·K/W)I_cl_m2 = I_cl * 0.155# 基础计算T_a_k = T_a + 273.15T_r_k = T_r + 273.15# 服装面积系数f_cl = 1.0 + 0.3 * I_cl if I_cl > 0 else 1.0# 皮肤温度估算T_sk = 35.7 - 0.0275 * M# 热损失计算h_c = 2.38 * abs(T_sk - T_a) ** 0.25 if v_air < 0.1 else 12.1 * np.sqrt(v_air)# PMV计算PMV = (0.303 * np.exp(-0.036 * M) + 0.0275) * (M - 3.05 * 0.001 * (5733 - 6.99 * M - 58.15) - # 潜热损失0.42 * (M - 58.15) - # 呼吸显热1.72 * 0.00001 * M * (5867 - 58.15) - # 呼吸潜热0.0014 * M * (34 - T_a) - # 对流热损失3.96 * 0.00000001 * f_cl * ((T_cl := (T_sk - I_cl_m2 * (3.96 + h_c)) + 273.15) ** 4 - T_r_k ** 4) -f_cl * h_c * (T_cl - T_a_k))# PPD计算PPD = 100 - 95 * np.exp(-0.03353 * PMV ** 4 - 0.2179 * PMV ** 2)return PMV, PPD@staticmethoddef equivalent_temperature(T_local, v_local, RH, body_part):"""等效温度模型 (Nilsson等)参数:T_local: 局部温度(°C), v_local: 局部风速(m/s)RH: 相对湿度(%), body_part: 身体部位"""# 不同部位的权重系数weights = {'head': 0.07, 'chest': 0.175, 'back': 0.175,'arms': 0.07, 'hands': 0.05, 'thighs': 0.19,'legs': 0.13, 'feet': 0.14}# 等效温度计算T_eq = T_local - 0.15 * v_local * (T_local - 25) - 0.001 * RH * (T_local - 25)return T_eq, weights.get(body_part, 0.1)@staticmethoddef dts_model(t, T_env, M, I_cl, T_skin_initial=33.0, T_core_initial=37.0):"""DTS动态热感觉模型 (Fiala, 1998)参数:t: 时间序列, T_env: 环境温度函数M: 代谢率, I_cl: 服装热阻"""def thermal_model(y, t):T_skin, T_core = yT_env_t = T_env(t)# 热生理方程dT_skin_dt = 0.1 * (T_env_t - T_skin) - 0.05 * (T_skin - T_core)dT_core_dt = 0.02 * (M / 58.15 - 1) - 0.01 * (T_core - 37) + 0.005 * (T_skin - T_core)return [dT_skin_dt, dT_core_dt]# 求解微分方程y0 = [T_skin_initial, T_core_initial]solution = odeint(thermal_model, y0, t)T_skin_hist, T_core_hist = solution.T# 热感觉计算TSV = 0.1 * (T_skin_hist - 33.5) + 0.3 * np.gradient(T_skin_hist, t)return TSV, T_skin_hist, T_core_hist@staticmethoddef ucb_model(T_local_dict, v_local_dict, M, I_cl, personal_factor=1.0):"""UCB模型 (Zhang Hui, 2003) - 局部热感觉整合参数:T_local_dict: 各部位温度字典, v_local_dict: 各部位风速字典M: 代谢率, I_cl: 服装热阻, personal_factor: 个性化因子"""# 身体部位权重body_parts = {'head': 0.07, 'chest': 0.175, 'back': 0.175, 'arms': 0.07,'hands': 0.05, 'thighs': 0.19, 'legs': 0.13, 'feet': 0.14}local_tsv = {}overall_tsv = 0for part, weight in body_parts.items():T_local = T_local_dict.get(part, 25)v_local = v_local_dict.get(part, 0.1)# 局部热感觉计算part_tsv = (0.15 * (T_local - 25) + 0.08 * v_local * (25 - T_local) +0.05 * (M / 58.15 - 1) - 0.1 * I_cl) * personal_factorlocal_tsv[part] = part_tsvoverall_tsv += weight * part_tsvreturn overall_tsv, local_tsvdef plot_comparison(models_data):"""模型对比可视化"""fig, axes = plt.subplots(2, 2, figsize=(15, 12))# PMV-PPD曲线pmv_values = np.linspace(-3, 3, 100)ppd_values = 100 - 95 * np.exp(-0.03353 * pmv_values ** 4 - 0.2179 * pmv_values ** 2)axes[0, 0].plot(pmv_values, ppd_values, 'b-', linewidth=2)axes[0, 0].set_xlabel('PMV')axes[0, 0].set_ylabel('PPD (%)')axes[0, 0].set_title('PMV-PPD关系曲线')axes[0, 0].grid(True)# 等效温度分布body_parts = ['头', '胸', '背', '手臂', '手', '大腿', '小腿', '脚']weights = [0.07, 0.175, 0.175, 0.07, 0.05, 0.19, 0.13, 0.14]axes[0, 1].bar(body_parts, weights, color='lightblue', alpha=0.7)axes[0, 1].set_xlabel('身体部位')axes[0, 1].set_ylabel('权重系数')axes[0, 1].set_title('等效温度模型部位权重')axes[0, 1].tick_params(axis='x', rotation=45)# DTS动态响应if 'dts_data' in models_data:t = models_data['dts_data']['time']tsv = models_data['dts_data']['tsv']axes[1, 0].plot(t, tsv, 'r-', linewidth=2)axes[1, 0].set_xlabel('时间 (分钟)')axes[1, 0].set_ylabel('热感觉投票 (TSV)')axes[1, 0].set_title('DTS模型动态响应')axes[1, 0].grid(True)# UCB局部热感觉if 'ucb_data' in models_data:parts = list(models_data['ucb_data']['local_tsv'].keys())values = list(models_data['ucb_data']['local_tsv'].values())axes[1, 1].bar(range(len(parts)), values, color='orange', alpha=0.7)axes[1, 1].set_xlabel('身体部位')axes[1, 1].set_ylabel('局部热感觉')axes[1, 1].set_title('UCB模型局部热感觉分布')axes[1, 1].set_xticks(range(len(parts)))axes[1, 1].set_xticklabels(parts, rotation=45)plt.tight_layout()return figdef display_formulas():"""显示模型公式"""st.header("模型公式展示")col1, col2 = st.columns(2)with col1:st.subheader("PMV-PPD模型公式")st.latex(r"PMV = (0.303e^{-0.036M} + 0.0275) \times (M - W - H_{resp} - H_{conv} - H_{rad})")st.latex(r"PPD = 100 - 95 \exp(-0.03353PMV^4 - 0.2179PMV^2)")st.subheader("等效温度模型")st.latex(r"T_{eq} = T_{local} - 0.15v(T_{local}-25) - 0.001RH(T_{local}-25)")st.latex(r"TSV_{整体} = \sum w_i \cdot TSV_i")with col2:st.subheader("DTS模型微分方程")st.latex(r"\frac{dT_{skin}}{dt} = \alpha(T_{env} - T_{skin}) - \beta(T_{skin} - T_{core})")st.latex(r"\frac{dT_{core}}{dt} = \gamma(\frac{M}{58.15}-1) - \delta(T_{core}-37)")st.latex(r"TSV = k_1(T_{skin}-33.5) + k_2\frac{dT_{skin}}{dt}")st.subheader("UCB局部整合模型")st.latex(r"TSV_i = a(T_i-25) + b v_i(25-T_i) + c(\frac{M}{58.15}-1) - d I_{cl}")st.latex(r"TSV_{整体} = \sum w_i \cdot TSV_i \cdot PF")def main():st.set_page_config(page_title="热舒适模型分析平台",page_icon="🌡️",layout="wide")st.title("🌡️ 汽车乘员热舒适预测模型可视化分析平台")st.markdown("基于四种主流热舒适模型的对比分析与可视化展示")# 侧边栏参数设置st.sidebar.header("模型参数设置")# 环境参数st.sidebar.subheader("环境参数")T_a = st.sidebar.slider("空气温度 (°C)", 15.0, 35.0, 25.0, 0.5)T_r = st.sidebar.slider("平均辐射温度 (°C)", 15.0, 35.0, 26.0, 0.5)v_air = st.sidebar.slider("空气流速 (m/s)", 0.0, 2.0, 0.1, 0.1)RH = st.sidebar.slider("相对湿度 (%)", 20.0, 80.0, 50.0, 5.0)# 人体参数st.sidebar.subheader("人体参数")M = st.sidebar.slider("代谢率 (W/m²)", 46.0, 116.0, 58.15, 5.0)I_cl = st.sidebar.slider("服装热阻 (clo)", 0.3, 1.5, 0.7, 0.1)# 模型选择st.sidebar.subheader("模型选择")show_pmv = st.sidebar.checkbox("PMV-PPD模型", value=True)show_et = st.sidebar.checkbox("等效温度模型", value=True)show_dts = st.sidebar.checkbox("DTS模型", value=True)show_ucb = st.sidebar.checkbox("UCB模型", value=True)# 初始化模型类models = ThermalComfortModels()models_data = {}# 执行选中的模型计算if show_pmv:with st.spinner("计算PMV-PPD模型..."):pmv, ppd = models.pmv_ppd(T_a, T_r, v_air, RH, M, I_cl)models_data['pmv'] = {'pmv': pmv, 'ppd': ppd}time.sleep(0.5)if show_et:with st.spinner("计算等效温度模型..."):# 为各部位计算等效温度body_parts = ['head', 'chest', 'back', 'arms', 'hands', 'thighs', 'legs', 'feet']teq_results = {}for part in body_parts:teq, weight = models.equivalent_temperature(T_a, v_air, RH, part)teq_results[part] = {'teq': teq, 'weight': weight}models_data['equivalent_temp'] = teq_resultstime.sleep(0.5)if show_dts:with st.spinner("计算DTS动态模型..."):# 模拟动态环境t = np.linspace(0, 60, 100) # 60分钟def T_env_func(t):return 20 + 10 * (1 - np.exp(-t / 20)) # 温度变化曲线tsv, T_skin, T_core = models.dts_model(t, T_env_func, M, I_cl)models_data['dts_data'] = {'time': t,'tsv': tsv,'T_skin': T_skin,'T_core': T_core}time.sleep(0.5)if show_ucb:with st.spinner("计算UCB局部模型..."):# 设置各部位参数T_local_dict = {'head': T_a + 1.0, 'chest': T_a, 'back': T_a - 0.5,'arms': T_a - 1.0, 'hands': T_a - 2.0, 'thighs': T_a - 0.5,'legs': T_a - 1.0, 'feet': T_a - 2.0}v_local_dict = {part: v_air * np.random.uniform(0.5, 1.5) for part in T_local_dict.keys()}overall_tsv, local_tsv = models.ucb_model(T_local_dict, v_local_dict, M, I_cl)models_data['ucb_data'] = {'overall_tsv': overall_tsv,'local_tsv': local_tsv}time.sleep(0.5)# 显示结果tab1, tab2, tab3, tab4 = st.tabs(["模型结果", "公式展示", "对比分析", "数据详情"])with tab1:st.header("各模型计算结果")cols = st.columns(2)model_count = 0if show_pmv and 'pmv' in models_data:with cols[model_count % 2]:st.subheader("📊 PMV-PPD模型")st.metric("预测平均投票(PMV)", f"{models_data['pmv']['pmv']:.3f}")st.metric("预测不满意百分比(PPD)", f"{models_data['pmv']['ppd']:.1f}%")# PMV解释pmv_val = models_data['pmv']['pmv']if pmv_val < -2.5:comfort = "非常冷"elif pmv_val < -1.5:comfort = "冷"elif pmv_val < -0.5:comfort = "稍凉"elif pmv_val < 0.5:comfort = "舒适"elif pmv_val < 1.5:comfort = "稍暖"elif pmv_val < 2.5:comfort = "热"else:comfort = "非常热"st.info(f"热感觉: {comfort}")model_count += 1if show_et and 'equivalent_temp' in models_data:with cols[model_count % 2]:st.subheader("🌡️ 等效温度模型")teq_values = [models_data['equivalent_temp'][part]['teq'] for part in models_data['equivalent_temp']]avg_teq = np.average(teq_values,weights=[models_data['equivalent_temp'][part]['weight']for part in models_data['equivalent_temp']])st.metric("加权平均等效温度", f"{avg_teq:.2f}°C")# 显示各部位等效温度teq_df = pd.DataFrame([{'部位': part, '等效温度': models_data['equivalent_temp'][part]['teq'],'权重': models_data['equivalent_temp'][part]['weight']}for part in models_data['equivalent_temp']])st.dataframe(teq_df, use_container_width=True)model_count += 1if show_dts and 'dts_data' in models_data:with cols[model_count % 2]:st.subheader("⏰ DTS动态模型")final_tsv = models_data['dts_data']['tsv'][-1]st.metric("最终热感觉投票(TSV)", f"{final_tsv:.3f}")st.metric("皮肤温度", f"{models_data['dts_data']['T_skin'][-1]:.2f}°C")st.metric("核心温度", f"{models_data['dts_data']['T_core'][-1]:.2f}°C")# 动态曲线fig_dts, ax = plt.subplots(figsize=(8, 4))ax.plot(models_data['dts_data']['time'], models_data['dts_data']['tsv'], 'r-', linewidth=2)ax.set_xlabel('时间 (分钟)')ax.set_ylabel('热感觉投票')ax.set_title('DTS动态响应曲线')ax.grid(True, alpha=0.3)st.pyplot(fig_dts)model_count += 1if show_ucb and 'ucb_data' in models_data:with cols[model_count % 2]:st.subheader("🔬 UCB局部模型")st.metric("整体热感觉", f"{models_data['ucb_data']['overall_tsv']:.3f}")# 局部热感觉条形图fig_ucb, ax = plt.subplots(figsize=(8, 4))parts = list(models_data['ucb_data']['local_tsv'].keys())values = list(models_data['ucb_data']['local_tsv'].values())ax.bar(parts, values, color='lightgreen', alpha=0.7)ax.set_xlabel('身体部位')ax.set_ylabel('局部热感觉')ax.set_title('UCB模型局部热感觉分布')ax.tick_params(axis='x', rotation=45)st.pyplot(fig_ucb)model_count += 1with tab2:display_formulas()with tab3:st.header("模型对比分析")if len(models_data) >= 2:comparison_fig = plot_comparison(models_data)st.pyplot(comparison_fig)# 模型特性对比表st.subheader("模型特性对比")comparison_data = {'模型': ['PMV-PPD', '等效温度', 'DTS', 'UCB'],'适用场景': ['稳态均匀环境', '非均匀环境', '动态环境', '动态+非均匀'],'考虑因素': ['环境参数', '局部环境', '生理动态', '局部+生理+心理'],'计算复杂度': ['低', '中', '高', '很高'],'个性化支持': ['无', '有限', '中等', '强']}comparison_df = pd.DataFrame(comparison_data)st.dataframe(comparison_df, use_container_width=True)else:st.warning("请至少选择两个模型进行对比分析")with tab4:st.header("详细数据")if models_data:# 显示原始数据st.subheader("模型输出数据")for model_name, data in models_data.items():with st.expander(f"{model_name} 数据"):st.json(data, expanded=False)# 参数说明st.subheader("参数说明")param_info = {'参数': ['T_a', 'T_r', 'v_air', 'RH', 'M', 'I_cl'],'含义': ['空气温度', '平均辐射温度', '空气流速', '相对湿度', '代谢率', '服装热阻'],'单位': ['°C', '°C', 'm/s', '%', 'W/m²', 'clo'],'典型范围': ['15-35', '15-35', '0-2', '20-80', '46-116', '0.3-1.5']}param_df = pd.DataFrame(param_info)st.dataframe(param_df, use_container_width=True)if __name__ == "__main__":main()
