简介:MATLAB中谐波平衡法实现项目提供了一套完整的非线性系统频域分析工具箱,面向电子电路设计、电力电子装置与通信系统研究人员,可用于求解振荡器、放大器等系统的周期稳态响应。谐波平衡法通过傅立叶级数展开将非线性微分方程转化为谐波域代数方程组,本项目覆盖从谐波选取、激励设置到雅可比矩阵构造、迭代求解及Floquet稳定性判断的完整流程。压缩包共58个文件,以53个.m脚本为主,细分为核心求解、数值线性化、激励与非线性处理、频响及分支分析、测试算例等模块;同时附带README说明、MATLAB项目配置与开源许可证。整个资源仅67KB,代码结构紧凑易读,适合学习原理并在此基础上二次开发。目前已有111人学习下载。项目内置了标准测试模型和多个验证脚本,可对照运行直观查看谐波平衡法在经典非线性电路中的表现,也可结合FFT/IFFT、窗函数等技术优化频谱分析精度。对希望深入掌握实现细节或开展自主研究的读者,这套代码提供了可直接运行的起点,多变量扩展版本也展示了向复杂系统推广的实现思路。
1. 谐波平衡法在MATLAB里怎么落地:从非线性频响计算说起
做非线性系统频响分析时,很多人第一反应是直接跑时域仿真,等瞬态衰减完再取稳态段做FFT。这个方法能看个大概,但每逢强非线性、高Q值或慢衰减工况,仿真时间被拉长几个数量级,参数扫描根本跑不动。谐波平衡法(Harmonic Balance Method, HBM)换了一条路:假设解是周期稳态的傅里叶级数,把微分方程变成一组代数方程,再用fsolve这类工具直接求频域系数。这条路在MATLAB里实现成本并不高,一旦跑通,扫频效率比时域仿真高一到两个数量级。这篇笔记就沿着最小可行实现展开,覆盖残差构造、参数设置和典型坑。
2. 建立HBM频域方程组:从Duffing方程到残差向量
2.1 把时域方程搬到频域:谐波平衡的核心假设
谐波平衡法处理的是周期稳态响应,比如单频激励下机械结构的定常振动、电力电子变换器的周期开关稳态。它的出发点很朴素:如果系统最终进入周期运动,那么位移解可以写成傅里叶级数
x(t) = x0 + Σ_{k=1..N} [a_k·cos(k·Ω·t) + b_k·sin(k·Ω·t)]
其中Ω是基频,通常是外部激励频率;N是谐波截断阶数。把这个表达式代回运动方程后,方程两侧都展开成傅里叶级数,让每个谐波分量的系数相等,就得到一组关于未知数x0、a_k、b_k的非线性代数方程。这就是“谐波平衡”四个字的来源:对每一个谐波,让方程左右两边达到平衡。
这个思路和打靶法、时域仿真最大的区别在于,它把微分方程在频域上直接离散,不再需要逐时间步推进。对慢衰减系统来说,时域仿真要等瞬态消失,HBM则天然只关心稳态,所以计算量不随系统阻尼比恶化。HBM也不是线性化:它没有假设非线性项是小量,只是把非线性映射到有限个谐波上做近似,截断阶数越高,近似越接近真实周期解。
2.2 ALT交替算法:非线性项怎么进入频域
线性项的谐波系数可以直接用导数关系算,但非线性项你没法随手写出它的傅里叶系数。比如α·x³这一项,展开后会产生3次、5次乃至更高次谐波,它的系数依赖所有a_k、b_k的乘积组合,解析推导非常繁琐。
工程上普遍采用的做法叫ALT(Alternating Time-Frequency Domain)方法,也叫交替时频域法:在时域把非线性项算出来,再用FFT把它转回频域。具体流程是,先由当前的谐波系数重构一个周期内的时域采样点,计算非线性函数g(x(t)),然后对这个时域序列做FFT,取出与0、1、2...N次谐波对应的谱线,作为非线性项在该谐波上的系数。
这一步需要格外小心的是采样点数M。若设非线性函数最高能产生p·N次谐波(p是非线性项的最高幂次,如x³对应p=3),那么采样点数必须满足M > 2·p·N,否则高于M/2的谱线会被折叠回低频段,污染你真正关心的0到N次分量。后面第4章会给出具体建议,这里先记住结论:三次非线性时,直接用M = 2^nextpow2(6·N+1)是最稳妥的。
2.3 残差方程的自由度与未知数排布
把线性项和非线性项分别整理后,每个谐波分量对应一个残差方程,最终形成R(q) = 0的形式,其中q是全部待求系数。为了避免复数运算带来的额外自由度,常用单边实系数展开,未知数排布是
q = [x0; a1; b1; a2; b2; ...; aN; bN]
总共有2N+1个实数未知数。直流项x0对应0次谐波,a_k和b_k分别对应k次谐波的余弦和正弦分量。采用这种排布有三个实际好处:一是fsolve处理实向量最方便,不需要做复数到实数的映射;二是初值设置直观,可以直接从线性稳态解提取a1、b1;三是后处理时幅值就是sqrt(a_k²+b_k²),不用额外求模。
对线性项,方程m·x'' + c·x' + k·x代入谐波形式后,k次谐波的余弦和正弦分量的系数分别是
余弦分量:[-m·(kΩ)² + k]·a_k + c·kΩ·b_k 正弦分量:[-m·(kΩ)² + k]·b_k - c·kΩ·a_k
外部激励F·cos(Ωt)只出现在1次谐波的余弦方程上。把这些线性项与非线性项的频域系数相加,再减去外激励,就得到完整的残差向量。
3. 用fsolve跑通最小代码:Duffing方程的HBM求解器
3.1 残差函数与ALT采样:一份能直接跑的MATLAB代码
下面以经典的Duffing方程为例,给出一个最小可用的残差函数。系统方程为m·x'' + c·x' + k·x + α·x³ = F·cos(Ω·t)。在MATLAB中,这个方程对应一个二阶非线性振荡器,是测试HBM实现的标准对象。
function R = hbm_residual(q, sys, N, omega) % q 的排布: [x0; a1; b1; a2; b2; ...; aN; bN] % 对应 x(t) = x0 + sum_{k=1..N}[a_k*cos(k*w*t) + b_k*sin(k*w*t)] % sys 是结构体, 包含 m, c, k, alpha, f K = 2*N + 1; % 未知数个数 R = zeros(K, 1); % ---- 交替时频域(ALT)求解非线性项 ---- % 三次非线性最高产生3N次谐波, 采样点数必须大于2*3N M = 2^nextpow2(6*N + 1); % 防混叠采样点数 n = (0:M-1)'; % 采样序号向量 phase_mat = (2*pi/M) * (1:N) * n'; % 相位矩阵, 尺寸 N x M % 由谐波系数重构时域位移采样 x_t = q(1) * ones(M, 1); % 直流分量 for k = 1:N x_t = x_t + q(2*k) * cos(phase_mat(k,:)).' ... + q(2*k+1) * sin(phase_mat(k,:)).'; end % 非线性项在时域计算, 再FFT回频域 g_t = sys.alpha * x_t.^3; % 对Duffing方程即 alpha*x^3 G = fft(g_t) / M; % 直流在G(1), k次谐波在G(k+1) % ---- 线性项系数 : 直接解析给出 ---- % 这里把 -(k*omega)^2*m + k 和 c*k*omega 一次性算好 lin_cos = -sys.m * (omega * (1:N)').^2 + sys.k; lin_sin = sys.c * omega * (1:N)'; % 直流残差: k*x0 + 非线性项的直流分量 = 0 R(1) = sys.k * q(1) + real(G(1)); % 1..N 次谐波, 每阶两个方程, 分别对应cos与sin分量 for k = 1:N r_cos = lin_cos(k) * q(2*k) + lin_sin(k) * q(2*k+1) ... + 2 * real(G(k+1)); % 反馈到cos系数 r_sin = lin_cos(k) * q(2*k+1) - lin_sin(k) * q(2*k) ... - 2 * imag(G(k+1)); % 反馈到sin系数 if k == 1 r_cos = r_cos - sys.f; % 外激励只作用在1次余弦方程 end R(2*k) = r_cos; R(2*k+1) = r_sin; end end这段代码的关键点有三个。第一,非线性项不是解析展开,而是通过时域重构和FFT获得频域系数,这样换任何一种非线性函数都只需要改动g_t那一行。第二,采样点数M直接取了6N+1向上取2的幂,避免高次谐波折叠回低频段,这是ALT实现最容易踩的坑。第三,线性项的符号需要仔细核对,尤其是sin分量上c·kΩ·a_k这一项是带负号的,符号错了根本收敛不了。
代码中real(G(1))用于直流残差,是因为非线性项在时域是实信号,其直流分量必然是实数。对于cos和sin分量,DFT谱线与实信号的对应关系是cos系数等于2倍的实部,sin系数等于负2倍的虚部,不能用abs代替,否则会丢掉相位信息。
3.2 主脚本与求解器调用:初值取线性稳态解
残差函数写好后,求解环节反而简单。这里最容易翻车的不是fsolve的选项,而是初值。直接给全零初值在强非线性下经常收敛到平凡解或者根本不收敛。常见做法是先把α置零,求出对应线性系统的稳态解,用它的1次谐波系数做初值。
% 系统参数: m*x'' + c*x' + k*x + alpha*x^3 = f*cos(omega*t) sys.m = 1.0; sys.c = 0.02; sys.k = 1.0; sys.alpha = 0.5; sys.f = 0.3; omega = 1.2; N = 5; % 谐波截断阶数, 先取5 q0 = zeros(2*N+1, 1); % 初值向量, 先占位 % 线性化稳态解: 令alpha=0, 得到1次谐波余弦/正弦系数 den = (sys.k - sys.m*omega^2)^2 + (sys.c*omega)^2; q0(1) = 0; % 线性系统无直流偏置 q0(2) = sys.f * (sys.k - sys.m*omega^2) / den; % a1 q0(3) = sys.f * sys.c * omega / den; % b1 % fsolve求解非线性方程组 options = optimoptions('fsolve', ... 'Display', 'iter', ... 'MaxFunctionEvaluations', 10000, ... 'FunctionTolerance', 1e-10, ... 'StepTolerance', 1e-12); [q, fval, exitflag] = fsolve(@(q) hbm_residual(q, sys, N, omega), q0, options); if exitflag <= 0 warning('求解器未收敛, 检查初值或增大谐波数'); end % 提取结果: 直流偏置和一阶谐波幅值 x0_hbm = q(1); amp_1st = sqrt(q(2)^2 + q(3)^2); fprintf('x0 = %.4e, 一阶谐波幅值 = %.4e\n', x0_hbm, amp_1st);这段代码里den的表达式是线性受迫振动幅频关系的分母。对线性系统m·x'' + c·x' + k·x = F·cos(Ωt),稳态解为a1 = F·(k - mΩ²)/den,b1 = F·cΩ/den,这个初值在非线性不太强时已经离真解很近了,fsolve通常几步就收敛。如果α很大,可以在得到线性解后把它乘以某个小于1的衰减因子,或者用上一个频点的解做延续,具体见第4章。
3.3 对求解结果做时域校验
fsolve返回exitflag>0只代表残差代数方程被满足,不代表解一定有物理意义。尤其是当N选取过小时,残差可能降得很低,但重构出来的时域波形和真实解差很远。常规做法是把HBM解重构回时域,再代回原微分方程检查余差。
% 重构一个周期内的位移, 检查运动方程残差 M_check = 2^nextpow2(6*N + 1); n_check = (0:M_check-1)'; x_check = q(1) * ones(M_check, 1); for k = 1:N x_check = x_check + q(2*k) * cos(2*pi*k*n_check/M_check) ... + q(2*k+1) * sin(2*pi*k*n_check/M_check); end % 用中心差分近似二阶导数, 检查运动方程残差 dt = (2*pi/omega) / M_check; x_ddot = (x_check([2:end 1]) - 2*x_check + x_check([end 1:end-1])) / dt^2; x_dot = (x_check([2:end 1]) - x_check([end 1:end-1])) / (2*dt); eq_res = sys.m*x_ddot + sys.c*x_dot + sys.k*x_check + sys.alpha*x_check.^3 ... - sys.f*cos(omega*n_check*2*pi/M_check); max_res = max(abs(eq_res)); fprintf('时域运动方程最大残差 = %.4e\n', max_res);注意这里使用了周期边界假设,差分格式在首尾相接时等价于周期信号求导。实际工程中,这个最大残差应该和激励幅值F比,比如小于F的1%就说明谐波截断足够。如果这个值偏大,问题基本锁定在N太小,而不是fsolve没收敛。
4. 关键参数怎么调:谐波数N、采样点数M、初值与容差
4.1 谐波截断数N:多取一阶,算得快也稳
N是HBM里最核心的自由度。它直接决定未知数个数(2N+1)和非线性项的最高频率覆盖范围。N选小了,高频能量无处安放,低频系数会被“挤”偏;N选大了,方程组规模线性增长,但单次求解成本其实增加不多,因为fsolve对这种中等规模问题很擅长。
我一般建议从N=3起步,然后按3、5、8、12这样递增观察解的变化。判断标准是:一阶谐波幅值在N=8和N=12之间变化小于0.1%,同时时域运动方程最大残差相对激励幅值低于1%,就可以固定下来。对Duffing这类多项式非线性,N取5到8已经非常充裕,因为x³的能量集中在低次谐波。遇到死区、饱和、迟滞这类强非线性,谐波能量衰减慢,N往往要取到20甚至更高。
这里有一个更客观的判据可以写进脚本里:求完解后,把非线性项的频域谱G打印出来,观察第N条谱线相对最大谱线的比值。如果这个比值大于1e-4,说明截断处还有不可忽略的能量,应该增大N。
4.2 采样点数M:为什么不能只取2N+1
很多教学代码里M取2N+1,理由是重构2N+1个傅里叶系数至少需要这么多采样点。这个说法在纯线性场景没错,但ALT流程里非线性项g(x(t))的频谱范围比x本身宽得多。以x³为例,x含0到N次谐波,它的立方至少含0到3N次谐波。如果在DFT长度M≤2·3N的情况下直接做FFT,高于M/2的谱线会被折叠回低频带,污染0到N次分量。
举个例子,N=5、M=2N+1=11时,x³最高会产生15次谐波,15在M=11的DFT里折叠到4(15 mod 11 = 4)。也就是说,4次谐波被混入了一个实际不存在的分量,整个方程组都被污染。把M提高到2^nextpow2(6N+1)之后,最高谱线15小于M/2,折叠问题消失。
对一般情形,设非线性函数内部最高幂次为p次,M要满足M > 2·p·N。除非你明确知道非线性项的谐波带宽比这个上限窄,否则不要在这个参数上节省。
4.3 初值策略:线性解起步与扫频延续法
HBM残差方程在强非线性下有多解,初值落在哪个解的吸引域,fsolve就会收敛到哪个解。单点计算时,用线性化稳态解起步最安全。扫频时则有一个近乎免费的加速技巧:把上一个频点的收敛解作为下一个频点的初值。这个技巧叫延续法,它利用了解随频率连续变化的性质。
% 扫频主循环: 用前一个频点的解作为下一个频点的初值 w_list = linspace(0.4, 2.0, 80); % 频率扫描范围 q_prev = []; % 存放上一个频点的解 amp_list = zeros(size(w_list)); for i = 1:length(w_list) omega = w_list(i); if i == 1 % 第一个频点用线性解起步 den = (sys.k - sys.m*omega^2)^2 + (sys.c*omega)^2; q_prev = zeros(2*N+1, 1); q_prev(2) = sys.f * (sys.k - sys.m*omega^2) / den; q_prev(3) = sys.f * sys.c * omega / den; end [q, ~, ef] = fsolve(@(q) hbm_residual(q, sys, N, omega), q_prev, options); if ef <= 0 % 若当前频点失败, 减小扫频步长或用更小的初值扰动重试 fprintf('omega=%.3f 未收敛\n', omega); q = q_prev; % 继续下一点, 避免中断整个扫描 end q_prev = q; amp_list(i) = sqrt(q(2)^2 + q(3)^2); end扫频失败时不要立刻调容差,优先检查是不是频率步长太大导致解跳变到另一分支。强非线性系统的频响曲线往往有弯曲甚至多值区,这时候需要更密的频率点,并在每个点尝试多个初值。
4.4 求解器容差与方程组归一化
fsolve的收敛判据有两层:FunctionTolerance控制残差向量范数的相对阈值,StepTolerance控制步长变化。对物理方程,残差的量级由系统参数决定。刚度k=1e6的系统和k=1.0的系统,残差自然差六个数量级,此时死磕FunctionTolerance=1e-12没有意义,甚至会让求解器在浮点精度极限附近空转。
我习惯先看求解结果的时域运动方程最大残差,再决定TolFun设多少。如果运动方程残差已经小于激励幅值的0.1%,计算层面的容差就够了。另一个更釜底抽薪的办法是归一化:把方程两边同时除以参考刚度或者参考质量,让主导系数降到同一个数量级。对Duffing方程来说,把m、c、k、α、F全部除以k,就相当于做了一次无量纲化,此时c的有效值变成c/k,α的有效值变成α/k,残差尺度会立刻均衡。
下表是这几个参数的速查建议:
| 参数 | 建议取值 | 调整依据 |
|---|---|---|
| 谐波数N | 3起步,5~8常用 | 观察第N条谱线相对幅值,比值应小于1e-4 |
| 采样点数M | 2^nextpow2(6N+1) | 非线性的最高幂次p,需满足M > 2·p·N |
| 初值 | 线性稳态解起步 | 强非线性时用延续法或扰动多个初值 |
| FunctionTolerance | 1e-8 ~ 1e-10 | 结合方程归一化,运动方程残差应小于激励幅值1% |
| 频率步长 | 密于非线性共振带宽 | 解跳变时加密频率点并用上一点初值重试 |
5. HBM实现避坑排查:不收敛、假解与混叠的5个典型问题
5.1 残差卡在某个值降不下去:先查谐波截断阶数
现象:fsolve显示已经收敛,exitflag大于0,但fval的范数始终在1e-3量级,怎么收紧容差都没用。重构时域波形后,运动方程残差在波峰附近有明显的周期误差。
原因:谐波截断数N不够。N限制了方程组能表达的频率成分,如果你关心的问题本身包含5次谐波,N=3时这5次分量根本无法进入未知数向量,残差就会被“逼”到其他低次系数上。
解决:把N按3、5、8、12扫描一遍,看fval范数和一阶幅值是否随N增大而稳定。如果fval单调下降且最后两级之间变化很小,说明N已经够了;如果fval始终降不到期望水平,还要检查采样点数M是否太小,或者系统本身存在非周期分量。
5.2 高次谐波对不上:时域采样点数M不够的混叠
现象:N取大之后解不但没变准,反而出现振荡。比如N=8时一阶幅值比N=5时偏了3%,继续增大N又偏回另一边。检查G向量会看到低次谱线附近有不该出现的高频折叠痕迹。
原因:ALT采样点数M没覆盖非线性项的最高谐波。对x³来说,3N次谐波在DFT长度不足时折叠回带内,直接污染低频系数。这个错非常隐蔽,因为程序完全不会报错,数值上一切正常,只有对比才知道结果不对。
解决:将M改成2^nextpow2(6N+1),重新计算后观察一阶幅值随N的变化是否单调收敛。如果混叠是主因,这个修正立竿见影。另一个排查技巧是故意把N设到很大,比如N=20,再看低次谐波是否还随N抖动,不抖才是干净的。
5.3 初值随手给:fsolve跑飞或收敛到平凡解
现象:求解器提示“未找到满足容差的点”,或者很快收敛但输出的幅值接近零,明显是平凡解。有时候换一个随机初值,结果又落在另一个物理上说不通的分支上。
原因:HBM残差是多解非线性方程组,零初值或随意初值很容易落在平凡解的吸引域。尤其当非线性刚度很强时,真实解与线性解已经偏离,随便给初值很可能直接发散。
解决:用线性稳态解作为初值是从零到一的关键一步。这个初值在α不算太大时几乎必然收敛。如果α很强,我习惯先解一个α减半的中间问题,用它的解作为原问题的初值,这种同伦思路比凭空猜初值稳定得多。扫频场景则无条件使用延续法,前一个频点收敛解直接当后一个频点的起点。
5.4 系数尺度差几个数量级:归一化与容差一起调
现象:残差向量里某些分量量级在1e6,另一些在1e-6,fsolve即便显示收敛,求解结果也明显不符合物理直觉,甚至一阶幅值为负。
原因:方程没有归一化。线性刚度项、非线性刚度项和外激励项的系数相差太大,导致雅可比矩阵条件数很差,浮点数精度被大数吃掉,小数项变成数值噪声。
解决:先把方程整体除以某个参考量做无量纲化,让主导系数集中在1附近。对机械系统,参考刚度或参考质量都可以;对电路系统,常以基波阻抗为参考。归一化之后再重新设置FunctionTolerance,比如1e-10,而不是沿用系统参数直接套默认值。注意归一化不是可选项,是HBM工程实现的常规动作。
5.5 亚谐波响应解不出来:基频选择问题
现象:扫频到某个频段时,HBM求解失败或给出明显错误的解,但同一参数下用ode45跑时域仿真,稳定后的波形是周期的,FFT结果显示含有二分之一或三分之一激励频率的成分。
原因:HBM的基频默认设为激励频率Ω,但系统发生了亚谐波响应,真实周期是激励周期的整数倍。此时解的基频实际上是Ω/2或Ω/3,只用Ω做基频无法表达这种响应。
解决:把基频Ω_base设为激励频率除以p(p=2或3),把外部激励写成F·cos(p·Ω_base·t),再让未知数的第p阶对应激励响应。分别用p=1、2、3求解后,对比总响应幅值,才能判断系统到底落在哪个工作分支。这一步是HBM相对时域仿真的一个天然盲区,遇到疑似亚谐波时不要硬调容差,直接换基频重算。
6. 进阶验证:扫频响应与ode45时域对比的完整流程
6.1 用延续法扫出完整频响曲线
延续法不仅让初值更稳,还能揭示多值响应。扫频时从低频往高频走和从高频往低频走,在某些频段可能得到不同分支的解,这正是强非线性系统的跳跃现象。把两个方向的扫频结果叠在一张图上,能很直观地看到不稳定的中间分支在哪里。
% 反向扫频: 从高频到低频, 初值同样用前一点解 w_rev = fliplr(w_list); q_rev_prev = []; amp_rev = zeros(size(w_rev)); for i = 1:length(w_rev) omega = w_rev(i); if i == 1 den = (sys.k - sys.m*omega^2)^2 + (sys.c*omega)^2; q_rev_prev = zeros(2*N+1, 1); q_rev_prev(2) = sys.f * (sys.k - sys.m*omega^2) / den; q_rev_prev(3) = sys.f * sys.c * omega / den; end [q, ~, ef] = fsolve(@(q) hbm_residual(q, sys, N, omega), q_rev_prev, options); if ef > 0 q_rev_prev = q; amp_rev(i) = sqrt(q(2)^2 + q(3)^2); end end正反向扫频曲线出现分叉时,配合ode45验证哪个分支是实际可观测的。在这个阶段我通常不再调参数,而是把HBM当作一个快速筛子,先用它找到可疑频段,再用时域仿真精确定位。
6.2 与ode45瞬态解对比:确认HBM解的物理性
HBM给出的是数学上满足频域方程组的解,物理上是否可观测还得靠时域验证。对Duffing方程,做法是让ode45跑足够长时间,等瞬态衰减到可忽略,取最后一个周期的波形做FFT,对比基波和谐波幅值。
% 时域验证: 跑多个周期, 取最后一段作为稳态 T_ode = 200 * (2*pi/omega); [t_ode, y_ode] = ode45(@(t,y) [y(2); ... -(sys.c*y(2) + sys.k*y(1) + sys.alpha*y(1)^3 - sys.f*cos(omega*t))/sys.m], ... linspace(0, T_ode, 50000), [0.1; 0]); % 取最后一个周期的采样点做FFT n_last = find(t_ode > T_ode - 2*pi/omega); x_last = y_ode(n_last, 1); Y = fft(x_last) / length(x_last); amp_ode_1st = 2 * abs(Y(2)); % 基波幅值 dc_ode = abs(Y(1)); fprintf('HBM: x0=%.4e amp1=%.4e | ODE45: x0=%.4e amp1=%.4e\n', ... x0_hbm, amp_1st, dc_ode, amp_ode_1st);对比时如果误差在1%~2%以内,说明谐波截断和采样设置都合理。如果误差明显,先用第4章的办法检查N和M,再考虑亚谐波问题。我自己做这套流程时,最常翻车的反而是ode45的仿真时长不够:高Q系统瞬态衰减极慢,看似稳定的波形其实还叠着没散尽的自由振动,对比结果自然对不上。这种时候我会让ode45多跑几百个周期,或者直接加大阻尼项验证趋势。
HBM在MATLAB里的实现,跨过初值、混叠、截断这三个坎,剩下的就是工程细节。希望这套流程能帮你把非线性频响分析从玄学变成可复现的脚本,省下几个小时的在跑仿真等待时间,把精力放到真正需要判断的问题上。
本文还有配套的精品资源,点击获取