简介:这是一款面向电路设计、电子工程和嵌入式学习者的跨平台电路设计与仿真计算工具。它借助Python、PyQt与MATLAB技术栈,整合了电路建模、参数计算、波形分析、频率响应、阻抗匹配、滤波器设计、放大器仿真、电源管理及PCB辅助设计等典型EDA功能,适合需要快速搭建仿真验证环境或深入理解EDA工具结构的工程师与研究人员。压缩包共2000个文件,大小45.63MB,文件类型以QML界面资源、SIP绑定、dylib/so动态库、py源码、m脚本、png图标及说明文档为主,其中QML用于界面绘制,动态库承载Qt运行依赖,源码与脚本则便于二次开发,并附有“附赠资源.docx”和“说明文件.txt”。目前已有39人学习下载,内容覆盖全套源码目录与Qt运行库,可用于研究Python与MATLAB混合仿真、跨平台界面交互和电路计算模块的实现思路,也可作为课程设计或电路仿真项目的基础框架。
1. Python PyQt 与 MATLAB 集成工具,为什么值得在电路设计里用
调试前置放大器时,通常开着 MATLAB 看幅频曲线,把数据抄到 Python 脚本里算匹配网络,再从示波器导出的波形里手动找谐波峰值。参数一多,哪份数据对应哪版电路就乱了。"基于 Python PyQt MATLAB 跨平台集成的电路设计与仿真计算工具"这个思路对应的是解决这条流水线:用 PyQt 搭跨平台界面,MATLAB 负责电路建模和仿真内核,numpy/scipy 补上波形分析、滤波器设计、信号处理这些高频操作,最后把阻抗匹配、电源管理结果整理成 PCB 前仿真能用的输入。它适合需要复用 MATLAB 算法,又要桌面化交付的硬件工程师,也适合正在搭板级 pre-layout 验证工具、不想在多个窗口间手动搬运数据的人。下面按集成架构、建模计算、频域分析、功能模块、PCB 衔接这条路线展开。
2. 跨平台集成层:PyQt 界面、MATLAB Engine、SciPy 计算各管哪一段
2.1 为什么不用纯 MATLAB 或纯 PyQt 做全套
纯 MATLAB 的方案用 App Designer 也能做,但交付时要让对方装完整 MATLAB,控件布局和版本兼容也常出问题;纯 PyQt 的问题反过来,工程上大量已验证的 .m 脚本、控制系统工具箱函数不能直接复用。所以我的做法是分层:PyQt6 只做窗口和交互,matplotlib 嵌入 FigureCanvas 接管绘图,numpy/scipy 处理 FFT、滤波、参数计算;MATLAB Engine API for Python 负责调用 .m 函数、控制系统工具箱和 Simulink 模型。这样每个运行时做自己最擅长的事,替换其中一层不会拖垮整条链路。
2.2 最小可跑的 MATLAB Engine 桥接代码
先从环境配起。在 MATLAB 安装目录的extern/engines/python下执行python setup.py install,把引擎包装进当前 Python 环境。很多同事在 VSCode 里配好 Python 环境后仍然 import 失败,多半是 setup.py 用的解释器和 VSCode 解释器不是同一个,先which python确认。之后最小调用如下:
import matlab.engine eng = matlab.engine.start_matlab() sys = eng.tf(matlab.double([1.0]), matlab.double([1.0, 1000.0])) eng.workspace["sys"] = sys eng.eval("figure('Color', 'white'); bode(sys); grid on;") eng.exit()代码先启动一个本机 MATLAB 进程;matlab.double把 Python 列表转成 MATLAB double 数组;eng.tf构造传递函数并放到工作区;eng.eval执行 Bode 绘图。start_matlab() 一次要几秒到十几秒,所以引擎要做成全局单例,在 PyQt 应用启动时初始化,退出时统一 exit,不要在按钮回调里反复启停。eng.exit()放在 closeEvent 里,否则后台会残留 MATLAB 进程,开发机上几天就能攒一堆。
2.3 复数和矩阵参数的类型转换
跨语言调用最常见的坑是数据布局。MATLAB 列优先,numpy 行优先,matlab.double返回的对象直接转 numpy 数组时,列向量的 shape 是 (n,1),直接参与广播常出维度错误。传复数用matlab.complex(real, imag);传逻辑数组用matlab.logical;普通的 list 也不能直接传,必须包一层 matlab 类型。总结如下表:
| 转换方向 | 推荐写法 | 常见错误 |
|---|---|---|
| list -> 实数数组 | matlab.double(list) | 直接传 list |
| numpy -> 实数数组 | 先.tolist()再包 | 转换后 shape 未核对 |
| 复数数组 | matlab.complex(real, imag) | 实虚部拆错 |
| MATLAB 返回 | np.array(ret)后np.ravel() | 多出一个长度为 1 的轴 |
我一般会在工具内部做一层from_numpy()/to_numpy()封装,所有数值进出都过这层,错误只会在一个文件里暴露,排查范围比散落在各个回调里小得多。
2.4 批量仿真时别让 UI 卡在 Engine 上
eng.eval是同步阻塞调用,直接在 PyQt 按钮回调里执行会让界面假死,没有线程处理经验的人第一版都会遇到。标准做法是用 QThread 包装 MATLAB 调用,完成后再通过信号把结果发回主线程。
from PyQt6.QtCore import QThread, pyqtSignal class SimThread(QThread): finished_ok = pyqtSignal(object) def __init__(self, caller): super().__init__() self.caller = caller def run(self): result = self.caller() self.finished_ok.emit(result)调用方只需把要执行的 MATLAB 任务塞进 caller,UI 回调里创建线程并启动。线程内部不要访问任何控件,全部通过 signal 传值。批量扫参时最有效的优化不是多线程,而是把参数列表整个传给一个 MATLAB 侧函数,让它一次算完返回结果矩阵,而不是在 Python 循环里反复 eval。多线程、批量函数、持久引擎三件事做好,集成层才算稳了。
3. 电路建模与参数计算:在 Python 侧建模型,回 MATLAB 验根轨迹
3.1 用 Python 把 RLC 网表变成矩阵和传递函数
电路建模的第一步是定模型表示。无源网络和小信号放大器用节点导纳矩阵最通用,但对多数设计场景,传递函数系数数组更直接。以 RLC 串联电路输出电阻电压为例,H(s)=s(R/L)/(s²+s(R/L)+1/(LC)),可以直接映射成 scipy 的 TransferFunction。
import numpy as np from scipy import signal def rlc_series_tf(r, l, c): num = [r / l, 0.0] den = [1.0, r / l, 1.0 / (l * c)] return signal.TransferFunction(num, den) def rlc_parameters(r, l, c): f0 = 1.0 / (2.0 * np.pi * np.sqrt(l * c)) q = (1.0 / r) * np.sqrt(l / c) bw = f0 / q return f0, q, bwrlc_series_tf里的 num 是分子系数,den 是分母系数,按 s 降幂排列;rlc_parameters返回谐振频率 f0、品质因数 Q 和 -3dB 带宽 BW。用这套结构,GUI 上的"电路类型"下拉只要切换不同的构造函数,后端参数计算和绘图逻辑不用改。网表更复杂时,我建议在 Python 侧写一个极简 SPICE 解析器,把节点映射成整数,再组装 MNA 矩阵,用 numpy.linalg.solve 求解;如果不想维护,直接引入 PySpice,它对线性电路够用,但你要接受它返回的不是 num/den,而是直流工作点加 AC 条件下每个节点电压。
3.2 参数计算先给理论值,再交给仿真校核
参数页不要只给仿真结果,理论公式能快速定位设计边界。下表是我在参数计算面板里常用的几组公式,用户在输入框改 R、L、C、增益电阻时,理论值实时刷新。
| 电路对象 | 关键公式 | 用途 |
|---|---|---|
| RLC 串联谐振 | f0=1/(2π√(LC)) | 确认目标频点 |
| RLC 选频 Q 值 | Q=(1/R)√(L/C) | 估计带宽和选择性 |
| RC 低通 | fc=1/(2πRC) | 滤波器初值 |
| L 型阻抗匹配 | Q=√(R_high/R_low−1),串联电抗=Q·R_low | 把负载电阻变换到目标值 |
| 同相放大器 | A_v=1+R2/R1 | 设定闭环增益 |
| 反馈电阻 | beta=R1/(R1+R2) | 用于稳定性计算 |
表格里的公式都不是最终答案,而是预期值。仿真完成后把 -3dB 点、谐振点和表格对比,偏差超过 5% 就先查单位换算。我见过最多的错误是把 uH 和 nH 混在一起算,GUI 里在下拉菜单统一单位,内部全部转 SI 之后再运算。
3.3 阶跃响应和根轨迹回 MATLAB 交叉验证
Python 侧算完传递函数,我会回 MATLAB 做两组交叉验证:阶跃响应看上升时间、超调量和稳定时间;根轨迹看闭环极点轨迹是否进入右半平面。这是 matlab 阶跃响应用得最值的场景。
eng = matlab.engine.start_matlab() # 假定 r, l, c 已经由 GUI 参数传入 r, l, c = 1e3, 1e-3, 1e-9 num_list = [r / l, 0.0] den_list = [1.0, r / l, 1.0 / (l * c)] num = matlab.double([float(v) for v in num_list]) den = matlab.double([float(v) for v in den_list]) eng.workspace["num"] = num eng.workspace["den"] = den eng.eval("sys = tf(num, den);") eng.eval("figure('Color','white'); step(sys); grid on;") eng.eval("figure('Color','white'); rlocus(sys); grid on;")这里 num/den 必须转成 list 再给 matlab.double,直接传 numpy 数组在部分版本会抛类型错误。step 和 rlocus 会弹出 MATLAB 窗口,如果工具跑在无显示环境的 Linux 服务器上,改为eng.stepinfo(sys)返回结构化数据,在 Python 里自己画,避免依赖 MATLAB 图形系统。两组曲线都出来后,与 scipy.signal.step 的结果对照,确认零极点一致,再进下一环节。
4. 波形分析、频率响应与滤波器设计:SciPy 负责出图,MATLAB 负责校核
4.1 波形分析:导入 CSV/WAV 后先解决横坐标太密集的绘图问题
波形数据一般两种来源:CSV 用 pandas 读,WAV 用 scipy.io.wavfile.read。基本指标峰峰值、RMS、均值和整段 FFT 很容易算,真正破坏体验的是绘图:扫频数据从 1Hz 到 10MHz,横坐标直接按原始点数画,刻度会挤成一条黑色,这就是常说的 python 画图横坐标太密集。解决方法是横轴一律用对数刻度并控制主刻度数量。
import matplotlib.pyplot as plt from matplotlib.ticker import LogLocator def plot_freq_axis(freq_hz, mag_db): fig, ax = plt.subplots(figsize=(8, 4)) ax.semilogx(freq_hz, mag_db) ax.xaxis.set_major_locator(LogLocator(base=10, numticks=6)) ax.set_xlabel("Frequency (Hz)") ax.set_ylabel("Magnitude (dB)") ax.grid(True, which="both", ls="--", alpha=0.5) return fig, axsemilogx 把横轴压缩成对数间距;LogLocator 限制只在 1Hz、10Hz、100Hz 等位置出现主刻度,配合 which="both" 的次要网格,曲线在宽频范围内仍然可读。如果返回的 fig、ax 要嵌进 PyQt,用 FigureCanvasQTAgg 包装,不要在回调里调用 plt.show() 阻塞事件循环。
4.2 频率响应与相位裕量:Bode 数据在 Python 里算
频率响应是电路工具的核心,scipy.signal.bode 返回角频率、幅度 dB 和相位 deg。相位裕量用在环路稳定性检查:找到幅度穿越 0dB 的频率,在该处取相位加 180°。
from scipy import signal import numpy as np def phase_margin(loop_tf, fmin=1.0, fmax=1e7, n=5000): f = np.logspace(np.log10(fmin), np.log10(fmax), n) w = 2 * np.pi * f _, mag_db, phase_deg = signal.bode(loop_tf, w=w) cross_idx = np.where(np.diff(np.sign(mag_db)) != 0)[0] if len(cross_idx) == 0: return None, None, mag_db, phase_deg, w idx = cross_idx[0] pm = phase_deg[idx] + 180.0 return pm, w[idx] / (2 * np.pi), mag_db, phase_deg, w注意 signal.bode 的 w 参数是角频率 rad/s,所以先构造频率 f 再乘 2π。返回值里 PM 单位是度,穿越频率换算回 Hz 便于显示。系统有多个穿越点时,用第一个不一定对,更稳的做法是判断穿越点前后幅度斜率正负,只取负斜率穿越。这个函数能直接给 GUI 的裕量显示用,也可以把 mag_db、phase_deg 交给 MATLAB 的 margin 做交叉验证。
4.3 滤波器设计:类型参数表和 SOS 实现
滤波器设计页是最好落地的功能。我常用的下拉选择:高通/低通/带通/带阻、阶数、截止频率、滤波器类型,输出用 SOS 而不是 ba,因为高阶直接使用 ba 系数进行滤波时,数值误差放大非常明显。参数对比如下:
| 类型 | 特性 | 关键参数 | 适用 |
|---|---|---|---|
| Butterworth | 最大平坦 | 阶数、截止频率 | 大多数通用滤波 |
| Chebyshev I | 通带等波纹 | 阶数、截止频率、纹波 rp | 要求过渡带更窄 |
| Bessel | 线性相位 | 阶数、截止频率 | 需要保持波形形状 |
对应实现:
from scipy.signal import butter, cheby1, bessel, sosfiltfilt def design_filter(fs_hz, cutoff_hz, order=4, kind="butter", filt_type="lowpass", rp=0.5): wn = np.atleast_1d(cutoff_hz) / (fs_hz / 2.0) funcs = {"butter": butter, "cheby1": cheby1, "bessel": bessel} extra = {"rp": rp} if kind == "cheby1" else {} sos = funcs[kind](order, wn, btype=filt_type, output="sos", **extra) return sos def apply_filter(x, sos): return sosfiltfilt(sos, x)cutoff_hz要先除以 Nyquist(采样率的一半)做归一化,带通/带阻时传[low, high]两个截止频率的列表,np.atleast_1d保证接口一致。sosfiltfilt 是零相位滤波,适合分析和展示;真正做嵌入式实时滤波时要用 sosfilt,并考虑相位延迟。MATLAB 校核时用 designfilt 或 butter 生成同参数系数,把幅频曲线拉出来对比 3dB 点,误差应在 1% 以内。
5. 放大器仿真、电源管理与信号处理:把三个实验串成一条链路
5.1 放大器仿真:用单极点模型算清增益带宽积
放大器仿真不一定要上完整 SPICE。做预设计时,单极点开环模型足以回答"闭环增益能到多少带宽"。A(s)=A0/(1+s/wp),闭环 H(s)=A/(1+beta·A),在频率域用复数直接算,避免传递函数符号运算的弯路。
import numpy as np def opamp_responses(a0_db, fp_hz, beta, fmin=10.0, fmax=10e6, n=5000): f = np.logspace(np.log10(fmin), np.log10(fmax), n) w = 2 * np.pi * f a0 = 10 ** (a0_db / 20.0) wp = 2 * np.pi * fp_hz A = a0 / (1 + 1j * (w / wp)) H = A / (1 + beta * A) mag_open = 20 * np.log10(np.abs(A)) mag_closed = 20 * np.log10(np.abs(H)) phase_closed = np.angle(H, deg=True) return f, mag_open, mag_closed, phase_closeda0_db 是开环直流增益,例如 100dB;fp_hz 是主极点频率;beta 由反馈网络算出。对 100dB、10Hz、beta=0.1 的运放,闭环增益 20dB,闭环 -3dB 带宽约 100kHz,这个结果比直接翻手册更直观。拿到 f、mag_closed 后,GUI 可以把预期 -3dB 带宽标在曲线上,便于和真实环路分析对齐。
5.2 电源管理:纹波、PSRR 与 PCB 布局提示
电源管理模块我做的是三个看起来简单但上线快的计算:Buck 输出纹波、LDO 功耗、PSRR 对噪声的抑制。这些公式在黑板级和板级估算都成立,也能在一分钟内给出去耦电容的容值初值。
def buck_ripple(i_out, f_sw, cout): return i_out / (8 * f_sw * cout) def ldo_power_loss(vin, vout, iout): return (vin - vout) * iout def ldo_noise_after_psrr(psrr_db, noise_in_uvrms): return noise_in_uvrms / (10 ** (psrr_db / 20.0))参数含义直接看函数名:Buck 纹波与输出电流正比,与开关频率、输出电容成反比;LDO 功耗是压差乘电流;PSRR 分贝数转成十进制倍数后相除。这三个结果在工具里同时输出布局提示:输出电容靠近负载放置、反馈走线远离电感开关节点、ADC/DAC 电源去耦电容按手册摆放。这些都是 PCB 布局中最常被检查的项,也是把仿真数据和板级经验绑在一起的地方。
5.3 信号处理链路:窗函数、去噪与 THD 指标
信号处理链路把前面所有模块连起来:取一段被测波形,先加窗,再做 FFT 求 THD,需要观察时域细节就先滤波去噪。THD 的可靠算法不是取单根谱线,而是把基波和谐波附近若干 bin 的功率都累加,避免泄漏造成跳变。
def thd_by_fft(x, fs, f_fund, num_harmonics=5, span_ratio=0.01): n = len(x) win = np.hanning(n) spectrum = np.fft.rfft(x * win) freqs = np.fft.rfftfreq(n, 1.0 / fs) def band_power(f_center): mask = np.abs(freqs - f_center) < span_ratio * f_center return np.sum(np.abs(spectrum[mask]) ** 2) p_fund = band_power(f_fund) p_harm = sum(band_power(f_fund * k) for k in range(2, num_harmonics + 1)) return np.sqrt(p_harm) / np.sqrt(p_fund)span_ratio=0.01表示取中心频率 ±1% 范围内的谱线功率,对常见的 50Hz/60Hz 工频和 kHz 级采样都稳定。汉宁窗能压低旁瓣泄漏,但会让基波能量分散到相邻 bin,所以累加窗口成为必要。MATLAB 的thd(x, fs, f_fund)可以直接用来校核,两个结果差超过 5% 时,先查采样率是否读错和窗类型是否一致。
6. PCB 前仿真到板级验证:导出数据比导出图纸更重要
6.1 阻抗匹配结果怎么指导走线宽度与层叠
标题里的 PCB 不是要替代 EDA,而是为 PCB 提供可执行输入。射频前端做完匹配网络后,目标阻抗已经有了,下一步是根据层叠反推微带线宽度。常见做法是在工具里放一个微带线近似公式:
import math def microstrip_z0(er, h_mil, w_mil, t_mil=1.2): return (87.0 / math.sqrt(er + 1.41)) * math.log(5.98 * h_mil / (0.8 * w_mil + t_mil))er 是介质常数,h_mil 是介质厚度,w_mil 是线宽,t_mil 是铜厚,输出单位是欧姆。这个公式只适合 0.1 < w/h < 3.0 的范围,超出就要用场求解器。工具里用二分法反解 w_mil,得到线宽初值后,再去 Cadence 做 PCB 板层设置,或在嘉立创 EDA 里按该线宽布线。仿真值作为起点,最终以阻抗测试为准。
6.2 导出 BOM、网表和报告给 DRC 环节
第二件事是导出。元件值、滤波器拓扑、匹配网络结果一键写成 CSV,同时导出 SPICE 网表子电路,方便后续在 PSpice、LTspice 或嘉立创 EDA 中复用。pandas 一行就能完成:
import pandas as pd rows = [("R1", 4700.0, "Ohm", "input bias"), ("C1", 1.0e-9, "F", "filter"), ("L1", 2.2e-6, "H", "bias decoupling")] df = pd.DataFrame(rows, columns=["ref", "value", "unit", "role"]) df.to_csv("preliminary_bom.csv", index=False)这套输出不是 Gerber,Gerber 必须在 EDA 里结合板框和层叠生成。AD20 的 DRC 检查、嘉立创 EDA 的在线 DRC,检查的是线宽、间距、过孔孔径这些布线规则;仿真工具能告诉你哪些网络需要放进差分约束和阻抗约束,真正执行检查在 EDA 里。
6.3 跨平台部署与验证技巧
打包部署有三个坑:PyInstaller 打包 PyQt 时,matplotlib backend 和 numpy 动态库容易漏,需要在 spec 文件里加 hiddenimports;matlab.engine 依赖本机 MATLAB,目标机没有 MATLAB 时,要用 MATLAB Compiler 把算法编成独立组件,再配 Runtime;路径拼接全部用 pathlib,别写死反斜杠,否则 Linux 下直接崩。
验证是所有功能里最容易被跳过但最值得做的一项。我在工程里内置三个回归样本:RC 低通、RLC 带通、单位增益放大器。RC 低通 -3dB 频率必须等于 1/(2πRC);RLC 谐振点必须落在 1/(2π√LC);放大器闭环带宽必须等于开环 0dB 交越频率乘以反馈系数 β。三个理论值用解析公式先算好放在配置文件里,GUI 上任何一项偏差超过 1% 就报红色。这套回归样本不依赖硬件,每一台开发机都能跑,也是新人接手时最快的理解入口。
本文还有配套的精品资源,点击获取