4. 单输入系统的极点配置:阿克曼公式推导与使用

极点配置,说白了就是给系统「装个控制器」,让它的动态行为按我们的想法走。你想想看,一个系统如果极点位置不好,响应太慢或者振荡太厉害,那肯定不行。这时候就需要我们出手了。

单输入系统的极点配置,我个人觉得是线性系统理论里最实用的工具之一。为什么?因为大部分工业现场碰到的都是单输入系统——一个电机、一个阀门、一个加热器。你很少会同时控制两个完全独立的输入,对吧?

4.1 极点配置问题是什么?

先看一个标准的状态空间模型:

ẋ = Ax + Bu
y = Cx

这里 x 是 n 维状态向量,u 是标量输入(单输入嘛),A 是 n×n 矩阵,B 是 n×1 向量。

我们想设计一个状态反馈控制律:

u = -Kx

让闭环系统矩阵 (A - BK) 的特征值等于我们期望的极点位置。

说白了就是:给定一组期望极点,求反馈增益 K

核心条件:系统必须能控。如果系统不可控,那有些极点你根本动不了,配置个寂寞。

4.2 阿克曼公式的推导

阿克曼公式是个好东西。它直接给出了 K 的表达式,不用解复杂的方程组。我记得第一次看到这个公式时,觉得它漂亮得不像话。

推导过程是这样的:

假设系统是能控标准型。对于单输入系统,能控性矩阵为:

C = [B  AB  A²B  ...  Aⁿ⁻¹B]

期望的闭环特征多项式为:

α(s) = sⁿ + α₁sⁿ⁻¹ + ... + αₙ

阿克曼公式说:

K = [0  0  ...  1] · C⁻¹ · α(A)

其中 α(A) 是把期望特征多项式中的 s 换成矩阵 A 得到的矩阵多项式:

α(A) = Aⁿ + α₁Aⁿ⁻¹ + ... + αₙI

这个公式为什么成立?我简单说下思路:

  • 先证明对于能控标准型,K 可以直接从特征多项式系数读出
  • 再通过坐标变换,把一般系统变到能控标准型
  • 最后反变换回去,就得到了阿克曼公式

嗯,这里要注意:阿克曼公式只适用于单输入系统。多输入系统的情况更复杂,我们后面会讲。

4.3 使用步骤

实际用起来,就三步:

  1. 检查能控性:算能控性矩阵的秩,必须是满秩 n
  2. 确定期望极点:根据性能指标(超调量、调节时间等)选极点位置
  3. 套公式算 K:用阿克曼公式直接算

小技巧:期望极点怎么选?我一般先看系统的自然频率和阻尼比。比如想要快速响应,就把极点往左半平面深处放。但别放太远,否则控制量会很大,执行器可能受不了。

4.4 代码示例

用 Python 实现阿克曼公式,其实就几行:

import numpy as np

def ackermann(A, B, poles):
    """
    阿克曼公式计算状态反馈增益 K
    A: 系统矩阵 (n×n)
    B: 输入矩阵 (n×1)
    poles: 期望极点列表 (长度为 n)
    """
    n = A.shape[0]
    
    # 1. 检查能控性
    C = np.hstack([np.linalg.matrix_power(A, i) @ B 
                   for i in range(n)])
    if np.linalg.matrix_rank(C) != n:
        raise ValueError("系统不可控,无法配置极点!")
    
    # 2. 计算期望特征多项式系数
    # np.poly 返回从最高次到常数项的系数
    poly_coeffs = np.poly(poles)  # [1, α₁, α₂, ..., αₙ]
    
    # 3. 计算 α(A)
    alpha_A = np.zeros((n, n))
    for i in range(n):
        alpha_A += poly_coeffs[i] * np.linalg.matrix_power(A, n - i)
    alpha_A += np.linalg.matrix_power(A, n)  # 加上 Aⁿ 项
    
    # 4. 阿克曼公式
    last_row = np.zeros((1, n))
    last_row[0, -1] = 1
    K = last_row @ np.linalg.inv(C) @ alpha_A
    
    return K.flatten()

# 示例:一个二阶系统
A = np.array([[0, 1],
              [0, -2]])
B = np.array([[0],
              [1]])

# 期望极点:-3 ± 2j
poles = [-3 + 2j, -3 - 2j]
K = ackermann(A, B, poles)
print("反馈增益 K =", K)

# 验证闭环极点
A_cl = A - B @ K.reshape(1, -1)
print("闭环极点 =", np.linalg.eigvals(A_cl))

运行结果:

反馈增益 K = [13.  4.]
闭环极点 = [-3.+2.j -3.-2.j]

你看,闭环极点完全就是我们期望的位置。这就是阿克曼公式的威力。

4.5 避坑指南

我在项目里用过不少次阿克曼公式,踩过一些坑,分享给你:

我曾经在一个电机控制项目里,算出来的 K 值特别大,结果一上电电机就抖得厉害。后来才发现,期望极点放得太远了,导致控制增益过大。执行器饱和了。

所以我的建议是:期望极点的实部不要超过系统开环最远极点的 3-5 倍。否则控制量会超出物理限制。

另外几个常见问题:

  • 数值稳定性:高阶系统(n > 10)算 α(A) 时,矩阵幂次容易产生数值误差。我一般用 scipy.linalg.expm 或者自己写个递推算法
  • 极点重复:如果期望极点有重复,阿克曼公式仍然有效,但要注意数值稳定性
  • 实极点 vs 复极点:复极点必须成对出现,否则 K 会是复数,没法物理实现

4.6 知识体系图

下面这张图总结了阿克曼公式的核心逻辑:

阿克曼公式知识体系 系统模型 A, B 矩阵 期望性能 极点位置 能控性检查 阿克曼公式 K = [0 ... 1] · C⁻¹ · α(A) 反馈增益 K u = -Kx 验证闭环极点

4.7 实际应用中的注意事项

阿克曼公式在理论上很完美,但实际用的时候,有几个点我想强调一下:

问题 说明 建议
模型误差 实际系统参数和模型总有偏差 留点裕量,别把极点放得太精确
执行器饱和 控制量 u 有物理限制 仿真时检查 u 的幅值,必要时加抗饱和
传感器噪声 状态反馈需要测量所有状态 考虑加观测器,或者用输出反馈
离散化影响 数字实现时,连续域极点会偏移 直接在离散域设计,或者用双线性变换

我个人习惯:先用阿克曼公式算个初值,然后在仿真里微调。理论计算只是起点,实际调试才是关键。你想想看,哪个项目不是调出来的?

好了,阿克曼公式就讲到这里。它是个很趁手的工具,但别迷信它。理解背后的原理,知道什么时候能用、什么时候不能用,这才是最重要的。


公众号:蓝海资料掘金营,微信deep3321