找回密码
 立即注册
首页 业界区 业界 串联控制环路仿真解析

串联控制环路仿真解析

乐敬 11 小时前
在环路设计时,我们经常使用串联控制环路,如下图所示:
1.png

需要调试内环、外环,甚至2个以上的环路。机器在工作过程中同时采集和控制多个不同的物理量,比如电压、电流、温度、频率等等,而这些物理量又常常相互影响和制约。而串联控制环路是一种简单有效的处理方法,它的工作原理是将外环的输出作为内环的输入,内环的输出作为整个系统的输出,从而使所有被控物理量工作在稳定状态。
内外环路的相互影响

一个双环的控制系统,等效于将内环的闭环传递函数串联在外环的开环传递函数上,那么必然在增益和相位上对外环造成影响。假设内环和外环的开环传递函数为:

\[\begin{align}    C_{inner}(s) &= \frac{s+100}{s} \\    C_{outer}(s) &= \frac{s+500}{s}\end{align}\]
在 Python 中,我们对这两个环路以及总的传递函数进行 Bode 图绘制:
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy import signal
  4. def bode_plot(tf_list, f_min, f_max, ax1=None, ax2=None, colors=None, linestyles=None, labels=None, title=""):
  5.     """
  6.     生成伯德图 - 支持多个传递函数
  7.    
  8.     参数:
  9.     tf_list: 传输函数列表或单个传输函数
  10.     f_min: 最小频率 (Hz)
  11.     f_max: 最大频率 (Hz)
  12.     ax1: 幅度图轴对象 (可选)
  13.     ax2: 相位图轴对象 (可选)
  14.     colors: 颜色列表 (可选)
  15.     linestyles: 线条样式列表 (可选)
  16.     labels: 标签列表 (可选)
  17.     title: 图形标题 (可选)
  18.     """
  19.    
  20.     # 如果输入是单个传递函数,转换为列表
  21.     if not isinstance(tf_list, (list, tuple)):
  22.         tf_list = [tf_list]
  23.    
  24.     # 设置默认值
  25.     if colors is None:
  26.         colors = plt.cm.tab10(np.linspace(0, 1, len(tf_list)))
  27.     if linestyles is None:
  28.         linestyles = ['-'] * len(tf_list)
  29.     if labels is None:
  30.         labels = [f'TF {i+1}' for i in range(len(tf_list))]
  31.    
  32.     # 确保列表长度一致
  33.     if len(colors) < len(tf_list):
  34.         colors = colors * (len(tf_list) // len(colors) + 1)
  35.     if len(linestyles) < len(tf_list):
  36.         linestyles = linestyles * (len(tf_list) // len(linestyles) + 1)
  37.     if len(labels) < len(tf_list):
  38.         labels = labels + [f'TF {i+1}' for i in range(len(labels), len(tf_list))]
  39.    
  40.     # 如果没有提供轴对象,则创建新的图形
  41.     if ax1 is None or ax2 is None:
  42.         fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
  43.         show_plot = True
  44.     else:
  45.         show_plot = False
  46.     # 为每个传递函数生成Bode图
  47.     for i, tf in enumerate(tf_list):
  48.         frequencies = np.logspace(np.log10(f_min), np.log10(f_max), 1000)
  49.         omega = 2 * np.pi * frequencies
  50.         w, mag, phase = signal.bode(tf, omega)
  51.         # 绘图参数
  52.         color = colors[i] if i < len(colors) else None
  53.         linestyle = linestyles[i] if i < len(linestyles) else '-'
  54.         label = labels[i] if i < len(labels) else f'TF {i+1}'
  55.         # 幅度图
  56.         ax1.semilogx(w, mag, color=color, linestyle=linestyle, label=label)
  57.         ax1.set_ylabel('幅度 (dB)')
  58.         ax1.grid(True, which="both", ls="-", alpha=0.3)
  59.         # 相位图
  60.         ax2.semilogx(w, phase, color=color, linestyle=linestyle, label=label)
  61.         ax2.set_xlabel('角频率 (Rad/s)')
  62.         ax2.set_ylabel('相位 (度)')
  63.         ax2.grid(True, which="both", ls="-", alpha=0.3)
  64.     # 设置标题和图例
  65.     if show_plot:
  66.         if title:
  67.             plt.suptitle(title)
  68.         ax1.legend()
  69.         ax2.legend()
  70.         plt.tight_layout()
  71.         plt.show()
  72.    
  73.     return ax1, ax2
  74. def series_transfer_functions(tf1, tf2):
  75.     """
  76.     将两个传递函数串联
  77.    
  78.     参数:
  79.     tf1: 第一个传递函数 (TransferFunction对象)
  80.     tf2: 第二个传递函数 (TransferFunction对象)
  81.    
  82.     返回:
  83.     串联后的传递函数
  84.     """
  85.     # 串联传递函数:H(s) = H1(s) * H2(s)
  86.     # 分子:num = conv(num1, num2)
  87.     # 分母:den = conv(den1, den2)
  88.    
  89.     num_result = np.convolve(tf1.num, tf2.num)
  90.     den_result = np.convolve(tf1.den, tf2.den)
  91.    
  92.     return signal.TransferFunction(num_result, den_result)
  93. def safe_vector_add_right(vec1, vec2):
  94.     """Right-aligned vector addition using basic loops"""
  95.     len1 = len(vec1)
  96.     len2 = len(vec2)
  97.    
  98.     if len1 >= len2:
  99.         # vec1 is longer
  100.         result = []
  101.         # Copy the beginning of vec1 that doesn't overlap
  102.         for i in range(len1 - len2):
  103.             result.append(vec1[i])
  104.         # Add the overlapping parts
  105.         for i in range(len2):
  106.             result.append(vec1[len1 - len2 + i] + vec2[i])
  107.     else:
  108.         # vec2 is longer
  109.         result = []
  110.         # Copy the beginning of vec2 that doesn't overlap
  111.         for i in range(len2 - len1):
  112.             result.append(vec2[i])
  113.         # Add the overlapping parts
  114.         for i in range(len1):
  115.             result.append(vec1[i] + vec2[len2 - len1 + i])
  116.    
  117.     return result
  118. def close_loop_transfer_function(tf):
  119.     """
  120.     计算闭环传输函数
  121.    
  122.     参数:
  123.     tf: 传递函数 (TransferFunction对象)
  124.    
  125.     返回:
  126.     闭环传输函数
  127.     """
  128.     return signal.TransferFunction(tf.num, safe_vector_add_right(tf.den, tf.num))
  129. if __name__ == "__main__":
  130.     # 输入参数
  131.     f_min = 1
  132.     f_max = 1e6
  133.    
  134.     # 外环传递函数
  135.     gain = 1
  136.     zeros = [-100]
  137.     poles = [0]
  138.     num1 = np.array(np.poly(zeros))*gain
  139.     den1 = np.poly(poles)
  140.     tf_outside = signal.TransferFunction(num1, den1)
  141.     # 内环传递函数
  142.     gain = 1
  143.     zeros = [-500]
  144.     poles = [0]
  145.     num2 = np.array(np.poly(zeros))*gain
  146.     den2 = np.poly(poles)
  147.     tf_inner = signal.TransferFunction(num2, den2)
  148.     # 内环闭环传输函数
  149.     close_tf = close_loop_transfer_function(tf_inner)
  150.     # 串联后的开环传递函数
  151.     series_tf = series_transfer_functions(tf_outside, close_tf)
  152.     # 传递函数列表
  153.     tf_list = [tf_outside, tf_inner, series_tf, close_tf]
  154.     labels = ['C_outside', 'C_inner', 'C_series_open', 'C_inner_close']
  155.     colors = ['blue', 'red', 'green', 'black']
  156.     linestyles = ['-', '--', '-.', ':']
  157.     # 绘制所有传递函数
  158.     bode_plot(tf_list, f_min, f_max, colors=colors, linestyles=linestyles,
  159.              labels=labels, title="串联控制环路仿真")
复制代码
运行 Python,输出以下结果:
2.png

从结果中可以看出,内环控制器的闭环传递函数在一些频率段,增益低于 0dB,相位产生延迟。在与外环控制器串联后,这些负增益、相位延迟叠加到了串联后的开环传递函数上。
原因在于,假如将内环传递函数写为

\[\begin{align}    & C_{inner}(s) = \frac{X(s)}{Y(s)} \\    & C_{close}(s) = \frac{C_{inner}(s)}{1+C_{inner}(s)} = \frac{X(s)}{X(s)+Y(s)} \\    & \Rightarrow |C_{close}(s)| = |X(s)| - |X(s)+Y(s)|  \\    & \Rightarrow \begin{cases}        |C_{close}(s)| \geq |X(s)| - |X(s)| - |Y(s)| = -|Y(s)|  \\         |C_{close}(s)| \leq |X(s) - X(s) - Y(s)| = |Y(s)|     \end{cases} \\    & \angle C_{close}(s) = \tan^{-1}\left(\frac{\mathrm{Im}(X(s))}{\mathrm{Re}(X(s))}\right)-\tan^{-1}\left(\frac{\mathrm{Im}(X(s)+Y(s))}{\mathrm{Re}(X(s)+Y(s))}\right)\end{align}\]
从上面的推导可以看出,在一个传递函数闭环后,增益在\([-|Y(s)|,|Y(s)|]\)范围内产生增强或者衰减,相位会产生超前或者滞后,外环控制器与这些偏移叠加后可能存在不确定、不可控的情况。
解决方法也很简单,在内环传递函数上再乘以一个较大的增益,使它的模远大于1,以上问题在很大程度上得到改善:

\[\begin{align}    & C_{inner}(s) = \frac{AX(s)}{Y(s)} , |AX(s)| \gg |Y(s)| \\    & C_{close}(s) = \frac{AX(s)}{AX(s)+Y(s)} \\    & \Rightarrow |C_{close}(s)| = |AX(s)| - |AX(s)+Y(s)| \approx |AX(s)| - |AX(s)| = 0 \\    & \Rightarrow \angle C_{close}(s) = \tan^{-1}\left(\frac{\mathrm{Im}(AX(s))}{\mathrm{Re}(AX(s))}\right)-\tan^{-1}\left(\frac{\mathrm{Im}(AX(s)+Y(s))}{\mathrm{Re}(AX(s)+Y(s))}\right) \\    & \approx \tan^{-1}\left(\frac{\mathrm{Im}(AX(s))}{\mathrm{Re}(AX(s))}\right)-\tan^{-1}\left(\frac{\mathrm{Im}(AX(s))}{\mathrm{Re}(AX(s))}\right) = 0\end{align}\]
修改代码重新测试:
  1.     # 内环传递函数
  2.     gain = 100
  3.     zeros = [-500]
  4.     poles = [0]
  5.     num2 = np.array(np.poly(zeros))*gain
  6.     den2 = np.poly(poles)
  7.     tf_inner = signal.TransferFunction(num2, den2)
复制代码
运行结果如下:
3.png

结果显示内环闭环传递函数幅度值为 0dB,相位值为 \(0^\circ\),开环传递函数在串联前后波形几乎重合了。
变增益系统的分析

考虑一种情况,假设内环是一个 Buck 电路的输出电压控制环路,其中包含 Buck 的传递函数:

\[\begin{align}G_{vd}(s) = \frac{\hat{v}_o}{\hat{d}} = \frac{RV_o}{LRCs^2+Ls+R}\end{align}\]
和一个 PI 控制器:

\[\begin{align}G_c(s) = \frac{s+a}{s}\end{align}\]
其中,\(R=10\Omega,C=300\mu F,L=1mH, a=900\) 。
外环可能输出变化的内环设定值,即变化的输出电压设定值。这意味着在稳态下,内环传递函数将工作在动态的增益 \(V_o\) 下。这时,我们可以将这个 \(V_o\) 分离出来,用一个变量 \(K\) 来表示,这个环路就成为一个变增益系统:
4.png

它的特征方程为:

\[\begin{align}1+KG'_{vd}(s)G_c(s) = 0 \end{align}\]
其中,\(G'_{vd}\) 是 \(G_{vd}\) 分离 \(V_o\) 后的传递函数:

\[\begin{align}G'_{vd}(s) = \frac{R}{LRCs^2+Ls+R}\end{align}\]
将特征方程变形为:

\[\begin{align}G'_{vd}(s)G_c(s) = -\frac{1}{K}\end{align}\]
在 Python 中分别画出 \(G'_{vd}(s)G_c(s)\) 和 \(-\frac{1}{K}\) 在复平面的轨迹,根据 Nyquist 判据,如果 \(G'_{vd}(s)G_c(s)\) 的轨迹没有包围 \(-\frac{1}{K}\) 的轨迹,那么系统是稳定的。
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from control import TransferFunction, nyquist_plot, series
  4. from scipy import signal
  5. def variable_gain_function(ax, A_range=None):
  6.     """
  7.     绘制非线性环节的 -1/N(A) 轨迹
  8.    
  9.     参数:
  10.     ax : Matplotlib坐标轴对象
  11.     A_range : tuple, optional
  12.         幅值范围,格式为 (min_A, max_A)。如果为None,则自动缩放
  13.     """
  14.    
  15.     # 如果没有指定A_range,则设置默认范围
  16.     if A_range is None:
  17.         A_range = (0.1, 10)
  18.    
  19.     # 生成增益K的范围(这里用A表示增益)
  20.     K_values = np.linspace(A_range[0], A_range[1], 100)
  21.    
  22.     # 计算-1/K的实部和虚部
  23.     neg_inv_K_real = [-1/K for K in K_values]
  24.     neg_inv_K_imag = [0 for _ in K_values]  # 对于线性增益,虚部为0
  25.    
  26.     # 在给定的坐标轴上绘制-1/K轨迹
  27.     ax.plot(neg_inv_K_real, neg_inv_K_imag, 'r-', linewidth=2, label='-1/K 轨迹')
  28.    
  29.     # 标记一些特殊的K值点
  30.     K_special = np.linspace(A_range[0], A_range[1], 5)
  31.     for K in K_special:
  32.         ax.plot(-1/K, 0, 'ro')  # 用红点标记特殊点
  33.         ax.text(-1/K, 0.1, f'K={K:.1f}', ha='center', va='bottom')
  34.    
  35.     # 添加标题和标签
  36.     ax.set_xlabel('实轴')
  37.     ax.set_ylabel('虚轴')
  38.     ax.grid(True, which='both', linestyle=':', alpha=0.6)
  39.     ax.legend()
  40.    
  41.     # 设置坐标轴比例一致
  42.     ax.axis('equal')
  43.    
  44.     return ax
  45. def generate_nyquist_plot(tf, omega_range=None, plot_title="Nyquist Plot",
  46.                          variable_gain=False, A_range=None):
  47.     """
  48.     基于给定的传递函数生成奈奎斯特图
  49.    
  50.     参数:
  51.     tf : TransferFunction
  52.         控制系统传递函数
  53.     omega_range : tuple, optional
  54.         频率范围,格式为 (min_freq, max_freq)。如果为None,则自动缩放
  55.     plot_title : str
  56.         图形标题
  57.     variable_gain : bool
  58.         是否包含变增益 -1/K 的轨迹
  59.    
  60.     返回:
  61.     fig: Matplotlib图形对象
  62.     ax: 坐标轴对象
  63.     """
  64.    
  65.     # 创建图形
  66.     fig, ax = plt.subplots(figsize=(10, 8))
  67.    
  68.     # 生成奈奎斯特图
  69.     if omega_range:
  70.         omega = np.logspace(np.log10(omega_range[0]), np.log10(omega_range[1]), 1000)
  71.         nyquist_plot(tf, omega=omega, ax=ax, label="传递函数的 Nyquist 图")
  72.     else:
  73.         nyquist_plot(tf, ax=ax, label="传递函数的 Nyquist 图")
  74.    
  75.     # 添加标题和网格
  76.     ax.set_title(plot_title)
  77.     ax.grid(True)
  78.    
  79.     # 如果需要,添加非线性环节的 -1/K 轨迹
  80.     if variable_gain:
  81.         variable_gain_function(ax, A_range)
  82.         ax.legend()
  83.    
  84.     return fig, ax
  85. # 示例用法:
  86. if __name__ == "__main__":
  87.     # 传递函数: G(s) = 10/(3e-6*s^2 + 1e-3*s + 10)
  88.     num1 = [10]
  89.     den1 = [3e-6, 1e-3, 10]
  90.     tf1 = TransferFunction(num1, den1)
  91.     # 传递函数: H(s) = (s+900)/s
  92.     num2 = [1, 900]
  93.     den2 = [1, 0]
  94.     tf2 = TransferFunction(num2, den2)
  95.     tf = series(tf1, tf2)
  96.     A_range = (0.1, 1)
  97.    
  98.     # 绘制包含非线性环节的奈奎斯特图
  99.     fig2, ax2 = generate_nyquist_plot(tf,
  100.                                      plot_title="变增益系统 Nyquist 图",
  101.                                      variable_gain=True, A_range=A_range)
  102.     plt.show()
复制代码
5.png

从输出结果可以看出,要想使这个系统稳定,\(K\) 值必须小于 0.6,才能保证 \(-\frac{1}{K}\) 的轨迹处于 \(G'_{vd}(s)G_c(s)\) 的外侧。
总结

本文通过仿真实例深入分析了串联控制环路的设计原理和关键问题。主要内容包括:

  • 内外环路相互影响机制:内环的闭环特性会直接影响外环的开环传递函数,表现为增益衰减和相位滞后,这对整个系统的稳定性构成挑战。
  • 解决方案验证:通过提高内环增益的方法,有效解决了内外环相互干扰的问题,使得内环闭环传递函数近似为单位增益系统,从而减少了对其他环路的影响。
  • 变增益系统分析方法:针对实际应用中常见的变增益系统(如Buck电路输出电压控制),提出了基于Nyquist图的稳定性分析方法,通过绘制\(G'_{vd}(s)G_c(s)\)轨迹和\(-1/K\)轨迹,直观判断系统在不同增益条件下的稳定性边界。
这些分析方法和解决方案不仅适用于文中提到的具体案例,也为其他复杂的多环控制系统设计提供了重要的理论指导和实践参考。在实际工程应用中,合理设计各环路的增益分配和频率特性,是确保整个控制系统稳定性和性能的关键。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册