毫米波雷达信号处理步骤顺序

现在看来毫米波雷达的相关资料太少了,初学者弄清相关的流程都是很费力,所以我在这里将自己的预处理方案开源,也许对向我一样的初学者有一点帮助。

先给出我的初始化代码,这一部分需要根据硬件进行设计

Matlab 复制代码
function params = initializeParams()
    % 初始化毫米波雷达参数
    params.startFreq = 77e9; % 起始频率 (Hz)
    params.endFreq = 81e9; % 结束频率 (Hz)
    params.adcStartTime = 6e-6; % ADC起始时间 (s)
    params.adcSamples = 250; % ADC采样点数
    params.sampleRate = 6250e3; % 采样率 (Hz)
    params.rampEndTime = 50e-6; % 调频斜坡结束时间 (s)
    params.idleTime = 7e-6; % 空闲时间 (s)
    params.frameCount = 1200; % 帧数
    params.framePeriodicity = 50e-3; % 帧周期 (s)
    params.chirpLoopCount = 128; % Chirp循环数
    params.rxGain = 30; % RX增益 (dB)
    params.txCount = 2; % 发送天线数
    params.rxCount = 4; % 接收天线数

    % 计算参数
    params.BW = params.endFreq - params.startFreq; % 带宽 (4GHz)
    params.fSlope = params.BW / (params.rampEndTime - params.adcStartTime); % 调频斜率 (~90.9e12 Hz/s)
    params.rangeFFTSize = 256;     % Range FFT点数
    params.c = 3e8;                % 光速 (m/s)
    % 距离分辨率
    params.rangeRes = params.c / (2 * params.BW); % [m]
    %多普勒FFT点数
    params.fft_points = 128;
    
    %DOA参数
    params.fc = (params.startFreq + params.endFreq) / 2; % 中心频率 (79GHz)
    params.lambda = params.c / params.fc;                % 波长
    params.ant_spacing = params.lambda / 2;       % 天线间距 (~1.898mm)

    

end

我将标题输入Deepseek,deepseek生成如下的流程图:

Range-FFT(距离维FFT)

将每个 chirp 的时域采样信号转换为频域信号。由于雷达发射的是线性调频波(FMCW),目标的距离信息正比于其回波频率(中频频率)。因此,通过FFT可以将不同距离的目标在频率轴上分离开,得到距离维信息。

输入:单个Chirp的时域采样信号

输出:每个 chirp 的距离谱(每个天线应该单独进行)

在这一步,理论上来讲就可以进行相位的求解了,所以可以在这之后直接进行去噪并求相位:

Matlab 复制代码
function [dynamic_data, range_axis] = processRangeFFTAndRemoveStatic(current_data, params)
    % Range FFT处理
    range_fft = fft(current_data, params.rangeFFTSize, 1);
    
    % 静态杂波去除
    static_clutter = mean(range_fft, 2); 
    dynamic_data = range_fft - static_clutter;
    
    % 关键修正:物理距离轴生成(取消fftshift)
    range_resolution = params.c/(2*params.BW); % 3.75cm
    max_range = (params.sampleRate * params.c)/(2*params.fSlope); % 理论最大无模糊距离
    
    % 生成正向距离轴(从0开始)
    range_axis = (0:params.rangeFFTSize-1) * range_resolution;
    
    % 截断到理论最大距离
    valid_bins = range_axis <= max_range;
    range_axis = range_axis(valid_bins);
    dynamic_data = dynamic_data(valid_bins, :, :);
end    
%     % 可视化部分
%     figure;
%     subplot(2, 1, 1);
%     % 绘制Range FFT的幅度(去除静态杂波前)
%     imagesc(abs(range_fft(:, :, 1))); % 假设你的数据是3维的,第一个维度是距离,第三个是时间
%     colorbar;
%     title('Range FFT (Before Static Clutter Removal)');
%     xlabel('Time Index');
%     ylabel('Range Bin');
%     
%     subplot(2, 1, 2);
%     % 绘制动态数据的幅度(去除静态杂波后)
%     imagesc(abs(dynamic_data(:, :, 1))); % 同样假设数据是3维的
%     colorbar;
%     title('Dynamic Data (After Static Clutter Removal)');
%     xlabel('Time Index');
%     ylabel('Range Bin');
%     
%     % 也可以画出一个简单的距离轴上的幅度变化(例如在某个时间索引上)
%     figure;
%     plot(range_axis, abs(dynamic_data(:, 1, 1)));
%     title('Range Profile at Time Index 1');
%     xlabel('Range (m)');
%     ylabel('Amplitude');

多普勒-FFT(速度维FFT)

在得到距离谱之后,我们对多个连续 chirp 在相同距离单元上的信号 进行第二次FFT。由于目标运动引起的相位变化是相干的,而噪声是非相干的,这次FFT可以将不同速度(即不同多普勒频率)的目标分离开,得到速度维信息。

输入:多个 chirp 在同一距离门(range bin) 上的数据。

输出:距离-多普勒谱(RD谱)。

Matlab 复制代码
function [doppler_fft_result, velocity_axis] = doppler_fft_processing(range_fft_data, params, plot_enable)
% 多普勒FFT处理函数
%
% 输入参数:
%   range_fft_data: 距离FFT后的数据,维度为256×153600×4
%   params: 参数结构体,来自initializeParams.m
%   plot_enable: 是否启用可视化 (可选,默认为true)
%
% 输出参数:
%   doppler_fft_result: 多普勒FFT结果,维度为256×128×1200×4
%   velocity_axis: 速度轴,用于绘图

% 设置默认参数
if nargin < 3
    plot_enable = false; % 默认不启用可视化
end

% 获取数据维度
[range_bins, ~, channels] = size(range_fft_data);
frames = params.frameCount;
chirps_per_frame = params.chirpLoopCount;
fft_points = params.fft_points;

% 重新组织数据: 256×128×1200×4
reshaped_data = reshape(range_fft_data, ...
                       [range_bins, chirps_per_frame, frames, channels]);

% 初始化多普勒FFT结果
doppler_fft_result = zeros(range_bins, fft_points, frames, channels);

% 计算每个chirp的时间
chirp_time = params.rampEndTime + params.idleTime;

% 对每个距离门、每帧和每个通道进行多普勒FFT
for channel = 1:channels
    for frame = 1:frames
        for range_bin = 1:range_bins
            % 提取当前距离门、当前帧、当前通道的所有啁啾数据
            chirp_data = squeeze(reshaped_data(range_bin, :, frame, channel));
            
            % 确保数据是行向量
            if iscolumn(chirp_data)
                chirp_data = chirp_data';
            end
            
            % 加窗处理 (减少频谱泄漏)
            if length(chirp_data) > 1
                window = hamming(length(chirp_data))';
                windowed_data = chirp_data .* window;
            else
                windowed_data = chirp_data;
            end
            
            % 执行多普勒FFT并进行fftshift
            doppler_fft = fft(windowed_data, fft_points);
            doppler_fft = fftshift(doppler_fft);
            
            % 确保结果是行向量后再赋值
            if iscolumn(doppler_fft)
                doppler_fft = doppler_fft';
            end
            
            % 存储结果
            doppler_fft_result(range_bin, :, frame, channel) = doppler_fft;
        end
    end
end

% 计算速度轴
prf = 1 / chirp_time; % 脉冲重复频率
max_doppler = prf / 2; % 最大多普勒频率

% 多普勒频率轴
doppler_axis = linspace(-max_doppler, max_doppler, fft_points);

% 转换为速度轴 (v = λ*fd/2)
velocity_axis = doppler_axis * params.lambda / 2;

% 可视化部分 - 只保留RDM图
if plot_enable
    % 选择要可视化的帧和通道
    frame_to_plot = min(1, frames); % 第一帧
    channel_to_plot = min(1, channels); % 第一通道
    
    % 创建距离-速度图 (RDM)
    figure('Name', '距离-速度图 (RDM)', 'Position', [100, 100, 800, 600]);
    rdm_data = 20*log10(abs(squeeze(doppler_fft_result(:, :, frame_to_plot, channel_to_plot))));
    
    % 使用兼容性更好的方法设置颜色范围
    imagesc(velocity_axis, (1:range_bins)*params.rangeRes, rdm_data);
    
    % 设置颜色范围的兼容方法
    caxis([max(rdm_data(:))-60, max(rdm_data(:))]); % 使用caxis而不是clim
    
    xlabel('速度 (m/s)');
    ylabel('距离 (m)');
    title(sprintf('距离-速度图 (帧: %d, 通道: %d)', frame_to_plot, channel_to_plot));
    colorbar;
    axis xy;
    colormap('jet');
end
end

在这步之后我们就可以解相位并进行后续工作了。

另一个分支:角度信息

**波束成形(Beamforming)**利用多个接收天线之间的空间相位差来实现。

对于RD谱中的每一个"点"(即一个特定的距离-速度单元),我们提取所有天线在该单元上的复数数据。对这些空间数据(跨天线维度)进行FFT(即Angle-FFT)或数字波束成形算法(如Capon, MUSIC),就可以得到该目标点的角度谱,峰值位置即对应角度信息。

这里面临一个问题:内存,4D数据[range,doppler,angle,time],这样的数据是非常庞大的,我个人在处理时,信号的长度为一分钟,如果保留完整的数据一分钟的4D数据内存占用约为100G,所以在波束成形前我们需要确定目标,缩小range,doppler的范围。一般使用CFAR,其实就是寻找峰值,所以我个人就是寻找峰值之后框选附近的几个bin。

Matlab 复制代码
function [targetWindows, rangeAngleProfile, physicalInfo] = extractStrongestTarget(data, params, visualize)
% 输入:
%   data - 4D矩阵 [256×128×1200×4] (Range x Doppler x Frame x Antenna)
%   params - 参数结构体,包含距离分辨率等信息
%   visualize - 布尔值,是否可视化结果
%
% 输出:
%   targetWindows - 4D矩阵 [7x7x1200x4] 提取的目标窗口(复数)
%   rangeAngleProfile - 4D矩阵 [7x7x1200x361] 距离-角度谱(复数,使用MUSIC算法)
%   physicalInfo - 结构体,包含物理距离和多普勒信息

% 设置默认参数
if nargin < 3
    visualize = false; % 默认不启用可视化
end

% 获取数据尺寸
[numRangeBins, numDopplerBins, numFrames, numAnt] = size(data);
targetWindows = zeros(7, 7, numFrames, numAnt);
targetRanges = zeros(numFrames, 1);
targetDopplers = zeros(numFrames, 1);

% 计算距离轴(基于筛选后的数据)
range_axis = (0:numRangeBins-1) * params.rangeRes;

% 计算多普勒轴
chirp_time = params.rampEndTime + params.idleTime;
prf = 1 / chirp_time;
max_doppler = prf / 2;
doppler_axis = linspace(-max_doppler, max_doppler, numDopplerBins);
velocity_axis = doppler_axis * params.lambda / 2;

% 定义角度搜索范围 (-90° 到 90°)
angle_range = -90:0.5:90; % 0.5° 分辨率
num_angles = length(angle_range); % 361个角度点
rangeAngleProfile = complex(zeros(7, 7, numFrames, num_angles)); % 确保为复数

% 构建天线阵列导向矢量
antenna_positions = (0:numAnt-1) * params.ant_spacing;
steering_vectors = zeros(num_angles, numAnt);

for ang_idx = 1:num_angles
    angle_rad = deg2rad(angle_range(ang_idx));
    steering_vectors(ang_idx, :) = exp(-1j * 2 * pi * antenna_positions * sin(angle_rad) / params.lambda);
end

% 存储物理信息
physicalInfo.range_axis = range_axis;
physicalInfo.velocity_axis = velocity_axis;
physicalInfo.angle_range = angle_range;
physicalInfo.targetRanges = zeros(numFrames, 1);
physicalInfo.targetDopplers = zeros(numFrames, 1);
physicalInfo.windowRangeBins = zeros(7, numFrames);
physicalInfo.windowDopplerBins = zeros(7, numFrames);

% 遍历所有帧
for frameIdx = 1:numFrames
    % 对4个天线的数据取平均,找到最强目标位置(非相干累加)
    combinedSlice = mean(abs(data(:,:,frameIdx,:)), 4);
    
    % 找到全局最大值位置
    [~, maxIdx] = max(combinedSlice(:));
    [rangeBinIdx, dopplerBinIdx] = ind2sub(size(combinedSlice), maxIdx);
    
    % 记录目标的物理距离和多普勒速度
    targetRanges(frameIdx) = range_axis(rangeBinIdx);
    targetDopplers(frameIdx) = velocity_axis(dopplerBinIdx);
    physicalInfo.targetRanges(frameIdx) = range_axis(rangeBinIdx);
    physicalInfo.targetDopplers(frameIdx) = velocity_axis(dopplerBinIdx);
    
    % 计算窗口边界(处理边界情况)
    rangeStart = max(1, rangeBinIdx - 3);
    rangeEnd = min(numRangeBins, rangeBinIdx + 3);
    dopplerStart = max(1, dopplerBinIdx - 3);
    dopplerEnd = min(numDopplerBins, dopplerBinIdx + 3);
    
    % 存储窗口对应的物理距离和多普勒值
    physicalInfo.windowRangeBins(:, frameIdx) = range_axis(rangeStart:rangeEnd);
    physicalInfo.windowDopplerBins(:, frameIdx) = velocity_axis(dopplerStart:dopplerEnd);
    
    % 遍历所有天线
    for antIdx = 1:numAnt
        % 获取当前帧和天线的距离-多普勒平面
        currentSlice = data(:,:,frameIdx,antIdx); % 使用复数数据
        
        % 提取目标窗口
        window = currentSlice(rangeStart:rangeEnd, dopplerStart:dopplerEnd);
        
        % 创建7x7窗口(边界不足时补零)
        fullWindow = zeros(7,7);
        r1 = max(1, 4 - (rangeBinIdx - rangeStart));
        r2 = min(7, 4 + (rangeEnd - rangeBinIdx));
        d1 = max(1, 4 - (dopplerBinIdx - dopplerStart));
        d2 = min(7, 4 + (dopplerEnd - dopplerBinIdx));
        
        fullWindow(r1:r2, d1:d2) = window;
        targetWindows(:,:,frameIdx,antIdx) = fullWindow;
    end
    
    % 使用MUSIC算法计算距离-角度谱
    for rangeBin = 1:7
        for dopplerBin = 1:7
            % 提取当前距离-多普勒单元的所有天线复数数据
            antData = squeeze(targetWindows(rangeBin, dopplerBin, frameIdx, :));
            
            % 计算协方差矩阵
            R = antData * antData';
            
            % 特征值分解
            [eigenvectors, eigenvalues] = eig(R);
            eigenvalues = diag(eigenvalues);
            [~, idx] = sort(eigenvalues, 'descend');
            eigenvectors = eigenvectors(:, idx);
            
            % 假设只有一个信号源,噪声子空间是除最大特征值对应的特征向量外的所有特征向量
            noise_subspace = eigenvectors(:, 2:end);
            
            % 计算MUSIC谱 - 保持复数形式
            music_spectrum = zeros(1, num_angles);
            for ang_idx = 1:num_angles
                a = steering_vectors(ang_idx, :).';
                music_spectrum(ang_idx) = 1 / (a' * (noise_subspace * noise_subspace') * a);
            end
            
            % 存储复数结果
            rangeAngleProfile(rangeBin, dopplerBin, frameIdx, :) = music_spectrum;
        end
    end
end

% 可视化第一帧结果
if visualize
    visualizeFirstFrame(data, targetWindows, physicalInfo, rangeAngleProfile);
end
end

function visualizeFirstFrame(data, windows, physicalInfo, rangeAngleProfile)
% 创建新图窗
figure('Position', [100, 100, 1400, 800], 'Name', 'First Frame Target Extraction and MUSIC Angle Spectrum');

% 获取物理信息
range_axis = physicalInfo.range_axis;
velocity_axis = physicalInfo.velocity_axis;
angle_range = physicalInfo.angle_range;
targetRange = physicalInfo.targetRanges(1);
targetDoppler = physicalInfo.targetDopplers(1);
windowRangeBins = physicalInfo.windowRangeBins(:, 1);
windowDopplerBins = physicalInfo.windowDopplerBins(:, 1);

% 找到对应的bin索引
[~, rangeBinIdx] = min(abs(range_axis - targetRange));
[~, dopplerBinIdx] = min(abs(velocity_axis - targetDoppler));

% 显示第一帧的4个天线结果
for antIdx = 1:4
    % 原始距离-多普勒平面
    subplot(3, 4, antIdx);
    imagesc(velocity_axis, range_axis, 10*log10(abs(data(:,:,1,antIdx))));
    hold on;
    plot(velocity_axis(dopplerBinIdx), range_axis(rangeBinIdx), 'rx', 'MarkerSize', 15, 'LineWidth', 2);
    hold off;
    title(sprintf('Antenna %d - Original', antIdx));
    subtitle(sprintf('Range: %.2fm, Doppler: %.2fm/s', targetRange, targetDoppler));
    xlabel('速度 (m/s)'); ylabel('距离 (m)');
    axis xy; colorbar;
    caxis([-50 0]);
    
    % 提取的目标窗口 - 使用物理坐标
    subplot(3, 4, antIdx+4);
    imagesc(windowDopplerBins, windowRangeBins, abs(windows(:,:,1,antIdx)));
    title(sprintf('Antenna %d - 7x7 Window', antIdx));
    xlabel('速度 (m/s)'); ylabel('距离 (m)');
    axis xy; colorbar;
    
    % 标记中心点
    hold on;
    plot(targetDoppler, targetRange, 'rx', 'MarkerSize', 15, 'LineWidth', 2);
    hold off;
end

% 显示距离-角度谱(在中心多普勒单元)
subplot(3, 4, [9, 10, 11, 12]);
centerDopplerBin = 4; % 中心多普勒单元
rangeAngleSlice = squeeze(rangeAngleProfile(:, centerDopplerBin, 1, :));

% 获取当前窗口对应的物理距离
currentRangeBins = windowRangeBins;

% 显示幅度谱
imagesc(angle_range, currentRangeBins, 10*log10(abs(rangeAngleSlice)));
title('MUSIC距离-角度谱 (中心多普勒单元)');
xlabel('角度 (度)'); ylabel('距离 (m)');
colorbar;
colormap jet;

% 添加总体信息
sgtitle(sprintf('目标提取结果 - 距离: %.2fm, 速度: %.2fm/s', targetRange, targetDoppler), ...
        'FontSize', 14, 'FontWeight', 'bold');
end

在这里有几个注意点:

需要现在天线维度累加来估计幅值

Matlab 复制代码
% 对4个天线的数据取平均,找到最强目标位置(非相干累加)
combinedSlice = mean(abs(data(:,:,frameIdx,:)), 4);

我用的是music方法,普遍来讲music方法只保存谱,而我需要后续其它的处理,所以将其保存为复数:

Matlab 复制代码
% 存储复数结果
rangeAngleProfile(rangeBin, dopplerBin, frameIdx, :) = music_spectrum;

% 存储实数结果
rangeAngleProfile(rangeBin, dopplerBin, frameIdx, :) = abs(music_spectrum);

相位解包裹(Phase Unwrapping)及降噪

Matlab 复制代码
    %% 步骤1:找到最大幅值距离门
    % 沿慢时间维度累加幅值
    amplitude_sum = sum(abs(beamformed_data), 2); % [range_bins, 1]
    
    % 找到最大幅值对应的距离门索引
    [~, max_range_idx] = max(amplitude_sum);
    target_signal = beamformed_data(max_range_idx, :); % [1, time_frames]

    %% 步骤2:相位计算与解包裹
    % 计算原始相位
    raw_phase = angle(target_signal); % [1, time_frames]
    
    % 相位解包裹(沿时间维度)
    unwrapped_phase = unwrap(raw_phase, [], 2); % [1, time_frames]

    %% 步骤3:去趋势/除直流分量(一阶差分)
    v = diff(unwrapped_phase); % [1, time_frames-1]
    v = detrend(v);
    %% 步骤4:滑动平均滤波
    N_w = 5;  % 可以是奇数或偶数
    % 1. 计算 movmean (会短 N_w-1 个点)
    V_smooth_short =  movmean(v, N_w, 2);  % 等价于 'Endpoints', 'shrink'

    %在最后两个点之间插入它们的平均值
    last_two_points = V_smooth_short(:, end-1:end);
    inserted_value = mean(last_two_points, 2);

matlab的unwrap可以自动解包裹,不需要额外处理。

至此我们应该以及解决了预处理所需要的一般操作,可在此基础上继续开发分析算法或调整预处理中的参数、操作来进行优化。

相关推荐
huangyuchi.2 天前
【Linux系统】万字解析,进程间的信号
linux·服务器·信号处理·信号产生·linux信号·信号保存·操作系统如何运行
半桔3 天前
【Linux手册】Unix/Linux 信号:原理、触发与响应机制实战
linux·运维·unix·信号处理
WSSWWWSSW6 天前
SciPy科学计算与应用:SciPy信号处理入门-从理论到实践
信号处理·scipy
FPGA_ADDA6 天前
基于复旦微ZYNQ7015+VU3P 的双FMC 基带信号处理平台(国产率100%)
fpga开发·信号处理·全国产·vu3p·adda射频采集
stbomei10 天前
基于 MATLAB 的信号处理实战:滤波、傅里叶变换与频谱分析
算法·matlab·信号处理
khystal11 天前
HUMS 2023齿轮箱数据分析
数据库·数据分析·信号处理
SKYDROID云卓小助手20 天前
三轴云台之控制信号解析与执行
运维·服务器·网络·人工智能·信号处理
khystal21 天前
ISTA为什么要加上软阈值激活函数?r若没有L1 正则化也要加其他激活函数吗?
神经网络·信号处理
2401_8238682221 天前
织构表面MATLAB仿真
人工智能·机器学习·matlab·信号处理