增加混电算例功能
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import gradio as gr
|
||||
import time
|
||||
from functools import partial
|
||||
@@ -10,8 +11,8 @@ from analysis_functions import (
|
||||
frequency_domain_analysis,
|
||||
root_locus_analysis
|
||||
)
|
||||
# ===== 新增:算例演示模块函数导入 =====
|
||||
from case_demo_functions import run_case_demo
|
||||
# ===== 算例演示模块函数导入(四阶段设计)=====
|
||||
from case_demo_functions import run_distillation_demo, run_gpr_training, run_engine_design, run_motor_design, run_hybrid_demo
|
||||
from chatbot import chat_with_ai
|
||||
from user_stats import get_online_status_html, update_user_activity
|
||||
from ui_components import (
|
||||
@@ -23,8 +24,98 @@ from ui_components import (
|
||||
create_chatbot_tab
|
||||
)
|
||||
|
||||
|
||||
# ===== 系统资源监控 =====
|
||||
def get_system_monitor_html():
|
||||
"""获取 CPU / 内存 / GPU 使用率的 HTML 小组件"""
|
||||
try:
|
||||
import psutil
|
||||
cpu_pct = psutil.cpu_percent(interval=0)
|
||||
mem = psutil.virtual_memory()
|
||||
mem_pct = mem.percent
|
||||
mem_used_gb = mem.used / (1024 ** 3)
|
||||
mem_total_gb = mem.total / (1024 ** 3)
|
||||
except ImportError:
|
||||
return "<div style='text-align:center;color:#999;font-size:0.8em;'>psutil 未安装,无法监控系统资源</div>"
|
||||
|
||||
# GPU 信息 — 优先用 nvidia-smi(不依赖 PyTorch CUDA 版本),再用 torch.cuda 兜底
|
||||
gpu_html = ""
|
||||
try:
|
||||
import subprocess as _sp
|
||||
_r = _sp.run(
|
||||
['nvidia-smi', '--query-gpu=name,memory.used,memory.total,utilization.gpu',
|
||||
'--format=csv,noheader,nounits'],
|
||||
capture_output=True, text=True, timeout=3
|
||||
)
|
||||
if _r.returncode == 0 and _r.stdout.strip():
|
||||
_parts = [p.strip() for p in _r.stdout.strip().split('\n')[0].split(',')]
|
||||
_gpu_mem_used = float(_parts[1]) / 1024 # MiB → GiB
|
||||
_gpu_mem_total = float(_parts[2]) / 1024
|
||||
_gpu_util = float(_parts[3])
|
||||
_gc = '#ff6b6b' if _gpu_util > 80 else '#ffd93d' if _gpu_util > 50 else '#6bcb77'
|
||||
gpu_html = (
|
||||
f"<div style='display:inline-flex;align-items:center;gap:6px;'>"
|
||||
f"<span>🎮 GPU</span>"
|
||||
f"<div style='width:90px;height:8px;background:#444;border-radius:4px;overflow:hidden;'>"
|
||||
f"<div style='width:{min(_gpu_util, 100):.0f}%;height:100%;background:{_gc};'></div>"
|
||||
f"</div>"
|
||||
f"<span>{_gpu_util:.0f}% {_gpu_mem_used:.1f}/{_gpu_mem_total:.0f} GB</span>"
|
||||
f"</div>"
|
||||
)
|
||||
else:
|
||||
raise RuntimeError("nvidia-smi no output")
|
||||
except Exception:
|
||||
try:
|
||||
import torch as _torch
|
||||
if _torch.cuda.is_available():
|
||||
_mem_alloc = _torch.cuda.memory_allocated(0) / (1024 ** 3)
|
||||
_mem_total = _torch.cuda.get_device_properties(0).total_memory / (1024 ** 3)
|
||||
_util = _mem_alloc / max(_mem_total, 0.01) * 100
|
||||
_gc = '#ff6b6b' if _util > 80 else '#ffd93d' if _util > 50 else '#6bcb77'
|
||||
gpu_html = (
|
||||
f"<div style='display:inline-flex;align-items:center;gap:6px;'>"
|
||||
f"<span>🎮 GPU</span>"
|
||||
f"<div style='width:90px;height:8px;background:#444;border-radius:4px;overflow:hidden;'>"
|
||||
f"<div style='width:{min(_util, 100):.0f}%;height:100%;background:{_gc};'></div>"
|
||||
f"</div>"
|
||||
f"<span>{_mem_alloc:.1f}/{_mem_total:.0f} GB</span>"
|
||||
f"</div>"
|
||||
)
|
||||
else:
|
||||
gpu_html = "<div style='display:inline-flex;align-items:center;gap:4px;'><span>🎮 GPU N/A</span></div>"
|
||||
except Exception:
|
||||
gpu_html = "<div style='display:inline-flex;align-items:center;gap:4px;'><span>🎮 GPU N/A</span></div>"
|
||||
|
||||
cpu_color = '#ff6b6b' if cpu_pct > 80 else '#ffd93d' if cpu_pct > 50 else '#6bcb77'
|
||||
mem_color = '#ff6b6b' if mem_pct > 80 else '#ffd93d' if mem_pct > 50 else '#6bcb77'
|
||||
|
||||
html = (
|
||||
f"<div style='display:flex;justify-content:center;gap:20px;flex-wrap:wrap;"
|
||||
f"font-size:0.82em;color:#ddd;padding:4px 10px;'>"
|
||||
# CPU
|
||||
f"<div style='display:inline-flex;align-items:center;gap:6px;'>"
|
||||
f"<span>🖥️ CPU</span>"
|
||||
f"<div style='width:90px;height:8px;background:#444;border-radius:4px;overflow:hidden;'>"
|
||||
f"<div style='width:{min(cpu_pct, 100):.0f}%;height:100%;background:{cpu_color};'></div>"
|
||||
f"</div>"
|
||||
f"<span>{cpu_pct:.0f}%</span>"
|
||||
f"</div>"
|
||||
# Memory
|
||||
f"<div style='display:inline-flex;align-items:center;gap:6px;'>"
|
||||
f"<span>💾 RAM</span>"
|
||||
f"<div style='width:90px;height:8px;background:#444;border-radius:4px;overflow:hidden;'>"
|
||||
f"<div style='width:{min(mem_pct, 100):.0f}%;height:100%;background:{mem_color};'></div>"
|
||||
f"</div>"
|
||||
f"<span>{mem_used_gb:.1f}/{mem_total_gb:.0f} GB ({mem_pct:.0f}%)</span>"
|
||||
f"</div>"
|
||||
# GPU
|
||||
f"{gpu_html}"
|
||||
f"</div>"
|
||||
)
|
||||
return html
|
||||
|
||||
# 加载外部CSS文件
|
||||
with open("assets/styles.css", "r", encoding="utf-8") as f:
|
||||
with open(os.path.join(os.path.dirname(__file__), "assets", "styles.css"), "r", encoding="utf-8") as f:
|
||||
custom_css = f.read()
|
||||
|
||||
# --- 主应用界面 ---
|
||||
@@ -35,24 +126,9 @@ with gr.Blocks(title="自动控制理论学习网站 - AI+数智平台", css=cus
|
||||
|
||||
# 创建头部信息和在线计数器
|
||||
online_counter = create_header()
|
||||
|
||||
# 创建共享的输入组件
|
||||
with gr.Row():
|
||||
with gr.Column(scale=1):
|
||||
with gr.Group():
|
||||
gr.HTML("<div class='card-title'>📊 通用系统参数</div>")
|
||||
num_input = gr.Textbox(
|
||||
label="传递函数分子系数 (Numerator)",
|
||||
value="1",
|
||||
placeholder="例如: 1 或 1,2,3",
|
||||
info="💡 用逗号分隔多个系数,从最高次项到常数项"
|
||||
)
|
||||
den_input = gr.Textbox(
|
||||
label="传递函数分母系数 (Denominator)",
|
||||
value="1,6,11,6",
|
||||
placeholder="例如: 1,2,1",
|
||||
info="💡 分母阶数通常高于或等于分子阶数"
|
||||
)
|
||||
|
||||
# 系统资源监控(始终可见)
|
||||
system_monitor = gr.HTML(value=get_system_monitor_html, elem_id="system-monitor")
|
||||
|
||||
# 创建功能选项卡
|
||||
with gr.Tabs() as tabs:
|
||||
@@ -80,13 +156,13 @@ with gr.Blocks(title="自动控制理论学习网站 - AI+数智平台", css=cus
|
||||
# --- 时域分析事件 ---
|
||||
time_domain_ui["confirm_button"].click(
|
||||
fn=display_transfer_function,
|
||||
inputs=[num_input, den_input],
|
||||
inputs=[time_domain_ui["num_input"], time_domain_ui["den_input"]],
|
||||
outputs=[time_domain_ui["tf_display"]]
|
||||
).then(lambda: get_online_status_html(), outputs=online_counter)
|
||||
|
||||
time_domain_ui["analyze_button"].click(
|
||||
fn=time_domain_analysis,
|
||||
inputs=[num_input, den_input],
|
||||
inputs=[time_domain_ui["num_input"], time_domain_ui["den_input"]],
|
||||
outputs=[time_domain_ui["output_plot"], time_domain_ui["output_metrics"]]
|
||||
).then(lambda: get_online_status_html(), outputs=online_counter)
|
||||
|
||||
@@ -96,7 +172,7 @@ with gr.Blocks(title="自动控制理论学习网站 - AI+数智平台", css=cus
|
||||
fig, metrics, tf_latex, stability = frequency_domain_analysis(num, den, k)
|
||||
return fig, metrics, tf_latex, stability, k, get_online_status_html()
|
||||
|
||||
freq_inputs = [num_input, den_input, freq_domain_ui["log_k_slider"]]
|
||||
freq_inputs = [freq_domain_ui["num_input"], freq_domain_ui["den_input"], freq_domain_ui["log_k_slider"]]
|
||||
freq_outputs = [
|
||||
freq_domain_ui["plot_output"],
|
||||
freq_domain_ui["metrics_display"],
|
||||
@@ -116,7 +192,7 @@ with gr.Blocks(title="自动控制理论学习网站 - AI+数智平台", css=cus
|
||||
fig, poles, k_val = root_locus_analysis(num, den, log_k)
|
||||
return fig, poles, k_val, get_online_status_html()
|
||||
|
||||
rl_inputs = [root_locus_ui["log_k_slider"], num_input, den_input]
|
||||
rl_inputs = [root_locus_ui["log_k_slider"], root_locus_ui["num_input"], root_locus_ui["den_input"]]
|
||||
rl_outputs = [
|
||||
root_locus_ui["plot_output"],
|
||||
root_locus_ui["poles_display"],
|
||||
@@ -129,59 +205,170 @@ with gr.Blocks(title="自动控制理论学习网站 - AI+数智平台", css=cus
|
||||
outputs=rl_outputs
|
||||
)
|
||||
|
||||
# 当输入框变化时,也更新频域和根轨迹(如果它们是当前可见的)
|
||||
def update_all_on_tf_change(num, den, log_k_freq, log_k_rl):
|
||||
# 更新频域
|
||||
k_freq = 10**log_k_freq
|
||||
fig_freq, metrics, tf_latex, stability = frequency_domain_analysis(num, den, k_freq)
|
||||
|
||||
# 更新根轨迹
|
||||
fig_rl, poles, k_val_rl = root_locus_analysis(num, den, log_k_rl)
|
||||
# 频域:当传递函数输入框变化时自动更新
|
||||
freq_domain_ui["num_input"].change(
|
||||
fn=update_frequency_analysis_wrapper,
|
||||
inputs=freq_inputs, outputs=freq_outputs
|
||||
)
|
||||
freq_domain_ui["den_input"].change(
|
||||
fn=update_frequency_analysis_wrapper,
|
||||
inputs=freq_inputs, outputs=freq_outputs
|
||||
)
|
||||
|
||||
return (
|
||||
fig_freq, metrics, tf_latex, stability, k_freq,
|
||||
fig_rl, poles, k_val_rl,
|
||||
get_online_status_html()
|
||||
)
|
||||
# 根轨迹:当传递函数输入框变化时自动更新
|
||||
root_locus_ui["num_input"].change(
|
||||
fn=update_rl_view_wrapper,
|
||||
inputs=rl_inputs, outputs=rl_outputs
|
||||
)
|
||||
root_locus_ui["den_input"].change(
|
||||
fn=update_rl_view_wrapper,
|
||||
inputs=rl_inputs, outputs=rl_outputs
|
||||
)
|
||||
|
||||
tf_change_inputs = [num_input, den_input, freq_domain_ui["log_k_slider"], root_locus_ui["log_k_slider"]]
|
||||
tf_change_outputs = freq_outputs[:-1] + rl_outputs[:-1] + [online_counter]
|
||||
|
||||
num_input.change(fn=update_all_on_tf_change, inputs=tf_change_inputs, outputs=tf_change_outputs)
|
||||
den_input.change(fn=update_all_on_tf_change, inputs=tf_change_inputs, outputs=tf_change_outputs)
|
||||
# ===== 算例演示事件绑定(四阶段)=====
|
||||
|
||||
# ===== 新增:算例演示事件包装器 =====
|
||||
def run_case_demo_wrapper(sim_time, dt, initial_soc, initial_engine_power, profile, rpm_scale, load_scale, sid):
|
||||
# --- 阶段零-A:GPR 模型训练 ---
|
||||
def run_gpr_wrapper(mode, sid, progress=gr.Progress(track_tqdm=True)):
|
||||
update_user_activity(sid)
|
||||
fig, summary, table_data = run_case_demo(
|
||||
sim_time_s=sim_time,
|
||||
dt=dt,
|
||||
initial_soc_pct=initial_soc,
|
||||
initial_engine_power_kw=initial_engine_power,
|
||||
profile_name=profile,
|
||||
rpm_scale=rpm_scale,
|
||||
load_scale=load_scale
|
||||
fig, summary = run_gpr_training(mode=mode, progress=progress)
|
||||
return fig, summary, get_online_status_html()
|
||||
|
||||
case_demo_ui["gpr_run_button"].click(
|
||||
fn=run_gpr_wrapper,
|
||||
inputs=[case_demo_ui["gpr_mode"], session_id],
|
||||
outputs=[case_demo_ui["gpr_plot"], case_demo_ui["gpr_summary"], online_counter]
|
||||
)
|
||||
|
||||
# --- 阶段零-B:NN 模型训练(蒸馏)---
|
||||
def run_distillation_wrapper(epochs, lr, hidden, sid, progress=gr.Progress(track_tqdm=True)):
|
||||
update_user_activity(sid)
|
||||
fig, summary = run_distillation_demo(epochs, lr, hidden, progress=progress)
|
||||
return fig, summary, get_online_status_html()
|
||||
|
||||
case_demo_ui["distill_run_button"].click(
|
||||
fn=run_distillation_wrapper,
|
||||
inputs=[
|
||||
case_demo_ui["distill_epochs"], case_demo_ui["distill_lr"],
|
||||
case_demo_ui["distill_hidden"],
|
||||
session_id
|
||||
],
|
||||
outputs=[case_demo_ui["distill_plot"], case_demo_ui["distill_summary"], online_counter]
|
||||
)
|
||||
|
||||
# --- 阶段一:发动机控制器设计 ---
|
||||
def run_engine_design_wrapper(sim_time, dt, init_power, target_power,
|
||||
controller_type,
|
||||
kp, ki, kd, tau_fuel, K_inertia,
|
||||
mpc_horizon, mpc_W_power, mpc_W_dcost, mpc_overshoot,
|
||||
sid, progress=gr.Progress(track_tqdm=True)):
|
||||
update_user_activity(sid)
|
||||
fig, summary = run_engine_design(
|
||||
sim_time, dt, init_power, target_power,
|
||||
controller_type,
|
||||
kp, ki, kd, tau_fuel, K_inertia,
|
||||
mpc_horizon, mpc_W_power, mpc_W_dcost, mpc_overshoot / 100.0,
|
||||
progress=progress
|
||||
)
|
||||
return fig, summary, get_online_status_html()
|
||||
|
||||
case_demo_ui["eng_run_button"].click(
|
||||
fn=run_engine_design_wrapper,
|
||||
inputs=[
|
||||
case_demo_ui["eng_sim_time"], case_demo_ui["eng_dt"],
|
||||
case_demo_ui["eng_init_power"], case_demo_ui["eng_target_power"],
|
||||
case_demo_ui["eng_controller_type"],
|
||||
case_demo_ui["eng_kp"], case_demo_ui["eng_ki"], case_demo_ui["eng_kd"],
|
||||
case_demo_ui["eng_tau_fuel"], case_demo_ui["eng_K_inertia"],
|
||||
case_demo_ui["eng_mpc_horizon"], case_demo_ui["eng_mpc_W_power"],
|
||||
case_demo_ui["eng_mpc_W_dcost"], case_demo_ui["eng_mpc_overshoot"],
|
||||
session_id
|
||||
],
|
||||
outputs=[case_demo_ui["eng_plot"], case_demo_ui["eng_summary"], online_counter]
|
||||
)
|
||||
|
||||
# --- 阶段二:电机控制器设计 ---
|
||||
def run_motor_design_wrapper(sim_time, dt, target_rpm, load_torque,
|
||||
controller_type,
|
||||
kp, ki, kd, J,
|
||||
mpc_W_speed, mpc_W_dcost, mpc_overshoot,
|
||||
sid, progress=gr.Progress(track_tqdm=True)):
|
||||
update_user_activity(sid)
|
||||
fig, summary = run_motor_design(
|
||||
sim_time, dt, target_rpm, load_torque,
|
||||
controller_type,
|
||||
kp, ki, kd, J,
|
||||
mpc_W_speed, mpc_W_dcost, mpc_overshoot / 100.0,
|
||||
progress=progress
|
||||
)
|
||||
return fig, summary, get_online_status_html()
|
||||
|
||||
case_demo_ui["mot_run_button"].click(
|
||||
fn=run_motor_design_wrapper,
|
||||
inputs=[
|
||||
case_demo_ui["mot_sim_time"], case_demo_ui["mot_dt"],
|
||||
case_demo_ui["mot_target_rpm"], case_demo_ui["mot_load_torque"],
|
||||
case_demo_ui["mot_controller_type"],
|
||||
case_demo_ui["mot_kp"], case_demo_ui["mot_ki"], case_demo_ui["mot_kd"],
|
||||
case_demo_ui["mot_J"],
|
||||
case_demo_ui["mot_mpc_W_speed"], case_demo_ui["mot_mpc_W_dcost"],
|
||||
case_demo_ui["mot_mpc_overshoot"],
|
||||
session_id
|
||||
],
|
||||
outputs=[case_demo_ui["mot_plot"], case_demo_ui["mot_summary"], online_counter]
|
||||
)
|
||||
|
||||
# --- 阶段三:能量管理策略设计(自动引用前两阶段控制器参数)---
|
||||
def run_hybrid_demo_wrapper(sim_time, dt, initial_soc, initial_engine_power,
|
||||
profile,
|
||||
eng_ctrl_type, eng_kp, eng_ki, eng_kd,
|
||||
eng_mpc_horizon, eng_mpc_W_power, eng_mpc_W_dcost, eng_mpc_overshoot,
|
||||
mot_ctrl_type, mot_kp, mot_ki, mot_kd, mot_J,
|
||||
mot_mpc_W_speed, mot_mpc_W_dcost, mot_mpc_overshoot,
|
||||
soc_target, soc_low, soc_high,
|
||||
p_eng_min, p_eng_max, p_charge, k_soc,
|
||||
power_reserve, battery_capacity, sid,
|
||||
progress=gr.Progress(track_tqdm=True)):
|
||||
update_user_activity(sid)
|
||||
fig, summary, table_data = run_hybrid_demo(
|
||||
sim_time, dt, initial_soc, initial_engine_power, profile,
|
||||
eng_ctrl_type, eng_kp, eng_ki, eng_kd,
|
||||
eng_mpc_horizon, eng_mpc_W_power, eng_mpc_W_dcost, eng_mpc_overshoot / 100.0,
|
||||
mot_ctrl_type, mot_kp, mot_ki, mot_kd, mot_J,
|
||||
mot_mpc_W_speed, mot_mpc_W_dcost, mot_mpc_overshoot / 100.0,
|
||||
soc_target, soc_low, soc_high,
|
||||
p_eng_min, p_eng_max, p_charge, k_soc,
|
||||
power_reserve, battery_capacity,
|
||||
progress=progress
|
||||
)
|
||||
return fig, summary, table_data, get_online_status_html()
|
||||
|
||||
# ===== 新增:算例演示按钮事件绑定 =====
|
||||
case_demo_ui["run_button"].click(
|
||||
fn=run_case_demo_wrapper,
|
||||
case_demo_ui["hybrid_run_button"].click(
|
||||
fn=run_hybrid_demo_wrapper,
|
||||
inputs=[
|
||||
case_demo_ui["sim_time"],
|
||||
case_demo_ui["dt"],
|
||||
case_demo_ui["initial_soc"],
|
||||
case_demo_ui["initial_engine_power"],
|
||||
case_demo_ui["sim_time"], case_demo_ui["dt"],
|
||||
case_demo_ui["initial_soc"], case_demo_ui["initial_engine_power"],
|
||||
case_demo_ui["profile"],
|
||||
case_demo_ui["rpm_scale"],
|
||||
case_demo_ui["load_scale"],
|
||||
# 发动机控制器参数
|
||||
case_demo_ui["eng_controller_type"],
|
||||
case_demo_ui["eng_kp"], case_demo_ui["eng_ki"], case_demo_ui["eng_kd"],
|
||||
case_demo_ui["eng_mpc_horizon"], case_demo_ui["eng_mpc_W_power"],
|
||||
case_demo_ui["eng_mpc_W_dcost"], case_demo_ui["eng_mpc_overshoot"],
|
||||
# 电机控制器参数
|
||||
case_demo_ui["mot_controller_type"],
|
||||
case_demo_ui["mot_kp"], case_demo_ui["mot_ki"], case_demo_ui["mot_kd"],
|
||||
case_demo_ui["mot_J"],
|
||||
case_demo_ui["mot_mpc_W_speed"], case_demo_ui["mot_mpc_W_dcost"],
|
||||
case_demo_ui["mot_mpc_overshoot"],
|
||||
# 能量管理策略参数
|
||||
case_demo_ui["soc_target"], case_demo_ui["soc_low"], case_demo_ui["soc_high"],
|
||||
case_demo_ui["p_eng_min"], case_demo_ui["p_eng_max"],
|
||||
case_demo_ui["p_charge"], case_demo_ui["k_soc"],
|
||||
case_demo_ui["power_reserve"], case_demo_ui["battery_capacity"],
|
||||
session_id
|
||||
],
|
||||
outputs=[
|
||||
case_demo_ui["plot"],
|
||||
case_demo_ui["summary"],
|
||||
case_demo_ui["table"],
|
||||
online_counter
|
||||
case_demo_ui["hybrid_plot"], case_demo_ui["hybrid_summary"],
|
||||
case_demo_ui["hybrid_table"], online_counter
|
||||
]
|
||||
)
|
||||
|
||||
@@ -215,14 +402,13 @@ with gr.Blocks(title="自动控制理论学习网站 - AI+数智平台", css=cus
|
||||
)
|
||||
|
||||
# --- 页面加载和定时器事件 ---
|
||||
def on_page_load(sid):
|
||||
update_user_activity(sid)
|
||||
return get_online_status_html()
|
||||
|
||||
demo.load(fn=on_page_load, inputs=[session_id], outputs=[online_counter])
|
||||
demo.load(fn=lambda: get_online_status_html(), outputs=[online_counter])
|
||||
|
||||
gr.Timer(10).tick(fn=get_online_status_html, outputs=online_counter)
|
||||
|
||||
# 系统资源监控定时刷新(每 3 秒)
|
||||
gr.Timer(3).tick(fn=get_system_monitor_html, outputs=system_monitor)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.queue().launch(
|
||||
|
||||
Reference in New Issue
Block a user