实验 09 · 切换系统
切换与脉冲系统仿真
实现模式切换和脉冲映射,固定事件顺序,并用事件表核对关键时刻。
完整讲义
Lab 09:切换与脉冲系统仿真
Lab 8 只有一个系统矩阵。本实验加入随时间变化的模式 \(\sigma(k)\),并允许切换时刻发生瞬时状态映射。重点不是堆叠条件判断,而是把事件顺序写成清晰、可核对的模型。
开始前复用 Lab 4 的 Git 检查点流程:用 git status 确认工作区干净;事件表、退化测试和 首个脉冲手算都通过后,按相同方法提交 Lab 9 检查点。
学习目标
- 用模式序列表示切换信号。
- 区分模式切换和脉冲映射。
- 把仿真循环封装为可复用函数。
- 用事件表验证切换前后状态和操作顺序。
目录结构
labs/lab09/
switched_impulsive_demo.m
simulateDeltaSwitched.m
results/
event-table.csv
figures/
switched-state.png
notes.md
小知识:切换与脉冲是两种不同事件
切换改变系统当前采用的动力学矩阵;脉冲则让状态在某个时刻发生瞬时映射。它们可以同时出现,也可以分别出现。程序若只用一个布尔变量混合两种含义,后续理论符号和实验记录都会变得含糊。
理论问题
考虑两个模式的 Delta 算子系统。为避免把瞬时跳变和一步状态更新混在一起,先区分同一 采样时刻的左右状态:
\[ \boldsymbol{x}(k^-)=\text{脉冲前状态},\qquad \boldsymbol{x}(k^+)=\text{脉冲后状态}. \]
讲义中的数学更新时刻从 \(k=0\) 开始,MATLAB 循环索引则从 k=1 开始。若记数学更新时刻 为 \(n=k-1\),那么 MATLAB 数组 x(:,k) 保存 \(\boldsymbol{x}(n^-)\), xTilde(:,k) 保存 \(\boldsymbol{x}(n^+)\),modeSequence(k) 保存 \(\sigma(n)\)。后面的理论公式继续使用数学索引 \(k\),代码说明则明确保留这一个单位的偏移。
同一数学时刻的左右状态关系定义为:
\[ \widetilde{\boldsymbol{x}}(k)=\boldsymbol{x}(k^+)= \begin{cases} M_{\sigma(k)}\boldsymbol{x}(k^-), & \text{脉冲时刻},\\ \boldsymbol{x}(k^-), & \text{普通时刻}. \end{cases} \]
瞬时映射结束后,定义这一采样步的流动起点 \(\boldsymbol{z}(k):=\boldsymbol{x}(k^+)\)。从右状态到下一时刻脉冲前状态的 后跳变流动差商为:
\[ \frac{\boldsymbol{x}((k+1)^-)-\boldsymbol{z}(k)}{T} =A_{\sigma(k)}\boldsymbol{z}(k), \]
也就是
\[ \boldsymbol{x}((k+1)^-) =(I+T A_{\sigma(k)})\boldsymbol{z}(k). \]
这个差商描述跳变后的局部流动,不是对 Lab 8 中 \(\delta\boldsymbol{x}(k)=[\boldsymbol{x}(k+1)-\boldsymbol{x}(k)]/T\) 的重新定义。 普通时刻没有跳变,\(\boldsymbol{z}(k)=\boldsymbol{x}(k^-)=\boldsymbol{x}(k)\),因此立即 恢复 Lab 8 的 \(\boldsymbol{x}(k+1)=(I+T A_{\sigma(k)})\boldsymbol{x}(k)\)。
因此本实验采用固定操作顺序:
读取当前模式 σ(k)
↓
读取脉冲前状态 x(k−)
↓
若有脉冲,计算 x(k+)=Mσx(k−);否则 x(k+)=x(k−)
↓
从 z(k)=x(k+) 计算后跳变流动并更新 x((k+1)−)
↓
记录模式、事件和状态
顺序是模型定义的一部分。把脉冲放到动力学更新之后,会得到另一个系统。
从脚本到函数
Lab 7 和 Lab 8 的计算都写在单个脚本中。本实验首次把重复仿真循环放进函数,因此需要认识 MATLAB 的函数、cell 数组与 struct:
function result = simulateDeltaSwitched(A, M, modeSequence, impulseMask, T, x0)
括号中的变量是函数输入,result 是输出。A 和 M 是 cell 数组,使用 A{mode} 取得 当前模式的矩阵;花括号表示取出 cell 中保存的矩阵,而不是保留一个 cell 容器。
result = struct("x", x, "modeSequence", modeSequence);
struct 把相关结果放在带名字的字段中,之后可通过 result.x 和 result.modeSequence 访问。 Starter 会完整保留无脉冲状态更新,只把脉冲机制留作新增部分。这样可以先确认熟悉的 Delta 递推仍然正确,再检查新事件是否改变了指定时刻。
公式到代码
把每个模式矩阵放在 cell 数组中:
A = {
[0.2, 1.0; -1.0, -0.4],
[-0.1, 0.6; -0.8, 0.1]
};
M = {1.10 * eye(2), 1.05 * eye(2)};
由于 MATLAB 索引从 1 开始,modeSequence(k) 保存的是 \(\sigma(k-1)\),impulseMask(k) 记录数学时刻 \(k-1\) 是否发生脉冲。函数接口使用结构体返回状态、脉冲后状态和事件信息:
result = simulateDeltaSwitched(A, M, modeSequence, impulseMask, T, x0);
不要在函数内部隐藏模式序列的生成规则。主脚本负责定义实验,仿真函数只执行给定模型。
MATLAB 实验
设计三组逐步增加复杂度的工况:
| 工况 | 模式 | 脉冲 | 目的 |
|---|---|---|---|
| A | 始终为模式 1 | 无 | 回归到 Lab 8 的单模式更新 |
| B | 每 10 步切换 | 无 | 隔离模式切换的影响 |
| C | 每 10 步切换 | 切换时应用 M |
观察脉冲与切换共同作用 |
- 先只运行工况 A,核对前两步。
- 再运行工况 B,检查第一个切换点的矩阵选择。
- 最后运行工况 C,保存每次事件的
k、切换前模式、当前模式、脉冲前状态和脉冲后状态。 - 图上方绘制阶梯状切换信号,下方绘制两个状态分量。
- 将工况 C 的事件记录写入
event-table.csv。
数学验证
对第一个脉冲切换时刻,手算并逐项比较:
\[ \boldsymbol{x}(k^+)=M_2\boldsymbol{x}(k^-),\qquad \boldsymbol{x}((k+1)^-)=(I+T A_2)\boldsymbol{x}(k^+). \]
程序还应检查以下不变量:
modeSequence的长度等于仿真步数。- 模式编号只能是
1或2。 impulseMask与模式序列长度相同。- 工况 A 与直接使用固定
A1的结果一致。 - 当所有
M都改成单位矩阵时,工况 C 退化为工况 B。
最后一项是很有价值的退化测试:关闭新增机制后,代码应回到已经理解的简单情形。
结果与记录
事件表应允许读者重建“何时切换、何时施加脉冲”。图注不能只写“系统响应”,而应说明模式规则、采样周期和是否包含脉冲。
notes.md 用一小段伪代码记录事件顺序,再回答:当前图像展示了哪些有限轨迹?哪些长期稳定性问题仍需理论分析?
OpenCode 审计
复用 Lab 4 的工作流。输入 /models,确认当前模型是 DeepSeek V4 Pro,再按 Tab 进入 Plan 模式并输入:
请检查 labs/lab09 的切换脉冲系统仿真。
模型约定:MATLAB 循环索引 k 对应数学时刻 n=k-1;x(:,k) 是 x(n-),
xTilde(:,k) 是 z(n)=x(n+)。先确定当前模式;
若 impulseMask(k) 为真,做 xTilde=M{mode}*x,否则 xTilde=x;然后从 xTilde
计算 deltaX=A{mode}*xTilde,最后更新下一时刻的脉冲前状态
xNext=xTilde+T*deltaX。不要把这个后跳变流动差商重新定义成 Lab 8 的 delta*x。
请:
1. 逐行核对 simulateDeltaSwitched.m 是否遵守这个顺序;
2. 检查无切换、仅切换、切换加脉冲三个工况能否隔离不同机制;
3. 检查事件表是否足以人工复算第一个脉冲时刻;
4. 增加必要的尺寸断言,但不要引入额外工具箱;
5. 指出图像不能回答的稳定性问题。
如果 OpenCode 把脉冲改到更新之后,即使代码更短也应拒绝,因为这改变了数学模型。接受计划后 按 Tab 进入 Build 模式。修改完成后,在第二个 PowerShell 终端运行 git status 和 git diff -- labs\lab09,再按事件顺序重新验证。
完成标准
- 三个工况可以从同一仿真函数生成。
- 第一个切换脉冲时刻能够手算复核。
- 事件表、图像和文字采用同一套事件顺序。