简体   繁体   English

估计两个二维点云之间的旋转

[英]Estimate the rotation between two 2D point clouds

I have two 2D point clouds with an equal number of elements.我有两个元素数量相同的二维点云。 For these elements I know their correspondence, ie for each point in PC1 I know the corresponding element in PC2 and vice versa.对于这些元素,我知道它们的对应关系,即对于 PC1 中的每个点,我知道 PC2 中的相应元素,反之亦然。

I would now like to estimate the rotation between these two point clouds.我现在想估计这两个点云之间的旋转。 That is, I would like to find the angle alpha by which I must rotate all points in PC1 around the origin such that the distance between corresponding points in PC1 and PC2 is minimized.也就是说,我想找到角度alpha ,我必须围绕原点旋转 PC1 中的所有点,以使 PC1 和 PC2 中对应点之间的距离最小化。

I can solve this using scipy's linear optimizer (see below);我可以使用 scipy 的线性优化器来解决这个问题(见下文); however, this optimization sits inside a loop along the critical path of my code and is the current bottleneck.然而,这种优化位于我代码关键路径的循环内,是当前的瓶颈。

import numpy as np
from scipy.optimize import minimize_scalar
from math import sin, cos

# generate some data for demonstration purposes
# points in each point cloud are ordered by correspondence
num_points = 10

distance = np.random.rand(num_points) * 3
radii = np.random.rand(num_points) * 2*np.pi
pc1 = distance[:, None] * np.stack([np.cos(radii), np.sin(radii)], axis=1)

distance = np.random.rand(num_points) * 3
radii = np.random.rand(num_points) * 2*np.pi
pc2 = distance[:, None] * np.stack([np.cos(radii), np.sin(radii)], axis=1)

# solve using scipy
def score(alpha):
    rot_matrix = np.array([
        [cos(alpha), -sin(alpha)],
        [sin(alpha), cos(alpha)]
    ])
    pc1_rotated = (rot_matrix @ pc1.T).T

    sum_of_squares = np.sum((pc2 - pc1_rotated)**2, axis=1)
    mse = np.mean(sum_of_squares)

    return mse

# simple solution via scipy
result = minimize_scalar(
            score,
            bounds=(0, 2*np.pi),
            method="bounded",
            options={"maxiter": 1000},
        )

if result.success:
    print(f"Best angle: {result.x}")
else:
    raise RuntimeError(f"IK failed. Reason: {result.message}")

Is there a faster (potentially analytic) solution to this problem?这个问题有更快的(潜在的分析)解决方案吗?

Since minimize_scalar only uses derivative-free methods, the optimization runtime depends heavily on the time needed to evaluate your objective function score .由于minimize_scalar仅使用无导数方法,因此优化运行时在很大程度上取决于评估目标函数score所需的时间。 Consequently, I'd recommend accelerating this function as much as possible.因此,我建议尽可能加速此功能。

Let's time your function and the optimizer as benchmark reference:让我们为您的函数和优化器计时作为基准参考:

In [68]: %timeit score(0.5)
20.2 µs ± 203 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [69]: %timeit result = minimize_scalar(score,bounds=(0, 2*np.pi),method="bounded",options={"maxiter": 1000})
415 µs ± 7.27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  • Firstly, note that (rot_matrix @ pc1.T).T is the same as pc1 @ rot_matrix.T , ie we only need to transpose one matrix instead of two.首先,请注意(rot_matrix @ pc1.T).Tpc1 @ rot_matrix.T相同,即我们只需要转置一个矩阵而不是两个。

  • Next, note that -sin(alpha) = cos(alpha + 5*pi/2) and sin(alpha) = cos(alpha + 3*pi/2) .接下来,请注意-sin(alpha) = cos(alpha + 5*pi/2)sin(alpha) = cos(alpha + 3*pi/2) This means that we only need one function call of np.cos to create the rot_matrix instead of four calls of math.sin or math.cos .这意味着我们只需要调用np.cos一个函数来创建rot_matrix而不是math.sinmath.cos的四个调用。

  • Lastly, you can compute the mse more efficiently by np.einsum .最后,您可以通过np.einsum更有效地计算mse

Considering all points, the function can look like this:考虑到所有要点,该函数可能如下所示:

k1 = 5*np.pi/2
k2 = 3*np.pi/2

def score2(alpha):
    rot_matrixT = np.cos((alpha, alpha+k2, alpha + k1, alpha)).reshape(2,2)
    pc1_rotated = pc1 @ rot_matrixT
    diff = pc2 - pc1_rotated
    return np.einsum('ij,ij->', diff, diff) / num_points

Timing the function again yields再次计时函数产生

In [70]: %timeit score(0.5)
9.26 µs ± 84.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

and therefore, the optimizer is much faster:因此,优化器要快得多:

In [71]: %timeit result = minimize_scalar(score, bounds=(0, 2*np.pi), method="bounded", options={"maxiter": 1000})
279 µs ± 1.79 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

If that still is not fast enough, you can just-in-time compile your function by Numba :如果仍然不够快,您可以通过Numba即时编译您的函数:

In [60]: from numba import njit

In [61]: @njit
    ...: def score3(alpha):
    ...:     rot_matrix = np.array([
    ...:         [cos(alpha), -sin(alpha)],
    ...:         [sin(alpha), cos(alpha)]
    ...:     ])
    ...:     pc1_rotated = (rot_matrix @ pc1.T).T
    ...:     sum_of_squares = np.sum((pc2 - pc1_rotated)**2, axis=1)
    ...:     mse = np.mean(sum_of_squares)
    ...:     return mse
In [62]: %timeit score3(0.5)
2.97 µs ± 47.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

or rewrite it using Cython .或使用Cython重写它。 Just for the sake of completeness, here's a fast Cython implementation:为了完整起见,这里有一个快速的 Cython 实现:

In [45]: %%cython -c=-O3 -c=-march=native -c=-Wno-deprecated-declarations -c=-Wno-#warnings
    ...:
    ...: from libc.math cimport cos, sin
    ...: cimport numpy as np
    ...: import numpy as np
    ...: from cython cimport wraparound, boundscheck
    ...:
    ...: @wraparound(False)
    ...: @boundscheck(False)
    ...: cpdef double score4(double alpha, double[:, ::1] pc1, double[:, ::1] pc2):
    ...:     cdef int i
    ...:     cdef int N = pc1.shape[0]
    ...:     cdef double diff1 = 0.0
    ...:     cdef double diff2 = 0.0
    ...:     cdef double   mse = 0.0
    ...:     cdef double  rmT00 = cos(alpha)
    ...:     cdef double  rmT01 = sin(alpha)
    ...:     cdef double  rmT10 = -rmT01
    ...:     cdef double  rmT11 = rmT00
    ...:
    ...:     for i in range(N):
    ...:         diff1 = pc2[i,0] - (pc1[i,0]*rmT00 + pc1[i,1]*rmT10)
    ...:         diff2 = pc2[i,1] - (pc1[i,0]*rmT01 + pc1[i,1]*rmT11)
    ...:         mse  += diff1*diff1 + diff2*diff2
    ...:     return mse / N

which yields这产生

In [48]: %timeit score4(0.5, pc1, pc2)
1.05 µs ± 15.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Last but not least, you can write down the first-order necessary condition of your problem and check whether it can be solved analytically.最后但并非最不重要的一点是,您可以写下问题的一阶必要条件并检查它是否可以解析解决。 Otherwise, you can try to solve the resulting nonlinear equation numerically.否则,您可以尝试数值求解所产生的非线性方程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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