【逐行注释】MATLAB下的EKF和UKF例程详解|无需下载,复制到MATLAB窗口即可运行

在这里插入图片描述

例程简述

这是一个状态量为三维的MATLAB下的滤波程序,分成EKF(扩展卡尔曼滤波)和UKF(无迹卡尔曼滤波)两种,分别滤波后,显示滤波值的曲线、滤波误差的对比曲线、滤波误差的最大值、平均值、标准差的输出。
模型是非线性的(状态方程和观测方程都是非线性的),我将模型设计得尽可能复杂一些,拿到手以后可以从难往简单的方向改,更容易上手。

代码运行情况

运行后,得到的绘图和数据输出如下:
第一幅图是三个维度的状态真实值、EKF滤波值、UKF滤波值对比:
在这里插入图片描述
第二幅图是两种滤波方法的误差曲线,对比:
在这里插入图片描述

第三幅图比较难理解,是误差大小的CDF图像:在这里插入图片描述
这个图像反映了误差的总体趋势,CDF相关的知识可见本人的另一篇文章,有详细叙述:https://blog.youkuaiyun.com/cal

clear; clc; close all; %% ========= 目标/工况 ========= Vin = 100; % 100Vdc Vdc_ref = 400; % 400Vdc P_rated = 700; % 700W f_grid = 50; % 50Hz w_grid = 2*pi*f_grid; % 三相线电压 220Vrms Vll_rms = 220; Vph_rms = Vll_rms/sqrt(3); Vph_pk = sqrt(2)*Vph_rms; %% ========= 电路参数(平均模型) ========= plant = struct(); % LCL(每相) plant.L1 = 1.5e-3; plant.R1 = 0.10; % inverter-side plant.L2 = 0.8e-3; plant.R2 = 0.10; % grid-side plant.Cf = 4.7e-6; plant.Rd = 1.0; % series Rd + Cf branch (阻尼) % Boost + DC link plant.Lb = 1.0e-3; plant.RLb = 0.05; plant.Cdc = 1000e-6; plant.Rbleed = 5000; %% ========= 仿真设置 ========= dt = 5e-6; % 电路积分步长 Ts_ctrl = 50e-6; % 20kHz 控制更新 Nctrl = max(1, round(Ts_ctrl/dt)); t_end = 0.40; N = floor(t_end/dt) + 1; t = (0:N-1)'*dt; x = zeros(11,1); x(1) = 0.0; % IL x(2) = 360.0; % Vdc 初始 %% ========= 日志 ========= log.Vdc = zeros(N,1); log.IL = zeros(N,1); log.mabc = zeros(N,3); log.dboost= zeros(N,1); log.vgabc = zeros(N,3); log.igabc = zeros(N,3); log.theta = zeros(N,1); log.omega = zeros(N,1); log.idref = zeros(N,1); log.iqref = zeros(N,1); log.vd = zeros(N,1); log.vq = zeros(N,1); %% ========= 控制量保持 ========= u = struct(); u.d = 0.0; u.m = [0;0;0]; theta = 0; omega = w_grid; id_ref = 0; iq_ref = 0; vd = 0; vq = 0; %% ========= 主循环 ========= for k = 1:N tk = t(k); % 三相电网相电压(相对中性点) va = Vph_pk*sin(w_grid*tk); vb = Vph_pk*sin(w_grid*tk - 2*pi/3); vc = Vph_pk*sin(w_grid*tk + 2*pi/3); vg = [va; vb; vc]; % 并网电流(取 L2 后的 i2,方向=逆变器->电网) ia = x(9); ib = x(10); ic = x(11); % 功率给定:0.05s 后上电到 700W if tk < 0.05 Pcmd = 0; else Pcmd = P_rated; end % 每 Ts_ctrl 更新一次"单片机算法" if mod(k-1, Nctrl) == 0 [d_boost, m_a, m_b, m_c, theta, omega, id_ref, iq_ref, vd, vq] = ... mcu_control_3ph_step( ... x(2), x(1), Vin, va, vb, vc, ia, ib, ic, Pcmd); m0 = (m_a + m_b + m_c)/3; m_a = m_a - m0; m_b = m_b - m0; m_c = m_c - m0; u.d = d_boost; u.m = [m_a; m_b; m_c]; end % 记录 log.Vdc(k) = x(2); log.IL(k) = x(1); log.mabc(k,:) = u.m.'; log.dboost(k) = u.d; log.vgabc(k,:) = vg.'; log.igabc(k,:) = [ia ib ic]; log.theta(k) = theta; log.omega(k) = omega; log.idref(k) = id_ref; log.iqref(k) = iq_ref; log.vd(k) = vd; log.vq(k) = vq; if k == N, break; end % RK4 积分 x = rk4_step(@(xx) plant_f(xx, u, vg, Vin, plant), x, dt); end %% ========= 计算 THD & PF(末尾10个基波周期) ========= res = calc_thd_pf(t, log.vgabc, log.igabc, f_grid); fprintf('\n===== 结果(取末尾10个基波周期)=====\n'); fprintf('PF_total = %.4f\n', res.pf_total); fprintf('THD_A = %.2f %%\n', res.thd_a); fprintf('THD_B = %.2f %%\n', res.thd_b); fprintf('THD_C = %.2f %%\n', res.thd_c); fprintf('P_total = %.1f W\n', res.P_total); fprintf('Q_total = %.1f var\n', res.Q_total); fprintf('S_total = %.1f VA\n', res.S_total); %% ========= 画图 ========= figure; plot(t, log.Vdc, 'LineWidth', 1.2); grid on; xlabel('t (s)'); ylabel('Vdc (V)'); title('DC Link Voltage (should ~400V)'); figure; plot(t, log.dboost, 'LineWidth', 1.2); grid on; xlabel('t (s)'); ylabel('d_{boost}'); title('Boost Duty'); figure; plot(t, log.igabc(:,1), 'LineWidth', 1.0); hold on; plot(t, log.igabc(:,2), 'LineWidth', 1.0); plot(t, log.igabc(:,3), 'LineWidth', 1.0); grid on; xlabel('t (s)'); ylabel('i_{grid} (A)'); title('Grid Currents i_a,i_b,i_c'); legend('ia','ib','ic'); figure; plot(t, log.vgabc(:,1), 'LineWidth', 1.0); hold on; plot(t, log.vgabc(:,2), 'LineWidth', 1.0); plot(t, log.vgabc(:,3), 'LineWidth', 1.0); grid on; xlabel('t (s)'); ylabel('v_{grid} (V)'); title('Grid Voltages v_a,v_b,v_c'); legend('va','vb','vc'); figure; plot(t, log.vq, 'LineWidth', 1.2); grid on; xlabel('t (s)'); ylabel('v_q (V)'); title('PLL alignment indicator (vq -> 0)'); %% ===================== 子函数区 ===================== function xnext = rk4_step(f, x, h) k1 = f(x); k2 = f(x + 0.5*h*k1); k3 = f(x + 0.5*h*k2); k4 = f(x + h*k3); xnext = x + (h/6)*(k1 + 2*k2 + 2*k3 + k4); end function dx = plant_f(x, u, vg, Vin, p) % 平均电路模型:Boost + DC Link + 三相两电平逆变 + LCL(等效Rd串Cf) + 电网源 IL = x(1); Vdc = max(x(2), 10); i1 = x(3:5); % inverter-side inductor currents vC = x(6:8); % capacitor voltage i2 = x(9:11); % grid-side inductor currents (= grid current, 方向=逆变器->电网) d = min(max(u.d, 0.0), 0.95); m = u.m; % 逆变器相电压(平均模型):v_inv = 0.5*m*Vdc v_inv = 0.5 .* m .* Vdc; % LCL 结点:i_f = i1 - i2 i_f = i1 - i2; % 结点电压 v_node = vC + Rd*i_f v_node = vC + p.Rd .* i_f; % L1: L1 di1/dt = v_inv - v_node - R1*i1 di1 = (v_inv - v_node - p.R1.*i1) ./ p.L1; % L2: L2 di2/dt = v_node - v_grid - R2*i2 di2 = (v_node - vg - p.R2.*i2) ./ p.L2; % Cf: dvC/dt = i_f / C dvC = i_f ./ p.Cf; % Boost: Lb dIL/dt = Vin - (1-d)*Vdc - RLb*IL dIL = (Vin - (1-d)*Vdc - p.RLb*IL) / p.Lb; % DC link: Cdc dVdc/dt = i_boost_to_dc - i_dc_inv - Vdc/Rbleed i_boost_to_dc = (1-d)*IL; % 逆变器从直流侧取电流:i_dc = p_ac / Vdc(p_ac = sum(v_inv * i1)) p_inv = sum(v_inv .* i1); % 逆变器AC侧瞬时功率(>0 表示逆变器向AC输出) i_dc_inv = p_inv / Vdc; dVdc = (i_boost_to_dc - i_dc_inv - Vdc/p.Rbleed) / p.Cdc; dx = [dIL; dVdc; di1(:); dvC(:); di2(:)]; end %% ======== "单片机逻辑"控制======== function [d_boost, m_a, m_b, m_c, theta, omega, id_ref, iq_ref, vd, vq] = ... mcu_control_3ph_step(Vdc, IL, Vin, va, vb, vc, ia, ib, ic, P_cmd) Ts_ctrl = 50e-6; % 20kHz Ts_outer = 500e-6; % 2kHz persistent p st if isempty(p) p = default_params(); st = default_state(); end % 1) Boost 双环(Vdc->ILref->duty) [st, d_boost] = boost_control_step(st, p, Vdc, IL, Vin, Ts_ctrl, Ts_outer); % 2) SRF-PLL(对三相电压) [st, theta, omega, vd, vq] = srf_pll_step(st, p, va, vb, vc, Ts_ctrl); % 3) 功率->电流给定(电压对齐:P=(3/2)*vd*id,iq=0) vd_eff = max(abs(vd), p.vd_min); id_ref = -(2.0 * P_cmd) / (3.0 * vd_eff); iq_ref = 0.0; id_ref = sat(id_ref, -p.i_grid_max, p.i_grid_max); % 4) 电流采样->dq [i_alpha, i_beta] = clarke(ia, ib, ic); [id_meas, iq_meas] = park(i_alpha, i_beta, theta); % 5) dq PI + 解耦 + 电压前馈 -> v_alpha_beta_ref [st, v_alpha_ref, v_beta_ref] = current_dq_control_step( ... st, p, id_ref, iq_ref, id_meas, iq_meas, vd, vq, omega, Ts_ctrl); % 6) SVPWM -> duty -> modulation(-1..1) [da, db, dc] = svpwm_duty(v_alpha_ref, v_beta_ref, Vdc); m_a = 2.0 * da - 1.0; m_b = 2.0 * db - 1.0; m_c = 2.0 * dc - 1.0; end function p = default_params() p = struct(); p.Vdc_ref = 400.0; p.w_nom = 2*pi*50; p.vd_min = 20.0; p.i_grid_max = 8.0; % 控制器用等效电感/电阻(按 L1+L2 量级) p.Leq = 2.5e-3; p.R_eq = 0.2; % PLL p.pll_kp = 60.0; p.pll_ki = 4000.0; p.pll_omega_min = 2*pi*45; p.pll_omega_max = 2*pi*55; % 电流环(带宽 ~300Hz 起步) wc_i = 2*pi*300; p.i_kp = p.Leq * wc_i; p.i_ki = p.R_eq * wc_i; p.vref_max_scale = 0.95; % Boost 外环(Vdc->ILref) p.boost_Imax = 20.0; p.boost_Imin = 0.0; p.boost_outer_kp = 0.08; p.boost_outer_ki = 40.0; % Boost 内环(IL->duty) p.boost_inner_kp = 0.02; p.boost_inner_ki = 200.0; p.boost_d_max = 0.95; end function st = default_state() st = struct(); st.vdc_int = 0.0; st.il_int = 0.0; st.il_ref = 0.0; st.outer_div = uint32(0); st.pll_int = 0.0; st.theta = 0.0; st.omega = 2*pi*50; st.id_int = 0.0; st.iq_int = 0.0; end function [st, d_boost] = boost_control_step(st, p, Vdc, IL, Vin, Ts_ctrl, Ts_outer) % 外环调度(慢环) if st.outer_div == 0 ev = p.Vdc_ref - Vdc; st.vdc_int = sat(st.vdc_int + ev * Ts_outer, -50.0, 50.0); il_ref = p.boost_outer_kp * ev + p.boost_outer_ki * st.vdc_int; st.il_ref = sat(il_ref, p.boost_Imin, p.boost_Imax); end N = uint32(max(1, round(Ts_outer / Ts_ctrl))); st.outer_div = st.outer_div + 1; if st.outer_div >= N st.outer_div = uint32(0); end % 内环(快环) ei = st.il_ref - IL; st.il_int = sat(st.il_int + ei * Ts_ctrl, -2.0, 2.0); % 前馈必须用"当前Vdc",否则母线会越抬越高 d_ff = 1.0 - Vin / max(Vdc, 1.0); d_pi = p.boost_inner_kp * ei + p.boost_inner_ki * st.il_int; d_boost = d_ff + d_pi; % 当母线高于参考且IL_ref已到0,允许彻底停止升压 if (st.il_ref <= (p.boost_Imin + 1e-6)) && (Vdc > p.Vdc_ref + 2.0) d_boost = 0.0; end d_boost = sat(d_boost, 0.0, p.boost_d_max); end function [st, theta, omega, vd, vq] = srf_pll_step(st, p, va, vb, vc, Ts) [v_alpha, v_beta] = clarke(va, vb, vc); [vd, vq] = park(v_alpha, v_beta, st.theta); e = -vq; % 目标 vq -> 0 st.pll_int = sat(st.pll_int + e*Ts, -200.0, 200.0); omega = p.w_nom + p.pll_kp*e + p.pll_ki*st.pll_int; omega = sat(omega, p.pll_omega_min, p.pll_omega_max); theta = wrap_0_2pi(st.theta + omega*Ts); st.theta = theta; st.omega = omega; end function [st, v_alpha_ref, v_beta_ref] = current_dq_control_step( ... st, p, id_ref, iq_ref, id_meas, iq_meas, vd, vq, omega, Ts) ed = id_ref - id_meas; eq = iq_ref - iq_meas; st.id_int = sat(st.id_int + ed*Ts, -200.0, 200.0); st.iq_int = sat(st.iq_int + eq*Ts, -200.0, 200.0); ud = p.i_kp*ed + p.i_ki*st.id_int; uq = p.i_kp*eq + p.i_ki*st.iq_int; % 解耦 + 前馈(Leq 模型) v_d_ref = vd + ud + p.R_eq*id_meas - omega*p.Leq*iq_meas; v_q_ref = vq + uq + p.R_eq*iq_meas + omega*p.Leq*id_meas; [v_alpha_ref, v_beta_ref] = inv_park(v_d_ref, v_q_ref, st.theta); end function [da, db, dc] = svpwm_duty(v_alpha, v_beta, Vdc) Vdc = max(Vdc, 10.0); Vref = hypot(v_alpha, v_beta); Vref_max = 0.95 * Vdc / sqrt(3); if Vref > Vref_max s = Vref_max / max(Vref, 1e-9); v_alpha = v_alpha*s; v_beta = v_beta*s; Vref = Vref_max; end ang = atan2(v_beta, v_alpha); if ang < 0, ang = ang + 2*pi; end sector = floor(ang/(pi/3)) + 1; sector = min(max(sector,1),6); ang_s = ang - (sector-1)*(pi/3); k = sqrt(3)/Vdc; T1 = k * Vref * sin(pi/3 - ang_s); T2 = k * Vref * sin(ang_s); T0 = 1 - T1 - T2; if T0 < 0, T0 = 0; end T0_2 = 0.5*T0; switch sector case 1 da = T1 + T2 + T0_2; db = T2 + T0_2; dc = T0_2; case 2 da = T1 + T0_2; db = T1 + T2 + T0_2; dc = T0_2; case 3 da = T0_2; db = T1 + T2 + T0_2; dc = T2 + T0_2; case 4 da = T0_2; db = T1 + T0_2; dc = T1 + T2 + T0_2; case 5 da = T2 + T0_2; db = T0_2; dc = T1 + T2 + T0_2; otherwise da = T1 + T2 + T0_2; db = T0_2; dc = T1 + T0_2; end da = sat(da,0,1); db = sat(db,0,1); dc = sat(dc,0,1); end function [alpha, beta] = clarke(a,b,c) alpha = (2/3)*(a - 0.5*b - 0.5*c); beta = (2/3)*((sqrt(3)/2)*b - (sqrt(3)/2)*c); end function [d,q] = park(alpha,beta,theta) ct = cos(theta); st = sin(theta); d = alpha*ct + beta*st; q = -alpha*st + beta*ct; end function [alpha,beta] = inv_park(d,q,theta) ct = cos(theta); st = sin(theta); alpha = d*ct - q*st; beta = d*st + q*ct; end function y = sat(x,lo,hi) y = min(max(x,lo),hi); end function th = wrap_0_2pi(th) twopi = 2*pi; th = th - floor(th/twopi)*twopi; end %% ======== THD / PF 计算 ======== function results = calc_thd_pf(t, vabc, iabc, f0) if nargin < 4, f0 = 50; end T0 = 1/f0; t_end = t(end); t_start = max(t(1), t_end - 10*T0); idx = (t >= t_start); tt = t(idx); vv = vabc(idx,:); ii = iabc(idx,:); vv = vv - mean(vv,1); ii = ii - mean(ii,1); fs = 1/mean(diff(tt)); N = length(tt); V = fft(vv).*2/N; I = fft(ii).*2/N; f = (0:N-1)'*fs/N; [~, k1] = min(abs(f - f0)); max_h = 40; bins = k1*(2:max_h); bins = bins(bins < N/2); thd = zeros(1,3); for ph = 1:3 I1 = abs(I(k1,ph)); Ih = abs(I(bins,ph)); thd(ph) = 100 * sqrt(sum(Ih.^2)) / max(I1,1e-9); end Vrms = rms(vv,1); Irms = rms(ii,1); p_inst = sum(vv.*ii, 2); P_total = mean(p_inst); S_total = sum(Vrms .* Irms); pf_total = P_total / max(S_total, 1e-9); % 粗略无功:用基波相量夹角估 V1 = squeeze(V(k1,:)); I1 = squeeze(I(k1,:)); phi = angle(V1) - angle(I1); Q_total = sum( (Vrms .* Irms) .* sin(phi) ); results = struct(); results.thd_a = thd(1); results.thd_b = thd(2); results.thd_c = thd(3); results.P_total = P_total; results.Q_total = Q_total; results.S_total = S_total; results.pf_total = pf_total; end 逐行注释这段代码,并且说明运行结果中的5副图片各代表着什么
12-20
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MATLAB卡尔曼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值