一、RANSAC 是解决什么问题的?
https://www.bilibili.com/video/BV1tM41187br/?spm_id_from=333.337.search-card.all.click&vd_source=52997da921a43b4ed3611981bbdf91a4
https://www.bilibili.com/video/BV1sj411v7Vo/?spm_id_from=333.337.search-card.all.click&vd_source=52997da921a43b4ed3611981bbdf91a4
👉在含有大量离群点(Outliers)的数据中,鲁棒地估计模型参数
典型场景
特征匹配中错误匹配很多
点云中噪声 + 伪点
拟合直线 / 平面 / 单应矩阵 / 基础矩阵
PnP 前的 2D–3D 对应点中有假点
普通最小二乘(LS)的问题:
对离群点极其敏感
一个坏点就能“拉歪”整个模型
RANSAC 的思想是:
宁可只用一小撮“干净点”,也不被脏点污染
宁可只用一小撮“干净点”,也不被脏点污染
宁可只用一小撮“干净点”,也不被脏点污染
二、RANSAC核心思想
随机抽最小样本 → 拟合模型 → 统计内点 → 重复 → 选内点最多的模型
利用随机采样和一致性检验的方法,来取分数据中的内点和外点,内点是指符合模型的数据,外点是指不符合模型的数据。
三、RANSAC 算法流程(标准版)
设模型参数为 θ
Step 1:随机采样(Minimal Sample Set)
随机选最小数量的点
例如:
直线:2 点
平面:3 点
单应矩阵:4 对点
基础矩阵:8 点(8-point)
分别计算所有点到当前模型的距离
Step 2:模型估计
用最小样本估计模型
通常是解析解 / DLT
Step 3:一致性检测(Inlier Test)
对所有点计算误差:
Step 4:统计内点数
记录内点数量
Step 5:重复 N 次
找到内点最多的模型
Step 6(可选):模型重估
用所有内点
再做一次最小二乘 / LM
📌RANSAC + LM = 工程黄金搭档
四、RANSAC 数学本质
1️⃣ 目标不是最小误差,而是:
2️⃣ 内点概率驱动迭代次数
若:
内点比例:w
最小样本数:s
成功概率:p
所需迭代次数:
案例一RANSAC 拟合二维直线(最经典)
1️⃣ 构造数据(含离群点)
clc; clear; close all; % 真实直线 y = 2x + 1 x_in = linspace(0,10,50); y_in = 2*x_in + 1 + randn(size(x_in))*0.3; % 离群点 x_out = rand(1,20)*10; y_out = rand(1,20)*20; x = [x_in x_out]; y = [y_in y_out]; figure; hold on; grid on; scatter(x, y, 'b'); title('原始数据(含离群点)');2️⃣ RANSAC 实现
clc; clear; close all; % 真实直线 y = 2x + 1 50个直线上的点 x_in = linspace(0,10,50); y_in = 2*x_in + 1 + randn(size(x_in))*0.3; % 离群点 20个离群点 x_out = rand(1,20)*10; y_out = rand(1,20)*20; % 直线点+离群点=70 个 x = [x_in x_out]; y = [y_in y_out]; figure; hold on; grid on; scatter(x, y, 'b'); title('原始数据(含离群点)'); % 迭代次数 num_iter = 1000; % 点到直线的距离阈值判断 threshold = 0.5; % 把最好的内点集合起来 best_inliers = []; for i = 1:num_iter % 随机选2个点 idx = randperm(length(x), 2); x1 = x(idx(1)); y1 = y(idx(1)); x2 = x(idx(2)); y2 = y(idx(2)); % 拟合直线 ax + by + c = 0 a = y2 - y1; b = x1 - x2; c = x2*y1 - x1*y2; % 计算所有(这里是70个点)点到直线距离 dist = abs(a*x + b*y + c) / sqrt(a^2 + b^2); inliers = find(dist < threshold); if length(inliers) > length(best_inliers) best_inliers = inliers; end end x_in = x(best_inliers); y_in = y(best_inliers); % 拟合 p = polyfit(x_in, y_in, 1); x_fit = linspace(0,10,100); y_fit = polyval(p, x_fit); figure; hold on; grid on; scatter(x, y, 'b'); scatter(x_in, y_in, 'r'); plot(x_fit, y_fit, 'k', 'LineWidth',2); legend('All points','Inliers','RANSAC Line'); title('RANSAC 直线拟合结果');案例二 RANSAC 拟合圆
原理:
1️⃣ 圆的一般方程
2️⃣ 最小样本数(Minimal Sample Set)
👉3 个不共线点
(这是圆的解析最小解)
3️⃣ 点到圆的误差(RANSAC 判据)
常用误差:
实例
1:构造测试数据(含离群点)
clc; clear; close all; % 真实圆参数 a0 = 5; b0 = 4; r0 = 3; theta = linspace(0,2*pi,80); x_in = a0 + r0*cos(theta) + randn(size(theta))*0.05; y_in = b0 + r0*sin(theta) + randn(size(theta))*0.05; % 离群点 x_out = rand(1,30)*10; y_out = rand(1,30)*10; x = [x_in x_out]; y = [y_in y_out]; figure; hold on; axis equal; grid on; scatter(x, y, 'b'); title('原始数据(含离群点)');2、RANSAC 拟合圆核心代码
num_iter = 2000; threshold = 0.1; best_inliers = []; best_circle = []; N = length(x); for k = 1:num_iter % 随机选 3 个点 idx = randperm(N,3); x1 = x(idx(1)); y1 = y(idx(1)); x2 = x(idx(2)); y2 = y(idx(2)); x3 = x(idx(3)); y3 = y(idx(3)); % 判断是否共线(行列式) if abs(det([x1 y1 1; x2 y2 1; x3 y3 1])) < 1e-3 continue; end % ===== 解析求圆心和半径 ===== A = 2*[x2-x1, y2-y1; x3-x1, y3-y1]; B = [x2^2+y2^2 - x1^2-y1^2; x3^2+y3^2 - x1^2-y1^2]; C = A\B; a = C(1); b = C(2); r = sqrt((x1-a)^2 + (y1-b)^2); % ===== 计算内点 ===== dist = abs(sqrt((x-a).^2 + (y-b).^2) - r); inliers = find(dist < threshold); % 更新最优模型 if length(inliers) > length(best_inliers) best_inliers = inliers; best_circle = [a b r]; end end3、用内点进行最小二乘精修(强烈推荐)
xin = x(best_inliers); yin = y(best_inliers); % 代数最小二乘(线性) A = [2*xin(:), 2*yin(:), ones(length(xin),1)]; B = xin(:).^2 + yin(:).^2; param = A\B; a = param(1); b = param(2); r = sqrt(param(3) + a^2 + b^2);4、可视化
theta = linspace(0,2*pi,200); xc = a + r*cos(theta); yc = b + r*sin(theta); figure; hold on; axis equal; grid on; scatter(x, y, 'b'); scatter(x(best_inliers), y(best_inliers), 'r'); plot(xc, yc, 'k', 'LineWidth',2); legend('All points','Inliers','RANSAC Circle'); title('RANSAC 圆拟合结果');案例三、RANSAC 拟合平面
典型应用:
点云地面分割(无人车)
工件基准面
结构光参考平面
ICP / PnP / BA 前的几何约束
平面数学模型
1️⃣ 平面一般式
2️⃣ 最小样本数(Minimal Sample Set)
👉3 个不共线点
3️⃣ 点到平面距离(RANSAC 判据)
实例:
构造测试点云(含离群点)
clc; clear; close all; % 真实平面:z = 0.5x + 0.2y + 1 [xg, yg] = meshgrid(linspace(0,5,20)); zg = 0.5*xg + 0.2*yg + 1 + randn(size(xg))*0.05; % 转为点集 pts_in = [xg(:), yg(:), zg(:)]; % 离群点 pts_out = rand(80,3)*6; pts = [pts_in; pts_out];RANSAC 主循环
num_iter = 2000; threshold = 0.1; best_inliers = []; best_plane = []; N = size(pts,1); for k = 1:num_iter % 随机选3点 idx = randperm(N,3); p1 = pts(idx(1),:); p2 = pts(idx(2),:); p3 = pts(idx(3),:); % 共线判断 v1 = p2 - p1; v2 = p3 - p1; n = cross(v1,v2); if norm(n) < 1e-6 continue; end % 平面参数 n = n / norm(n); d = -dot(n,p1); % 点到平面距离 dist = abs(pts*n' + d); inliers = find(dist < threshold); if length(inliers) > length(best_inliers) best_inliers = inliers; best_plane = [n d]; end end用内点进行最小二乘精修(SVD)
in_pts = pts(best_inliers,:); % 去中心 centroid = mean(in_pts,1); Q = in_pts - centroid; % SVD [~,~,V] = svd(Q,0); n = V(:,end); d = -dot(n,centroid); best_plane = [n' d];结果可视化
figure; hold on; grid on; axis equal; scatter3(pts(:,1),pts(:,2),pts(:,3),'b.'); scatter3(in_pts(:,1),in_pts(:,2),in_pts(:,3),'r.'); % 绘制平面 [xp,yp] = meshgrid(linspace(0,5,10)); zp = -(best_plane(1)*xp + best_plane(2)*yp + best_plane(4)) / best_plane(3); surf(xp,yp,zp,'FaceAlpha',0.5,'EdgeColor','none'); legend('All points','Inliers','Plane'); title('RANSAC 平面拟合');HALCON / PCL 对应参数
segment_planes_object_model_3d( ObjectModel3D, 'distance_threshold', DistanceThreshold, 'min_support', MinSupport, ObjectModel3DPlanes, PlaneInfo ) pcl::SACSegmentation<pcl::PointXYZ> seg; seg.setModelType(pcl::SACMODEL_PLANE); seg.setMethodType(pcl::SAC_RANSAC); seg.setDistanceThreshold(0.01); seg.setMaxIterations(1000); seg.setProbability(0.99);