学习数值分析与电路分析后的练手程序,较粗糙。 没有 加入更多电器元件的数学模型,如二极管、三极管、放大器、数字器件等等。
m脚本在:gittee仓库;
编程实现电路仿真,使用Matlab 。
背景:
电网络分析课程中,学习了电路的矩阵形式,和数值求解方法;数值分析课程了解了数值计算的概念和一些数值算法思想。于是我想,如何编程实现电路的仿真呢?
总是理不清思路:编程计算电路?理论分析电路?怎么转换呢?有什么不一样吗?得到各参数变量与待求量之间的关系?得到不同电路参数下的解???
所以首先要明确模糊的概念,以便理清思路。
利用计算机求解数学问题,称为数值计算。计算机可以大量、快速的运算,但是它是基于硬件结构的机械式计算,不同于人的计算(更高级、更灵活、更包容、更连续)。
需要考虑到计算机计算与编程的特点,设计恰当的数学模型、合适的数值计算方法和形式。
求解即已知题目求解答案。有得到精确解析解的数值计算方法,也有迭代近似解的数值方法。计算只是个过程,重要的是,题目是什么,想要得到什么样的结果?
需要确定什么是已知的,什么是未知的。再理清求解思路:先分析,列出已知条件,然后计算出结果,再进一步得到更多结果,这才是完整的逻辑。这个逻辑是理论(课程)中学到的,这说明理论与求解是相辅相成的,理论上的分析虽然不需要数值计算,但没有算例验证理论,可信度也不高;同时,算例数据可以直观的反映出问题的特点,给分析带来参考,有助于得出结论。
计算机仿真:一般以时间为变量,将仿真过程分成极短的片段,求解出每一时刻的状态近似作为对应片段内的状态。从而近似得到整个过程中任意时刻的状态。
而求解得到的是一个问题的答案(一个电路状态信息),而仿真需要每个时间片段的答案(每个时刻电路的状态信息),这意味着“连续求解”一个“连续的题目”,需要将前一个问题的答案作为已知去求解当前问题。
一次仿真,得到的往往是大量的求解,仿真的结果应当是连续的(连续的时间序列)、大量的解的集合(按照时间序列依次求解的解序列)。
清楚上面的概念后,我理清了思路:
这是最难的一部分:概念上,混淆了求解与仿真;思想上,纠结于解析法,而忽略数值方法;实现上,多方面知识结合。
利用电网络理论的知识,可得到电路的关联矩阵 A \boldsymbol A A,用于描述电路的拓扑结构。
若有N+1个节点,M条支路,a个电导(电阻),b个电压源(包括电容),c个电流源(包括电感),
并考虑到极短时间内各参数不变,有以下矩阵:
将电容器和电感器分别看作电压源和电流源,“瞬间”(极短的时间,不是时刻)电路近似为一个只有电源和电阻组成的直流电路。
我只想到了网孔电流法和节点电压法,相比之下,节点电压法不需要“确定网孔”环节,故使用节点电压法。
由电路的 KCL 可知:
A I = 0 (1) \boldsymbol {AI}=\bf 0 \tag 1 AI=0(1)
支路电压可用节点电压表示:
U = A T U n (2) {\boldsymbol U} = {\boldsymbol A}^{\rm T} {\boldsymbol U}_{\rm n} \tag 2 U=ATUn(2)
支路电流可以分为电导支路电流、电压源支路电流、电流源支路电流三个部分:
I = Y U + I s + I u (3) {\boldsymbol I} = \boldsymbol {YU} + {\boldsymbol I}_{\rm s} + \boldsymbol {I}_{\rm u} \tag 3 I=YU+Is+Iu(3)
其中 I u \boldsymbol {I}_{\rm u} Iu是除电压源支路以外的支路电流均为0的支路电流向量。
联立(1)(2)(3),消去的矩阵 U {\boldsymbol U} U和 I {\boldsymbol I} I,得
A Y A T U n + A I u + A I s = 0 (4) \boldsymbol {AYA}^{\rm T} \boldsymbol{U}_{\rm n} + \boldsymbol {AI}_{\rm u} + \boldsymbol {AI}_{\rm s} = 0 \tag 4 AYATUn+AIu+AIs=0(4)
在(4)表示的方程组中, U n \boldsymbol{U}_{\rm n} Un与 I u \boldsymbol{I}_{\rm u} Iu是待求量,有 N + b 个;独立方程数是 N 个( A \boldsymbol A A行数)。
显然, U s \boldsymbol U_{\rm s} Us这个已知量还没有用到。
支路电压也可分为电导支路电压、电压源支路电压、电流源安支路电压三个部分:
U = U y + U s + U i (5) \boldsymbol U = \boldsymbol {U}_{\rm y} + \boldsymbol {U}_{\rm s} + \boldsymbol {U}_{\rm i} \tag 5 U=Uy+Us+Ui(5)
其中 U y \boldsymbol U_{\rm y} Uy和 U i \boldsymbol U_{\rm i} Ui分别是除电导支路以外的支路电压均为0的支路电压向量,和除电流源支路以外的支路电压均为0的之路电压向量。
由(2)和(5)可得,
A T U n = U y + U s + U i \boldsymbol A^{\rm T} \boldsymbol U_{\rm n} = \boldsymbol {U}_{\rm y} + \boldsymbol {U}_{\rm s} + \boldsymbol {U}_{\rm i} ATUn=Uy+Us+Ui
取出该 方程组 中,等号右侧是已知的 u s u_{\rm s} us的方程,表示为:
B U n = U s ′ (6) \boldsymbol B \boldsymbol U_{\rm n} = \boldsymbol U'_{\rm s} \tag 6 BUn=Us′(6)
其中, B \boldsymbol B B和 U s ′ \boldsymbol U'_{\rm s} Us′分别是 A T \boldsymbol A^{\rm T} AT和 U s \boldsymbol U_{\rm s} Us删除 U s \boldsymbol U_{\rm s} Us中0所在行得到的。
(6)所表示的方程组中,未知量是 U n \boldsymbol U_{\rm n} Un,独立方程数为 b ,即电压源支路的个数。
将(4)和(6)组成方程组,
{ A Y A T U n + A I u = − A I s B U n = U s ′ \left \{ \begin{matrix} \boldsymbol {AYA}^{\rm T} \boldsymbol{U}_{\rm n} + \boldsymbol {AI}_{\rm u} = - \boldsymbol {AI}_{\rm s} \\ \boldsymbol B \boldsymbol U_{\rm n} = \boldsymbol U'_{\rm s} \end{matrix} \right. {AYATUn+AIu=−AIsBUn=Us′
其矩阵形式为:
[ A Y A T A B 0 ] [ U n I u ] = [ − A I s U s ′ ] (7) \begin{bmatrix} \boldsymbol {AYA}^{\rm T} & \boldsymbol A \\ \boldsymbol B & 0 \end{bmatrix} \begin{bmatrix} \boldsymbol U_{\rm n} \\ \boldsymbol I_{\rm u} \end{bmatrix} = \begin{bmatrix} \boldsymbol -{AI}_{\rm s} \\ \boldsymbol U'_{\rm s} \end{bmatrix} \tag 7 [AYATBA0][UnIu]=[−AIsUs′](7)
当当当! 该方程组有 N + b 个未知量,N + b 个独立方程,可解出 U n \boldsymbol U_{\rm n} Un和 I u \boldsymbol I_{\rm u} Iu。
还可以进一步精简 A I u \boldsymbol {AI}_{\rm u} AIu部分的形式:
根据 I u \boldsymbol I_{\rm u} Iu中0行的位置,删去 A I u \boldsymbol {AI}_{\rm u} AIu部分中系数矩阵 A \boldsymbol A A无用的列,和 I u \boldsymbol I_{\rm u} Iu中无用的行,
表示为 D I u ′ \boldsymbol {DI}'_{\rm u} DIu′,则(7)可写为
[ A Y A T D B 0 ] [ U n I u ′ ] = [ − A I s U s ′ ] (8) \begin{bmatrix} \boldsymbol {AYA}^{\rm T} & \boldsymbol D \\ \boldsymbol B & 0 \end{bmatrix} \begin{bmatrix} \boldsymbol U_{\rm n} \\ \boldsymbol I'_{\rm u} \end{bmatrix} = \begin{bmatrix} \boldsymbol -{AI}_{\rm s} \\ \boldsymbol U'_{\rm s} \end{bmatrix} \tag 8 [AYATBD0][UnIu′]=[−AIsUs′](8)
将它们分别回代到(2)和(3)则得到电路的此时的所有信息。
对于含动态元件(一般为电容、电感)的电路,每个瞬间里动态元件的电压和电流都在改变。
所以在每个瞬间电路求解完后,要利用求得的解对动态元件的初始值更新,作为下一瞬间动态元件的初值状态。
易知电容和电感的微分方程可写为:
d u C = 1 C i C d t d i L = 1 L u L d t {\rm d}u_{\rm C} = \frac 1C i_{\rm C} {\rm d}t \\ {\rm d}i_{\rm L} = \frac 1L u_{\rm L} {\rm d}t duC=C1iCdtdiL=L1uLdt
其中 d t {\rm d}t dt可近似看作是一个“瞬间”的时间,在计算中称为步长,设为 Δ t \Delta t Δt,即 d t = Δ t {\rm d}t = \Delta t dt=Δt。
则得到电容电压变化量 Δ u C \Delta u_{\rm C} ΔuC和电感电流变化量 Δ i L \Delta i_{\rm L} ΔiL的表达式:
Δ u C = 1 C i C Δ t (8) \Delta u_{\rm C} = \frac 1C i_{\rm C} \Delta t \tag 8 ΔuC=C1iCΔt(8)
Δ i L = 1 L u L Δ t (9) \Delta i_{\rm L} = \frac 1L u_{\rm L} \Delta t \tag 9 ΔiL=L1uLΔt(9)
其中 i C i_{\rm C} iC和 u L u_{\rm L} uL分别是电容支路的电流和电感支路的电压,可从 I \boldsymbol I I与 U \boldsymbol U U中得到。
每次求解后,对每个电容的初始电压根据(8)式更新,每个电感的初始电流根据(9)式更新:
u C = u C + Δ u C (10) u_{\rm C} = u_{\rm C} + \Delta u_{\rm C} \tag {10} uC=uC+ΔuC(10)
i L = i L + Δ i L (11) i_{\rm L} = i_{\rm L} + \Delta i_{\rm L} \tag {11} iL=iL+ΔiL(11)
使用 matlab 实现。
仿真电路需要输入:电路结构模型、信号源(电源)信号、仿真时间、动态元件初始值;
输出:电路的节点电压、支路电压、支路电流矩阵,包含历史记录。
变量 | 规模 | 说明 | 意义 |
---|---|---|---|
A \boldsymbol A A | n × b n \times b n×b | 节点支路的关联情况 行数 = 节点数,列数 = 支路数 |
电路线图的关联矩阵 |
Y \boldsymbol Y Y | b b b | 支路电导分布在对角线上 不可为空向量 |
支路电导向量 |
C \boldsymbol C C | b b b | 支路上的电容值,单位 F 非电容支路值为 0 |
支路电容向量 |
L \boldsymbol L L | b b b | 支路上的电感值,单位 H 非电感支路值为 0 |
支路电感向量 |
变量 | 规模 | 说明 | 意义 |
---|---|---|---|
U s s \boldsymbol U_{\rm ss} Uss | b b b | 支路恒压源电压值,单位 V 非恒压源支路值为 0 不可为空向量 |
支路恒压源电压向量 |
I s s \boldsymbol I_{\rm ss} Iss | b b b | 支路恒流源电流值,单位 V 非恒流源支路值为 0 不可为空向量 |
支路恒流源电压向量 |
U d s \boldsymbol U_{\rm ds} Uds | u d s × ( 1 + T ) uds\times (1+T) uds×(1+T) | 第一列是索引 其余列是电压源随时间的输入数据 单位 V u d s uds uds:变化的电压源数 T T T:时间步数 |
指定变化电压源的 位置和时变信号 |
I d s \boldsymbol I_{\rm ds} Ids | i d s × ( 1 + T ) ids \times (1+T) ids×(1+T) | 第一列是索引 其余列是电流源随时间的输入数据 单位 A u d s uds uds:变化的电流源数 T T T:时间步数 |
指定变化电流源的 位置和时变信号 |
变量 | 规模 | 说明 | 意义 |
---|---|---|---|
T s e q \boldsymbol T_{\rm seq} Tseq | T T T | 时刻组成的序列,单位 s | 仿真的时间序列 |
变量 | 规模 | 说明 | 意义 |
---|---|---|---|
U c \boldsymbol U_c Uc | c c c | 仿真开始前,各电容的初始值 | 电容初始值的向量 |
I l \boldsymbol I_l Il | l l l | 仿真开始前,各电感的初始值 | 电感初始值的向量 |
变量 | 规模 | 说明 | 意义 |
---|---|---|---|
U n \boldsymbol U_{\rm n} Un | n × T n \times T n×T | 每一时刻的节点电压向量组成的矩阵 | 节点电压矩阵 |
U \boldsymbol U U | b × T b \times T b×T | 每一时刻的支路电压向量组成的矩阵 | 支路电压矩阵 |
I \boldsymbol I I | b × T b \times T b×T | 每一时刻的支路电流向量组成的矩阵 | 支路电流矩阵 |
处理输入信息,生成必要中间变量,构建求解矩阵。
U_ds = []
U_ss = U_ss(:)
等Y = diag(Y)
U = repmat(U_ss, 1, T)
U_ss_idx = find(U_ss)
U_ds_idx = U_ds(:, 1)
U(U_ds_idx,:) = U_ds(:, 2:end)
C_idx = find(C)
U(C_idx, 1) = U_c
I = repmat(I_ss, 1, T)
I_ss_idx = find(I_ss)
I_ds_idx = I_ds(:, 1)
I(I_ds_idx,:) = I_ds(:, 2:end)
L_idx = find(L)
I(L_idx, 1) = I_l
U_sc_idx = [U_ss_idx; C_idx; U_ds_idx]
I_sl_idx = [I_ss_idx; L_idx; I_ds_idx]
系数矩阵固定不变,方程组(8)的系数矩阵为:
[ A Y A T D B 0 ] \begin{bmatrix} \boldsymbol {AYA}^{\rm T} & \boldsymbol D \\ \boldsymbol B & 0 \end{bmatrix} [AYATBD0]
实现方式为:
AT = A'; % A 的转置矩阵
Y = diag(Y); % 电导向量转化为对角矩阵
B = AT(U_sc_idx,:); % 已知的节点电压关系矩阵
D = A(:,U_sc_idx); % KCL 中电压源和电容支路电流的系数矩阵
R = [A*Y*AT D; B zeros(length(B(:,1)),length(D(1,:)))]; % 构建求解方程组的矩阵
使用for
循环遍历时间序列。每一时刻(每一步)都进行一次求解,将结果填入 U n U I \boldsymbol U_{\rm n} ~ \boldsymbol U ~\boldsymbol I Un U I矩阵的相应列。
方程组的常数项向量为:
[ − A I s U s ′ ] \begin{bmatrix} \boldsymbol -{AI}_{\rm s} \\ \boldsymbol U'_{\rm s} \end{bmatrix} [−AIsUs′]
常数项向量中的变化源、电容电压、电感电流是随时间变化的,
每次求解前都需要读取当前时刻(t时刻)的值:
I_sl = zeros(b, 1); % I_sl 电流源和电感支路的电流向量
I_sl(I_sl_idx) = I(I_sl_idx,t); % 更新常数项
U_sc = U(U_sc_idx,t); %
I_sl
即是 I s \boldsymbol I_{\rm s} Is,长度为 b b b,除电流源和电感支路的电流以外,其他支路均为0。
U_sc
即是 U s ′ \boldsymbol U'_{\rm s} Us′,长度为电压源和电容个数之和,可以为空;电压源和电容的电压向量。
求解方程组得到的向量是:
[ U n I u ′ ] \begin{bmatrix} \boldsymbol U_{\rm n} \\ \boldsymbol I'_{\rm u} \end{bmatrix} [UnIu′]
U n \boldsymbol U_{\rm n} Un是此刻的节点电压向量, I u ′ \boldsymbol I'_{\rm u} Iu′是此刻电压源和电容的电流;
根据方程组(2)得到此刻的 U \boldsymbol U U,再根据(3)得到此刻的 I \boldsymbol I I;
最后通过公式(10)、(11)分别更新电容的电压值、电感的电流值,作为下一时刻的初始值。
实现:
temp = R\[-A*I_sl; U_sc]; % 求解方程
U_n(:,t) = temp(1:sizeA(1)); % 得到 t 时刻的 U_n
U(:,t) = AT*U_n(:,t); % 得到 t 时刻的 U
temp(1:sizeA(1)) = []; % 删除结果中 U_n 的部分
I_sc = temp; % 得到 I_sc
I(U_sc_idx,t) = I_sc; % 将 t 时刻的 I_sc 添加到 I
I(:,t) = I(:,t) + Y*U(:,t); % 得到 t 时刻的 I
if t < length(T_seq) % 最后一次不需要计算电容或电感的值
a = T_seq(t+1) - T_seq(t); % 计算步长
U_c = U_c + a.*I(C_idx,t)./C(C_idx); % 更新 U_c I(C_idx,t)即是电容的电流向量
I_l = I_l + a.*U(L_idx,t)./L(L_idx); % 更新 I_l U(L_idx,t)即是电感的电压向量
U(C_idx, t+1) = U_c;
I(L_idx, t+1) = I_l;
end
其中,I_sc
即是 I u ′ \boldsymbol I'_{\rm u} Iu′,电压源和电容的电流向量;
完成最后一次求解后,不再需要计算下一时刻电容或电感的值,使用if
实现。