diff --git a/GUIDANCE.docx b/GUIDANCE.docx new file mode 100644 index 0000000..5acb8eb Binary files /dev/null and b/GUIDANCE.docx differ diff --git a/GUIDANCE.md b/GUIDANCE.md new file mode 100644 index 0000000..dd1e7dd --- /dev/null +++ b/GUIDANCE.md @@ -0,0 +1,279 @@ +# 自动控制理论AI+数智平台 - 学生使用指南 + +## 一、平台访问 + +**平台地址**: http://182.148.54.29:7860/ + +**推荐浏览器**: Chrome、Edge 或 Firefox 最新版本 + +--- + +## 二、平台功能简介 + +本平台是《自动控制理论》课程的配套学习工具,提供四大核心功能: + +1. **时域分析** - 分析系统的阶跃响应和脉冲响应 +2. **频域分析** - 绘制Bode图和Nyquist图,分析系统稳定性 +3. **根轨迹分析** - 观察增益变化对系统极点的影响 +4. **AI智能问答** - 24小时在线的自动控制理论助教 + +--- + +## 三、详细使用教程 + +### 3.1 时域分析 + +#### 3.1.1 功能说明 + +输入系统的传递函数,查看系统的时域响应特性。 + +#### 3.1.2 使用步骤 + +1. 点击顶部"时域分析"标签页 +2. 在左侧输入框中输入: + - **分子系数**:例如 `1` 或 `1,2,3`(用逗号分隔) + - **分母系数**:例如 `1,6,11,6`(从最高次项到常数项) +3. 点击"显示传递函数"按钮查看数学公式 +4. 点击"开始分析"按钮生成响应曲线 + +#### 3.1.3 输出结果 + +- **左图**:单位阶跃响应曲线 +- **右图**:单位脉冲响应曲线 +- **性能指标**:上升时间、峰值时间、超调量、调节时间等 + +#### 3.1.4 示例输入 + +```text +分子系数: 1 +分母系数: 1,2,1 +``` + +这是一个典型的二阶系统:$G(s) = \frac{1}{s^2+2s+1}$ + +--- + +### 3.2 频域分析 + +#### 3.2.1 功能说明 + +通过Bode图和Nyquist图分析系统的频率特性和稳定性。 + +#### 3.2.2 使用步骤 + +1. 点击顶部"频域分析"标签页 +2. 输入传递函数的分子和分母系数(同时域分析) +3. 拖动"对数增益 log₁₀(K)"滑块,调整系统增益 +4. 观察右侧的Bode图和Nyquist图实时变化 + +#### 3.2.3 输出结果 + +- **Bode图**:幅频特性和相频特性曲线 +- **Nyquist图**:极坐标表示的频率响应 +- **稳定裕度**:增益裕度(GM)和相角裕度(PM) +- **稳定性评估**:系统是否稳定的判断结果 + +#### 3.2.4 稳定性判断标准 + +- GM > 0 dB 且 PM > 0° → **系统稳定** +- GM < 0 dB 或 PM < 0° → **系统不稳定** + +--- + +### 3.3 根轨迹分析 + +#### 3.3.1 功能说明 + +观察增益K从0到∞变化时,闭环极点在s平面上的移动轨迹。 + +#### 3.3.2 使用步骤 + +1. 点击顶部"根轨迹"标签页 +2. 输入开环传递函数的分子和分母系数 +3. 拖动"对数增益 log₁₀(K)"滑块 +4. 观察极点位置的实时变化 + +#### 3.3.3 输出结果 + +- **根轨迹图**:蓝色曲线表示极点运动轨迹 +- **当前极点**:红色×标记表示当前增益K下的闭环极点位置 +- **极点坐标**:实部和虚部的具体数值 + +#### 3.3.4 稳定性判断标准 + +- 极点在左半平面(实部 < 0)→ **稳定** +- 极点在虚轴上(实部 = 0)→ **临界稳定** +- 极点在右半平面(实部 > 0)→ **不稳定** + +--- + +### 3.4 AI智能问答 + +#### 3.4.1 功能说明 + +随时向AI助教提问自动控制理论相关的问题。 + +#### 3.4.2 使用步骤 + +1. 点击顶部"智能问答"标签页 +2. 在底部输入框输入你的问题 +3. 点击"发送"按钮或按Enter键 +4. 等待AI助教回复(支持LaTeX公式显示) + +#### 3.4.3 可提问的内容 + +- **概念解释**:"什么是传递函数?" +- **公式推导**:"如何计算二阶系统的超调量?" +- **例题讲解**:"如何用劳斯判据判断稳定性?" +- **知识点讨论**:"PID控制器的三个参数分别有什么作用?" + +#### 3.4.4 示例问题 + +```text +1. 什么是增益裕度和相角裕度? +2. 如何从根轨迹图判断系统的稳定性? +3. 解释一下Nyquist稳定判据 +4. 二阶系统的阻尼比对响应有什么影响? +``` + +--- + +## 四、常用传递函数示例 + +### 4.1 一阶系统 + +```text +分子: 1 +分母: 1,1 +``` + +传递函数:$G(s) = \frac{1}{s+1}$ + +### 4.2 二阶欠阻尼系统 + +```text +分子: 1 +分母: 1,2,1 +``` + +传递函数:$G(s) = \frac{1}{s^2+2s+1}$ + +### 4.3 二阶无阻尼系统 + +```text +分子: 4 +分母: 1,0,4 +``` + +传递函数:$G(s) = \frac{4}{s^2+4}$ + +### 4.4 三阶系统 + +```text +分子: 1 +分母: 1,6,11,6 +``` + +传递函数:$G(s) = \frac{1}{s^3+6s^2+11s+6}$ + +--- + +## 五、使用技巧与建议 + +### 5.1 推荐做法 + +1. **先显示传递函数** - 确认输入正确再进行分析 +2. **对比不同参数** - 修改系数观察系统特性变化 +3. **结合AI问答** - 不理解的地方随时提问 +4. **多尝试示例** - 从简单系统开始,逐步深入 + +### 5.2 常见问题及解决方法 + +**问题1:输入后没有反应** + +- 检查输入格式:系数之间用英文逗号分隔 +- 确保分子和分母都已输入 + +**问题2:图表显示异常** + +- 刷新页面重试 +- 检查传递函数的阶数是否合理(分母阶数≥分子阶数) + +**问题3:AI回答不准确** + +- 尝试更具体地描述问题 +- 问题中包含关键词和背景信息 + +**问题4:在线人数显示说明** + +- 页面顶部显示当前有多少同学在线使用平台 +- 每10秒自动更新 + +--- + +## 六、学习建议 + +### 6.1 时域分析练习方向 + +1. 对比一阶和二阶系统的响应差异 +2. 观察阻尼比对超调量的影响 +3. 分析零点对系统响应的作用 + +### 6.2 频域分析练习方向 + +1. 调整增益K,观察稳定裕度变化 +2. 找出系统临界稳定的增益值 +3. 理解Bode图和Nyquist图的关系 + +### 6.3 根轨迹分析练习方向 + +1. 找出根轨迹的分离点 +2. 确定系统稳定的增益范围 +3. 观察主导极点的移动规律 + +--- + +## 七、系统要求 + +### 7.1 设备要求 + +- **浏览器**: Chrome 90+、Edge 90+、Firefox 88+ +- **网络**: 稳定的互联网连接 +- **屏幕分辨率**: 建议 1366×768 及以上 + +### 7.2 网络要求 + +- 可访问校园网或互联网 +- 建议带宽不低于2Mbps + +--- + +## 八、技术支持 + +**联系方式**: + +- 邮箱: pengfeiwei@nwpu.edu.cn +- 机构: 西北工业大学 +- 负责人: 魏鹏飞 + +**说明**: + +- 课程相关问题:请联系任课教师或助教 +- 平台技术问题:请通过上述邮箱反馈 + +--- + +## 九、附录 + +### 9.1 平台特色功能 + +1. **实时在线人数统计** - 页面顶部显示当前在线用户数 +2. **自动保存会话** - AI问答记录自动保存 +3. **LaTeX公式支持** - 完美显示数学公式 +4. **响应式设计** - 适配不同屏幕尺寸 + +### 9.2 更新说明 + +- 版本: v1.1.0 +- 更新日期: 2025年10月19日 +- 主要更新: 新增实时在线人数统计功能 diff --git a/GUIDANCE.pdf b/GUIDANCE.pdf new file mode 100644 index 0000000..18106a8 Binary files /dev/null and b/GUIDANCE.pdf differ diff --git a/README_MODULE.md b/README_MODULE.md deleted file mode 100644 index a3998ff..0000000 --- a/README_MODULE.md +++ /dev/null @@ -1,284 +0,0 @@ -# 自动控制原理AI+数智平台 - 模块化架构说明 - -## 📁 项目结构 - -``` -AutoControl/ -├── app.py # 原始完整程序(已废弃,保留作参考) -├── app_main.py # 新的主程序入口 ⭐ 使用这个启动 -├── config.py # 配置文件:API密钥、系统参数 -├── utils.py # 工具函数:解析、验证、格式化 -├── analysis.py # 分析功能:时域、频域、根轨迹 -├── ai_chat.py # AI聊天:DeepSeek/Gemini接口 -├── ui_styles.py # CSS样式:480+行现代化样式 -├── ui_components.py # UI组件:横幅、知识卡片HTML -└── README_MODULE.md # 本文件 -``` - -## 🚀 快速开始 - -### 方法1:使用新的模块化主程序(推荐) - -```bash -python app_main.py -``` - -### 方法2:使用原始完整程序 - -```bash -python app.py -``` - -## 📦 模块说明 - -### 1. `config.py` - 配置管理 -**功能:** 集中管理所有配置常量 - -**内容:** -- `API_KEY`: DeepSeek API密钥 -- `API_BASE_URL`: API基础URL -- `API_MODEL`: 使用的模型名称 -- `DEFAULT_NUM_COEFFS`: 默认分子系数 -- `DEFAULT_DEN_COEFFS`: 默认分母系数 -- `TIME_SPAN`: 时间范围设置 -- `SERVER_PORT`: 服务器端口 -- `PROJECT_INFO`: 项目信息字典 - -**修改配置:** -```python -# 在 config.py 中修改 -API_KEY = "你的API密钥" -SERVER_PORT = 8080 # 更改端口 -``` - -### 2. `utils.py` - 工具函数库 -**功能:** 提供数据处理和格式化工具 - -**主要函数:** -- `coeffs_to_latex(coeffs, var='s')`: 系数转LaTeX多项式 -- `parse_coefficients(coeffs_str)`: 解析系数字符串 -- `validate_transfer_function(num, den)`: 验证传递函数 -- `format_complex_number(c)`: 格式化复数显示 - -**使用示例:** -```python -from utils import parse_coefficients, validate_transfer_function - -num = parse_coefficients("1,2,3") -den = parse_coefficients("1,4,5,2") -is_valid, msg = validate_transfer_function(num, den) -``` - -### 3. `analysis.py` - 核心分析功能 -**功能:** 控制系统的时域、频域、根轨迹分析 - -**主要函数:** -- `display_transfer_function(num_str, den_str)`: 显示传递函数 -- `time_domain_analysis(num_str, den_str)`: 时域分析(阶跃/脉冲响应) -- `frequency_domain_analysis(num_str, den_str, K)`: 频域分析(Bode/Nyquist图) -- `root_locus_analysis(num_str, den_str, log_k)`: 根轨迹分析 - -**返回值:** -- 图表:matplotlib Figure对象 -- 文本:性能指标、极点位置等 - -### 4. `ai_chat.py` - AI聊天功能 -**功能:** 与DeepSeek/Gemini大模型交互 - -**主要函数:** -- `chat_with_ai(message, history)`: 异步聊天函数(支持流式输出) - -**特点:** -- 支持对话历史记录 -- 流式响应(实时显示) -- 自动错误处理 -- Emoji状态指示 - -**系统提示词:** -``` -你是一位精通自动控制原理的专家教授,擅长用通俗易懂的方式解释复杂概念。 -回答要准确、专业,适当使用LaTeX公式,并举例说明。 -``` - -### 5. `ui_styles.py` - CSS样式定义 -**功能:** 提供480+行现代化CSS样式 - -**函数:** -- `get_custom_css()`: 返回完整CSS字符串 - -**样式特点:** -- 渐变背景和阴影效果 -- 动画过渡效果 -- 响应式设计(移动端适配) -- 暗色主题支持 -- Emoji彩色显示修复 - -### 6. `ui_components.py` - UI组件库 -**功能:** 提供HTML组件和知识卡片 - -**主要函数:** -- `get_main_title()`: 主标题 -- `get_subtitle()`: 副标题 -- `get_project_info_banner()`: 项目信息横幅 -- `get_tab_tip(tab_name)`: 标签页提示 -- `get_time_domain_knowledge_card()`: 时域知识卡片 -- `get_frequency_domain_knowledge_card()`: 频域知识卡片 -- `get_root_locus_knowledge_card()`: 根轨迹知识卡片 -- `get_ai_example_questions()`: AI示例问题 - -### 7. `app_main.py` - 主程序入口 -**功能:** 简化的主程序,组装所有模块 - -**代码行数:** ~350行(相比原来的2134行减少84%) - -**结构:** -```python -# 导入所有模块 -from config import * -from utils import * -from analysis import * -from ai_chat import * -from ui_styles import * -from ui_components import * - -# 使用Gradio构建UI -with gr.Blocks(css=get_custom_css()) as demo: - # UI布局 - # 事件绑定 - pass - -# 启动服务器 -demo.launch() -``` - -## 🎯 模块化的优势 - -### 1. 易于维护 -- 每个模块职责单一,代码清晰 -- 修改某个功能只需编辑对应模块 -- 减少代码重复,提高复用性 - -### 2. 易于扩展 -- 添加新功能:创建新模块或在现有模块添加函数 -- 添加新UI:在`ui_components.py`添加新函数 -- 添加新样式:在`ui_styles.py`修改CSS - -### 3. 易于调试 -- 模块独立,可单独测试 -- 错误定位更快速 -- 日志和异常处理更精确 - -### 4. 团队协作友好 -- 不同开发者可并行工作在不同模块 -- 代码冲突减少 -- 代码审查更高效 - -## 📝 常见修改场景 - -### 场景1:更改API密钥 -**文件:** `config.py` -```python -API_KEY = "sk-新的密钥" -``` - -### 场景2:添加新的分析功能 -**文件:** `analysis.py` -```python -def new_analysis_function(num_str, den_str): - """新的分析功能""" - # 实现代码 - return figure, metrics -``` - -**文件:** `app_main.py`(添加UI和事件绑定) -```python -from analysis import new_analysis_function - -# 添加UI组件 -new_button = gr.Button("新分析") -new_output = gr.Plot() - -# 绑定事件 -new_button.click(fn=new_analysis_function, inputs=[...], outputs=[...]) -``` - -### 场景3:修改知识卡片内容 -**文件:** `ui_components.py` -```python -def get_time_domain_knowledge_card(): - return """ -
- -
- """ -``` - -### 场景4:调整样式颜色 -**文件:** `ui_styles.py` -```python -def get_custom_css(): - return """ - .main-title { - background: linear-gradient(135deg, #新颜色1, #新颜色2); - } - """ -``` - -### 场景5:更改默认参数 -**文件:** `config.py` -```python -DEFAULT_DEN_COEFFS = "1,8,15,8" # 更改默认分母系数 -SERVER_PORT = 8080 # 更改服务器端口 -``` - -## 🔧 依赖库 - -```txt -gradio>=5.0.0 -numpy>=1.21.0 -matplotlib>=3.5.0 -scipy>=1.7.0 -control>=0.9.0 -aiohttp>=3.8.0 # AI聊天功能 -``` - -**安装命令:** -```bash -pip install gradio numpy matplotlib scipy control aiohttp -``` - -## ⚠️ 注意事项 - -1. **首次运行:** 需要在`config.py`中配置DeepSeek API密钥才能使用AI聊天功能 -2. **端口冲突:** 如果7860端口被占用,修改`config.py`中的`SERVER_PORT` -3. **模块导入:** 所有模块必须在同一目录下 -4. **Python版本:** 建议使用Python 3.8+ - -## 📊 代码对比 - -| 指标 | 原版 (app.py) | 模块化版本 | -|-----|--------------|-----------| -| 总代码行数 | 2134行 | 分散在7个文件 | -| 主程序行数 | 2134行 | 350行 (↓84%) | -| 可维护性 | ⭐⭐ | ⭐⭐⭐⭐⭐ | -| 可扩展性 | ⭐⭐ | ⭐⭐⭐⭐⭐ | -| 团队协作 | ⭐⭐ | ⭐⭐⭐⭐⭐ | - -## 🎓 学习建议 - -1. **初学者:** 先运行`app_main.py`,熟悉整体功能 -2. **进阶学习:** 阅读各模块代码,理解功能实现 -3. **高级应用:** 尝试添加新功能或修改现有模块 -4. **问题排查:** 参考模块注释和本文档 - -## 📮 联系方式 - -**项目负责人:** 魏鹏飞 教授 -**邮箱:** pengfeiwei@nwpu.edu.cn -**单位:** 西北工业大学 -**资助:** 2025年校级本科生建设项目 - ---- - -**最后更新:** 2025年1月 -**版本:** 2.0 (模块化架构) diff --git a/app.py b/app.py index 88d05ee..0997258 100644 --- a/app.py +++ b/app.py @@ -68,13 +68,79 @@ import os import time from datetime import datetime from threading import Lock +import atexit -# ==================== 在线人数统计 ==================== -# 全局变量:追踪活跃用户 -active_users = {} # 存储用户最后活跃时间 {session_id: timestamp} -users_lock = Lock() # 线程锁,保证并发安全 +""" +==================== 在线/总人数统计与持久化 ==================== +新增:在项目根目录下自动创建数据文件夹,持久化累计人数, +防止容器重启后总人数从 0 开始。 +保存策略:每 SAVE_INTERVAL_SECONDS 秒写盘一次(由在线人数刷新触发)。 +""" +# 当前活跃用户(在线) +active_users = {} # {session_id: last_active_timestamp} +users_lock = Lock() TIMEOUT_SECONDS = 300 # 5分钟无活动视为离线 +# 累计人数(总人数)持久化 +stats_lock = Lock() +STATS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data_usage") +STATS_FILE = os.path.join(STATS_DIR, "usage_stats.json") +SAVE_INTERVAL_SECONDS = int(os.environ.get("USAGE_SAVE_INTERVAL", "60")) # 默认60秒写盘一次 +total_users = 0 # 累计会话人数(近似代表累计访问人数) +seen_sessions = set() # 仅在本次进程内去重,避免同一session重复计数 +_LAST_SAVE_TS = 0.0 + +def _ensure_stats_dir(): + try: + os.makedirs(STATS_DIR, exist_ok=True) + except Exception: + # 即便目录创建失败,不影响主流程,仅导致不持久化 + pass + +def _load_usage_stats(): + """从磁盘加载累计人数,若无文件则从0开始。""" + global total_users, _LAST_SAVE_TS + try: + if os.path.exists(STATS_FILE): + with open(STATS_FILE, "r", encoding="utf-8") as f: + data = json.load(f) + total_users = int(data.get("total_users", 0)) + _LAST_SAVE_TS = float(data.get("last_saved_at", time.time())) + else: + total_users = 0 + _LAST_SAVE_TS = time.time() + except Exception: + # 文件损坏或解析失败时,安全回退为0 + total_users = 0 + _LAST_SAVE_TS = time.time() + +def _save_usage_stats(): + """将累计人数持久化到磁盘。""" + global _LAST_SAVE_TS + try: + tmp_path = STATS_FILE + ".tmp" + with open(tmp_path, "w", encoding="utf-8") as f: + json.dump({"total_users": total_users, "last_saved_at": time.time()}, f, ensure_ascii=False, indent=2) + os.replace(tmp_path, STATS_FILE) + _LAST_SAVE_TS = time.time() + except Exception: + # 写盘失败不阻塞主流程 + pass + +def _maybe_save_usage_stats(): + """按间隔条件触发写盘。调用点:在线人数UI刷新时。""" + try: + with stats_lock: + if time.time() - _LAST_SAVE_TS >= SAVE_INTERVAL_SECONDS: + _save_usage_stats() + except Exception: + pass + +# 初始化目录与读取历史累计值 +_ensure_stats_dir() +_load_usage_stats() +atexit.register(_save_usage_stats) + def get_active_users_count(): """获取当前活跃用户数量""" current_time = time.time() @@ -87,15 +153,34 @@ def get_active_users_count(): return len(active_users) def update_user_activity(session_id): - """更新用户活跃时间""" + """更新用户活跃时间并在首次出现时累计总人数。""" + # 更新在线用户心跳 with users_lock: active_users[session_id] = time.time() + # 首次见到该 session,累计 +1(仅在本进程内去重),并等待定时写盘 + try: + with stats_lock: + global total_users + if session_id not in seen_sessions: + seen_sessions.add(session_id) + total_users += 1 + except Exception: + # 统计异常不影响主流程 + pass def get_online_status_html(): - """生成在线人数显示的HTML""" + """生成在线人数与总人数的显示HTML,并按需触发持久化。""" count = get_active_users_count() + # 读取累计人数(读锁即可) + try: + with stats_lock: + total = int(total_users) + except Exception: + total = 0 + # 触发间隔写盘 + _maybe_save_usage_stats() return f""" -
@@ -104,6 +189,12 @@ def get_online_status_html(): {count} +  |  + 👥 + 总人数: + {total}
""" # ================================================== @@ -2210,9 +2301,15 @@ with gr.Blocks(title="自动控制理论学习网站 - AI+数智平台", css=cus outputs=[freq_plot_output, freq_metrics_display, freq_tf_display, freq_stability_display, k_number_display_freq, rl_plot_output, rl_poles_display, k_number_display, online_counter] ) - # 页面加载时初始化在线人数 + # 页面加载时:记录一次活跃,计入总人数,并初始化在线人数 + def _on_page_load(sid): + try: + update_user_activity(sid) + finally: + return (get_online_status_html(),) + demo.load( - fn=lambda sid: (get_online_status_html(),), + fn=_on_page_load, inputs=[session_id], outputs=[online_counter] ) diff --git a/data_usage/usage_stats.json b/data_usage/usage_stats.json new file mode 100644 index 0000000..7de30b1 --- /dev/null +++ b/data_usage/usage_stats.json @@ -0,0 +1,4 @@ +{ + "total_users": 1, + "last_saved_at": 1760886050.8138287 +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 5b23b8b..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: '3.8' - -services: - autocontrol: - build: . - container_name: autocontrol-app - ports: - - "7860:7860" - restart: unless-stopped - environment: - - TZ=Asia/Shanghai - - PYTHONUNBUFFERED=1 - # 可选:资源限制 - deploy: - resources: - limits: - cpus: '2.0' - memory: 2G - reservations: - cpus: '0.5' - memory: 512M - # 可选:日志持久化 - volumes: - - ./logs:/app/logs - # 健康检查 - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:7860"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s