如何找到平面上点投影的坐标
问题描述
希望一切顺利.我有两个 numpy 数组,都是空间中的一些点.使用python,我想首先找到通过第一个数据集(surface_maker
)的表面,然后找到第二个数组的投影相邻点的x,y和z(contact_maker代码>)在创建的表面上.
surface_maker
总是创建平面表面.对于投影,我只想要从相邻点到表面的垂直方向.实际上,我在这两组中都有很多要点,但我在这里复制一个简单的案例:
surface_maker=np.array([[50., 15., 46.04750574],[50., 5., 45.56400925],[44.83018398, 5., 25.],[44.76296902, 15., 25.],[50., 25., 45.56400925],[44.83018398, 25., 25.],[59.8336792, 5., 75.],[59.71483707, 15., 75.],[59.8336792, 25., 75.]])contact_maker=np.array([[10., 5., 70.00014782],[10., 15., 70.00018358],[10., 25., 70.0001955],[30., 5., 69.99981105],[30., 15., 69.99982297],[30., 25., 69.99985874],[70., 5., 50.00000298],[70., 15., 50.00002682],[70., 25., 50.00005066],[90., 5., 49.99996871],[90., 15., 49.99999255],[90., 25., 50.00001788]])
我尝试了几种解决方案,例如
在此先感谢您的帮助.
解决方案我知道你需要解决两个问题:
- 找到适合点集合的平面
- 将第二组点沿特定方向投影到该平面上
第二个问题已在另一个答案中得到充分解决,因此我正在为第一个问题提供更通用的方法.
确实,当您确定所有点都位于一个平面上时,您可以只选择三个未对齐的点并计算平面.但是您的分数可能来自带有一些噪音的真实测量结果,您可能希望找到最适合您的分数的平面.
以下函数解决了寻找最适合点集合的平面的一般问题.请参阅评论中的解释:
将 numpy 导入为 npPRECISION = 1e-8 # 用于现实世界的任意零def plane_from_points(点):# 调整后的平面穿过点集合的质心质心= np.mean(点,轴= 0)# 使用SVD计算点集合的主轴#(特征向量)及其相对大小(特征值)_,值,向量 = np.linalg.svd(点 - 质心)# 每个奇异值都与它的向量配对,并从# 从最大值到最小值.# 调整后的平面必须包含对应的特征向量# 两个最大的特征值.如果只有一个特征向量不同# 从零开始,然后点对齐并且它们不定义平面.如果值[1] <精确:raise ValueError("点对齐,不能定义平面")# 所以平面法线是特征值最小的特征向量正常 = 向量[2]# 计算平面方程ax+by+cz+d=0的系数(a,b,c,d).# 前三个系数由法线给出,第四个# 一 (d) 是平面到坐标原点的有符号距离d = -np.dot(质心,正常)平面 = np.append(正常,d)# 如果最小特征向量接近于零,则集合# 点完全平坦.特征向量越大,越不平坦.# 你可能想知道这一点.厚度 = 值[2]返回平面,厚度
你可以检查一下:
>>>surface_maker=np.array([[50., 15., 46.04750574], [50., 5., 45.56400925], [44.83018398, 5., 25.], [44.76296902, 15., 25.], [50., 25., 45.56400925], [44.83018398, 25., 25.], [59.8336792, 5., 75.], [59.71483707, 15., 75.], [59.8336792, 25., 75.]])>>>平面,厚度 = plane_from_points(surface_maker)>>>打印(平面)[-0.95725318 0. 0.28925136 35.2806339]>>>打印(厚度)1.3825669490602308
所以,事实上,你的点分布并不平坦(厚度明显不同于零),你不能随便选择三个点来解决你的问题.
Hope doing well. I have two numpy array, both are some points in the space. Using python, I want to firstly find the surface passing the first data set (surface_maker
) and then find the x,y and z of the projection adjacent opoints of the second array (contact_maker
) on the created surface. surface_maker
always created planar surfaces. For projection, I only want a vertical going from adjacent point toward the surface. In reality I have lots of points in both sets but I copies a simple case here:
surface_maker=np.array([[50., 15., 46.04750574],
[50., 5., 45.56400925],
[44.83018398, 5., 25.],
[44.76296902, 15., 25.],
[50., 25., 45.56400925],
[44.83018398, 25., 25.],
[59.8336792, 5., 75.],
[59.71483707, 15., 75.],
[59.8336792, 25., 75.]])
contact_maker=np.array([[10., 5., 70.00014782],
[10., 15., 70.00018358],
[10., 25., 70.0001955 ],
[30., 5., 69.99981105],
[30., 15., 69.99982297],
[30., 25., 69.99985874],
[70., 5., 50.00000298],
[70., 15., 50.00002682],
[70., 25., 50.00005066],
[90., 5., 49.99996871],
[90., 15., 49.99999255],
[90., 25., 50.00001788]])
I have tried several solutions like 1, 2 and so on. But I was successful to solve my issue.
For me it is important to have the location of projection as x, y and z. The figure also shows what I want (as it shows, I need only location six adjacent point of the contact_maker
projected on the surface created by surface_maker
):
In advance, I truely appreciate any help.
解决方案I understand you need to solve two problems:
- Find the plane that fits a collection of points
- Project a second collection of points onto that plane along a specific direction
The second problem has been fully addressed in another answer, so I'm contributing a more generic approach to the first problem.
It's true that when you positively know that all your points lie on a plane, you may just select three non-aligned ones and calculate the plane. But your points may come from real measurements with some noise, and you may wish to find the plane that best fists your points.
The following function solves the general problem of finding the plane that best fits a collection of points. See the explanations in the comments:
import numpy as np
PRECISION = 1e-8 # Arbitrary zero for real-world purposes
def plane_from_points(points):
# The adjusted plane crosses the centroid of the point collection
centroid = np.mean(points, axis=0)
# Use SVD to calculate the principal axes of the point collection
# (eigenvectors) and their relative size (eigenvalues)
_, values, vectors = np.linalg.svd(points - centroid)
# Each singular value is paired with its vector and they are sorted from
# largest to smallest value.
# The adjusted plane plane must contain the eigenvectors corresponding to
# the two largest eigenvalues. If only one eigenvector is different
# from zero, then points are aligned and they don't define a plane.
if values[1] < PRECISION:
raise ValueError("Points are aligned, can't define a plane")
# So the plane normal is the eigenvector with the smallest eigenvalue
normal = vectors[2]
# Calculate the coefficients (a,b,c,d) of the plane's equation ax+by+cz+d=0.
# The first three coefficients are given by the normal, and the fourth
# one (d) is the plane's signed distance to the origin of coordinates
d = -np.dot(centroid, normal)
plane = np.append(normal, d)
# If the smallest eigenvector is close to zero, the collection of
# points is perfectly flat. The larger the eigenvector, the less flat.
# You may wish to know this.
thickness = values[2]
return plane, thickness
You can check this:
>>> surface_maker=np.array([[50., 15., 46.04750574], [50., 5., 45.56400925], [44.83018398, 5., 25.], [44.76296902, 15., 25.], [50., 25., 45.56400925], [44.83018398, 25., 25.], [59.8336792, 5., 75.], [59.71483707, 15., 75.], [59.8336792, 25., 75.]])
>>> plane, thickness = plane_from_points(surface_maker)
>>> print(plane)
[-0.95725318 0. 0.28925136 35.2806339 ]
>>> print(thickness)
1.3825669490602308
So, in fact, your point distribution is not flat (thickness clearly different from zero), and you can't just select three arbitrary points to solve your problem.
相关文章