本次我们分享使用Matlab进行点云的随机下采样。点云随机下采样是一种通过随机选取部分点来减少点云数据量的方法,旨在降低计算复杂度、提高处理效率,同时保留原始点云的整体结构特征。在 MATLAB 中,随机下采样可通过内置函数或自定义代码实现,适用于无需保留局部几何特征的快速预处理场景。
一、处理流程
以下是使用 MATLAB 进行点云随机下采样的标准流程:
读取点云数据
ptCloud = pcread('example.ply'); % 支持 .ply、.pcd 等格式
设置采样比例(如保留 20% 的点)
percentage = 0.2;
执行随机下采样
ptCloudDownsampled = pcdownsample(ptCloud, 'random', percentage);
✅ 说明:`pcdownsample` 是 MATLAB 中专门用于点云下采样的函数,支持多种采样方式,包括 `'random'`、`'gridAverage'` 等。
可视化对比(可选)
figure;
subplot(1,2,1);
pcshow(ptCloud); title('原始点云');subplot(1,2,2);
pcshow(ptCloudDownsampled); title(['随机下采样 ', num2str(percentage*100), '%']);二、应用场景
|-----------|-----------------------------------|
| 场景 | 说明 |
| 快速预览与可视化 | 原始点云数据量过大,直接显示卡顿,先随机采样降低点数以便快速查看。 |
| 深度学习预处理 | 在训练点云分类/分割模型前,统一输入规模,减少计算负担。 |
| SLAM 前端处理 | 在实时定位与建图任务中,降低点云密度以加快配准与建图速度。 |
| 数据压缩与传输 | 减少点云数据量,便于存储、传输或上传至云端平台。 |
| 算法测试与原型开发 | 在开发新算法时,使用小规模点云快速验证逻辑,避免处理完整大数据集。 |三、优缺点总结
|----------------|------------------------|
| 优点 | 缺点 |
| ✅ 实现简单,计算速度快 | ❌ 可能丢失关键几何特征(如边缘、角点) |
| ✅ 不依赖点云结构,适用性广 | ❌ 采样结果不稳定(每次运行结果可能不同) |
| ✅ 可控制保留比例,灵活性强 | ❌ 不适用于对细节要求高的任务(如特征提取) |四、扩展建议
若需保留几何特征,可结合 体素下采样(`pcdownsample(ptCloud, 'gridAverage', gridStep)`)或 曲率采样。
若需固定输出点数,可自定义随机索引选择逻辑:
numPoints = 2048;
indices = randperm(ptCloud.Count, numPoints);
ptCloudDown = pointCloud(ptCloud.Location(indices, :));五、结语
点云随机下采样是 MATLAB 中最简单高效的点云精简方法之一,适用于对几何精度要求不高的快速处理场景。在实际应用中,建议根据任务需求组合使用多种采样策略,以平衡效率与精度。如需进一步处理(如去噪、配准、重建),可继续结合 MATLAB 的点云工具箱进行后续分析。
本次数据,大家猜猜,除了窗帘。猜对了,兔砸!!

一、点云随机下采样程序
1、最简版
%% 0. 清空环境
clear; clc; close all;
%% 1. 读入点云
% pcdFile = 'bunny.pcd'; % 换成你的完整路径
[file,path] = uigetfile({'*.pcd;*.ply;*.xyz','点云文件 (*.pcd,*.ply,*.xyz)'},...
'请选择点云');
if file==0; return; end
fname = fullfile(path,file);
ptCloud = pcread(fname); % 返回 pointCloud 对象
N = ptCloud.Count; % 原始点数
fprintf('原始点云有 %d 个点\n', N);
%% 2. 随机下采样 50 %
idx50 = randperm(N, round(0.5*N));
cloud50 = select(ptCloud, idx50);
fprintf('随机下采样(50%%) 后 %d 个点\n', cloud50.Count);
%% 3. 随机下采样到固定 5000 点
k = 5000;
idx5k = randperm(N, k);
cloud5k = select(ptCloud, idx5k);
fprintf('随机固定 5000 点后 %d 个点\n', cloud5k.Count);
%% 4. 可视化
figure('Name','原始点云','NumberTitle','off');
pcshow(ptCloud); axis on; view(3);
figure('Name','随机下采样(50%)','NumberTitle','off');
pcshow(cloud50); axis on; view(3);
figure('Name','随机固定5000点','NumberTitle','off');
pcshow(cloud5k); axis on; view(3);
2、GUI版本
function pointCloudGUI_safe
% 2020a 兼容 / 空点云暖启动 / 标题不遮挡 / 双路保存
fig = figure('Name','点云随机下采样','NumberTitle','off',...
'MenuBar','none','ToolBar','none','Position',[100 100 1280 720]);
%% 左侧三栏 panel(标题栏自带,永不被冲)
imgWidth = 0.78; % ← 图像区总宽
panelW = imgWidth/3 - 0.01; % 每个 panel 再留 1 % 间隙
pnlOrig = uipanel('Parent',fig,'Units','normalized',...
'Position',[0.02, 0.02, panelW, 0.96],...
'FontSize',16,...
'Title','原始点云');
pnlRatio = uipanel('Parent',fig,'Units','normalized',...
'Position',[0.02+panelW+0.01, 0.02, panelW, 0.96],...
'FontSize',16,...
'Title','比例采样');
pnlFix = uipanel('Parent',fig,'Units','normalized',...
'Position',[0.02+2*(panelW+0.01), 0.02, panelW, 0.96],...
'FontSize',16,...
'Title','固定点数');
axOrig = axes('Parent',pnlOrig , 'Units','normalized','Position',[0.05 0.05 0.90 0.90]);
axRatio = axes('Parent',pnlRatio, 'Units','normalized','Position',[0.05 0.05 0.90 0.90]);
axFix = axes('Parent',pnlFix , 'Units','normalized','Position',[0.05 0.05 0.90 0.90]);
%% 右侧控制
pnlCtrl = uipanel('Parent',fig,'Units','normalized',...
'Position',[0.78 0 0.22 1],...
'Title','控制');
uicontrol('Parent',pnlCtrl,'Style','pushbutton','String','浏览...',...
'Units','normalized','Position',[0.05 0.87 0.90 0.06],...
'FontSize',16,...
'Callback',@loadCloud);
lblInfo = uicontrol('Parent',pnlCtrl,'Style','text','String','未加载点云',...
'Units','normalized','Position',[0.05 0.81 0.90 0.04],...
'FontSize',10,...
'HorizontalAlignment','left');
uicontrol('Parent',pnlCtrl,'Style','text','String','比例控制 (%)',...
'Units','normalized','Position',[0.05 0.72 0.90 0.04],...
'FontSize',12,'FontWeight','bold','HorizontalAlignment','left');
% 比例
sliderRatio = uicontrol('Parent',pnlCtrl,'Style','slider','Min',0,'Max',100,'Value',50,...
'FontSize',12,...
'Units','normalized','Position',[0.05 0.68 0.65 0.04],...
'Callback',@refreshRatio);
txtRatio = uicontrol('Parent',pnlCtrl,'Style','edit','String','50',...
'FontSize',12,...
'Units','normalized','Position',[0.75 0.68 0.20 0.04],...
'Callback',@editRatioCB);
uicontrol('Parent',pnlCtrl,'Style','text','String','个数控制',...
'Units','normalized','Position',[0.05 0.58 0.90 0.04],...
'FontSize',12,'FontWeight','bold','HorizontalAlignment','left');
% 固定点数
sliderFix = uicontrol('Parent',pnlCtrl,'Style','slider','Min',1,'Max',100,'Value',50,...
'FontSize',12,...
'Units','normalized','Position',[0.05 0.55 0.65 0.04],...
'Callback',@refreshFix);
txtFix = uicontrol('Parent',pnlCtrl,'Style','edit','String','50',...
'FontSize',12,...
'Units','normalized','Position',[0.75 0.55 0.20 0.04],...
'Callback',@editFixCB);
% 保存
uicontrol('Parent',pnlCtrl,'Style','pushbutton','String','保存比例采样',...
'Units','normalized','Position',[0.05 0.40 0.90 0.06],...
'FontSize',16,...
'Callback',@(s,e)saveCloud(ptCloudRatio));
uicontrol('Parent',pnlCtrl,'Style','pushbutton','String','保存固定点数',...
'Units','normalized','Position',[0.05 0.32 0.90 0.06],...
'FontSize',16,...
'Callback',@(s,e)saveCloud(ptCloudFix));
%% 数据
ptCloudOrig = pointCloud.empty;
ptCloudRatio = pointCloud.empty;
ptCloudFix = pointCloud.empty;
%% -------- 回调 --------
function loadCloud(~,~)
[file,path] = uigetfile({'*.pcd;*.ply;*.xyz','点云文件'},'选择点云');
if isequal(file,0), return; end
try
ptCloudOrig = pcread(fullfile(path,file));
catch ME
errordlg(ME.message,'读取失败'); return;
end
N = ptCloudOrig.Count;
set(lblInfo,'String',sprintf('已加载:%s (%d 点)',file,N));
set(sliderFix,'Max',N,'Value',min(5000,N));
set(txtFix,'String',num2str(min(5000,N)));
safePcshow(axOrig,ptCloudOrig);
refreshRatio();
refreshFix();
end
function refreshRatio(~,~)
if isempty(ptCloudOrig), return; end
ratio = get(sliderRatio,'Value')/100;
N = ptCloudOrig.Count;
k = max(1,round(ratio*N));
idx = randperm(N,k);
ptCloudRatio = select(ptCloudOrig,idx);
safePcshow(axRatio,ptCloudRatio);
set(txtRatio,'String',num2str(round(ratio*100)));
end
function editRatioCB(src,~)
v = str2double(get(src,'String'));
if isnan(v), v = 50; end
v = max(0,min(100,v));
set(sliderRatio,'Value',v);
refreshRatio();
end
function refreshFix(~,~)
if isempty(ptCloudOrig), return; end
k = round(get(sliderFix,'Value'));
N = ptCloudOrig.Count;
k = max(1,min(N,k));
idx = randperm(N,k);
ptCloudFix = select(ptCloudOrig,idx);
safePcshow(axFix,ptCloudFix);
set(txtFix,'String',num2str(k));
end
function editFixCB(src,~)
v = str2double(get(src,'String'));
if isnan(v), v = 100; end
N = ptCloudOrig.Count;
v = max(1,min(N,v));
set(sliderFix,'Value',v);
refreshFix();
end
function saveCloud(cloud)
if isempty(cloud)
errordlg('请先完成采样','提示'); return;
end
[file,path] = uiputfile({'*.pcd','PCD';'*.ply','PLY';'*.xyz','XYZ'},'保存采样点云');
if isequal(file,0), return; end
try
pcwrite(cloud,fullfile(path,file),'Precision','double');
msgbox('保存成功!','提示');
catch ME
errordlg(ME.message,'保存失败');
end
end
%% -------- 安全 pcshow(2020a 兼容) --------
function safePcshow(ax,pc)
cla(ax);
set(ax,'Color','w'); % 白背景
pcshow(pointCloud(nan(0,3)),'Parent',ax); % ① 暖启动
pcshow(pc,'Parent',ax,'MarkerSize',15); % ② 真点云
axis(ax,'tight'); grid(ax,'on'); view(ax,3);
end
end
二、点云随机下采样结果

这里只演示了GUI版本的(简单版本有手就行,没必要演示了。。。),功能有比例随机下采样和固定点数随机下采样,而且可以旋转图像观看3D效果。不过中间有点小瑕疵,显示之前需要暖启动下(就是先点一下图像,然后再处理),然后才能旋转。不过总体没有大的问题。(虽然运行显示有错误,但是不影响总体的,程序员第一准则,能跑就行)有兴趣的同学可以多试试发展下新功能。
就酱,下次见^_^