摘要

在 5G 新空口中,OFDM 是核心波形,而信道估计的质量直接影响系统吞吐量。传统的 LS 和 MMSE 估计器在低信噪比或复杂多径下性能受限。近年来,深度学习凭借其强大的特征提取能力,为信道估计提供了新的思路。本文从零搭建一个 OFDM 仿真平台,实现 LS、MMSE 以及基于 CNN 和 LSTM 的估计器,并对比它们在 Rayleigh 衰落信道下的 MSE 和 BER 性能。

1 背景意义

1.1 项目背景

  • 长期以来,最小二乘(LS) 和最小均方误差(MMSE) 估计器因其简单性和解析可行性而被广泛使用。LS估计器最小化平方误差,无需信道先验信息,计算复杂度低,但对噪声非常敏感;MMSE估计器在已知信道统计特性的高斯假设下能够达到最优性能,但需要预先知道信道的二阶统计量,且涉及矩阵求逆运算,计算复杂度较高。
  • 近年来,深度学习技术的快速发展为信道估计问题提供了全新的解决思路。深度学习方法凭借其强大的数据拟合能力和特征提取能力,能够自适应地学习复杂的信道环境和信号模式。
  • 目前,多种深度学习架构已被成功应用于信道估计任务,包括卷积神经网络(CNN)、循环神经网络(RNN)、长短期记忆网络(LSTM)、图神经网络(GNN)和生成对抗网络(GAN) 等。研究表明,与传统的LS和MMSE方法相比,基于深度学习的方案能够在保持可行计算开销的同时,显著降低估计误差和误码率。

1.2 项目意义

本文构建了一个完整的OFDM信道估计对比研究框架,从传统方法到深度学习方法进行了系统的实现和评估。在同一OFDM仿真框架下实现了LS、MMSE、CNN和LSTM四种信道估计器,在相同的信道条件和SNR设置下进行公平对比,为评估不同方法的性能提供了可靠的基准。实验结果表明,在高SNR条件下,基于LSTM的信道估计器在BER性能上显著优于传统LS和MMSE方法,验证了深度学习在信道估计任务中的有效性和潜力

2 算法设计

2.1 OFDM系统模型

在这里插入图片描述
在这里插入图片描述

2.2 ls估计

在这里插入图片描述

2.3 mmse估计

在这里插入图片描述

2.4 基于深度学习的估计

深度学习将信道估计视为一个 回归问题:以 LS 初始估计(含噪声)为输入,学习到真实信道H的映射。其中:
其中,CNN 擅长提取局部空间特征。对于 OFDM 信道估计,子载波相邻之间存在频率相关性,CNN 的卷积核可捕捉这种局部模式。低层卷积(大核)提取宽带频谱包络,高层卷积(小核)提取精细的频域细节。残差连接保证了网络不会因层次加深而退化,并加速收敛。

  • 编码器:通过多级卷积逐步提取高层次特征,通道数递增。

  • 解码器:通过对称的卷积将特征逐步恢复回原始尺寸,通道数递减。

  • 残差连接(Residual Connection):在输入和输出之间添加一条捷径,使网络学习残差。

此外,考虑到OFDM 子载波频率响应是一个 有序序列,且子载波间存在长距离相关性(由多径时延导致)。LSTM 通过门控机制解决了传统 RNN 的梯度消失/爆炸问题,能够高效捕捉长程依赖。采用 双向 LSTM(BiLSTM),因为子载波的频率响应不仅依赖于左侧,也依赖于右侧——双向结构能同时利用上下文信息。

模块 层类型 核大小 步长 填充 输入通道 输出通道 激活 / 备注 输出尺寸 (B, C, L) 参数量
输入 - - - - - - - (B, 2, 64) 0
编码器 Conv1d + BN 9 1 4 2 32 ReLU (B, 32, 64) 608
Conv1d + BN 7 1 3 32 64 ReLU (B, 64, 64) 14,400
Conv1d + BN 5 1 2 64 128 ReLU (B, 128, 64) 41,088
解码器 Conv1d + BN 5 1 2 128 64 ReLU (B, 64, 64) 41,024
Conv1d + BN 7 1 3 64 32 ReLU (B, 32, 64) 14,368
Conv1d 9 1 4 32 2 线性 (B, 2, 64) 578
残差分支 Conv1d 1 1 0 2 2 线性 (B, 2, 64) 6
输出 加法 - - - - - 解码输出 + 残差输出 (B, 2, 64) 0
模块 层类型 输入尺寸 输出尺寸 隐藏单元数 双向 Dropout 备注 参数量
输入 - (B, 2, 64) (B, 2, 64) - - - 转置为 (B, 64, 2) 后送入 LSTM 0
LSTM 层 BiLSTM 2 512 (256×2) 256 0.0 返回序列 528,384
BiLSTM 512 512 256 0.2 返回序列 1,572,864
BiLSTM 512 512 256 0.2 返回序列 1,572,864
BiLSTM 512 512 256 0.0 返回序列(最后层) 1,572,864
全连接映射 Linear 512 128 - - - ReLU 激活 65,664
Linear 128 2 - - - 线性输出 258
输出 - - - - - - 转置回 (B, 2, 64) 0

3 仿真结果

  • ofdm参数表
参数名称 取值 说明
FFT 点数 64 总子载波数(含导频、数据、保护)
循环前缀长度 16 采样点,对应 25% 的 OFDM 符号长度
有效子载波数 55 去掉 DC 子载波和两侧保护频带后的子载波数
导频子载波数 16 用于信道估计的参考信号
数据子载波数 39 承载用户数据的子载波数量(自动计算)
保护子载波数 8 左右各 4 个,防止邻带干扰(含 DC 子载波)
调制方式 QPSK 每符号携带 2 比特数据
每帧 OFDM 符号数 14 等效于 5G NR 一个时隙(slot)
导频符号值 1 + j 0 1+j0 1+j0 幅度归一化
  • 多径瑞利衰落信道参数表
参数名称 取值 说明
多径数目 6 时间离散信道抽头数
最大时延扩展 16 采样点 小于循环前缀长度,避免符号间干扰
功率延迟分布 指数衰减( e − 0.2 ℓ e^{-0.2 \ell} e0.2 各径功率随路径序号指数递减
功率归一化 所有径功率之和 = 1 保证信道平均增益为 0 dB
信道类型 瑞利衰落 每条路径的实部和虚部均为独立零均值高斯变量
多普勒频移 0(准静态) 假设信道在一个 OFDM 符号内保持不变
路径时延分布 随机整数采样点 6 条路径随机分布在 0 ~ 15 采样点之间

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

模型 总参数量 训练时间*(40 epochs) 主要优势
CNN ≈ 112,072 ~5 分钟 轻量、快速,适合边缘部署
BiLSTM ≈ 5,283,954 ~15 分钟 高精度,擅长捕捉长程频域依赖

可以看到:

  • 深度学习的信道轨迹相比于ls和mmse更准
  • 所有估计器的 BER 均随 SNR 提高而下降,符合通信系统的基本规律。
  • 深度学习方法(CNN/LSTM)在全 SNR 区间内一致优于传统方法(LS/MMSE),且优势随 SNR 提高而扩大。

部分代码:

import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"   # 解决 OpenMP 冲突

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from scipy.linalg import toeplitz
from tqdm import tqdm
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import warnings
warnings.filterwarnings('ignore')

# 若 pandas 导入失败,请先安装或降级 numpy/pandas
import pandas as pd

# 检查 GPU 设备
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'✅ 正在使用设备: {device}')
if torch.cuda.is_available():
    print(f'   GPU: {torch.cuda.get_device_name(0)}')

# 设置随机种子以保证可重复性
np.random.seed(42)
torch.manual_seed(42)
print('✅ 所有依赖已加载成功!')

# ============================================================
#  OFDM 系统配置 (参考 5G NR,自动计算子载波分配)
# ============================================================
class OFDMConfig:
    """5G 启发式 OFDM 系统参数,自动完成子载波分配。"""
    N_FFT       = 64
    N_guard     = 8                      # 左右各 4 个保护子载波(含 DC)
    N_pilot     = 16                     # 导频数量
    CP_len      = 16
    N_sym       = 14
    MOD_ORDER   = 4
    BITS_PER_SYM = 2
    N_paths     = 6
    MAX_DELAY   = 16
    SNR_TRAIN_DB = 20
    N_SAMPLES   = 80000
    N_TEST      = 8000

    # ---- 自动计算子载波分配 ----
    guard_left  = np.arange(1, N_guard//2 + 1)          # 1~4
    guard_right = np.arange(N_FFT - N_guard//2, N_FFT)  # 60~63
    dc          = np.array([0])
    # 有效子载波(去掉保护频带和 DC)
    effective   = np.setdiff1d(np.arange(N_FFT), np.concatenate([guard_left, dc, guard_right]))
    # 在有效子载波内均匀放置导频
    pilot_pos   = np.linspace(0, len(effective)-1, N_pilot, dtype=int)
    PILOT_IDX   = effective[pilot_pos]                 # 例如 [5, 8, 11, ..., 56]
    # 数据子载波 = 有效子载波 - 导频
    DATA_IDX    = np.setdiff1d(effective, PILOT_IDX)
    N_data      = len(DATA_IDX)                        # 自动计算(例如 39)
    PILOT_VAL   = np.ones(N_pilot, dtype=complex)

# 实例化配置
cfg = OFDMConfig()

print('=' * 50)
print('  OFDM 系统配置 (自动计算)')
print('=' * 50)
print(f'  FFT 点数          : {cfg.N_FFT}')
print(f'  数据子载波数      : {cfg.N_data}')
print(f'  导频子载波数      : {cfg.N_pilot}')
print(f'  循环前缀长度      : {cfg.CP_len}')
print(f'  调制方式          : {cfg.MOD_ORDER}-QAM (QPSK)')
print(f'  多径数            : {cfg.N_paths}')
print(f'  训练样本数        : {cfg.N_SAMPLES}')
print('=' * 50)

# ============================================================
#  OFDM 发射机 / 接收机 (使用 cfg.DATA_IDX)
# ============================================================
def qpsk_modulate(bits):
    """QPSK 调制:将比特对映射为复数符号"""
    bits = bits.reshape(-1, 2)
    lut = np.array([1+1j, 1-1j, -1+1j, -1-1j]) / np.sqrt(2)
    idx = bits[:, 0] * 2 + bits[:, 1]
    return lut[idx]

def qpsk_demodulate(symbols):
    """QPSK 硬判决解调"""
    bits = np.zeros((len(symbols), 2), dtype=int)
    bits[:, 0] = (np.real(symbols) < 0).astype(int)
    bits[:, 1] = (np.imag(symbols) < 0).astype(int)
    return bits.flatten()

def ofdm_transmit(bits, cfg):
    """
    OFDM 发送端流程:
    bits 长度必须为 len(cfg.DATA_IDX) * cfg.BITS_PER_SYM。
    返回: tx_signal(时域发送信号), tx_freq(频域符号), data_idx(数据子载波索引)
    """
    data_sym = qpsk_modulate(bits)
    tx_freq = np.zeros(cfg.N_FFT, dtype=complex)
    tx_freq[cfg.PILOT_IDX] = cfg.PILOT_VAL
    # 直接使用配置中的数据子载波索引
    tx_freq[cfg.DATA_IDX] = data_sym[:len(cfg.DATA_IDX)]   # 确保长度匹配
    tx_time = np.fft.ifft(tx_freq, n=cfg.N_FFT)
    tx_signal = np.concatenate([tx_time[-cfg.CP_len:], tx_time])
    return tx_signal, tx_freq, cfg.DATA_IDX

def ofdm_receive(rx_signal, cfg):
    """OFDM 接收端:去除 CP -> FFT -> 频域符号"""
    rx_no_cp = rx_signal[cfg.CP_len: cfg.CP_len + cfg.N_FFT]
    Y = np.fft.fft(rx_no_cp, n=cfg.N_FFT)
    return Y

# ============================================================
#  多径信道模型
# ============================================================
def generate_multipath_channel(cfg):
    """
    生成随机多径瑞利衰落信道。
    返回: h_time(时域冲激响应), H_freq(频域响应)
    """
    delays = np.sort(np.random.choice(cfg.MAX_DELAY, cfg.N_paths, replace=False))
    power_profile = np.exp(-0.2 * np.arange(cfg.N_paths))
    power_profile /= power_profile.sum()
    h_taps = (np.random.randn(cfg.N_paths) + 1j * np.random.randn(cfg.N_paths)) * np.sqrt(power_profile / 2)
    h_time = np.zeros(cfg.N_FFT, dtype=complex)
    for i, d in enumerate(delays):
        h_time[d] += h_taps[i]
    H_freq = np.fft.fft(h_time, n=cfg.N_FFT)
    return h_time, H_freq

def apply_channel(tx_signal, h_time, snr_db):
    """信号经过多径信道并加 AWGN"""
    h_nonzero = h_time[:np.where(h_time != 0)[0][-1] + 1] if np.any(h_time != 0) else h_time[:1]
    rx_conv = np.convolve(tx_signal, h_nonzero)
    rx_conv = rx_conv[:len(tx_signal)]
    sig_power = np.mean(np.abs(rx_conv) ** 2)
    snr_linear = 10 ** (snr_db / 10)
    noise_power = sig_power / snr_linear
    noise = np.sqrt(noise_power / 2) * (np.random.randn(*rx_conv.shape) + 1j * np.random.randn(*rx_conv.shape))
    return rx_conv + noise

# ============================================================
#  传统估计器 (基于 DFT 插值,MMSE 使用统计相关矩阵)
# ============================================================
def ls_estimator(Y, cfg):
    """LS 估计:导频处除法 + DFT 插值(时域补零截断)"""
    H_pilots = Y[cfg.PILOT_IDX] / cfg.PILOT_VAL
    H_full = np.zeros(cfg.N_FFT, dtype=complex)
    H_full[cfg.PILOT_IDX] = H_pilots
    h_est = np.fft.ifft(H_full)
    h_est[cfg.MAX_DELAY:] = 0
    H_ls = np.fft.fft(h_est)
    return H_ls

# 预计算 MMSE 相关矩阵(从大量信道样本统计得到)
def compute_mmse_matrix(cfg, n_samples=10000):
    """计算导频位置的信道自相关矩阵 R_hh"""
    H_pilots_list = []
    for _ in range(n_samples):
        _, H_true = generate_multipath_channel(cfg)
        H_pilots_list.append(H_true[cfg.PILOT_IDX])
    H_pilots = np.array(H_pilots_list)
    R_hh = np.cov(H_pilots.T)
    return R_hh

R_hh_mmse = compute_mmse_matrix(cfg, n_samples=5000)

def mmse_estimator(Y, cfg, snr_db, R_hh=R_hh_mmse):
    """MMSE 估计:导频处 MMSE 加权 + DFT 插值"""
    snr_lin = 10 ** (snr_db / 10)
    Np = len(cfg.PILOT_IDX)
    H_ls_p = Y[cfg.PILOT_IDX] / cfg.PILOT_VAL
    beta = 1.0 / snr_lin
    W = R_hh @ np.linalg.inv(R_hh + beta * np.eye(Np))
    H_mmse_p = W @ H_ls_p
    H_full = np.zeros(cfg.N_FFT, dtype=complex)
    H_full[cfg.PILOT_IDX] = H_mmse_p
    h_est = np.fft.ifft(H_full)
    h_est[cfg.MAX_DELAY:] = 0
    H_mmse = np.fft.fft(h_est)
    return H_mmse

在这里插入图片描述

4 总结

本文围绕 OFDM 系统中基于深度学习的信道估计 这一核心主题,从理论到实践,从传统方法到前沿技术,系统性地构建了一个完整的性能对比框架。文章首先阐述了信道估计在无线通信系统中的基石地位及其面临的挑战,继而深入剖析了 LS 和 MMSE 两类经典估计器的理论基础与内在局限。在此基础上,将深度学习引入这一领域,分别设计了基于残差学习的 CNN 估计器和基于双向 LSTM 的序列估计器,并在统一的多径瑞利衰落信道环境下完成了端到端的仿真验证。

  • 源代码 出图所见即所得,代码获取方式见VX公众号

更多推荐