如何检查矢量何时转一圈 python

How to check when a vector has made one turns in python

我实际上正在处理代表 2d 中的(大致)嘈杂圆的数据 space。我一次获取一个点的数据,目标是知道这些点何时形成一个圆圈。

为了做到这一点,我将每个连续的点都看作只是一个旋转了一点的矢量。并且要知道转一圈的时间(意味着何时形成圆圈),我检查 x 和 y 坐标何时更改那里的符号两次(意味着矢量转了 2*0.5 圈 = 1 圈)。然后我再等半圈来补偿启动误差。事实上,根据它在 space 的四分之一开始的位置,它可能没有完成一整圈。

我不需要非常精确。所以这对我来说很好,但我想知道是否有另一种方法更有效并且可以告诉真实的圈数。这可以加快一点点的过程,因为点数到达缓慢(避免我再等半圈无用)

重要的一点是我只能使用 Numpy。

EDIT :更精确,每个点之间的距离不规则。起初,圆开始缓慢形成,然后加速。因此,开始时点比结束时更密集。另一件事是 (0,0) 点甚至可能不包含在圆中。最后,我说大致是圆形的,因为它往往是椭圆形的,但形状还不错,只是嘈杂。

抱歉,我无法提供数据,至少目前无法提供。我会告诉你这周是否可以。

如果保证每个新数据点都具有比前一个数据点更大的极角,即在过程中没有任何点 "stepping-back" 的情况下逐渐形成圆,那么对于每一对连续的点,您可以计算它们之间的角度,然后当总和达到 2 pi 时就可以停止。例如:

angle = 0
points = [next(generator)]  # 'generator' produces the data points
while angle < 2*pi:
    points.append(next(generator))
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below 2 pi

这是使用上述方法的示例图:

以及示例代码:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = 0
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        radius = random.uniform(0.95, 1.05)
        yield radius*np.cos(angle), radius*np.sin(angle)
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()

angle = 0
points = [next(generator)]  # 'generator' produces the data points
while angle < 2*pi:
    points.append(next(generator))
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below 2 pi

fig, ax = plt.subplots()
ax.scatter(*np.stack(points, axis=1), s=5)
ax.set_title(f'Total angle: {angle/pi:.2f} pi')
ax.plot([0, points[ 0][0]], [0, points[ 0][1]], '--s', ms=8, label='First point', color='#2ca02c')
ax.plot([0, points[-1][0]], [0, points[-1][1]], '--*', ms=12, label='Last point', color='#ff7f0e')
ax.legend()

plt.show()

您可以使用第一个点作为偏移量,它将从所有点中减去。这会将圆的边缘移动到原点。现在想象圆的切线(在任何一点,但具体我们将使用原点),然后圆完全位于切线的一侧。切线本身跨越 180 度,如果我们从原点开始沿着圆走,总是测量连续向量之间的角度,一旦我们回到原点,我们将总共测量 180 度(如果圆上的点是无穷小间隔)。这允许计算角度的累加和,并在达到 180 度 (= pi) 时停止。现在,由于实际上这些点的间距是有限的,我们将错过圆开始和结束时 180 度的一部分(w.r.t。原点)。这意味着当我们达到 180 度时,我们将收集比闭合圆所需的点略多的点; OP 表示这是期望的行为,即圆必须闭合(重叠总比不闭合好)。

这是算法的相关代码:

angle = 0
offset = next(generator)  # 'generator' produces the data points
points = [next(generator) - offset]
while angle <= pi:
    points.append(next(generator) - offset)
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below pi

下图显示了一个示例,其中每个点的半径完全相同,只是极角不同:

这是完整的示例代码:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = pi/4
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        radius = 1
        yield np.array([3 + radius*np.cos(angle), 5 + radius*np.sin(angle)])
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()

angle = 0
offset = next(generator)  # 'generator' produces the data points
points = [next(generator) - offset]
while angle <= pi:
    points.append(next(generator) - offset)
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below pi

fig, ax = plt.subplots(figsize=(4.8, 4.8))
ax.scatter(*np.stack(points, axis=1), s=5, c=np.arange(len(points)))
ax.set_title(f'Total angle: {angle/pi:.2f} pi')
ax.plot(*points[ 0], 's', ms=8, label='First point', color='#2ca02c')
ax.plot(*points[-1], '*', ms=12, label='Last point', color='#ff7f0e')
ax.legend()

plt.show()

变化的半径

但是,如果数据点也具有不同的半径,则从原点看,该方法会受到这些变化的影响。在这种情况下,可以将连续的数据点组合在一起,然后使用平均值以获得更高的稳定性:

这是使用一组数据点以提高稳定性的代码:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = pi/4
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        radius = random.uniform(0.95, 1.05)
        yield np.array([3 + radius*np.cos(angle), 5 + radius*np.sin(angle)])
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()


def next_point(n=5):
    """n: number of points per group"""
    return sum(next(generator) for __ in range(n)) / n


angle = 0
offset = next(generator)  # 'generator' produces the data points
points = [next_point() - offset]
while angle <= pi:
    points.append(next_point() - offset)
    angle += np.arccos(
        np.dot(points[-2], points[-1]) / 
        (np.linalg.norm(points[-2]) * np.linalg.norm(points[-1]))
    )
del points[-1]  # optionally delete the last point in order to stay below pi

fig, ax = plt.subplots(figsize=(4.8, 4.8))
ax.scatter(*np.stack(points, axis=1), s=5, c=np.arange(len(points)))
ax.set_title(f'Total angle: {angle/pi:.2f} pi')
ax.plot(*points[ 0], 's', ms=8, label='First point', color='#2ca02c')
ax.plot(*points[-1], '*', ms=12, label='Last point', color='#ff7f0e')
ax.legend(loc='center')

plt.show()

您可以监控每个点到第一个点的距离,当这个距离达到最小值时,表示圆已经闭合。下面显示了沿圆的第一个点的点距离图:

这是算法的相关代码:

distances = []
points = [next_point()]
while True:  # break below
    points.append(next_point())
    distances.append(np.linalg.norm(points[-1] - points[0]))
    if len(distances) >= 3:
        left = distances[-2] - distances[-3]
        right = distances[-1] - distances[-2]
        if left < 0 and right > 0:  # minimum detected
            break
del points[-1], distances[-1]  # optionally delete the last point in order to leave the circle open

在同时改变角度差和半径的数据集上进行测试,得到以下结果:

这是完整的示例代码:

from math import pi
import random

import matplotlib.pyplot as plt
import numpy as np


def generate():
    angle = pi/4
    angle_upper_lim = 0.002
    while True:
        angle += 2*pi * random.uniform(0.001, angle_upper_lim)
        # radius = random.uniform(0.95, 1.05)
        radius = 1
        yield np.array([3 + radius*np.cos(angle), 5 + radius*np.sin(angle)])
        angle_upper_lim *= 1.03  # make the circle fill faster


generator = generate()


def next_point(n=1):
    """n: number of points per group"""
    return sum(next(generator) for __ in range(n)) / n


distances = []
points = [next_point()]
while True:  # break below
    points.append(next_point())
    distances.append(np.linalg.norm(points[-1] - points[0]))
    if len(distances) >= 3:
        left = distances[-2] - distances[-3]
        right = distances[-1] - distances[-2]
        if left < 0 and right > 0:  # minimum detected
            break
del points[-1], distances[-1]  # optionally delete the last point in order to leave the circle open

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10.8, 4.8))
plt.subplots_adjust(wspace=0.11)
ax1.set_title('Data points')
ax1.scatter(*np.stack(points, axis=1), s=5, c=np.arange(len(points)))
ax1.plot(*points[ 0], 's', ms=8, label='First point', color='#2ca02c')
ax1.plot(*points[-1], '*', ms=12, label='Last point', color='#ff7f0e')
ax1.legend(loc='center')

ax2.set(title='Distance of circle points to first point', xlabel='Point #', ylabel='Distance')
ax2.yaxis.tick_right()
ax2.yaxis.set_label_position('right')
ax2.plot(distances, '-o', ms=4)
ax2.plot(len(distances)-1, distances[-1], '*', ms=10, label='circle closed')
ax2.legend()

plt.show()

变化的半径

如果数据点的半径也发生变化,选择足够大小的 window 非常重要,它将对连续的数据点进行分组和平均,以获得更高的稳定性。例如,可以使用 n=5 来调整函数 next_point。通过取消注释上述代码中的半径变化并使用 window 大小 n=5 获得以下结果: