Skip to content

Commit 9ed33e4

Browse files
Fix teaching-content issues and UKF numerical stability
- t22: Lead compensator (s+0.5)/(s+5) no longer cancels plant pole at s=-2 - t23: rewrite as standard MRAC (same pole, different DC gain b_p); theta* = b_m/b_p = 2, parameter jump changes b_p so theta* -> 4 - t25: honest rename to force-balance modeling; drop bogus ver('simscape') check (physical ports can't be wired programmatically) - t26: DH table now the correct 6-joint PUMA560, matching the computation - t33/t34: UKF alpha 1e-3 -> 0.5, removes W0 ~ -1e6 weight degeneracy
1 parent 838a277 commit 9ed33e4

6 files changed

Lines changed: 74 additions & 68 deletions

File tree

‎t22_root_locus.m‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,18 @@
174174
title('目标:ζ>0.7, ωn>3 的区域');
175175
% 原始根轨迹偏右 → 需要 Lead 补偿器把轨迹往左拉
176176

177-
% 加一个零点 s=-2(把极点往左吸)
178-
C_lead = tf([1, 2], 1); % 零点在 -2
177+
% 加一个 Lead 补偿器 C(s)=(s+0.5)/(s+5):零点把轨迹往左吸,极点保证可实现
178+
C_lead = tf([1, 0.5], [1, 5]); % 零点 -0.5,极点 -5(proper,无零极点对消)
179179
sys_comp = C_lead * sys;
180180
subplot(1,2,2);
181181
rlocus(sys_comp); grid on;
182182
sgrid(0.7, 3);
183-
title('加零点 s=-2 后:根轨迹被"吸"向左');
183+
title('加 Lead 补偿器后:根轨迹被"吸"向左');
184184
hold off;
185185

186186
fprintf('【补偿器设计演示】\n');
187187
fprintf(' 原始系统: 根轨迹偏右 → 响应慢\n');
188-
fprintf(' 加零点 s=-2: 轨迹向左弯 → 更快更稳定\n');
188+
fprintf(' 加 Lead 补偿器 (s+0.5)/(s+5): 零点吸轨迹向左 → 更快更稳定\n');
189189
fprintf(' 这就是 Lead 补偿器的根轨迹解释!\n\n');
190190

191191
fprintf('========================================\n');

‎t23_mrac.m‎

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,18 @@
3232
%% ===== 第 1 步:参考模型与被控对象 =====
3333

3434
% 参考模型:我们希望系统表现成的样子
35-
tau_m = 0.5; % 参考时间常数(快!)
36-
sys_model = tf([1], [tau_m, 1]);
35+
tau = 0.5; % 公共时间常数(对象和模型相同,这是 MRAC 能完全匹配的前提)
36+
b_m = 1.0; % 参考模型直流增益
37+
sys_model = tf([b_m], [tau, 1]);
3738

38-
% 真实被控对象:参数会变化(我们假装不知道)
39-
tau_real = 2.0; % 真实时间常数(比参考模型慢很多)
40-
sys_real = tf([1], [tau_real, 1]);
39+
% 真实被控对象:增益未知(比模型小),时间常数与模型相同
40+
b_p = 0.5; % 真实对象直流增益(控制器不知道的真实值)
41+
sys_real = tf([b_p], [tau, 1]);
4142

4243
fprintf('【系统参数】\n');
43-
fprintf(' 参考模型: G_m(s) = 1/(%.1fs+1) — 理想行为(快)\n', tau_m);
44-
fprintf(' 真实系统: G_p(s) = 1/(%.1fs+1) — 实际行为(慢 4 倍)\n', tau_real);
45-
fprintf(' 目标: 通过自适应调整增益,让真实系统追平参考模型\n\n');
44+
fprintf(' 参考模型: G_m(s) = %.0f/(%.1fs+1) — 理想行为\n', b_m, tau);
45+
fprintf(' 真实系统: G_p(s) = %.1f/(%.1fs+1) — 增益未知(真实 %.1f)\n', b_p, tau, b_p);
46+
fprintf(' 目标: 自适应增益 θ 补偿增益差异,使 θ·b_p → b_m\n\n');
4647

4748
%% ===== 第 2 步:Simulink 模型搭建 =====
4849

@@ -59,7 +60,7 @@
5960
add_block('simulink/Continuous/Transfer Fcn', [mdl '/Ref Model'], ...
6061
'Position', [350, 50, 440, 90]);
6162
set_param([mdl '/Ref Model'], ...
62-
'Numerator', '[1]', 'Denominator', ['[' num2str(tau_m) ' 1]']);
63+
'Numerator', num2str(b_m), 'Denominator', ['[' num2str(tau) ' 1]']);
6364

6465
% --- 自适应控制器路径 ---
6566
% Apply Gain 模块:计算 θ(t) * r(t),直接驱动被控对象
@@ -70,7 +71,7 @@
7071
add_block('simulink/Continuous/Transfer Fcn', [mdl '/Real Plant'], ...
7172
'Position', [350, 160, 440, 200]);
7273
set_param([mdl '/Real Plant'], ...
73-
'Numerator', '[1]', 'Denominator', ['[' num2str(tau_real) ' 1]']);
74+
'Numerator', num2str(b_p), 'Denominator', ['[' num2str(tau) ' 1]']);
7475

7576
% --- 固定增益对比路径 ---
7677
add_block('simulink/Math Operations/Gain', [mdl '/Fixed Gain'], ...
@@ -80,7 +81,7 @@
8081
add_block('simulink/Continuous/Transfer Fcn', [mdl '/Real Plant Fixed'], ...
8182
'Position', [350, 310, 440, 350]);
8283
set_param([mdl '/Real Plant Fixed'], ...
83-
'Numerator', '[1]', 'Denominator', ['[' num2str(tau_real) ' 1]']);
84+
'Numerator', num2str(b_p), 'Denominator', ['[' num2str(tau) ' 1]']);
8485

8586
% --- MIT 自适应律:θ̇ = -γ · e · ym ---
8687
add_block('simulink/Math Operations/Add', [mdl '/Error Calc'], ...
@@ -167,16 +168,16 @@
167168

168169
% 欧拉仿真
169170
for k = 1:N-1
170-
% 参考模型: τm·ẏm + ym = r
171-
ym_dot = (r(k) - ym(k)) / tau_m;
171+
% 参考模型: τ·ẏm + ym = b_m·r
172+
ym_dot = (b_m * r(k) - ym(k)) / tau;
172173
ym(k+1) = ym(k) + ym_dot * dt;
173174

174-
% 自适应对象: τp·ẏp + yp = θ·r
175-
yp_dot = (theta(k) * r(k) - yp_adapt(k)) / tau_real;
175+
% 自适应对象: τ·ẏp + yp = θ·b_p·r
176+
yp_dot = (theta(k) * b_p * r(k) - yp_adapt(k)) / tau;
176177
yp_adapt(k+1) = yp_adapt(k) + yp_dot * dt;
177178

178179
% 固定增益对象
179-
yp_dot_f = (K_fixed * r(k) - yp_fixed(k)) / tau_real;
180+
yp_dot_f = (K_fixed * b_p * r(k) - yp_fixed(k)) / tau;
180181
yp_fixed(k+1) = yp_fixed(k) + yp_dot_f * dt;
181182

182183
% MIT 规则:dθ/dt = -γ · (yp - ym) · ym
@@ -187,30 +188,30 @@
187188

188189
%% ===== 第 4 步:系统参数突变 — 自适应 vs 固定 =====
189190

190-
% 5 秒时 τ_real 从 2.0 突变为 4.0
191-
tau_init = tau_real;
192-
tau_change_time = 5;
193-
tau_changed = 4.0;
191+
% 5 秒时对象增益 b_p 从 0.5 突变为 0.25
192+
b_p_init = b_p;
193+
b_change_time = 5;
194+
b_p_changed = 0.25;
194195

195196
ym2 = zeros(1, N); yp_a2 = zeros(1, N);
196197
yp_f2 = zeros(1, N); theta2 = zeros(1, N);
197198
theta2(1) = 1.0;
198199

199200
for k = 1:N-1
200-
% 当前真实时间常数
201-
if t(k) < tau_change_time
202-
tau_now = tau_init;
201+
% 当前真实对象增益
202+
if t(k) < b_change_time
203+
b_now = b_p_init;
203204
else
204-
tau_now = tau_changed;
205+
b_now = b_p_changed;
205206
end
206207

207-
ym_dot = (r(k) - ym2(k)) / tau_m;
208+
ym_dot = (b_m * r(k) - ym2(k)) / tau;
208209
ym2(k+1) = ym2(k) + ym_dot * dt;
209210

210-
yp_dot = (theta2(k) * r(k) - yp_a2(k)) / tau_now;
211+
yp_dot = (theta2(k) * b_now * r(k) - yp_a2(k)) / tau;
211212
yp_a2(k+1) = yp_a2(k) + yp_dot * dt;
212213

213-
yp_dot_f = (K_fixed * r(k) - yp_f2(k)) / tau_now;
214+
yp_dot_f = (K_fixed * b_now * r(k) - yp_f2(k)) / tau;
214215
yp_f2(k+1) = yp_f2(k) + yp_dot_f * dt;
215216

216217
e_adapt = yp_a2(k) - ym2(k);
@@ -236,11 +237,11 @@
236237
% 图2:自适应增益变化
237238
subplot(2,2,2); hold on;
238239
plot(t, theta, 'r', 'LineWidth', 1.5);
239-
yline(1, 'b--', 'LineWidth', 1); % 理论最优值 θ* = 1(直流增益匹配)
240-
text(t_end*0.6, 1*1.1, '理论最优 θ*=1(直流增益匹配)');
240+
yline(b_m/b_p, 'b--', 'LineWidth', 1); % 理论最优 θ* = b_m/b_p
241+
text(t_end*0.6, b_m/b_p*1.1, sprintf('理论最优 θ*=b_m/b_p=%.1f', b_m/b_p));
241242
title('自适应增益 θ(t)');
242243
xlabel('时间 (s)'); ylabel('增益'); grid on;
243-
fprintf(' 理论最优增益: θ* = 1 (直流增益匹配,G_p(0)=G_m(0)=1)\n');
244+
fprintf(' 理论最优增益: θ* = %.1f (b_m/b_p = %.1f/%.1f)\n', b_m/b_p, b_m, b_p);
244245
fprintf(' MIT 自适应最终收敛于 θ* 附近 ✓\n\n');
245246

246247
% 图3:参数突变工况
@@ -249,18 +250,19 @@
249250
plot(t, ym2, 'b', 'LineWidth', 1.5);
250251
plot(t, yp_a2, 'r', 'LineWidth', 1.5);
251252
plot(t, yp_f2, 'Color', [0 0.6 0], 'LineWidth', 1);
252-
xline(tau_change_time, 'k:', 'LineWidth', 1.5);
253-
text(tau_change_time+0.2, 2.5, '参数突变!');
253+
xline(b_change_time, 'k:', 'LineWidth', 1.5);
254+
text(b_change_time+0.2, 2.5, '参数突变!');
254255
legend('目标', '参考模型', '自适应', '固定增益', 'Location', 'southeast');
255-
title(sprintf('参数突变 (τ: %.1f→%.1f) 时对比', tau_init, tau_changed));
256+
title(sprintf('参数突变 (b_p: %.2f→%.2f) 时对比', b_p_init, b_p_changed));
256257
xlabel('时间 (s)'); ylabel('输出'); grid on;
257258

258259
% 图4:突变后增益重新调整
259260
subplot(2,2,4); hold on;
260261
plot(t, theta2, 'r', 'LineWidth', 1.5);
261-
yline(1, 'b--', 'LineWidth', 1);
262-
xline(tau_change_time, 'k:', 'LineWidth', 1.5);
263-
legend('θ(t)', 'θ*=1', 'Location', 'best');
262+
yline(b_m/b_p_init, 'b--', 'LineWidth', 1);
263+
yline(b_m/b_p_changed, 'g--', 'LineWidth', 1);
264+
xline(b_change_time, 'k:', 'LineWidth', 1.5);
265+
legend('θ(t)', '旧最优值', '新最优值', 'Location', 'best');
264266
title('参数突变后增益自适应重新收敛');
265267
xlabel('时间 (s)'); ylabel('增益'); grid on;
266268

‎t25_simscape.m‎

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
%% ============================================================
2-
% 教程 25:Simscape 物理建模 — 不用推公式的建模方法
2+
% 教程 25:物理建模 — 力平衡法 vs 传递函数
33
%
4-
% 【Simscape vs 传统 Simulink】
5-
% 传统 Simulink: 你先推出传递函数 G(s),再用 Transfer Fcn 模块
6-
% Simscape: 你直接放置物理元件(电阻、弹簧、质量块),
7-
% 连线代表物理连接(电流、力),系统自动推导方程!
4+
% 【物理建模 vs 数学建模】
5+
% 数学建模: 先推出传递函数 G(s),再用 Transfer Fcn 模块
6+
% 物理建模(力平衡法): 按 F=ma 直接搭"加速度→速度→位移"积分链,
7+
% 不写传递函数,思路更贴近物理直觉
88
%
9-
% Simulink 是"数学建模":G(s) = 1/(s²+2s+5)
10-
% Simscape 是"物理建模":放一块质量 + 一个弹簧 + 一个阻尼器
9+
% 传递函数是"数学建模":G(s) = 1/(s²+0.5s+4)
10+
% 力平衡法是"物理建模":按 F=ma 搭积分链(本课实际演示的方法)
11+
%
12+
% 注:MathWorks 的 Simscape 工具箱可用物理元件(Mass/Spring/Damper)
13+
% 做更彻底的物理建模,但本课用 Simulink 信号框图演示同一思路
1114
%
1215
% 【Simscape 的优势】
1316
% 1. 不需要推导数学方程 → 减少人为错误
@@ -25,18 +28,15 @@
2528
clear; close all;
2629

2730
fprintf('============================================\n');
28-
fprintf(' 教程 25:Simscape 物理建模\n');
31+
fprintf(' 教程 25:物理建模(力平衡法)\n');
2932
fprintf('============================================\n\n');
3033

31-
% 检查 Simscape 是否可用
32-
if isempty(ver('simscape'))
33-
error('需要 Simscape 工具箱。如果没有安装,请阅读本脚本了解概念。');
34-
end
34+
% 本课用 Simulink 信号框图演示力平衡法,无需 Simscape 工具箱
3535

36-
fprintf('【Simscape 核心理念】\n');
36+
fprintf('【力平衡法核心理念】\n');
3737
fprintf(' 传统方法: 物理定律 → 微分方程 → 传递函数 → Transfer Fcn 模块\n');
38-
fprintf(' Simscape: 物理元件 → 连线 → 自动生成方程 → 仿真\n');
39-
fprintf(' → 省掉了最容易出错的三步推导!\n\n');
38+
fprintf(' 力平衡法: F=ma → 加速度 → 积分 → 速度/位移(不写 G(s))\n');
39+
fprintf(' → 省掉了拉普拉斯变换这一步!\n\n');
4040

4141
%% ===== 第 1 步:物理建模 vs 传递函数对比模型 =====
4242

@@ -111,7 +111,7 @@
111111
add_line(mdl, 'Step Force/1', 'MSD Transfer Fcn/1');
112112
add_line(mdl, 'MSD Transfer Fcn/1', 'Scope/2');
113113

114-
fprintf('【Simscape 概念模型已创建】tutorial25_simscape.slx\n');
114+
fprintf('【物理模型已创建】tutorial25_simscape.slx\n');
115115
fprintf(' 左侧: MSD 物理模型 (力平衡 + 积分链)\n');
116116
fprintf(' 右侧: 传统传递函数 G(s)=1/(s²+0.5s+4)\n');
117117
fprintf(' 两条路径结果一致,但建模思路完全不同\n\n');
@@ -191,9 +191,9 @@
191191
fprintf(' 3. 拉普拉斯: G(s) = 1/(ms²+cs+k)\n');
192192
fprintf(' 4. 填入 Transfer Fcn 模块\n');
193193
fprintf(' → 三步推导,一步填错全错\n\n');
194-
fprintf(' Simscape 流程:\n');
195-
fprintf(' 1. 拖入 Mass, Spring, Damper 元件\n');
196-
fprintf(' 2. 连线(物理连接,不是信号流向)\n');
194+
fprintf(' 力平衡法流程:\n');
195+
fprintf(' 1. 画 F=ma 力平衡图(F_net = F - cv - kx)\n');
196+
fprintf(' 2. 用 Gain/Integrator 搭"加速度→速度→位移"链\n');
197197
fprintf(' 3. 填参数: m=1, k=4, c=0.5\n');
198198
fprintf(' 4. 运行!\n');
199199
fprintf(' → 零公式推导,直接来自物理直觉\n\n');
@@ -206,8 +206,8 @@
206206
fprintf(' ✗ 已有精确传递函数 → 没必要重搭物理模型\n\n');
207207

208208
fprintf('→ 打开 tutorial25_simscape.slx\n');
209-
fprintf('→ 双击 Mass 模块,把 mass 从 1 改成 5\n');
210-
fprintf('→ 重新运行 → 观察响应变慢\n');
209+
fprintf('→ 双击 c_damper 或 k_spring 模块改阻尼/刚度\n');
210+
fprintf('→ 重新运行 → 观察响应变化\n');
211211
fprintf('→ 这就是"物理直觉驱动建模"!\n');
212212

213213
save_system(mdl, fullfile(fileparts(mfilename('fullpath')), 'models', [mdl '.slx']));

‎t26_robot_kinematics.m‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,21 @@
4444
%% ---------- 第 2 步:读 PUMA560 的 DH 参数表 ----------
4545
% 每一行是 [a, α, d, θ],θ 是变量(关节角)
4646
dh = [
47-
0 0 0.672 0; % Joint 1 — 腰部旋转
48-
0.432 -pi/2 0.149 0; % Joint 2 — 大臂俯仰
49-
0.020 pi/2 0 0; % Joint 3 — 小臂俯仰
50-
0 0 0.433 0; % Joint 4 — 腕部旋转
47+
0 0 0.67183 0; % Joint 1 — 腰部旋转
48+
0.4318 0 0 0; % Joint 2 — 大臂俯仰
49+
0.0203 -pi/2 0.15005 0; % Joint 3 — 小臂俯仰
50+
0 pi/2 0.4318 0; % Joint 4 — 腕部旋转
51+
0 -pi/2 0 0; % Joint 5 — 腕部弯曲
52+
0 0 0.2 0; % Joint 6 — 末端旋转
5153
];
5254

5355
fprintf('\n【PUMA560 DH 参数】\n');
5456
fprintf(' J1(腰): a=%.3f α=%.0f° d=%.3f\n', dh(1,1), rad2deg(dh(1,2)), dh(1,3));
5557
fprintf(' J2(肩): a=%.3f α=%.0f° d=%.3f\n', dh(2,1), rad2deg(dh(2,2)), dh(2,3));
5658
fprintf(' J3(肘): a=%.3f α=%.0f° d=%.3f\n', dh(3,1), rad2deg(dh(3,2)), dh(3,3));
57-
fprintf(' J4(腕): a=%.3f α=%.0f° d=%.3f\n\n', dh(4,1), rad2deg(dh(4,2)), dh(4,3));
59+
fprintf(' J4(腕): a=%.3f α=%.0f° d=%.3f\n', dh(4,1), rad2deg(dh(4,2)), dh(4,3));
60+
fprintf(' J5(腕): a=%.3f α=%.0f° d=%.3f\n', dh(5,1), rad2deg(dh(5,2)), dh(5,3));
61+
fprintf(' J6(腕): a=%.3f α=%.0f° d=%.3f\n\n', dh(6,1), rad2deg(dh(6,2)), dh(6,3));
5862

5963
%% ---------- 第 3 步:Simulink 模型 — DH 正运动学 ----------
6064

‎t33_ukf_basics.m‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@
181181

182182
% UKF 参数
183183
n = 2; % 状态维度
184-
alpha = 1e-3; % Sigma 点散布参数 (小→靠近均值)
184+
alpha = 0.5; % Sigma 点散布参数(避免 α 过小导致权重退化)
185185
beta = 2; % 高斯分布最优值
186186
kappa = 0; % 辅助缩放参数
187187
lambda = alpha^2 * (n + kappa) - n;

‎t34_ekf_vs_ukf.m‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151

152152
% UKF 参数
153153
n_st = 2;
154-
alpha_ukf = 1e-3; beta_ukf = 2; kappa_ukf = 0;
154+
alpha_ukf = 0.5; beta_ukf = 2; kappa_ukf = 0;
155155
lambda_ukf = alpha_ukf^2 * (n_st + kappa_ukf) - n_st;
156156
Wm = zeros(2*n_st+1, 1); Wc = zeros(2*n_st+1, 1);
157157
Wm(1) = lambda_ukf/(n_st+lambda_ukf);

0 commit comments

Comments
 (0)