DDMA信号处理以及数据处理的流程---聚类
你好呀!很高兴见到你!我是Xiaojie,请允许我邀请大家深入探讨毫米波雷达技术。作为一名专业的研究者与教育工作者,在本次分享中我将带领大家一起学习毫米波雷达相关知识,并详细阐述这些关键环节的处理流程。我们将会从目标生成在内的重要内容开始讨论,并系统性地探讨包括信号仿真在内的重要内容。此外,在这一系列文章中我们将涵盖以下核心模块:目标生成、信号仿真、测距与测速等核心技术环节,并结合实际案例进行深入分析与讨论。预计共有七到八篇文章组成这个系列,请持续关注后续更新内容!
最终效果如下:

整体文件的目录树如下:

从这篇开始转入数据处理部分。本文着重讨论聚类技术及其应用情况。完成雷达信号处理后,则会生成目标点云信息,并包含距离参数、速度指标以及角度数据等关键参数。
dbscan聚类
名称为DBSCAN的数据挖掘算法即为Density-Based Spatial Clustering of Application with Noise(DBSCAN),其属于一种基于数据密度的空间聚类方法。
密度聚类算法主要假设类别是由样本之间的分布紧密程度所决定的。在同一个类别内部的所有样本之间具有高度相关性,在这种情况下,则会将这些高度相关的样本分组到同一类别中,并由此形成一个有意义的聚类结构。
核心思想:DBSCAN的核心思想是以样本点密度为基础实现聚类目标的方式,并通过在样本空间中识别密集区域来完成簇划分的过程。
算法主要参数由两部分组成:一是邻域范围d;二是最低样本数n_{\text{min}}。只有在数据集中某一点x其局部区域内的样本数量s(x)至少达到n_{\text{min}}时才能被认定为核心数据点
在DBSCAN算法中,空间中的数据样本被划分为三个互斥类别:核心对象、边缘对象和噪音对象。其中,核心对象被定义为在一定范围内包含足够数量的数据样本的对象;边缘对象则位于这些核心对象的周围区域,并不满足成为核心对象的标准;最后一种类型是噪音样本或异常值,在其所在区域内不具备足够的密度特征。
数据点之间的关系:在DBSCAN算法中,默认将数据点之间的关系划分为直接密度联系、间接密度联系以及基于连通性的密度联系三种类型。这些关系描述了如何从核心数据点延伸至整个数据集中的簇结构。
此处表述较为正式;实际上网上通常会有大量对dbscan聚类算法的解释;因此无需在此进一步阐述。
此处表述较为正式;实际上网上通常会有大量对dbscan聚类算法的解释;因此无需在此进一步阐述。
代码仿真
clusterProcess.m文件
该文件负责聚类核心功能的实现,并执行该算法的计算任务。该系统完成数据特征的统计分析,并对数据集进行分组整合过程处理。
代码如下:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%点云处理模块
%%1. dbscan聚类
%%2. 计算聚类信息
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [parameter,pointList] = clusterProcess(parameter,pointList,pointNums)
%% dbscan聚类
[pointList,clusterNums] = dbscanCore(parameter.clusterPara,pointList,pointNums);
%% 计算聚类信息
parameter = calClusterMsg(parameter,pointList,pointNums,clusterNums);
%% 聚类合并
if parameter.frameCount ~= 1 %后面进行聚类合并
parameter = clusterMergeCluster(parameter);
else %第一帧聚类直接赋值
parameter = clusterEqualNewCluster(parameter);
end
end
代码解读
dbscanCore.m文件
dbscanCore.m文件是核心聚类函数,在该文件中使用聚类算法将点云数据被分类为多个类别
%% dbscan聚类核心函数
function [pointList,clusterNums] = dbscanCore(clusterPara,pointList,pointNums)
[pointDistance] = calPointEuclideanDistance(pointList,pointNums); %计算点云的距离速度角度欧式距离
clusterNums = 0; %聚类数目初始化
IDX=zeros(pointNums,1);
pointIsVisited = zeros(pointNums,1); %初始化所有点云都未被初始化
for pointIdx = 1:1:pointNums %循环所有点
if ~pointIsVisited(pointIdx) %当前点没被访问过
pointIsVisited(pointIdx) = 1; %标记当前点被访问过
neighbor = getPointNeighbor(pointDistance,pointIdx,clusterPara); %获取领域点数
neighborNums = numel(neighbor); %领域点数
if neighborNums < clusterPara.minPoints %领域点数小于阈值
else %领域点数大于阈值
clusterNums = clusterNums + 1; %聚类数目增加
expandCluster(pointIdx,neighbor,clusterNums); %根据当前点访问领域点扩充
end
end
end
function expandCluster(pointIdx,neighbor,clusterNums)
IDX(pointIdx)=clusterNums; %标记当前点云属于几号聚类
pointList(pointIdx).clusterId = clusterNums; %标记当前点云属于几号聚类
count = 1; %计数器
while true
pointIdxTmp = neighbor(count); %获取领域中的其他点云ID
if ~pointIsVisited(pointIdxTmp) %该点云没有被访问过
pointIsVisited(pointIdxTmp) = 1; %标记其被访问过
neighbor2 = getPointNeighbor(pointDistance,pointIdxTmp,clusterPara); %获取pointIdxTmp的领域点数
neighbor2Nums = numel(neighbor2); %领域点数
if neighbor2Nums >= clusterPara.minPoints %领域点数大于阈值
neighbor = [neighbor,neighbor2]; %扩充neighbor
end
end
if IDX(pointIdxTmp)==0 %当前点属于空闲点
IDX(pointIdxTmp)=clusterNums; %标记为几号聚类
pointList(pointIdxTmp).clusterId = clusterNums; %标记为几号聚类
end
count = count + 1; %计数器累加
if count > numel(neighbor) %循环完毕所有邻域中的点云
break;
end
end
end
end
代码解读
calPointEuclideanDistance.m文件
calPointEuclideanDistance.m文件是计算点云与点云之间的欧式距离
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%计算距离、速度、角度的欧式距离
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [pointDistance] = calPointEuclideanDistance(pointList,pointNums)
for pointIdxI = 1:1:pointNums
for pointIdxJ = 1:1:pointNums
%距离欧式距离
pointDistance.rangeD(pointIdxI,pointIdxJ) = ...
sqrt((abs(pointList(pointIdxI).range)-abs(pointList(pointIdxJ).range)).^2);
%速度欧式距离
pointDistance.speedD(pointIdxI,pointIdxJ) = ...
sqrt((abs(pointList(pointIdxI).speed)-abs(pointList(pointIdxJ).speed)).^2);
%角度欧式距离
pointDistance.angleD(pointIdxI,pointIdxJ) = ...
sqrt((abs(pointList(pointIdxI).angle)-abs(pointList(pointIdxJ).angle)).^2);
end
end
end
代码解读
getPointNeighbor.m文件
getPointNeighbor.m文件是获取当前点的邻域信息
%% 获取当前点的领域信息
function neighbor = getPointNeighbor(pointDistance,currPointIdx,clusterPara)
neighbor = [];
pointRangeDistance = pointDistance.rangeD;[rangeRow,rangeCol] = size(pointRangeDistance);
pointSpeedDistance = pointDistance.speedD;[speedRow,speedCol] = size(pointSpeedDistance);
pointAngleDistance = pointDistance.angleD;[angleRow,angleCol] = size(pointAngleDistance);
if (rangeCol == speedCol) && (rangeCol == angleCol) && (speedCol && angleCol)
for colIdx = 1:1:rangeCol
if pointRangeDistance(currPointIdx,colIdx) < clusterPara.rangeThreshold && ...
pointSpeedDistance(currPointIdx,colIdx) < clusterPara.speedThreshold && ...
pointAngleDistance(currPointIdx,colIdx) < clusterPara.angleThreshold
neighbor = [neighbor, colIdx];
end
end
end
end
代码解读
clusterMergeCluster.m
该M文件的功能是实现将新增的聚类与现有聚类进行合并。
该M文件的功能是实现将新增的聚类与现有聚类进行合并。
function parameter = clusterMergeCluster(parameter)
distanceThresh = 6; %合并聚类的阈值
% clusterCurrNums = 0;
for newClusterIdx = 1:1:parameter.clusterPara.newClusterCurrNums %下一帧形成聚类
newLa = parameter.clusterPara.newClusterTarget(newClusterIdx).la;
newLo = parameter.clusterPara.newClusterTarget(newClusterIdx).lo;
newVLa = parameter.clusterPara.newClusterTarget(newClusterIdx).V_La;
newVLo = parameter.clusterPara.newClusterTarget(newClusterIdx).V_Lo;
maxDistance = 999; %默认最大距离
updateClusterIdx = -1; %要更新的ID
distance = -1; %默认距离
for clusterIdx = 1:1:parameter.clusterPara.clusterCurrNums %已经存在的聚类
la = parameter.clusterPara.clusterTarget(clusterIdx).la;
lo = parameter.clusterPara.clusterTarget(clusterIdx).lo;
vLa = parameter.clusterPara.clusterTarget(clusterIdx).V_La;
vLo = parameter.clusterPara.clusterTarget(clusterIdx).V_Lo;
distance = sqrt((abs(la) - abs(newLa)).^2 + (abs(lo) - abs(newLo)).^2 + ...
(abs(vLa) - abs(newVLa)).^2 + (abs(vLo) - abs(newVLo)).^2);
if distance < maxDistance && distance < distanceThresh %取最小值
updateClusterIdx = clusterIdx;
% distance = maxDistance;
maxDistance = distance;
end
end
if updateClusterIdx == -1 %新建聚类
clusterCurrNums = parameter.clusterPara.clusterCurrNums + 1; %TODO
parameter.clusterPara.clusterTarget(clusterCurrNums) = ...
parameter.clusterPara.newClusterTarget(newClusterIdx); %聚类赋值
parameter.clusterPara.newClusterTarget(clusterCurrNums).isMerge = 2;
else %以前存在的聚类
isMerge = parameter.clusterPara.newClusterTarget(updateClusterIdx).isMerge; %获取合并标志
if isMerge == 0 %表明聚类没有合并过
count = parameter.clusterPara.clusterTarget(updateClusterIdx).count; %获取已经存在聚类的出现次数
parameter.clusterPara.clusterTarget(updateClusterIdx) = ...
parameter.clusterPara.newClusterTarget(newClusterIdx); %聚类信息赋值
parameter.clusterPara.clusterTarget(updateClusterIdx).count = ...
count + 1; %标记出现次数增加
parameter.clusterPara.newClusterTarget(updateClusterIdx).isMerge = 1; %表明新建的聚和以前存在的聚类合并
end
end
end
parameter.clusterPara.clusterCurrNums = parameter.clusterPara.newClusterCurrNums; %聚类数目更新 TODO
% 合并标志重新赋值为0
for newClusterIdx = 1:1:parameter.clusterPara.newClusterCurrNums
parameter.clusterPara.newClusterTarget(newClusterIdx).isMerge = 0;
end
end
代码解读
calClusterMsg.m文件
calClusterMsg.m文件计算聚类结果的信息
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%计算聚类相关信息
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function parameter = calClusterMsg(parameter,pointList,pointNums,clusterNums)
for clusterIdx = 1:1:clusterNums
%% 初始信息初始化
pointLa = 0; %点云纵距和
pointLo = 0; %点云横距和
pointVLa = 0; %点云纵向速度和
pointVLo = 0; %点云横向速度和
pointCount = 0; %点云总数
%% 统计当前聚类所有点云的点云数、横距、纵距、纵向速度、横向速度的和
for pointIdx = 1:1:pointNums
if pointList(pointIdx).clusterId == clusterIdx
pointCount = pointCount + 1;
pointLa = pointLa + pointList(pointIdx).la;
pointLo = pointLo + pointList(pointIdx).lo;
pointVLa = pointVLa + pointList(pointIdx).V_La;
pointVLo = pointVLo + pointList(pointIdx).V_Lo;
end
end
%% 信息赋值
parameter.clusterPara.newClusterTarget(clusterIdx).pointNums = pointCount; %点云数赋值
parameter.clusterPara.newClusterTarget(clusterIdx).count = 1; %聚类计数值每次都初始化为1
parameter.clusterPara.newClusterTarget(clusterIdx).V_La = pointVLa / pointCount; %纵向速度平均
parameter.clusterPara.newClusterTarget(clusterIdx).V_Lo = pointVLo / pointCount; %横向速度平均
parameter.clusterPara.newClusterTarget(clusterIdx).la = pointLa / pointCount; %纵距平均
parameter.clusterPara.newClusterTarget(clusterIdx).lo = pointLo / pointCount; %横距平均
%% 聚类四个角度赋值
parameter.clusterPara.newClusterTarget(clusterIdx).leftUPCornerLa = ...
parameter.clusterPara.newClusterTarget(clusterIdx).la + 1.5;
parameter.clusterPara.newClusterTarget(clusterIdx).leftUPCornerLo = ...
parameter.clusterPara.newClusterTarget(clusterIdx).lo - 1.5;
parameter.clusterPara.newClusterTarget(clusterIdx).rightUPCornerLa = ...
parameter.clusterPara.newClusterTarget(clusterIdx).la + 1.5;
parameter.clusterPara.newClusterTarget(clusterIdx).rightUPCornerLo = ...
parameter.clusterPara.newClusterTarget(clusterIdx).lo + 1.5;
parameter.clusterPara.newClusterTarget(clusterIdx).leftDownCornerLa = ...
parameter.clusterPara.newClusterTarget(clusterIdx).la - 1.5;
parameter.clusterPara.newClusterTarget(clusterIdx).leftDownCornerLo = ...
parameter.clusterPara.newClusterTarget(clusterIdx).lo - 1.5;
parameter.clusterPara.newClusterTarget(clusterIdx).rightDownCornerLa = ...
parameter.clusterPara.newClusterTarget(clusterIdx).la - 1.5;
parameter.clusterPara.newClusterTarget(clusterIdx).rightDownCornerLo = ...
parameter.clusterPara.newClusterTarget(clusterIdx).lo + 1.5;
%% 赋值给newClusterCurrNums
parameter.clusterPara.newClusterCurrNums = clusterNums;
end
end
代码解读
运行结果如下:

至此,本片文章就此结束了。
