简体   繁体   中英

Python - find closest point to 3D point on 3D spline

I have 2 arrays with 3D points (name, X, Y, Z). First array contains reference points, through which I'm drawing spline. Second array contains measured points, from which I need to calculate normals to spline and get the coordinates of the normal on spline (I need to calculate the XY and height standard deviations of the measured points). This is the test data (in fact, I have several thousand points):

1st array - reference points/ generate spline:

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

2nd array - measured points:

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

The code I wrote, to read the data, calculate spline (using scipy) and display it via 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, delimiter=',')

# 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, y, z, 'ro')     # ref points
ax3d.plot(xs, ys, zs, 'yo')     # spline knots
ax3d.plot(x_new, y_new, z_new, 'b--')     # spline
ax3d.plot(meas_pts[:, 2], meas_pts[:, 1], meas_pts[:, 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()

To sum up: I need array contains pairs: [[measured point X, Y, Z], [closest (normal) point on the spline X,Y,Z]]

Given a point P and a line in a 3d space, the distance from the point P and the points of the line is the diagonal of the box, so you wish to minimize this diagonal, the minimum distance will be normal to the line

在此处输入图像描述

You can use this property. So, for example

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, y, 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, y, z, P, precision=5):
    """
    Compute minimum/a distance/s between
    a point P[x0,y0,z0] and a curve (x,y,z)
    rounded at `precision`.
    
    ARGS:
        x, y, z   (array)
        P         (3dtuple)
        precision (integer)
        
    Returns min indexes and distances array.
    """
    # compute distance
    d = distance_3d(x, y, z, P[0], P[1], 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

that gives

min_idx, d = min_distance(x, y, z, P)

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')
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()

在此处输入图像描述

print("distance:", d[min_idx])

distance: [2.4721]

You can implement a similar function for your needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM