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");
相关推荐
寻寻觅觅☆11 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
偷吃的耗子12 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
化学在逃硬闯CS13 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar12313 小时前
C++使用format
开发语言·c++·算法
Gofarlic_OMS13 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
夏鹏今天学习了吗14 小时前
【LeetCode热题100(100/100)】数据流的中位数
算法·leetcode·职场和发展
忙什么果14 小时前
上位机、下位机、FPGA、算法放在哪层合适?
算法·fpga开发
董董灿是个攻城狮14 小时前
AI 视觉连载4:YUV 的图像表示
算法
ArturiaZ15 小时前
【day24】
c++·算法·图论
大江东去浪淘尽千古风流人物16 小时前
【SLAM】Hydra-Foundations 层次化空间感知:机器人如何像人类一样理解3D环境
深度学习·算法·3d·机器人·概率论·slam