Движение вектора на плоскости
Столкнулся со следующей задачей, очень нужна помощь, уже который день ищу проблему.
Мой скрипт на Python подключается к визуалке, где мне нужно перемещать объект, пока просто на плоскости. Очевидно, что для этого я использую AB вектор. К сожалению, в линейной алгебре не силён, но вроде как нужные формулы нашёл.
Представим себе следующую простую задачу в цикле:
- Двигаем вектор вперёд на 0.1 (движение у объекта постоянное)
- Нормализуем вектор, т.к. он увеличился после движения.
- Поворачиваем его (вектор) на 1 градус.
Т.е. получаем такое вот круговое движение. На данный момент имею следующие 3 функции под движение, нормализацию и поворот:
from math import sin, cos, sqrt, pow, radians
def forward(v: list, distance: float = 0.1) -> list:
vector_direction = [v[1][0] - v[0][0], v[1][1] - v[0][1]]
cos_dir = vector_direction[0] / sqrt(pow(vector_direction[0], 2) + pow(vector_direction[1], 2))
sin_dir = vector_direction[1] / sqrt(pow(vector_direction[1], 2) + pow(vector_direction[1], 2))
new_x, new_y = (distance * cos_dir) + v[1][0], (distance * sin_dir) + v[1][1]
return [v[0], [new_x, new_y]]
def normalization(v: list) -> list:
len_vec = sqrt((v[1][0] - v[0][0]) ** 2 + (v[1][1] - v[0][1]) ** 2)
kv = (len_vec - 1) / len_vec
return [[v[0][0] + (v[1][0] - v[0][0]) * kv, v[0][1] + (v[1][1] - v[0][1]) * kv], v[1]]
def turn(v: list, direction: bool) -> list:
ONE_GRAD = -radians(1) if direction else radians(1)
new_x = -sin(ONE_GRAD) * (v[1][1] - v[0][1]) + cos(ONE_GRAD) * (v[1][0] - v[0][0]) + v[0][0]
new_y = cos(ONE_GRAD) * (v[1][1] - v[0][1]) + sin(ONE_GRAD) * (v[1][0] - v[0][0]) + v[0][1]
return [v[0], [new_x, new_y]]
Далее пробую всё это дело тестировать, например вот так:
from matplotlib import pyplot as plt
from vector_displacement import normalization, forward, turn
START = 0
vector = [[START, START], [15, 15]]
fig = plt.figure(figsize=(6, 6))
ax = plt.axes(xlim=(0, 40), ylim=(0, 40))
def draw_vectors(vector_a: list):
# plt.plot([vector_a[0][0], vector_a[1][0]], [vector_a[0][1], vector_a[1][1]])
ax.plot(vector_a[1][0], vector_a[1][1], "go", markersize=1)
for number in range(360):
draw_vectors(vector) # Отрисовка вектора на графике
vector = forward(vector) # Двигаем вперёд
draw_vectors(vector) # Отрисовка вектора на графике
vector = normalization(vector) # Нормализовали
vector = turn(vector, False) # Повернули
plt.savefig('./dir/graph{}.png'.format("result5"))
Т.е. должно получиться круговое движение. А получается по этому коду вот это:

В другую сторону и с векторами разных знаков получается примерно то же самое. Т.е. в какой то момент он перестаёт поворачиваться:
Экспериментов за это время была масса, и со специалистом по линейной алгебре я тоже консультировался, но его расчёты на первый цикл полностью совпали с моими. Думаю, может где-то глупая ошибка затесалась, которую не вижу. В районе 30 градусов он поворачивать не хочет. По сути наблюдаю в логах постоянное уменьшение угла поворота, вплоть до мизерных значений, что приводит к остановке.
Поворот без движения и нормализации - работает:

Движение с поворотом без нормализации - тоже работает:

Если нормализовать и поворачивать, но без движения - аналогично всё работает:

Буду благодарен за любую помощь и подсказку.
Ответы (1 шт):
Сдаётся мне, что вы путаете векторы положения и скорости, иначе бы не делали с одним и тем же "Двигаем вектор" и "Поворачиваем его (вектор)"
Задайте позицию - я буду писать без индексов, чтобы не путаться - с компонентами px, py и скорость vx, vy.
На каждом элементарном шаге (после единичного интервала времени) позиция меняется
px += vx
py += vy
Если интервал времени может меняться, то домножайте на него скорость
Если нужно изменить направление движение на угол Fi без изменения абсолютного значения скорости:
t = vx
vx = vx * cos(Fi) - vy * sin(Fi)
vy = t * sin(Fi) + vy * cos(Fi)
Если нужно задать абсолютное направление под углом Theta со скоростью V:
vx = V * cos(Theta)
vy = V * sin(Theta)
Если нужно изменить модуль скорости без изменения направления в K раз:
vx *= K
vy *= K
Нормализованный вектор направления
V_Mag = sqrt(vx*vx+vy*vy)
u_vx = vx / V_Mag (== cos(направления))
u_vy = vy / V_Mag (== sin(направления))
Если нужно задать скорость V без изменения направления:
V_Mag = sqrt(vx*vx+vy*vy)
vx *= V/V_Mag
vy *= V/V_Mag
Движение вперед на расстояние Dist
px += u_vx * Dist
py += u_vy * Dist
Проверочка:
from matplotlib import pyplot as plt
from math import sin, cos, sqrt, pow, radians, pi
fig = plt.figure(figsize=(6, 6))
ax = plt.axes(xlim=(0, 25), ylim=(0, 25))
def draw_vectors(px, py):
# plt.plot([vector_a[0][0], vector_a[1][0]], [vector_a[0][1], vector_a[1][1]])
ax.plot(px, py, "go", markersize=1)
px = 15
py = 15
vx = 15
vy = 15
V_Mag = sqrt(vx*vx+vy*vy)
u_vx = vx / V_Mag
u_vy = vy / V_Mag
for number in range(360):
draw_vectors(px, py) # Отрисовка вектора на графике
px += u_vx * 0.1
py += u_vy * 0.1
draw_vectors(px, py) # Отрисовка вектора на графике
t = u_vx
u_vx = u_vx * cos(pi/180) - u_vy * sin(pi/180)
u_vy = t * sin(pi/180) + u_vy * cos(pi/180)
plt.savefig('e:/graph{}.png'.format("result5"))

