一、理解离散时间序列
在正式开始Z变换文章开始的时候我们要理解什么叫理解离散时间序列?
1. 什么是 x[n]x[n]x[n] 离散时间序列?
(1) 从连续信号到离散序列
a. 三种连续信号的数学表达式
i. 气温变化
典型的日气温变化可用正弦函数近似描述:
T(t)=Tavg+Asin(2π24t+φ) T(t) = T_{\text{avg}} + A \sin\left(\frac{2\pi}{24}t + \varphi\right) T(t)=Tavg+Asin(242πt+φ)
其中:
- TavgT_{\text{avg}}Tavg:日平均气温(℃)
- AAA:气温变化幅度(℃)
- ttt:时间(小时,t=0t = 0t=0 对应凌晨某时刻)
- φ\varphiφ:初始相位
您给出的简化形式(省略相位,设平均值为 202020,振幅为 555):
T(t)=20+5sin(2πt) T(t) = 20 + 5\sin(2\pi t) T(t)=20+5sin(2πt)
(此时 ttt 以"天"为单位,t=0t=0t=0 对应正弦波的零点)

ii. 声音的声压
声音在空气中传播时,声压 p(t)p(t)p(t) 通常可表示为多个正弦波的叠加。最简单的情况是单频纯音(如音叉发出的声音):
p(t)=P0cos(2πft+φ) p(t) = P_0 \cos(2\pi f t + \varphi) p(t)=P0cos(2πft+φ)
或等价地:
p(t)=P0sin(2πft+φ) p(t) = P_0 \sin(2\pi f t + \varphi) p(t)=P0sin(2πft+φ)
其中:
- P0P_0P0:声压振幅(Pa,帕斯卡)
- fff:声音频率(Hz,人耳可听范围约 202020--200002000020000 Hz)
- φ\varphiφ:初始相位
实际语音或音乐信号更为复杂,可写为傅里叶级数(周期信号)或傅里叶积分(非周期信号):
p(t)=∑k=1∞Akcos(2πfkt+φk) p(t) = \sum_{k=1}^{\infty} A_k \cos(2\pi f_k t + \varphi_k) p(t)=k=1∑∞Akcos(2πfkt+φk)
或复指数形式:
p(t)=∫−∞∞P(f)ej2πft df p(t) = \int_{-\infty}^{\infty} P(f) e^{j2\pi f t} \, df p(t)=∫−∞∞P(f)ej2πftdf
其中 P(f)P(f)P(f) 为声压的频谱密度函数。

- 其实实际情况更接近下图

iii. 心电图的电压
心电图(ECG)信号是心脏电活动在体表的反映,其波形特征包括 P 波、QRS 复合波、T 波等。虽然无法用一个初等函数精确描述,但常用分段函数 或高斯函数叠加来近似建模。一种简化的单心拍数学模型(McSharry et al. 提出的动态模型):
VECG(t)=∑i∈{P,Q,R,S,T}Ai⋅Δθi2⋅exp(−Δθi22bi2) V_{\text{ECG}}(t) = \sum_{i \in \{P,Q,R,S,T\}} A_i \cdot \Delta\theta_i^2 \cdot \exp\left(-\frac{\Delta\theta_i^2}{2b_i^2}\right) VECG(t)=i∈{P,Q,R,S,T}∑Ai⋅Δθi2⋅exp(−2bi2Δθi2)
其中:
- Δθi=(θ(t)−θi)mod 2π\Delta\theta_i = (\theta(t) - \theta_i) \mod 2\piΔθi=(θ(t)−θi)mod2π,θ(t)\theta(t)θ(t) 为相位变量
- AiA_iAi:各波形的幅度系数
- bib_ibi:各波形的宽度系数
- θi\theta_iθi:各波形在心动周期中的中心相位
更直观的近似表达式(单个 QRS 波可用高斯函数模拟):
VQRS(t)=AQRS⋅exp(−(t−tQRS)22σQRS2) V_{\text{QRS}}(t) = A_{\text{QRS}} \cdot \exp\left(-\frac{(t - t_{\text{QRS}})^2}{2\sigma_{\text{QRS}}^2}\right) VQRS(t)=AQRS⋅exp(−2σQRS2(t−tQRS)2)
完整的一个心动周期可写为分段形式:
VECG(t)={APexp(−(t−tP)22σP2),P 波ARexp(−(t−tR)22σR2),R 波(QRS 峰)ATexp(−(t−tT)22σT2),T 波 V_{\text{ECG}}(t) = \begin{cases} A_P \exp\left(-\frac{(t-t_P)^2}{2\sigma_P^2}\right), & \text{P 波} \\[1ex] A_R \exp\left(-\frac{(t-t_R)^2}{2\sigma_R^2}\right), & \text{R 波(QRS 峰)} \\[1ex] A_T \exp\left(-\frac{(t-t_T)^2}{2\sigma_T^2}\right), & \text{T 波} \end{cases} VECG(t)=⎩ ⎨ ⎧APexp(−2σP2(t−tP)2),ARexp(−2σR2(t−tR)2),ATexp(−2σT2(t−tT)2),P 波R 波(QRS 峰)T 波

(2) 什么是离散时间序列 x[n]x[n]x[n]?
离散时间序列 x[n]x[n]x[n] 表示:只在离散的、等间隔的时间点上取值的信号。
x[n],n=0,1,2,3,⋯ x[n], \quad n = 0, 1, 2, 3, \cdots x[n],n=0,1,2,3,⋯
其中:
- nnn 是整数索引 (不是连续时间 ttt)
- x[n]x[n]x[n] 表示第 nnn 个采样点的信号值
- 相邻采样点的时间间隔为 TsT_sTs(采样周期),实际时间 t=n⋅Tst = n \cdot T_st=n⋅Ts
(3) 具体例子
假设您用单片机ADC 每 0.010.010.01 秒采集一次温度:
| 索引 nnn | 实际时间 t=n×0.01t = n \times 0.01t=n×0.01 秒 | 温度值 x[n]x[n]x[n](℃) |
|---|---|---|
| 0 | 0.00 | 23.5 |
| 1 | 0.01 | 23.6 |
| 2 | 0.02 | 23.8 |
| 3 | 0.03 | 23.7 |
| 4 | 0.04 | 23.9 |
| ⋯\cdots⋯ | ⋯\cdots⋯ | ⋯\cdots⋯ |
这个温度值序列:
x[0]=23.5,x[1]=23.6,x[2]=23.8,x[3]=23.7,x[4]=23.9,⋯ x[0] = 23.5,\quad x[1] = 23.6,\quad x[2] = 23.8,\quad x[3] = 23.7,\quad x[4] = 23.9,\quad \cdots x[0]=23.5,x[1]=23.6,x[2]=23.8,x[3]=23.7,x[4]=23.9,⋯
那么上面的式子就是离散时间序列 x[n]x[n]x[n]。
(4) 直观对比
| 特征 | 连续信号 x(t)x(t)x(t) | 离散信号 x[n]x[n]x[n] |
|---|---|---|
| 自变量 | ttt(连续实数) | nnn(整数索引) |
| 取值时刻 | 任意时刻 | 只在 nTsnT_snTs 时刻 |
| 图形 | 平滑曲线 | 一个个竖立的"脉冲"或"棒状图" |
| 数学工具 | 拉普拉斯变换、傅里叶变换 | Z变换、离散时间傅里叶变换 |
| 例子 | 模拟麦克风输出 | 计算机ADC采样后的数据 |
(5) 图形示意(文字描述)
-
连续信号 x(t)=sin(2πt)x(t) = \sin(2\pi t)x(t)=sin(2πt):

-
离散序列 x[n]=sin(2πn/10)x[n] = \sin(2\pi n / 10)x[n]=sin(2πn/10):

(6) 为什么叫"离散"?
- 时间上离散 :只在 n=0,1,2,⋯n=0,1,2,\cdotsn=0,1,2,⋯ 时刻有定义
- 幅度上可以连续(比如温度 23.5℃),也可以量化(数字信号)
计算机只能处理离散序列,这背后有两个根本原因:
a. 存储容量有限
连续信号在任意短的时间区间内(如 000 到 111 秒)存在无穷多个时间点(t=0.001,0.0001,0.00001,⋯t = 0.001, 0.0001, 0.00001, \cdotst=0.001,0.0001,0.00001,⋯)。而任何存储介质(RAM、硬盘)的容量都是有限的,只能用有限个二进制位去近似(量化)和记录(采样)这个信号。因此,无限个点无法被完整存储。
b. CPU 的节拍式执行
CPU 的执行依赖于晶振产生的时钟周期 。指令只能在时钟周期的整数倍时刻开始执行,无法在"半个周期"的随机时刻动作。这一物理约束可以用公式表达:
时钟周期=1晶振频率 \text{时钟周期} = \frac{1}{\text{晶振频率}} 时钟周期=晶振频率1
指令执行时间=n×时钟周期(n 为整数) \text{指令执行时间} = n \times \text{时钟周期} \quad (n \text{ 为整数}) 指令执行时间=n×时钟周期(n 为整数)
举例 :若单片机使用 161616 MHz 晶振,时钟周期为 0.06250.06250.0625 微秒。一条简单指令(如 MOV)可能耗时 111 个时钟周期,一条复杂除法指令可能耗时 202020 个周期,但无论如何,总时间永远是 0.06250.06250.0625 微秒的整数倍。
c. 硬件特性催生了 Z 变换
上述两点共同决定了:计算机只能在离散的时钟节拍 T,2T,3T,...T, 2T, 3T, \dotsT,2T,3T,... 上动作。因此:
i. 传感器 只能在采样时刻读取数据,得到离散序列 x[1],x[2],x[3],...x[1], x[2], x[3], \dotsx[1],x[2],x[3],...;
ii. 控制器 只能在离散时刻计算输出,得到 y[1],y[2],y[3],...y[1], y[2], y[3], \dotsy[1],y[2],y[3],...;
iii. 连续系统的微分方程 dydt\dfrac{dy}{dt}dtdy 无法直接使用,必须近似为差分方程 y[n]−y[n−1]T\dfrac{y[n] - y[n-1]}{T}Ty[n]−y[n−1]。

正是这一系列"物理成因"(晶振时钟 + 有限存储)与"数学结果"(离散序列 x[n]x[n]x[n])的串联,使得 Z 变换 成为分析离散系统的必然工具。Z变换就是用来分析这类离散序列的数学工具,它把复杂的差分方程变成简单的代数方程,就像拉普拉斯变换处理连续信号一样。
二、Z变换的基础概念
1. 提出背景 ------ Z变换的诞生:从雷达难题到数学工具
(1) 时代背景:采样控制系统的困境
第二次世界大战期间,雷达技术的快速发展催生了一个棘手的问题:雷达系统需要周期性地扫描、采样目标回波信号,但工程师们发现,他们熟悉的拉普拉斯变换 (用于连续系统分析)在面对这种 "采样数据" 时失效了。

为什么失效?因为采样系统每隔固定时间 TTT 才记录一次数据,信号不再是连续的曲线,而是一个个离散的脉冲序列 x(0),x(T),x(2T),⋯x(0), x(T), x(2T), \cdotsx(0),x(T),x(2T),⋯。工程师们迫切需要一种能够处理"离散信号"的数学工具,来分析和设计这类采样控制系统。

(2) 关键突破:Hurewicz的贡献(1947年)
1947年,波兰裔数学家维托尔德·胡雷维奇(Witold Hurewicz) 在参与雷达相关的采样数据控制系统研究时,迈出了关键的一步。他引入了一种新的变换方法,将离散序列映射到一个复函数,从而能够系统性地求解线性常系数差分方程 。这一方法的精妙之处在于:它将复杂的差分方程(离散系统的"运动方程")转化为了简单的代数方程,就像拉普拉斯变换将微分方程转化为代数方程一样。
(3) 命名与完善:Ragazzini和Zadeh(1952年)
1952年,哥伦比亚大学的约翰·R·拉加齐尼(John R. Ragazzini) 和洛特菲·A·扎德(Lotfi A. Zadeh) ------后者后来因"模糊集理论"闻名于世------在采样数据控制研究组中,正式将这一方法冠名为 "the z-transform" (Z变换)。他们选择了字母 zzz 作为变换域的复变量,这个命名沿用至今。扎德等人不仅完善了Z变换的数学框架,还将其推广到电气工程和控制系统的分析中。
(4) 更早的渊源:从De Moivre到Laplace
Z变换的思想其实可以追溯到更早。1730年,法国数学家亚伯拉罕·棣莫弗(Abraham de Moivre) 在研究概率论时引入了"生成函数"方法------这正是Z变换在数学上的本质。
19世纪初,皮埃尔-西蒙·拉普拉斯(Pierre-Simon Laplace) 在其著作中也涉及了类似的思想。但从"生成函数"到"Z变换",中间跨越了两百多年------直到20世纪中叶的工程需求,才真正催生了这一工具的成熟。
(5) 后续发展:Jury与广义Z变换
1950年代后期,埃利亚胡·I·朱里(Eliahu I. Jury) 进一步发展了修正Z变换(modified Z-transform) ,解决了原变换在处理非整数倍采样周期延迟时的局限性,使其在数字控制系统设计中更加灵活实用。
至此,Z变换的理论体系基本成型。
(6) 核心定义(简要公式)
Z变换的基本定义式为:
X(z)=Z{x[n]}=∑n=0∞x[n]z−n X(z) = \mathcal{Z}\{x[n]\} = \sum_{n=0}^{\infty} x[n] z^{-n} X(z)=Z{x[n]}=n=0∑∞x[n]z−n
- x[n]x[n]x[n] 为离散时间序列
- zzz 为复变量(z=rejωz = re^{j\omega}z=rejω)。
这一变换将时域离散序列映射到 zzz 平面(复频域),使差分方程变为代数方程。
2. Z变换的应用领域
(1) 数字信号处理(DSP)
Z变换是数字信号处理的核心数学工具之一。
- 典型应用包括:
a. 数字滤波器设计
无论是有限脉冲响应(FIR)滤波器还是无限脉冲响应(IIR)滤波器,其设计过程都依赖Z变换。系统函数 H(z)H(z)H(z) 的零极点分布直接决定了滤波器的幅频特性与相频特性。
b. 频谱分析
在单位圆上(即 z=ejωTz = e^{j\omega T}z=ejωT)计算Z变换,得到的就是离散时间傅里叶变换(DTFT),用于分析信号的频率成分。
c. 卷积运算的简化
时域的卷积运算 y[n]=x[n]∗h[n]y[n] = x[n] * h[n]y[n]=x[n]∗h[n] 在Z域变为乘法 Y(z)=X(z)H(z)Y(z) = X(z) H(z)Y(z)=X(z)H(z),大幅降低了计算复杂度。
(2) 自动控制与数字控制系统
Z变换在离散控制系统中的应用尤为广泛:
a. 系统稳定性分析
在Z域中,系统的极点(使分母为零的 zzz 值)必须位于单位圆内(∣z∣<1|z| < 1∣z∣<1),系统才是稳定的。这一判据清晰直观,工程师可直接通过零极点图判断系统性能。
b. 数字PID控制器设计
将连续域的PID控制器离散化时,Z变换提供了从 sss 平面到 zzz 平面的映射方法(如双线性变换法),使单片机、DSP等数字设备能够实现实时控制。
c. 采样系统分析
Z变换专门用于处理采样数据系统,能够精确描述采样时刻之间的动态行为。
(3) 通信系统
a. 信道均衡
在数字通信中,接收端使用均衡器来补偿信道失真,Z变换用于分析均衡器的传递函数与收敛特性。
b. 调制与解调
Z变换帮助分析数字调制信号的频谱特性。
(4) 图像处理
二维Z变换应用于图像滤波、边缘检测、图像压缩等任务。二维系统函数 H(z1,z2)H(z_1, z_2)H(z1,z2) 的零极点分析是设计二维数字滤波器的理论基础。
(5) 经济学与统计学
Z变换的数学本质------生成函数------在概率论和组合数学中具有悠久历史。在时间序列分析、计量经济学中,Z变换用于分析自回归滑动平均(ARMA)模型等随机过程。
4. 条件优势
a. 将差分方程转化为代数方程,简化计算
b. 引入Z平面零极点图,直观判断系统稳定性(极点位于单位圆内系统稳定)
c. 可处理初始条件非零的离散系统
d. 与傅里叶变换、拉普拉斯变换形成统一的变换体系
i. 单位圆上的Z变换即离散时间傅里叶变换(DTFT)
ii. Z变换可视为拉普拉斯变换在离散域的对映:z=esTz = e^{sT}z=esT
三、为什么要用Z变换?
Z变换在离散系统分析中的地位,相当于拉普拉斯变换在连续系统分析中的地位。其核心动机如下:
1. 简化运算
a. 卷积运算在Z域变为乘法:
y[n]=x[n]∗h[n]→ZY(z)=X(z)H(z) y[n] = x[n] * h[n] \xrightarrow{Z} Y(z) = X(z)H(z) y[n]=x[n]∗h[n]Z Y(z)=X(z)H(z)
b. 差分方程变为代数方程,避免递推求解的繁琐
2. 统一分析框架
a. 系统函数 H(z)H(z)H(z) 统一描述系统的幅频特性、相频特性
b. 收敛域(ROC)信息包含系统的因果性与稳定性
3. 工程实用性强
a. 计算机可直接处理离散数据
b. 便于硬件实现(FPGA、DSP芯片)
四、Z变换应对什么场合?
1. 机械系统
a. 数控机床的离散位置控制
i. 采样PID控制器设计
ii. 伺服电机的离散速度环、位置环分析
b. 机器人关节控制
i. 多轴运动控制系统的解耦与补偿
c. 振动主动控制
i. 离散状态观测器设计
2. 电气系统
a. 开关电源数字控制
i. DC-DC变换器的数字PID补偿
ii. 数字PWM控制器的z域建模
b. 电机驱动系统
i. 无刷直流电机的离散电流环
ii. 永磁同步电机的磁场定向控制离散化
c. 数字信号处理
i. 数字滤波器设计(低通、高通、带通、陷波器)
ii. 自适应滤波算法(LMS、RLS的z域分析)
五、Z变换的数学定义与基本公式
1. 定义式
对于离散序列 x[n]x[n]x[n](n=0,1,2,⋯n = 0, 1, 2, \cdotsn=0,1,2,⋯),其Z变换定义为:
X(z)=Z{x[n]}=∑n=0∞x[n]z−n X(z) = \mathcal{Z}\{x[n]\} = \sum_{n=0}^{\infty} x[n] z^{-n} X(z)=Z{x[n]}=n=0∑∞x[n]z−n
其中 zzz 为复变量,收敛域(ROC)是使级数绝对收敛的 zzz 值集合。
2. 常用序列的Z变换
a. 单位冲激序列:δ[n]→Z1\delta[n] \xrightarrow{Z} 1δ[n]Z 1,ROC:整个 zzz 平面
b. 单位阶跃序列:u[n]→Z11−z−1=zz−1u[n] \xrightarrow{Z} \frac{1}{1 - z^{-1}} = \frac{z}{z - 1}u[n]Z 1−z−11=z−1z,ROC:∣z∣>1|z| > 1∣z∣>1
c. 指数序列:anu[n]→Z11−az−1=zz−aa^n u[n] \xrightarrow{Z} \frac{1}{1 - a z^{-1}} = \frac{z}{z - a}anu[n]Z 1−az−11=z−az,ROC:∣z∣>∣a∣|z| > |a|∣z∣>∣a∣
d. 正弦序列:sin(ωn)u[n]→Zzsinωz2−2zcosω+1\sin(\omega n) u[n] \xrightarrow{Z} \frac{z \sin\omega}{z^2 - 2z\cos\omega + 1}sin(ωn)u[n]Z z2−2zcosω+1zsinω,ROC:∣z∣>1|z| > 1∣z∣>1
3. 重要性质
a. 线性性:Z{ax1[n]+bx2[n]}=aX1(z)+bX2(z)\mathcal{Z}\{a x_1[n] + b x_2[n]\} = a X_1(z) + b X_2(z)Z{ax1[n]+bx2[n]}=aX1(z)+bX2(z)
b. 时移性:Z{x[n−k]}=z−kX(z)\mathcal{Z}\{x[n - k]\} = z^{-k} X(z)Z{x[n−k]}=z−kX(z)(单边Z变换,x[−1]=x[−2]=⋯=0x[-1]=x[-2]=\cdots=0x[−1]=x[−2]=⋯=0)
c. 卷积定理:Z{x1[n]∗x2[n]}=X1(z)X2(z)\mathcal{Z}\{x_1[n] * x_2[n]\} = X_1(z) X_2(z)Z{x1[n]∗x2[n]}=X1(z)X2(z)
d. 初值定理:x[0]=limz→∞X(z)x[0] = \lim_{z \to \infty} X(z)x[0]=limz→∞X(z)
e. 终值定理:limn→∞x[n]=limz→1(1−z−1)X(z)\lim_{n \to \infty} x[n] = \lim_{z \to 1} (1 - z^{-1}) X(z)limn→∞x[n]=limz→1(1−z−1)X(z)(极点需在单位圆内)
六、Z变换的C语言代码(基础实现)
1. 直接计算Z变换(按定义求和)
c
/*
* 函数功能:计算有限长序列的Z变换在指定z点处的值
* 参数说明:
* x[] : 输入序列数组
* N : 序列长度
* z_re : z点的实部
* z_im : z点的虚部
* X_re : 输出X(z)的实部(指针)
* X_im : 输出X(z)的虚部(指针)
*/
void ZTransformDirect(double* x, int N, double z_re, double z_im,
double* X_re, double* X_im)
{
double sum_re = 0.0, sum_im = 0.0;
double zn_re, zn_im, temp_re, temp_im;
double mag2;
for (int n = 0; n < N; n++)
{
// 计算 z^{-n} = 1 / z^n
// 先用极坐标计算 z^n,再求倒数
double r = sqrt(z_re * z_re + z_im * z_im);
double theta = atan2(z_im, z_re);
double r_pow_n = pow(r, n);
double cos_n_theta = cos(n * theta);
double sin_n_theta = sin(n * theta);
// z^n = r^n * (cos(nθ) + j sin(nθ))
double zn_re_pow = r_pow_n * cos_n_theta;
double zn_im_pow = r_pow_n * sin_n_theta;
// z^{-n} = 1 / z^n = (cos(nθ) - j sin(nθ)) / r^n
double factor = 1.0 / (r_pow_n * r_pow_n); // 除以模长平方
zn_re = factor * (zn_re_pow);
zn_im = factor * (-zn_im_pow);
// 累加 x[n] * z^{-n}
sum_re += x[n] * zn_re;
sum_im += x[n] * zn_im;
}
*X_re = sum_re;
*X_im = sum_im;
}
2. 有理函数形式求值(零极点形式)
c
/*
* 函数功能:计算Z变换有理函数 H(z) = B(z)/A(z) 在给定z点的值
* 参数说明:
* b[] : 分子多项式系数(从z^0到z^M)
* M : 分子阶数
* a[] : 分母多项式系数(从z^0到z^N)
* N : 分母阶数
* z_re : z点实部
* z_im : z点虚部
*/
void ZTransformRational(double* b, int M, double* a, int N,
double z_re, double z_im, double* H_re, double* H_im)
{
double B_re = 0.0, B_im = 0.0;
double A_re = 0.0, A_im = 0.0;
double z_pow_re = 1.0, z_pow_im = 0.0;
double temp_re, temp_im;
// 计算分子 B(z) = b0 + b1*z + b2*z^2 + ...
for (int k = 0; k <= M; k++)
{
if (k == 0) {
z_pow_re = 1.0;
z_pow_im = 0.0;
} else {
// 更新 z^k
temp_re = z_pow_re * z_re - z_pow_im * z_im;
temp_im = z_pow_re * z_im + z_pow_im * z_re;
z_pow_re = temp_re;
z_pow_im = temp_im;
}
B_re += b[k] * z_pow_re;
B_im += b[k] * z_pow_im;
}
// 重置z_pow
z_pow_re = 1.0;
z_pow_im = 0.0;
// 计算分母 A(z) = a0 + a1*z + a2*z^2 + ...
for (int k = 0; k <= N; k++)
{
if (k == 0) {
z_pow_re = 1.0;
z_pow_im = 0.0;
} else {
temp_re = z_pow_re * z_re - z_pow_im * z_im;
temp_im = z_pow_re * z_im + z_pow_im * z_re;
z_pow_re = temp_re;
z_pow_im = temp_im;
}
A_re += a[k] * z_pow_re;
A_im += a[k] * z_pow_im;
}
// 复数除法 H = B / A
double denom = A_re * A_re + A_im * A_im;
*H_re = (B_re * A_re + B_im * A_im) / denom;
*H_im = (B_im * A_re - B_re * A_im) / denom;
}
七、Z变换的C语言代码(库函数实现)
1. 使用GNU Scientific Library (GSL)
c
#include <stdio.h>
#include <gsl/gsl_poly.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
/*
* 使用GSL库求Z变换的有理函数形式
* 通过多项式求根获得零点和极点
*/
void ZTransformUsingGSL(double* b, int M, double* a, int N)
{
double zeros[2 * M]; // 存储零点(实部和虚部交替)
double poles[2 * N]; // 存储极点
// 求分子多项式的根(零点)
gsl_poly_complex_workspace* w = gsl_poly_complex_workspace_alloc(M + 1);
gsl_poly_complex_solve(b, M + 1, w, zeros);
gsl_poly_complex_workspace_free(w);
// 求分母多项式的根(极点)
w = gsl_poly_complex_workspace_alloc(N + 1);
gsl_poly_complex_solve(a, N + 1, w, poles);
gsl_poly_complex_workspace_free(w);
printf("零点 (Zeros):\n");
for (int i = 0; i < M; i++) {
printf("z%d = %.4f + %.4fj\n", i, zeros[2*i], zeros[2*i+1]);
}
printf("\n极点 (Poles):\n");
for (int i = 0; i < N; i++) {
printf("p%d = %.4f + %.4fj\n", i, poles[2*i], poles[2*i+1]);
}
}
2. 使用自制复数运算库(轻量级)
c
typedef struct
{
double re;
double im;
} Complex;
Complex complex_add(Complex a, Complex b)
{
return (Complex){a.re + b.re, a.im + b.im};
}
Complex complex_mul(Complex a, Complex b) {
return (Complex){a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re};
}
Complex complex_div(Complex a, Complex b)
{
double denom = b.re * b.re + b.im * b.im;
return (Complex){(a.re * b.re + a.im * b.im) / denom,
(a.im * b.re - a.re * b.im) / denom};
}
/*
* 利用差分方程递推计算系统响应(时域等价于Z变换求逆)
* 差分方程:y[n] = b0*x[n] + b1*x[n-1] + ... - a1*y[n-1] - a2*y[n-2] - ...
*/
void DifferenceEquation(double* b, int M, double* a, int N,
double* x, int len_x, double* y)
{
// 初始化历史缓冲区
double x_history[10] = {0};
double y_history[10] = {0};
for (int n = 0; n < len_x; n++)
{
double sum = 0.0;
// 前向路径(分子部分)
for (int k = 0; k <= M && k <= n; k++)
{
sum += b[k] * x[n - k];
}
// 反馈路径(分母部分,注意a[0]=1通常)
for (int k = 1; k <= N && k <= n; k++)
{
sum -= a[k] * y[n - k];
}
y[n] = sum / a[0]; // 归一化
}
}
八、Linux环境配置与使用
1. 安装库命令
a. 安装GSL科学计算库(包含多项式求根、FFT等Z变换相关工具)
bash
# Ubuntu/Debian系统
sudo apt update
sudo apt install libgsl-dev libgslcblas0
# CentOS/RHEL系统
sudo yum install gsl gsl-devel
# Arch Linux系统
sudo pacman -S gsl
b. 安装FFTW库(用于离散傅里叶变换,与Z变换在单位圆上关联)
bash
sudo apt install libfftw3-dev libfftw3-doc
c. 安装Octave(用于快速验证Z变换结果)
bash
sudo apt install octave octave-signal
2. 头文件与库文件路径
a. GSL头文件路径
bash
/usr/include/gsl/
├── gsl_poly.h # 多项式求根
├── gsl_complex.h # 复数运算
├── gsl_fft.h # 快速傅里叶变换
└── ...
b. 库文件路径
bash
/usr/lib/x86_64-linux-gnu/libgsl.so
/usr/lib/x86_64-linux-gnu/libgslcblas.so
c. 编译时链接参数
bash
gcc -o ztransform_demo ztransform_demo.c -lgsl -lgslcblas -lm
3. 使用方法
a. 基础示例:计算一阶系统 H(z)=11−0.5z−1H(z) = \frac{1}{1 - 0.5z^{-1}}H(z)=1−0.5z−11 的幅频响应
c
#include <stdio.h>
#include <math.h>
int main()
{
double b[] = {1.0}; // 分子:b0 = 1
double a[] = {1.0, -0.5}; // 分母:a0 = 1, a1 = -0.5
// 在单位圆上求频率响应:z = e^(jω)
printf("频率 (rad/sample)\t|H(e^{jω})| (dB)\n");
for (int k = 0; k <= 100; k++)
{
double omega = M_PI * k / 100.0; // 0 到 π
double z_re = cos(omega);
double z_im = sin(omega);
double H_re, H_im;
ZTransformRational(b, 0, a, 1, z_re, z_im, &H_re, &H_im);
double mag = sqrt(H_re * H_re + H_im * H_im);
double mag_dB = 20 * log10(mag);
printf("%.4f\t\t%.2f\n", omega, mag_dB);
}
return 0;
}
b. 使用GSL求零极点并判断稳定性
c
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_poly.h>
int main()
{
// 系统:H(z) = (1 - 1.5z^{-1} + 0.5z^{-2}) / (1 - 0.8z^{-1} + 0.2z^{-2})
double b[] = {1.0, -1.5, 0.5}; // 分子系数
double a[] = {1.0, -0.8, 0.2}; // 分母系数
int M = 2, N = 2;
double zeros[2 * M];
double poles[2 * N];
gsl_poly_complex_workspace* w;
// 求零点
w = gsl_poly_complex_workspace_alloc(M + 1);
gsl_poly_complex_solve(b, M + 1, w, zeros);
gsl_poly_complex_workspace_free(w);
// 求极点
w = gsl_poly_complex_workspace_alloc(N + 1);
gsl_poly_complex_solve(a, N + 1, w, poles);
gsl_poly_complex_workspace_free(w);
printf("零点:\n");
for (int i = 0; i < M; i++) {
double r = sqrt(zeros[2*i]*zeros[2*i] + zeros[2*i+1]*zeros[2*i+1]);
printf(" z%d = %.4f ± %.4fj, |z| = %.4f\n",
i, zeros[2*i], zeros[2*i+1], r);
}
printf("\n极点:\n");
int stable = 1;
for (int i = 0; i < N; i++) {
double r = sqrt(poles[2*i]*poles[2*i] + poles[2*i+1]*poles[2*i+1]);
printf(" p%d = %.4f ± %.4fj, |p| = %.4f %s\n",
i, poles[2*i], poles[2*i+1], r,
r < 1.0 ? "(稳定)" : "(不稳定)");
if (r >= 1.0) stable = 0;
}
printf("\n系统稳定性: %s\n", stable ? "稳定" : "不稳定");
return 0;
}
九、例题
以下是一个以机器人关节控制 为背景的Z变换例题,突出其解耦与补偿优势,格式与您之前的规范统一。
例题:两关节机器人手臂的离散控制
考虑一个简化的两关节机器人手臂,每个关节由一个独立电机驱动。当第一个关节快速运动时,会产生离心力和科里奥利力,耦合到第二个关节上,导致第二个关节产生不希望的位置偏差。
假设第二个关节的耦合动态可用如下差分方程描述:
y[n]=0.8y[n−1]+0.2u[n]+0.5d[n] y[n] = 0.8 y[n-1] + 0.2 u[n] + 0.5 d[n] y[n]=0.8y[n−1]+0.2u[n]+0.5d[n]
其中:
- y[n]y[n]y[n]:第 nnn 个采样时刻关节2的实际位置(单位:度)
- u[n]u[n]u[n]:关节2的控制输入(电压)
- d[n]d[n]d[n]:关节1运动对关节2的耦合干扰(单位:度)
已知关节1运动产生的干扰序列为 d[0]=5d[0] = 5d[0]=5,d[1]=4d[1] = 4d[1]=4,d[2]=3d[2] = 3d[2]=3,d[3]=2d[3] = 2d[3]=2,d[4]=1d[4] = 1d[4]=1(随时间衰减)。设计一个补偿器 C(z)C(z)C(z) 来消除干扰的影响。
解:方法一:不使用Z变换(时域递推)
若直接用时域递推,每步都需要手动计算,且难以设计补偿器。例如,令 u[n]=0u[n] = 0u[n]=0(不加控制),观察干扰的影响:
y[0]=0.8y[−1]+0.2×0+0.5×5=0+0+2.5=2.5 y[0] = 0.8 y[-1] + 0.2 \times 0 + 0.5 \times 5 = 0 + 0 + 2.5 = 2.5 y[0]=0.8y[−1]+0.2×0+0.5×5=0+0+2.5=2.5
y[1]=0.8×2.5+0+0.5×4=2.0+2.0=4.0 y[1] = 0.8 \times 2.5 + 0 + 0.5 \times 4 = 2.0 + 2.0 = 4.0 y[1]=0.8×2.5+0+0.5×4=2.0+2.0=4.0
y[2]=0.8×4.0+0+0.5×3=3.2+1.5=4.7 y[2] = 0.8 \times 4.0 + 0 + 0.5 \times 3 = 3.2 + 1.5 = 4.7 y[2]=0.8×4.0+0+0.5×3=3.2+1.5=4.7
y[3]=0.8×4.7+0+0.5×2=3.76+1.0=4.76 y[3] = 0.8 \times 4.7 + 0 + 0.5 \times 2 = 3.76 + 1.0 = 4.76 y[3]=0.8×4.7+0+0.5×2=3.76+1.0=4.76
y[4]=0.8×4.76+0+0.5×1=3.808+0.5=4.308 y[4] = 0.8 \times 4.76 + 0 + 0.5 \times 1 = 3.808 + 0.5 = 4.308 y[4]=0.8×4.76+0+0.5×1=3.808+0.5=4.308

可见,即使不加控制,干扰也会使关节2产生约 4∘4^\circ4∘--5∘5^\circ5∘ 的误差。要设计补偿器消除干扰,时域方法只能"试凑",效率极低。
方法二:使用Z变换(频域设计补偿器)
对差分方程两边取Z变换:
Y(z)=0.8z−1Y(z)+0.2U(z)+0.5D(z) Y(z) = 0.8 z^{-1} Y(z) + 0.2 U(z) + 0.5 D(z) Y(z)=0.8z−1Y(z)+0.2U(z)+0.5D(z)
整理得:
Y(z)(1−0.8z−1)=0.2U(z)+0.5D(z) Y(z) \left(1 - 0.8 z^{-1}\right) = 0.2 U(z) + 0.5 D(z) Y(z)(1−0.8z−1)=0.2U(z)+0.5D(z)
系统的传递函数为:
G(z)=Y(z)U(z)=0.21−0.8z−1=0.2zz−0.8 G(z) = \frac{Y(z)}{U(z)} = \frac{0.2}{1 - 0.8 z^{-1}} = \frac{0.2 z}{z - 0.8} G(z)=U(z)Y(z)=1−0.8z−10.2=z−0.80.2z
干扰通道的传递函数为:
Gd(z)=Y(z)D(z)=0.51−0.8z−1=0.5zz−0.8 G_d(z) = \frac{Y(z)}{D(z)} = \frac{0.5}{1 - 0.8 z^{-1}} = \frac{0.5 z}{z - 0.8} Gd(z)=D(z)Y(z)=1−0.8z−10.5=z−0.80.5z
为消除干扰,我们设计一个前馈补偿器 C(z)C(z)C(z),使得控制输入 U(z)=−C(z)D(z)U(z) = -C(z) D(z)U(z)=−C(z)D(z),即:
Y(z)=G(z)U(z)+Gd(z)D(z)=−G(z)C(z)D(z)+Gd(z)D(z) Y(z) = G(z) U(z) + G_d(z) D(z) = -G(z) C(z) D(z) + G_d(z) D(z) Y(z)=G(z)U(z)+Gd(z)D(z)=−G(z)C(z)D(z)+Gd(z)D(z)
令干扰项为零:−G(z)C(z)+Gd(z)=0-G(z) C(z) + G_d(z) = 0−G(z)C(z)+Gd(z)=0,解得:
C(z)=Gd(z)G(z)=0.5zz−0.80.2zz−0.8=0.50.2=2.5 C(z) = \frac{G_d(z)}{G(z)} = \frac{\frac{0.5 z}{z - 0.8}}{\frac{0.2 z}{z - 0.8}} = \frac{0.5}{0.2} = 2.5 C(z)=G(z)Gd(z)=z−0.80.2zz−0.80.5z=0.20.5=2.5
即补偿器就是一个比例系数 C(z)=2.5C(z) = 2.5C(z)=2.5,控制律为 u[n]=−2.5⋅d[n]u[n] = -2.5 \cdot d[n]u[n]=−2.5⋅d[n]。
代入验证:
y[0]=0.8×0+0.2×(−2.5×5)+0.5×5=0−2.5+2.5=0 y[0] = 0.8 \times 0 + 0.2 \times (-2.5 \times 5) + 0.5 \times 5 = 0 - 2.5 + 2.5 = 0 y[0]=0.8×0+0.2×(−2.5×5)+0.5×5=0−2.5+2.5=0
y[1]=0.8×0+0.2×(−2.5×4)+0.5×4=0−2.0+2.0=0 y[1] = 0.8 \times 0 + 0.2 \times (-2.5 \times 4) + 0.5 \times 4 = 0 - 2.0 + 2.0 = 0 y[1]=0.8×0+0.2×(−2.5×4)+0.5×4=0−2.0+2.0=0
依次类推,所有时刻的干扰都被完全抵消。
结论:Z变换的优势对比
| 对比项 | 不使用Z变换(时域递推) | 使用Z变换 |
|---|---|---|
| 干扰分析 | 逐点计算,耗时且无法预判 | 通过传递函数直接分析 |
| 补偿器设计 | 试凑法,效率极低 | 代数运算,一步求解 C(z)=Gd(z)/G(z)C(z) = G_d(z)/G(z)C(z)=Gd(z)/G(z) |
| 物理意义 | 不直观 | 零极点、频域响应清晰 |
| 适用场景 | 简单低阶系统 | 多轴耦合、复杂控制系统 |
在机器人关节控制 中,多轴之间的耦合干扰是普遍问题。Z变换将差分方程转化为代数方程,使得解耦补偿器的设计变得像除法一样简单------这正是Z变换的核心优势。
根据您的要求,以下是为您撰写的 "十、现代的解决办法------MATLAB" 章节内容,格式与您之前的规范统一,可直接复制使用。
十、现代的解决办法------MATLAB
从本章前面的推导可以看出,Z变换的手工计算(尤其是部分分式展开、留数法求逆变换、零极点图绘制)较为繁琐。现代工程实践中,MATLAB 已成为离散系统分析与设计的标准工具。
1. MATLAB 中的 Z 变换相关函数
| 功能 | 函数名 | 示例用法 |
|---|---|---|
| Z变换 | ztrans |
syms n; ztrans(sin(n)) |
| 逆Z变换 | iztrans |
syms z; iztrans(z/(z-0.5)) |
| 传递函数建模 | tf |
H = tf([1,0], [1,-0.5], 1) |
| 零极点图 | zplane |
zplane([1,0], [1,-0.5]) |
| 频率响应 | freqz |
freqz([1,0], [1,-0.5]) |
| 离散系统仿真 | filter |
y = filter(b, a, x) |
- 用 MATLAB 求解本章例题
以本章机器人关节控制的例题为例:
matlab
% 定义系统参数
b = 0.2; % 分子系数
a = [1, -0.8]; % 分母系数 (1 - 0.8z^{-1})
% 定义干扰序列 d[n]
d = [5, 4, 3, 2, 1];
% 方法一:直接递推求解(无补偿)
y_no_comp = filter(b, a, d);
disp('补偿前的位置误差:');
disp(y_no_comp);
% 设计补偿器 C(z) = 2.5
C = 2.5;
u = -C * d; % 控制输入
% 方法二:带补偿的响应
y_comp = filter(b, a, u + d);
disp('补偿后的位置误差:');
disp(y_comp);
运行结果:
补偿前的位置误差:
2.5000 4.0000 4.7000 4.7600 4.3080
补偿后的位置误差:
0 0 0 0 0
3. 零极点图与稳定性判断
matlab
% 绘制系统 H(z) = 0.2z/(z-0.8) 的零极点图
num = [0.2, 0]; % 分子系数 (0.2z)
den = [1, -0.8]; % 分母系数 (z - 0.8)
zplane(num, den);
title('系统零极点图');
从图中可见:极点位于 z=0.8z = 0.8z=0.8,在单位圆内,系统稳定。
4. 频率响应分析
matlab
% 绘制幅频响应和相频响应
freqz(num, den);
该命令生成两个子图:
- 幅频响应 :横坐标为归一化频率(000 到 π\piπ rad/sample),纵坐标为幅值(dB)
- 相频响应:横坐标为频率,纵坐标为相位(度)
5. MATLAB 与手工计算对比
| 对比项 | 手工计算 | MATLAB |
|---|---|---|
| Z变换正逆 | 查表 + 部分分式 | ztrans / iztrans 一行代码 |
| 差分方程求解 | 逐次递推(易出错) | filter 自动完成 |
| 零极点图 | 手绘复平面 | zplane 自动标注 |
| 频率响应 | 代入 z=ejωz = e^{j\omega}z=ejω 逐点计算 | freqz 自动生成曲线 |
| 系统稳定性 | 手工判断 $ | p_i |
6. 补充:Python 替代方案(开源免费)
若没有 MATLAB 许可证,可使用 Python + SciPy 实现相同功能:
python
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
# 定义系统
b = [0.2, 0] # 分子
a = [1, -0.8] # 分母
# 干扰序列
d = [5, 4, 3, 2, 1]
# 无补偿响应
y_no_comp = signal.lfilter(b, a, d)
print('补偿前:', y_no_comp)
# 有补偿响应
C = 2.5
u = -C * np.array(d)
y_comp = signal.lfilter(b, a, u + d)
print('补偿后:', y_comp)
# 绘制零极点图
zeros, poles, _ = signal.tf2zpk(b, a)
plt.scatter(np.real(zeros), np.imag(zeros), marker='o', label='零点')
plt.scatter(np.real(poles), np.imag(poles), marker='x', label='极点')
plt.axhline(y=0, color='k', linewidth=0.5)
plt.axvline(x=0, color='k', linewidth=0.5)
circle = plt.Circle((0, 0), 1, fill=False, linestyle='--')
plt.gca().add_patch(circle)
plt.axis('equal')
plt.legend()
plt.title('零极点图')
plt.show()
7. 小结
MATLAB(及 Python)将 Z 变换从"手工推导的数学工具"转化为"工程设计的实用手段"。工程师无需记忆繁琐的变换对和分式展开技巧,而是将精力集中在系统建模、性能分析和控制器设计 上。这正是现代工程教育中"计算思维"的体现------让软件完成计算,让人完成思考。