第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θ

这个公式告诉你三件事:

  1. 指数函数可以表示旋转——e^(jθ) 就是单位圆上的一个点
  2. 相位就是指数上的角度——θ 就是相位
  3. 乘指数等于旋转——乘以 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 知识结构图

下面这张图把本章的核心逻辑串起来了:

复数与相位知识结构 复数 z = a + jb 代数形式 a + jb 极坐标形式 r∠φ 指数形式 r·e^(jφ) 欧拉公式 e^(jθ) = cosθ + j·sinθ 相位表示:A·e^(jφ)·e^(jωt) 核心:复数用一个数同时表达幅度和相位,是信号处理的基础工具 三种表示法可互相转换,根据场景选择最方便的形式

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少了一半。

好了,复数与相位的基础就讲到这里。记住一句话:复数就是带相位的数。后面所有章节——傅里叶变换、滤波器设计、锁相环——都建立在这个基础上。把这个概念吃透,后面的路就好走了。