Python - 在 3D 样条上找到最接近 3D 点的点

问题描述

我有 2 个带有 3D 点的数组(名称、X、Y、Z)。第一个数组包含参考点,我通过它绘制样条。第二个数组包含测量点,从中我需要计算样条的法线并获得样条上法线的坐标(我需要计算测量点的 XY 和高度标准偏差)。这是测试数据(其实我有几千分):

一个数组 - 参考点/生成样条:

r1,1.5602,6.0310,4.8289
r2,1.6453,5.8504,4.8428
r3,1.7172,5.6732,4.8428
r4,1.8018,5.5296,4.8474
r5,1.8700,5.3597,4.8414

第二个数组 - 测量点:

m1,1.8592,5.4707,4.8212
m2,1.7642,5.6362,4.8441
m3,1.6842,5.7920,4.8424
m4,1.6048,5.9707,4.8465

我写的代码,读取数据,计算样条(使用scipy)并通过matplotlib显示

import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate

# import measured points
filename = "measpts.csv"
meas_pts = np.genfromtxt(filename,delimiter=',')

# import reference points
filename = "refpts.csv"
ref = np.genfromtxt(filename,')

# divide data to X,Y,Z
x = ref[:,2]
y = ref[:,1]
z = ref[:,3]

# spline interpolation
tck,u = interpolate.splprep([x,y,z],s=0)
u_new = np.linspace(u.min(),u.max(),1000000)
x_new,y_new,z_new = interpolate.splev(u_new,tck,der=0)

xs = tck[1][0]
ys = tck[1][1]
zs = tck[1][2]

# PLOT 3D
fig = plt.figure()
ax3d = fig.add_subplot(111,projection='3d',proj_type='ortho')
ax3d.plot(x,z,'ro')     # ref points
ax3d.plot(xs,ys,zs,'yo')     # spline knots
ax3d.plot(x_new,z_new,'b--')     # spline
ax3d.plot(meas_pts[:,2],meas_pts[:,1],3],'g*')     # measured points

# ax3d.view_init(90,-90)     # 2D TOP view
# ax3d.view_init(0,-90)     # 2D from SOUTH to norTH view
# ax3d.view_init(0,0)     # 2D from EAST to WEST view

plt.show()

总结:我需要数组包含对:[[测量点X,Y,Z],[样条X,Y,Z上最近(法线)点]]

解决方法

给定 3d 空间中的一个点 P 和一条线,点 P 到线的点的距离是盒子的对角线,所以你希望最小化这条对角线,最小距离将垂直于线

enter image description here

您可以使用此属性。所以,例如

tablefunc()

enter image description here

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# generate sample line
x = np.linspace(-2,2,100)
y = np.cbrt( np.exp(2*x) -1 )
z = (y + 1) * (y - 2)
# a point
P = (-1,3,2)
# 3d plot
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d',proj_type='ortho')
ax.plot(x,y,z)
ax.plot(P[0],P[1],P[2],'or')
plt.show()

给予

def distance_3d(x,z,x0,y0,z0):
    """
    3d distance from a point and a line
    """
    dx = x - x0
    dy = y - y0
    dz = z - z0
    d = np.sqrt(dx**2 + dy**2 + dz**2)
    return d

def min_distance(x,P,precision=5):
    """
    Compute minimum/a distance/s between
    a point P[x0,z0] and a curve (x,z)
    rounded at `precision`.
    
    ARGS:
        x,z   (array)
        P         (3dtuple)
        precision (integer)
        
    Returns min indexes and distances array.
    """
    # compute distance
    d = distance_3d(x,P[0],P[2])
    d = np.round(d,precision)
    # find the minima
    glob_min_idxs = np.argwhere(d==np.min(d)).ravel()
    return glob_min_idxs,d

enter image description here

min_idx,d = min_distance(x,P)

fig = plt.figure()
ax = fig.add_subplot(111,'or')
ax.plot(x[min_idx],y[min_idx],z[min_idx],'ok')
for idx in min_idx:
    ax.plot(
        [P[0],x[idx]],[P[1],y[idx]],[P[2],z[idx]],'k--'
    )
plt.show()

您可以根据需要实现类似的功能。