PYQT实战:无刷电机模拟(只是模拟,没有写接口接收外部数据)

发布于:2025-07-10 ⋅ 阅读:(14) ⋅ 点赞:(0)

       本文介绍了一个基于PyQt5的无刷电机(BLDC)驱动控制仿真平台。该系统采用Python开发,结合Matplotlib实现数据可视化,主要功能包括: 电机仿真模块:可模拟电机转速、电流、扭矩和温度等参数,支持FOC(磁场定向控制)和BLDC(方波控制)两种算法 性能分析模块:提供效率参数、扭矩参数分析及FFT频谱分析 文档资料模块:包含电机控制原理和技术文档 代码查看器:支持导入、编辑和保存C控制代码 系统采用模块化设计,包含GUI界面、仿真引擎和数据可视化组件,能够直观展示电机运行状态和性能指标。

代码

import sys
import os
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                            QLabel, QPushButton, QTextEdit, QFileDialog, QTabWidget, 
                            QSlider, QSpinBox, QComboBox, QProgressBar, QSplitter, 
                            QMessageBox, QGridLayout, QGroupBox, QTreeWidget, QTreeWidgetItem,
                            QTextBrowser)
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtCore import Qt, QTimer
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
import subprocess
import tempfile




class MotorSimulationWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 主图表:电机性能
        self.figure = Figure(figsize=(8, 6), dpi=100)
        self.canvas = FigureCanvas(self.figure)
        self.figure.set_facecolor('#f0f0f0')
        
        # 子图表:相电流波形
        self.phase_figure = Figure(figsize=(8, 3), dpi=100)
        self.phase_canvas = FigureCanvas(self.phase_figure)
        self.phase_figure.set_facecolor('#f0f0f0')
        
        # 初始化仿真数据
        self.time = np.linspace(0, 10, 1000)
        self.speed = np.zeros(1000)
        self.current = np.zeros(1000)
        self.torque = np.zeros(1000)
        self.temp = np.zeros(1000)
        self.phase_u = np.zeros(1000)
        self.phase_v = np.zeros(1000)
        self.phase_w = np.zeros(1000)
        
        self.last_loaded_file = ""
        self.algorithm_type = "FOC"
        
        layout = QVBoxLayout()
        layout.addWidget(self.canvas, 30)      # 70%空间给主图表
        layout.addWidget(self.phase_canvas, 30) # 30%空间给相电流图表
        self.setLayout(layout)
        
        # 初始绘制
        self.reset_data()
    
    def get_simulation_data(self):
        """返回当前仿真数据"""
        return {
            'time': self.time,
            'speed': self.speed,
            'current': self.current,
            'torque': self.torque,
            'temperature': self.temp,
            'phase_u': self.phase_u,
            'phase_v': self.phase_v,
            'phase_w': self.phase_w
        }

    def reset_data(self):
        """重置仿真数据"""
        self.speed = np.zeros(1000)
        self.current = np.zeros(1000)
        self.torque = np.zeros(1000)
        self.temp = np.zeros(1000)
        self.phase_u = np.zeros(1000)
        self.phase_v = np.zeros(1000)
        self.phase_w = np.zeros(1000)
        self.update_plots()

    def update_plots(self):
        """更新图表"""
        self.figure.clear()
        self.phase_figure.clear()
        
        # 主图表 - 电机性能
        ax = self.figure.add_subplot(111)
        ax.set_facecolor('#f0f0f0')
        ax.grid(True, linestyle='--', alpha=0.6)
        
        ax.plot(self.time, self.speed, 'b-', linewidth=2, label='Speed (RPM)')
        ax.plot(self.time, self.current * 100, 'g-', linewidth=2, label='Current (x100 A)')
        ax.plot(self.time, self.torque * 10, 'r-', linewidth=2, label='Torque (x10 N·m)')
        ax.plot(self.time, self.temp / 10, 'm-', linewidth=2, label='Temp (°C /10)')
        
        ax.set_title(f'BLDC Motor Performance ({self.algorithm_type} Control)')
        ax.set_xlabel('Time (s)')
        ax.set_ylabel('Value')
        ax.legend(loc='upper right')
        
        # 子图表 - 相电流波形
        ax_phase = self.phase_figure.add_subplot(111)
        ax_phase.set_facecolor('#f0f0f0')
        ax_phase.grid(True, linestyle='--', alpha=0.6)
        
        ax_phase.plot(self.time, self.phase_u, 'r-', linewidth=1, label='U Phase')
        ax_phase.plot(self.time, self.phase_v, 'g-', linewidth=1, label='V Phase')
        ax_phase.plot(self.time, self.phase_w, 'b-', linewidth=1, label='W Phase')
        ax_phase.set_title('Phase Currents')
        ax_phase.set_xlabel('Time (s)')
        ax_phase.set_ylabel('Current (A)')
        ax_phase.legend(loc='upper right')
        
        self.canvas.draw()
        self.phase_canvas.draw()

    def generate_data(self, sim_time):
        """生成仿真数据到指定时间点"""
        if sim_time < 0 or sim_time > 10:
            return
        
        # 找到当前时间对应的索引
        idx = int(sim_time * 100)  # 1000个点覆盖10秒,每0.01秒一个点
        
        # 确保索引不超出范围
        if idx >= len(self.time):
            return
            
        t = sim_time
        
        # 计算当前时间点的值
        # 启动阶段 (0-2秒)
        if t <= 2:
            speed = 1000 * t / 2
            current = 10 - 8 * t / 2
            torque = 8 - 6 * t / 2
            temp = 30 + 20 * t / 2
        # 稳态运行 (2-5秒)
        elif t <= 5:
            speed = 1000
            current = 2
            torque = 2
            temp = 50 + 20 * (t - 2) / 3
        # 负载增加 (5-7秒)
        elif t <= 7:
            speed = 1000 - 200 * (t - 5) / 2
            current = 2 + 3 * (t - 5) / 2
            torque = 2 + 3 * (t - 5) / 2
            temp = 70 + 20 * (t - 5) / 2
        # 制动阶段 (7-10秒)
        else:
            speed = 800 - 800 * (t - 7) / 3
            current = 5 - 4 * (t - 7) / 3
            torque = 5 - 4 * (t - 7) / 3
            temp = 90 - 30 * (t - 7) / 3
        
        # 添加噪声使数据更真实
        noise_scale = 0.1 * t if t < 5 else 0.3  # 随时间增加噪声
        speed += np.random.normal(0, 15 * noise_scale)
        current += np.random.normal(0, 0.2 * noise_scale)
        torque += np.random.normal(0, 0.3 * noise_scale)
        temp += np.random.normal(0, 1 * noise_scale)
        
        # 更新数据数组中的当前点
        self.speed[idx] = speed
        self.current[idx] = current
        self.torque[idx] = torque
        self.temp[idx] = temp
        
        # 生成相电流
        freq = 5  # 5Hz基础频率
        phase_scale = current * 0.8  # 电流幅值缩放
        
        # 根据控制算法调整波形
        if self.algorithm_type == "FOC":
            # FOC - 正弦波
            self.phase_u[idx] = phase_scale * np.sin(2 * np.pi * freq * t)
            self.phase_v[idx] = phase_scale * np.sin(2 * np.pi * freq * t + 2*np.pi/3)
            self.phase_w[idx] = phase_scale * np.sin(2 * np.pi * freq * t + 4*np.pi/3)
        elif self.algorithm_type == "BLDC":
            # BLDC - 方波
            angle = (freq * t * 6) % 6
            self.phase_u[idx] = phase_scale if angle < 3 else -phase_scale
            self.phase_v[idx] = phase_scale if 2 <= angle < 5 else -phase_scale
            self.phase_w[idx] = phase_scale if (angle < 1 or angle >= 4) else -phase_scale
        else:  # 正弦波控制
            self.phase_u[idx] = phase_scale * np.sin(2 * np.pi * freq * t)
            self.phase_v[idx] = phase_scale * np.sin(2 * np.pi * freq * t + 2*np.pi/3)
            self.phase_w[idx] = phase_scale * np.sin(2 * np.pi * freq * t + 4*np.pi/3)
        
        # 更新图表显示到当前时间点
        self.update_plots()

    def simulate_from_file(self, filename, algorithm_type):
        """从C文件启动仿真"""
        self.last_loaded_file = os.path.basename(filename)
        self.algorithm_type = algorithm_type.split(' ')[0]  # 取第一个单词作为算法类型
        self.reset_data()
        return f"已加载 {self.last_loaded_file} ({algorithm_type})"

class BLDCControlSimulator(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("TianQi AI - 无刷电机驱动仿真平台")
        self.setGeometry(100, 100, 1200, 800)
        
        # 设置窗口图标
        try:
            self.setWindowIcon(QIcon("bldc_icon.png"))
        except:
            pass
        
        # 创建主部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 创建标签页
        self.tab_widget = QTabWidget()
        main_layout.addWidget(self.tab_widget)
        
        # 创建各标签页
        self.create_simulation_tab()
        self.create_analysis_tab()
        self.create_documentation_tab()
        self.create_code_viewer_tab()
        
        # 创建状态栏
        self.status_bar = self.statusBar()
        self.progress_bar = QProgressBar()
        self.progress_bar.setFixedWidth(250)
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        self.status_bar.addPermanentWidget(self.progress_bar)
        self.status_bar.showMessage("就绪 | 点击 '导入C文件' 开始仿真")
        
        # 初始化仿真定时器
        self.sim_timer = QTimer()
        self.sim_timer.timeout.connect(self.update_simulation)
        self.sim_time = 0
        self.sim_running = False
        
        # 温度监控
        self.current_temp = 30
        self.peak_temp = 30
    
    # 标签页创建函数保持不变(使用之前的实现)
    def create_simulation_tab(self):
        """创建仿真标签页"""
        sim_tab = QWidget()
        layout = QVBoxLayout(sim_tab)
        
        # 标题
        header = QLabel("无刷电机(BLDC)驱动控制仿真平台")
        header.setStyleSheet("font-size: 20px; font-weight: bold; color: #0047AB; margin: 10px;")
        layout.addWidget(header)
        
        # 仿真图表部件
        self.sim_widget = MotorSimulationWidget()
        layout.addWidget(self.sim_widget, 80)
        
        # 控制面板
        control_panel = QWidget()
        control_layout = QHBoxLayout(control_panel)
        
        # 文件导入区域
        file_box = QWidget()
        file_layout = QVBoxLayout(file_box)
        file_label = QLabel("控制算法导入")
        file_label.setStyleSheet("font-weight: bold;")
        self.file_path_label = QLabel("尚未加载任何文件")
        self.file_path_label.setWordWrap(True)
        self.file_path_label.setStyleSheet("color: #555555; padding: 2px; border: 1px solid #cccccc; border-radius: 3px;")
        
        # 导入按钮
        import_btn = QPushButton("导入C文件")
        import_btn.clicked.connect(self.import_file)
        import_btn.setStyleSheet("background-color: #4CAF50; color: white; padding: 8px; border-radius: 4px;")
        
        file_layout.addWidget(file_label)
        file_layout.addWidget(self.file_path_label)
        file_layout.addWidget(import_btn)
        
        # 控制算法设置区域
        algo_box = QWidget()
        algo_layout = QVBoxLayout(algo_box)
        algo_label = QLabel("控制算法设置")
        algo_label.setStyleSheet("font-weight: bold;")
        
        # 算法选择
        algo_type_layout = QHBoxLayout()
        algo_type_layout.addWidget(QLabel("算法类型:"))
        self.algo_combo = QComboBox()
        self.algo_combo.addItems(["FOC (磁场定向控制)", "BLDC (方波控制)", "正弦波控制"])
        self.algo_combo.setStyleSheet("padding: 5px;")
        algo_type_layout.addWidget(self.algo_combo)
        
        # 仿真速度控制
        speed_layout = QHBoxLayout()
        speed_layout.addWidget(QLabel("仿真速度:"))
        self.speed_slider = QSlider(Qt.Horizontal)
        self.speed_slider.setMinimum(1)
        self.speed_slider.setMaximum(10)
        self.speed_slider.setValue(5)
        self.speed_slider.valueChanged.connect(self.update_speed_label)
        speed_layout.addWidget(self.speed_slider)
        self.speed_label = QLabel("5x")
        self.speed_label.setStyleSheet("min-width: 40px;")
        speed_layout.addWidget(self.speed_label)
        
        # 操作按钮
        btn_layout = QHBoxLayout()
        self.start_btn = QPushButton("开始仿真")
        self.start_btn.setStyleSheet("background-color: #2196F3; color: white; padding: 8px; border-radius: 4px;")
        self.start_btn.clicked.connect(self.start_simulation)
        
        stop_btn = QPushButton("停止仿真")
        stop_btn.setStyleSheet("background-color: #F44336; color: white; padding: 8px; border-radius: 4px;")
        stop_btn.clicked.connect(self.stop_simulation)
        
        reset_btn = QPushButton("重置")
        reset_btn.setStyleSheet("padding: 8px; border-radius: 4px;")
        reset_btn.clicked.connect(self.reset_simulation)
        
        btn_layout.addWidget(self.start_btn)
        btn_layout.addWidget(stop_btn)
        btn_layout.addWidget(reset_btn)
        
        # 温度监控
        temp_layout = QHBoxLayout()
        temp_layout.addWidget(QLabel("电机温度:"))
        self.temp_display = QLabel("30°C")
        self.temp_display.setStyleSheet("font-weight: bold; color: #E91E63; font-size: 16px;")
        temp_layout.addStretch(1)
        temp_layout.addWidget(QLabel("峰值温度:"))
        self.peak_temp_display = QLabel("30°C")
        self.peak_temp_display.setStyleSheet("font-weight: bold; color: #D32F2F; font-size: 16px;")
        
        # 布局组装
        algo_layout.addWidget(algo_label)
        algo_layout.addLayout(algo_type_layout)
        algo_layout.addLayout(speed_layout)
        algo_layout.addLayout(btn_layout)
        algo_layout.addLayout(temp_layout)
        
        control_layout.addWidget(file_box, 40)
        control_layout.addWidget(algo_box, 60)
        
        layout.addWidget(control_panel, 20)
        
        self.tab_widget.addTab(sim_tab, "电机仿真")
    
    def create_analysis_tab(self):
        """创建性能分析标签页"""
        analysis_tab = QWidget()
        layout = QVBoxLayout(analysis_tab)
        
        # 标题
        header = QLabel("性能参数分析")
        header.setStyleSheet("font-size: 18px; font-weight: bold; color: #0047AB; margin: 10px;")
        layout.addWidget(header)
        
        # 参数网格布局
        param_widget = QWidget()
        param_layout = QHBoxLayout(param_widget)
        
        # 效率参数区域
        eff_group = QGroupBox("效率参数")
        eff_layout = QVBoxLayout(eff_group)
        
        # 效率参数项
        eff_params = [
            ("系统效率", "92%", "#388E3C"),
            ("电流谐波失真(THD)", "4.5%", "#F57C00"),
            ("开关损耗", "85W", "#D32F2F"),
            ("热损耗", "65W", "#7B1FA2"),
            ("最大功率点", "86%", "#0288D1")
        ]
        
        for name, value, color in eff_params:
            param_row = QWidget()
            row_layout = QHBoxLayout(param_row)
            
            name_label = QLabel(name)
            value_label = QLabel(value)
            value_label.setStyleSheet(f"color: {color}; font-weight: bold; font-size: 14px;")
            
            row_layout.addWidget(name_label, 60)
            row_layout.addWidget(value_label, 40)
            eff_layout.addWidget(param_row)
        
        # 扭矩参数区域
        torque_group = QGroupBox("扭矩参数")
        torque_layout = QVBoxLayout(torque_group)
        
        # 扭矩参数项
        torque_params = [
            ("平均扭矩", "2.5 N·m", "#1976D2"),
            ("扭矩波动", "0.8 N·m", "#5D4037"),
            ("启动扭矩", "4.2 N·m", "#0097A7"),
            ("转矩响应时间", "15 ms", "#00796B"),
            ("最大扭矩", "5.6 N·m", "#689F38")
        ]
        
        for name, value, color in torque_params:
            param_row = QWidget()
            row_layout = QHBoxLayout(param_row)
            
            name_label = QLabel(name)
            value_label = QLabel(value)
            value_label.setStyleSheet(f"color: {color}; font-weight: bold; font-size: 14px;")
            
            row_layout.addWidget(name_label, 60)
            row_layout.addWidget(value_label, 40)
            torque_layout.addWidget(param_row)
        
        param_layout.addWidget(eff_group)
        param_layout.addWidget(torque_group)
        
        layout.addWidget(param_widget, 40)
        
        # 波形分析区域
        waveform_group = QGroupBox("波形分析")
        waveform_layout = QVBoxLayout(waveform_group)
        
        # 波形分析图形
        self.waveform_figure = Figure(figsize=(10, 4), dpi=100)
        self.waveform_figure.set_facecolor('#f0f0f0')
        self.waveform_canvas = FigureCanvas(self.waveform_figure)
        
        # 创建分析控制按钮
        analyze_btn = QPushButton("开始波形分析")
        analyze_btn.setStyleSheet("background-color: #9C27B0; color: white; padding: 8px; border-radius: 4px;")
        analyze_btn.clicked.connect(self.run_analysis)
        
        waveform_layout.addWidget(self.waveform_canvas, 80)
        waveform_layout.addWidget(analyze_btn, 20)
        
        layout.addWidget(waveform_group, 60)
        
        self.tab_widget.addTab(analysis_tab, "性能分析")
        
        # 初始化波形分析图
        self.init_waveform_analysis()
    
    def init_waveform_analysis(self):
        """初始化波形分析图"""
        ax = self.waveform_figure.add_subplot(111)
        ax.set_facecolor('#f0f0f0')
        ax.grid(True, linestyle='--', alpha=0.6)
        ax.set_title("FFT分析 - 频谱特性")
        ax.set_xlabel("频率 (Hz)")
        ax.set_ylabel("幅值")
        self.waveform_canvas.draw()
    
    def run_analysis(self):
        """运行波形分析"""
        # 这里暂时使用示例数据,实际应用中会使用仿真数据
        try:
            ax = self.waveform_figure.get_axes()[0]
            ax.clear()
            
            # 生成示例频谱数据
            freq = np.arange(0, 100, 0.1)
            amplitude = np.exp(-(freq-10)**2/(2*5**2)) + 0.6*np.exp(-(freq-30)**2/(2*8**2)) + 0.3*np.exp(-(freq-60)**2/(2*12**2))
            amplitude += 0.1 * np.random.randn(len(freq))
            
            ax.plot(freq, amplitude, 'b-', linewidth=1.5)
            ax.set_title("FFT分析 - 频谱特性")
            ax.set_xlabel("频率 (Hz)")
            ax.set_ylabel("幅值")
            ax.grid(True, linestyle='--', alpha=0.6)
            
            # 标记主要频率分量
            ax.annotate('基波频率', xy=(10, 1), xytext=(20, 0.8),
                        arrowprops=dict(facecolor='black', shrink=0.05))
            ax.annotate('谐波分量', xy=(30, 0.6), xytext=(40, 0.75),
                        arrowprops=dict(facecolor='black', shrink=0.05))
                        
            self.waveform_canvas.draw()
            self.status_bar.showMessage("波形分析完成 | FFT频谱计算成功")
        except Exception as e:
            self.status_bar.showMessage(f"波形分析错误: {str(e)}")
    
    def create_documentation_tab(self):
        """创建文档标签页"""
        doc_tab = QWidget()
        layout = QVBoxLayout(doc_tab)
        
        # 标题
        header = QLabel("无刷电机控制原理与文档")
        header.setStyleSheet("font-size: 18px; font-weight: bold; color: #0047AB; margin: 10px;")
        layout.addWidget(header)
        
        # 创建分割区域
        splitter = QSplitter(Qt.Horizontal)
        
        # 文档目录树
        doc_tree = QTreeWidget()
        doc_tree.setHeaderLabels(["文档目录"])
        
        # 文档结构
        basic_item = QTreeWidgetItem(doc_tree, ["基本概念"])
        QTreeWidgetItem(basic_item, ["无刷电机工作原理"])
        QTreeWidgetItem(basic_item, ["霍尔传感器原理"])
        QTreeWidgetItem(basic_item, ["电机驱动基础"])
        
        foc_item = QTreeWidgetItem(doc_tree, ["FOC控制算法"])
        QTreeWidgetItem(foc_item, ["Clark变换原理"])
        QTreeWidgetItem(foc_item, ["Park变换原理"])
        QTreeWidgetItem(foc_item, ["空间矢量调制(SVPWM)"])
        
        advanced_item = QTreeWidgetItem(doc_tree, ["高级控制技术"])
        QTreeWidgetItem(advanced_item, ["无传感器控制算法"])
        QTreeWidgetItem(advanced_item, ["电机参数辨识"])
        QTreeWidgetItem(advanced_item, ["振动抑制技术"])
        
        for i in range(doc_tree.topLevelItemCount()):
            doc_tree.expandItem(doc_tree.topLevelItem(i))
        
        # 文档内容显示
        doc_text = QTextBrowser()
        doc_text.setOpenExternalLinks(True)
        doc_text.setStyleSheet("background-color: #ffffff; padding: 10px; border: 1px solid #cccccc;")
        
        # 初始文档内容
        initial_doc = """
        <h2>无刷直流电机(BLDC)控制基础</h2>
        <p>无刷直流电机是一种采用电子换向代替机械换向的电机。它具有以下优点:</p>
        <ul>
            <li>高效率:无机械换向损耗</li>
            <li>高功率密度:体积小、出力大</li>
            <li>长寿命:无电刷磨损</li>
            <li>低噪声:电子换向平稳安静</li>
        </ul>
        
        <h3>基本控制方法</h3>
        <p><b>方波控制:</b> 梯形波控制,简单易实现</p>
        <p><b>FOC控制:</b> 磁场定向控制,高效平稳</p>
        <p><b>正弦波控制:</b> 介于方波和FOC之间</p>
        
        <h3>学习资源</h3>
        <p>推荐参考资料:</p>
        <ul>
            <li><a href='https://www.st.com/resource/en/application_note/dm00035939.pdf'>STM32 FOC电机控制库</a></li>
            <li><a href='https://www.ti.com/lit/ml/slyp173/slyp173.pdf'>TI无刷直流电机控制指南</a></li>
            <li><a href='https://github.com/simplefoc/Arduino-FOC'>Arduino FOC项目</a></li>
        </ul>
        """
        doc_text.setHtml(initial_doc)
        
        splitter.addWidget(doc_tree)
        splitter.addWidget(doc_text)
        splitter.setSizes([200, 600])
        
        # 目录点击事件
        doc_tree.itemClicked.connect(lambda item, text=doc_text: self.show_doc_content(item, text))
        
        layout.addWidget(splitter)
        self.tab_widget.addTab(doc_tab, "文档资料")
    
    def show_doc_content(self, title):
        """显示文档具体内容"""
        content = {
            '基本概念': {
                '无刷电机工作原理': '<h2>工作原理</h2><p>通过霍尔传感器检测转子位置...</p>',
                '霍尔传感器原理': '<h2>霍尔效应</h2><p>利用霍尔元件检测磁场变化...</p>'
            },
            'FOC控制算法': {
                'Clark变换原理': '<h2>三相转两相</h2><p>将三相坐标系转换为静止两相坐标系...</p>',
                'Park变换原理': '<h2>坐标旋转</h2><p>将静止坐标系转换为旋转坐标系...</p>'
            }
        }
        
        # 遍历文档结构查找匹配内容
        for category in content.values():
            if title in category:
                return category[title]
        
        # 添加默认返回内容
        return f'<h3>{title}</h3><p style="color:#666">文档内容正在编写中,预计下个版本发布</p>'
        
    def show_doc_content(self, item, text_browser):
        """显示选中的文档内容"""
        if item.parent() is None:  # 顶级目录项
            return
        
        doc_title = item.text(0)
        content = ""
        
        if doc_title == "无刷电机工作原理":
            content = """
            <h2>无刷电机工作原理</h2>
            <p>无刷电机通过电子换相器按顺序给线圈通电,产生旋转磁场带动永磁体转子旋转。</p>
            <p><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/BrushlessDC_motor_animation.gif/220px-BrushlessDC_motor_animation.gif' width='200'></p>
            """
        elif doc_title == "FOC控制算法":
            content = """
            <h2>磁场定向控制(FOC)</h2>
            <p>FOC通过将三相电流转换到两相旋转坐标系(dq坐标系),实现对转矩和磁链的解耦控制。</p>
            <p>主要步骤:</p>
            <ol>
                <li>Clarke变换:三相静止abc坐标系 → 两相静止αβ坐标系</li>
                <li>Park变换:静止αβ坐标系 → 旋转dq坐标系</li>
                <li>PID调节器:分别控制d轴和q轴电流</li>
                <li>反Park变换:dq坐标系 → αβ坐标系</li>
                <li>SVPWM:产生三相PWM波驱动电机</li>
            </ol>
            """
        # 其他文档内容... 
        else:
            content = f"<h2>{doc_title}</h2><p>文档内容正在建设中...</p>"
        
        text_browser.setHtml(content)
    
    def create_code_viewer_tab(self):
        """创建代码查看器标签页"""
        code_tab = QWidget()
        layout = QVBoxLayout(code_tab)
        
        # 标题
        header = QLabel("控制代码查看器")
        header.setStyleSheet("font-size: 18px; font-weight: bold; color: #0047AB; margin: 10px;")
        layout.addWidget(header)
        
        # 创建带有行号的代码编辑区
        self.code_editor = QTextEdit()
        self.code_editor.setFont(QFont("Consolas", 10))
        self.code_editor.setStyleSheet("background-color: #f8f8f8; border: 1px solid #cccccc;")
        
        # 添加代码行号
        self.code_editor.setLineWrapMode(QTextEdit.NoWrap)
        
        # 加载示例代码
        example_code = """// 无刷电机FOC控制示例代码
#include "bldc_foc.h"

// 电机参数
Motor motor = {
    .pole_pairs = 7,
    .resistance = 0.8,
    .inductance = 0.0012,
    .kv = 180
};

void setup() {
    foc_init(&motor);  // 初始化FOC控制器
    foc_set_pwm_frequency(20000);  // 20kHz PWM
}

void loop() {
    // 读取目标速度 (0-1000 RPM)
    float target_speed = get_target_speed();  
    
    // FOC控制循环
    foc_control_loop(target_speed);
    
    // 温度保护
    if (motor.temperature > 85) {
        foc_set_output(0);  // 超温停机
    }
}"""
        self.code_editor.setPlainText(example_code)
        
        # 代码控制区域
        control_box = QWidget()
        control_layout = QHBoxLayout(control_box)
        
        save_btn = QPushButton("保存代码")
        save_btn.setStyleSheet("background-color: #4CAF50; color: white; padding: 8px; border-radius: 4px;")
        save_btn.clicked.connect(self.save_code)
        
        compile_btn = QPushButton("编译代码")
        compile_btn.setStyleSheet("background-color: #2196F3; color: white; padding: 8px; border-radius: 4px;")
        
        format_btn = QPushButton("格式化代码")
        format_btn.setStyleSheet("padding: 8px; border-radius: 4px;")
        format_btn.clicked.connect(self.format_code)
        
        control_layout.addWidget(save_btn)
        control_layout.addWidget(compile_btn)
        control_layout.addWidget(format_btn)
        control_layout.addStretch(1)
        
        layout.addWidget(self.code_editor, 90)
        layout.addWidget(control_box, 10)
        
        self.tab_widget.addTab(code_tab, "代码查看器")
    
    def import_file(self):
        """导入C控制文件"""
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getOpenFileName(
            self, "导入电机控制文件", 
            "", "C/C++ Files (*.c *.cpp *.h *.hpp);;All Files (*)", 
            options=options
        )
        
        if file_name:
            self.file_path_label.setText(file_name)
            try:
                with open(file_name, 'r') as file:
                    content = file.read()
                    self.code_editor.setPlainText(content)
                
                # 启动仿真
                algo_type = self.algo_combo.currentText()
                status = self.sim_widget.simulate_from_file(file_name, algo_type)
                self.status_bar.showMessage(f"已加载: {os.path.basename(file_name)} | {status}")
                self.progress_bar.setValue(10)  # 进度10%
            except Exception as e:
                QMessageBox.critical(self, "文件导入错误", f"无法导入文件:\n{str(e)}")
    
    def update_speed_label(self, value):
        """更新仿真速度标签"""
        self.speed_label.setText(f"{value}x")
    
    def start_simulation(self):
        """开始仿真"""
        if not self.sim_running:
            self.sim_running = True
            self.start_btn.setText("仿真运行中...")
            self.start_btn.setStyleSheet("background-color: #FF9800; color: white;")
            self.sim_time = 0
            
            # 设置定时器间隔 (x1.0对应100ms, 倍速减少间隔)
            interval = 100 // self.speed_slider.value()
            self.sim_timer.start(interval)
            self.status_bar.showMessage("仿真启动 | 正在模拟电机运行状态")
            self.progress_bar.setValue(0)
    
    def stop_simulation(self):
        """停止仿真"""
        self.sim_running = False
        self.sim_timer.stop()
        self.start_btn.setText("开始仿真")
        self.start_btn.setStyleSheet("background-color: #2196F3; color: white;")
        self.status_bar.showMessage("仿真已停止 | 分析当前数据")
        self.progress_bar.setValue(100)
    
    def reset_simulation(self):
        """重置仿真"""
        self.stop_simulation()
        self.sim_time = 0
        self.sim_widget.reset_data()
        self.current_temp = 30
        self.peak_temp = 30
        self.temp_display.setText(f"{self.current_temp}°C")
        self.peak_temp_display.setText(f"{self.peak_temp}°C")
        self.status_bar.showMessage("仿真已重置 | 准备重新开始")
        self.progress_bar.setValue(0)
    
    def update_simulation(self):
        """定时更新仿真"""
        if self.sim_time < 10:
            # 更新仿真时间 (步长0.1秒)
            self.sim_time += 0.1
            self.sim_widget.generate_data(self.sim_time)
            
            # 更新温度显示
            temp = self.sim_widget.temp[int(self.sim_time * 10)]  # 每0.1秒一个数据点
            self.current_temp = int(temp)
            if self.current_temp > self.peak_temp:
                self.peak_temp = self.current_temp
                
            # 温度警告颜色
            temp_style = "font-weight: bold; font-size: 16px;"
            if self.current_temp > 80:
                temp_style += "color: #D32F2F;"
            elif self.current_temp > 70:
                temp_style += "color: #F57C00;"
            else:
                temp_style += "color: #388E3C;"
            
            self.temp_display.setStyleSheet(temp_style)
            self.temp_display.setText(f"{self.current_temp}°C")
            self.peak_temp_display.setText(f"{self.peak_temp}°C")
            
            # 更新进度条 (每0.1秒更新10%)
            progress = int(self.sim_time * 10)
            if progress > 100:
                progress = 100
            self.progress_bar.setValue(progress)
            
            # 更新状态栏
            status_message = f"仿真运行中 | 时间: {self.sim_time:.1f}s | 速度: {self.speed_slider.value()}x | 温度: {self.current_temp}°C"
            self.status_bar.showMessage(status_message)
            
            # 每隔1秒更新性能参数
            if int(self.sim_time * 10) % 10 == 0:
                self.update_performance_params()
        else:
            self.stop_simulation()
            self.status_bar.showMessage("仿真完成 | 达到最大仿真时间10秒")
    
    def update_performance_params(self):
        """更新性能分析标签页的参数显示"""
        try:
            # 获取最新仿真数据
            sim_data = self.sim_widget.get_simulation_data()
            
            # 更新效率参数
            eff_widget = self.tab_widget.widget(0)  # 分析标签页
            for i in range(1, 6):  # 效率参数前5项
                row_widget = eff_widget.findChildren(QWidget)[0].layout().itemAt(i-1).widget()
                value_label = row_widget.layout().itemAt(1).widget()
                value = value_label.text().strip('%')
                # 根据仿真数据动态调整
                value = float(value) + np.random.uniform(-0.5, 0.5)
                value_label.setText(f"{max(80, min(100, value)):.1f}%")
            
            # 更新扭矩参数
            for i in range(6, 11):  # 扭矩参数后5项
                row_widget = eff_widget.findChildren(QWidget)[0].layout().itemAt(i-1).widget()
                value_label = row_widget.layout().itemAt(1).widget()
                base_value = float(value_label.text().split()[0])
                # 根据仿真数据动态调整
                value = base_value + np.random.uniform(-0.1, 0.1)
                value_label.setText(f"{value:.2f} N·m")
        
        except Exception as e:
            print(f"更新参数错误: {e}")
    
    def save_code(self):
        """保存代码文件"""
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getSaveFileName(
            self, "保存代码文件", 
            "", "C Files (*.c);;All Files (*)", 
            options=options
        )
        
        if file_name:
            try:
                with open(file_name, 'w') as file:
                    content = self.code_editor.toPlainText()
                    file.write(content)
                self.status_bar.showMessage(f"代码已保存: {os.path.basename(file_name)}")
                QMessageBox.information(self, "保存成功", "代码文件保存成功!")
            except Exception as e:
                QMessageBox.critical(self, "保存错误", f"无法保存文件:\n{str(e)}")
    
    def format_code(self):
        """使用clang-format进行专业代码格式化"""
        try:
            # 临时保存代码内容
            text = self.code_editor.toPlainText()
            with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.c') as tmp:
                tmp.write(text)
                tmp_path = tmp.name
            
            # 使用项目根目录的.clang-format配置
            clang_format = os.path.join(os.getcwd(), '.clang-format')
            cmd = f'clang-format -style=file -i "{tmp_path}"'
            
            # 执行格式化命令
            subprocess.run(cmd, shell=True, check=True)
            
            # 读取格式化后的内容
            with open(tmp_path, 'r') as f:
                formatted = f.read()
            
            self.code_editor.setPlainText(formatted)
            self.status_bar.showMessage("代码已使用clang-format格式化")
            
        except subprocess.CalledProcessError:
            QMessageBox.warning(self, "格式错误", "请安装clang-format并添加到PATH环境变量")
        except Exception as e:
            QMessageBox.critical(self, "格式化错误", f"{str(e)}")
        finally:
            if 'tmp_path' in locals():
                os.remove(tmp_path)
    
    # 保留原有简单格式化作为fallback
        def simple_format_code(self):
            """简单格式化代码"""
            text = self.code_editor.toPlainText()
            
            # 简单格式化 - 缩进和大括号对齐
            formatted = ""
            indent_level = 0
            for line in text.splitlines():
                line = line.strip()
                if line.startswith('}') and indent_level > 0:
                    indent_level -= 1
                
                indent = "    " * indent_level
                formatted += indent + line + "\n"
                
                if line.endswith('{'):
                    indent_level += 1
            
            self.code_editor.setPlainText(formatted)
            self.status_bar.showMessage("代码格式化完成")



# 主程序入口
if __name__ == "__main__":
    app = QApplication(sys.argv)
    # 添加字体配置
    font = QFont('Microsoft YaHei', 10)
    app.setFont(font)
    app.setStyle('Fusion')  # 使用Fusion样式
    
    # 设置全局样式
    app.setStyleSheet("""
        QMainWindow {
            background-color: #f5f5f5;
        }
        QGroupBox {
            font-weight: bold;
            border: 1px solid #cccccc;
            border-radius: 6px;
            margin-top: 1ex;
        }
        QGroupBox::title {
            subcontrol-origin: margin;
            left: 10px;
            padding: 0 3px;
        }
        QPushButton {
            border-radius: 4px;
            padding: 6px;
        }
        QFont{
        font-family: "Microsoft YaHei";
        font-size: 9pt;
         }
    """)
    
    window = BLDCControlSimulator()
    window.show()
    sys.exit(app.exec())

运行结果 

 

 

 


网站公告

今日签到

点亮在社区的每一天
去签到