PCL 点云匹配 NDT详解

1. NDT 是什么?

Normal Distributions Transform(NDT)是由 Biber & Straßer (2003) 提出的 用于点云配准(scan matching) 的方法。

它是 ICP 的概率版,核心思想是:

将目标点云划分为网格,每个网格估计一个高斯分布,源点云点作为观测输入该高斯分布的概率。通过最大化整体概率来求刚体变换。

2. NDT 数学原理(最重要)

2.1 Step 1:把目标点云分为体素 (Voxel Grid)

每个 voxel(体素)内的点:

2.2 Step 2:使用高斯分布建模每个 voxel

对每个 voxel,我们有:

2.3 Step 3:源点云经过变换后最大化目标点云的概率

2.4 Step 4:对刚体变换线性化(LM / Gauss-Newton)

对微扰:

3、MATLAB 完整实现

MATLAB NDT 配准代码

cpp 复制代码
function T = ndt_registration(P, Q, voxel_size, max_iter)
    % P : target point cloud Nx3
    % Q : source point cloud Nx3

    % 1. Voxelization of target cloud
    [voxels, mu, SigmaInv] = ndt_build_model(P, voxel_size);

    T = eye(4);  % initial pose
    
    for iter = 1:max_iter
        R = T(1:3,1:3); 
        t = T(1:3,4);
        
        J = zeros(6,6);
        b = zeros(6,1);

        for i = 1:size(Q,1)
            q = R*Q(i,:)' + t;     % transformed source point
            
            % find voxel ID
            vid = ndt_find_voxel(q, voxel_size);
            if ~isKey(voxels, vid), continue; end
            
            mu_i = mu(vid,:);
            S   = SigmaInv{vid};

            d = q - mu_i';          % residual
            
            e = d' * S * d;         % error
            
            % Jacobian wrt ξ (Lie algebra)
            % dq/dξ = [I, -R*[q]_x]
            Jq = [eye(3), -R*skew(Q(i,:)')];

            Ji = 2 * Jq' * S * d;

            J = J + Ji * Ji';
            b = b + Ji * e;
        end
        
        delta = -J \ b;

        % update pose
        T = expmap_se3(delta) * T;

        if norm(delta) < 1e-5, break; end
    end
end

构建 NDT 体素模型

cpp 复制代码
function [voxels, mu, SigmaInv] = ndt_build_model(P, voxel_size)
    voxels = containers.Map();
    mu = containers.Map();
    SigmaInv = containers.Map();

    % Assign points to voxels
    ids = floor(P / voxel_size);

    for i = 1:size(P,1)
        key = sprintf('%d_%d_%d', ids(i,1), ids(i,2), ids(i,3));
        if ~isKey(voxels, key)
            voxels(key) = [];
        end
        voxels(key) = [voxels(key); P(i,:)];
    end

    % Compute Gaussian per voxel
    keys = voxels.keys;
    for i = 1:length(keys)
        k = keys{i};
        pts = voxels(k);

        m = mean(pts,1);
        C = cov(pts) + 1e-3 * eye(3);
        
        mu(k) = m;
        SigmaInv{k} = inv(C);
    end
end

SE(3) 李代数工具

cpp 复制代码
function S = skew(v)
    S = [   0   -v(3)  v(2)
          v(3)    0   -v(1)
         -v(2)  v(1)    0 ];
end

function T = expmap_se3(xi)
    w = xi(1:3);
    v = xi(4:6);
    theta = norm(w);

    if theta < 1e-8
        R = eye(3);
        V = eye(3);
    else
        wn = w / theta;
        wx = skew(wn);        
        R = eye(3) + sin(theta)*wx + (1-cos(theta))*(wx*wx);
        V = eye(3) + (1-cos(theta))*wx + (theta-sin(theta))*(wx*wx);
    end

    T = eye(4);
    T(1:3,1:3) = R;
    T(1:3,4) = V * v;
end

可视化示例

cpp 复制代码
P = pcread("bun000.ply").Location;
Q = pcread("bun000.ply").Location; % rotated version

T = ndt_registration(P, Q, 0.05, 30);

Q_reg = (T(1:3,1:3)*Q' + T(1:3,4))';

pcshow(P,'b.'); hold on;
pcshow(Q_reg,'r.');
legend("Target","Registered");
相关推荐
徐小夕3 小时前
pxcharts Ultra V2.3更新:多维表一键导出 PDF,渲染兼容性拉满!
vue.js·算法·github
CoovallyAIHub4 小时前
OpenClaw一脚踩碎传统CV?机器终于不再只是看世界
深度学习·算法·计算机视觉
CoovallyAIHub5 小时前
仅凭单目相机实现3D锥桶定位?UNet-RKNet破解自动驾驶锥桶检测难题
深度学习·算法·计算机视觉
zone77395 小时前
002:RAG 入门-LangChain 读取文本
后端·算法·面试
得物技术6 小时前
得物社区搜推公式融合调参框架-加乘树3.0实战
算法
会员源码网1 天前
使用`mysql_*`废弃函数(PHP7+完全移除,导致代码无法运行)
后端·算法
木心月转码ing1 天前
Hot100-Day10-T438T438找到字符串中所有字母异位词
算法
HelloReader1 天前
Wi-Fi CSI 感知技术用无线信号“看见“室内的人
算法
颜酱1 天前
二叉树分解问题思路解题模式
javascript·后端·算法
qianpeng8971 天前
水声匹配场定位原理及实验
算法