为何Numpy SVD对完全共面的三维点集返回非标准法向量?
用NumPy SVD拟合平面的异常问题分析
测试背景
尝试用np.linalg.svd()拟合R³中完全处于Z=100平面的点集,预期结果:
- 第三个奇异值在机器精度范围内为0;
vh的第三列近似为[0, 0, 1](机器精度范围内)。
点集1测试
点集部分点的前两维度数值远大于第三维度:
import numpy as np pts = [[2345,-124, 100], [981, -123, 100], [4987,12345, 100], [-1324, 0, 100]] svd = np.linalg.svd(pts)
结果大致符合预期:
svd[1]为array([13349.56221861, 2705.21722461, 158.26983058]),第三奇异值虽未接近0,但svd[2]第三列前两维接近0,可作为平面法向量满足拟合需求。
点集2测试
点集所有点的前两维度数值均小于第三维度:
import numpy as np pts = [[57, 37, 100], [34, 37, 100], [11, -37, 100], [-11, 38, 100]] svd = np.linalg.svd(pts)
结果异常:
svd[1]为array([209.35774076, 64.78329726, 46.58820429]),第三奇异值未接近0;svd[2]的第三列与[0, 0, 1]偏差极大,更接近[1, 0, 0]。
原因分析
问题核心是未对原始点集做中心化处理,这是SVD拟合平面的关键前提:
- 拟合平面的本质是寻找包含点集的最小方差平面,需要先消除点集的整体平移分量——即计算点集均值,用每个点减去均值得到中心化后的点矩阵
centered_pts = pts - np.mean(pts, axis=0)。 - 原始点集所有点Z值均为100,均值的Z分量也是100,此时原始点矩阵的列向量包含指向
[0,0,100]方向的强分量。当XY方向的点分布范围远小于Z值(如点集2),SVD会优先捕捉这个整体偏移方向作为主分量,平面法向量对应的分量被掩盖,导致结果偏离预期。 - 点集1中XY方向数值远大于Z值,整体偏移分量相对较弱,SVD仍能捕捉到平面法向量对应的分量,结果接近预期。
修正后的验证
对两组点集做中心化后再执行SVD:
import numpy as np # 点集1中心化处理 pts1 = np.array([[2345,-124, 100], [981, -123, 100], [4987,12345, 100], [-1324, 0, 100]]) centered_pts1 = pts1 - np.mean(pts1, axis=0) svd1 = np.linalg.svd(centered_pts1) print("点集1中心化后的奇异值:", svd1[1]) print("点集1中心化后的vh第三列:", svd1[2][2]) # 点集2中心化处理 pts2 = np.array([[57, 37, 100], [34, 37, 100], [11, -37, 100], [-11, 38, 100]]) centered_pts2 = pts2 - np.mean(pts2, axis=0) svd2 = np.linalg.svd(centered_pts2) print("点集2中心化后的奇异值:", svd2[1]) print("点集2中心化后的vh第三列:", svd2[2][2])
执行结果会符合预期:
- 第三奇异值趋近于机器精度下的0;
vh的第三列近似为[0, 0, 1](或其负方向,SVD的符号不影响法向量有效性)。
内容的提问来源于stack exchange,提问作者sjohns
相关产品推荐
相关产品推荐

