第2章 复数与相位:复数的概念,欧拉公式,用复数表示相位
相位这东西,刚开始学的时候总觉得抽象。明明就是个角度,为什么要绕到复数上去?
我当年也有这个疑问。直到有一次做雷达信号处理,需要同时跟踪信号的幅度和相位变化,才发现实数坐标系根本搞不定——你没法用一个数同时表达两个正交的信息。复数,就是为这个场景量身定做的工具。
2.1 为什么是复数?
先想一个问题:一个正弦波 A·cos(ωt + φ),我们关心什么?
- 幅度 A:信号有多强
- 频率 ω:信号变化快慢
- 相位 φ:信号在时间轴上的偏移
频率可以单独处理,但幅度和相位是耦合的。你用实数表示一个点,只能描述一个维度。而相位天然需要两个维度——一个代表实部(cos分量),一个代表虚部(sin分量)。
核心思想:复数 z = a + jb 的本质,是用两个正交分量表示一个旋转矢量。相位就是该矢量与实轴的夹角。
我在项目中遇到过这样一个坑:直接用实数做相位差计算,结果在0°和360°边界附近频繁跳变。后来改用复数表示,所有问题迎刃而解。因为复数天然是连续的,没有边界问题。
2.2 复数的三种表示法
同一个复数,有三种视角。我个人习惯根据场景切换:
| 表示法 | 数学形式 | 适用场景 |
|---|---|---|
| 代数形式 | z = a + jb |
加减运算、实部虚部分离 |
| 极坐标形式 | z = r∠φ |
乘除运算、相位直观展示 |
| 指数形式 | z = r·ejφ |
傅里叶变换、系统分析 |
三种形式可以互相转换:
a = r·cosφ,b = r·sinφr = √(a² + b²),φ = arctan(b/a)
小技巧:Python里用 cmath.phase(z) 直接取相位,用 abs(z) 取模。别自己写arctan,容易踩象限的坑。
2.3 欧拉公式:连接指数和三角的桥梁
欧拉公式是相位计算里最重要的公式,没有之一。
e^(jθ) = cosθ + j·sinθ
这个公式告诉你三件事:
- 指数函数可以表示旋转——
e^(jθ)就是单位圆上的一个点 - 相位就是指数上的角度——θ 就是相位
- 乘指数等于旋转——乘以
e^(jθ)相当于把矢量旋转 θ 角度
为什么会这样?你可以把 e^(jθ) 展开成泰勒级数,cos和sin的级数恰好就是它的实部和虚部。但说实话,做工程不需要纠结这个证明,记住结论就够了。
工程直觉:e^(jωt) 代表一个以角速度 ω 旋转的矢量。它的实部就是 cos(ωt),虚部就是 sin(ωt)。相位就是当前时刻矢量转过的角度。
2.4 用复数表示相位
现在把前面的东西串起来。一个正弦信号:
x(t) = A·cos(ωt + φ)
用复数怎么表示?
X(t) = A·e^(j(ωt + φ)) = A·e^(jφ)·e^(jωt)
这里:
A·e^(jφ)是复振幅,包含了幅度和初始相位e^(jωt)是旋转因子,表示随时间旋转
实际信号 x(t) 就是 X(t) 的实部。但我们在做相位计算时,直接操作复数 X(t),最后再取实部。
我曾经踩过的坑:直接用 np.cos() 做相位计算,结果在相位差接近0°时精度丢失严重。后来改用复数乘法 np.exp(1j*phi),精度问题直接消失。因为复数运算在数值上更稳定。
2.5 知识结构图
下面这张图把本章的核心逻辑串起来了:
2.6 Python实战:复数与相位
光说不练假把式。我们直接用Python验证上面的概念。
import numpy as np
import cmath
# 1. 创建复数
z1 = 3 + 4j
print(f"复数: {z1}")
print(f"实部: {z1.real}, 虚部: {z1.imag}")
print(f"模: {abs(z1):.3f}")
print(f"相位(弧度): {cmath.phase(z1):.3f}")
print(f"相位(角度): {np.degrees(cmath.phase(z1)):.2f}°")
# 2. 欧拉公式验证
theta = np.pi/4 # 45°
z2 = np.exp(1j * theta)
print(f"\ne^(j*45°): {z2:.4f}")
print(f"cos(45°): {np.cos(theta):.4f}")
print(f"sin(45°): {np.sin(theta):.4f}")
print(f"验证: cos + j·sin = {np.cos(theta) + 1j*np.sin(theta):.4f}")
# 3. 用复数表示相位
A = 2.0
phi = np.pi/3 # 60°
omega = 2*np.pi * 10 # 10Hz
t = 0.1
complex_signal = A * np.exp(1j * (omega*t + phi))
print(f"\n在t={t}s时的复数表示: {complex_signal:.4f}")
print(f"实际信号值(取实部): {complex_signal.real:.4f}")
print(f"理论cos值: {A*np.cos(omega*t + phi):.4f}")
运行结果解读:你会发现 np.exp(1j*theta) 和 cos + j·sin 完全一致。这就是欧拉公式在数值上的验证。而复数信号的实部,恰好就是我们的实际信号。
2.7 避坑指南
最后分享几个我实际工作中遇到的坑:
- 相位范围:
cmath.phase()返回的是[-π, π],不是[0, 2π)。需要[0, 2π)时自己加% (2*np.pi)。 - 浮点精度:当相位接近0或π时,arctan计算容易产生误差。优先用
cmath.phase()而不是自己算。 - 虚数单位:数学上用
i,工程上(尤其电气)用j。Python里用j,别写错了。 - 复数乘法:两个复数相乘,模相乘、相位相加。这是做频移和相位旋转的基础。
特别提醒:不要用 math.atan2(b, a) 替代 cmath.phase()。前者返回的是标量,后者返回的是复数相位,处理边界情况更鲁棒。我早期代码里全是 atan2,后来重构时全换成了 cmath.phase,bug少了一半。
好了,复数与相位的基础就讲到这里。记住一句话:复数就是带相位的数。后面所有章节——傅里叶变换、滤波器设计、锁相环——都建立在这个基础上。把这个概念吃透,后面的路就好走了。