Угол между двумя трехмерными векторами с одинаковым началом в одной плоскости - PullRequest
46 голосов
/ 04 марта 2011

Мне нужен угол поворота со знаком между двумя векторами Va и Vb, лежащими в одной трехмерной плоскости и имеющими одинаковое начало, зная, что:

  1. Плоскость, заключающая оба вектора, является произвольной и не параллельна XY или какой-либо другой из кардинальных плоскостей
  2. Vn - это плоскость нормальная
  3. Оба вектора вместе с нормалью имеют одинаковое происхождение O = {0, 0, 0}
  4. Va - это эталон для измерения вращения левой рукой при Vn

Угол должен измеряться таким образом, чтобы, если бы плоскость была плоскостью XY, Va обозначал бы ее единичный вектор оси X.

Полагаю, мне следует выполнить своего рода преобразование координатного пространства, используя Va в качестве оси X и перекрестное произведение Vb и Vn в качестве оси Y, а затем просто используя некоторый 2d-метод, такой как atan2 () или что-то в этом роде. , Есть идеи? Формулы?

Ответы [ 9 ]

57 голосов
/ 04 марта 2011

Используйте перекрестное произведение двух векторов, чтобы получить нормаль плоскости, образованной двумя векторами. Затем проверьте точечное произведение между ним и исходной плоскостью нормали, чтобы увидеть, смотрят ли они в одном направлении.

angle = acos(dotProduct(Va.normalize(), Vb.normalize()));
cross = crossProduct(Va, Vb);
if (dotProduct(Vn, cross) < 0) { // Or > 0
  angle = -angle;
}
31 голосов
/ 25 ноября 2015

Решение, которое я сейчас использую, похоже, здесь отсутствует. Предполагая, что нормаль плоскости нормализована (|Vn| == 1), угол со знаком просто:

atan2((Vb x Va) . Vn, Va . Vb)

, который возвращает угол в диапазоне [-PI, + PI] (или независимо от того, что возвращает доступная реализация atan2).

. и x являются точечным и перекрестным произведением соответственно.

Нет необходимости в явном ветвлении и вычислении длины деления / вектора. Используйте Va x Vb для правого вращения вместо левого

Объяснение того, почему это работает: пусть альфа будет прямым углом между векторами (от 0 ° до 180 °), а бета - искомым углом (от 0 ° до 360 °) с помощью beta == alpha или beta == 360° - alpha

Va . Vb == |Va| * |Vb| * cos(alpha)    (by definition) 
        == |Va| * |Vb| * cos(beta)     (cos(alpha) == cos(-alpha) == cos(360° - alpha)


Va x Vb == |Va| * |Vb| * sin(alpha) * n1  
    (by definition; n1 is a unit vector perpendicular to Va and Vb with 
     orientation matching the right-hand rule)

Therefore (again assuming Vn is normalized):
   n1 . Vn == 1 when beta < 180
   n1 . Vn == -1 when beta > 180

==>  (Va x Vb) . Vn == |Va| * |Vb| * sin(beta)

Наконец

tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)
13 голосов
/ 04 марта 2011

Вы можете сделать это в два этапа:

  1. Определить угол между двумя векторами

    theta = acos (скалярное произведение Va, Vb). Предполагая, Va, Vb нормализуются. Это даст минимальный угол между двумя векторами

  2. Определить знак угла

    Найти вектор V3 = перекрестное произведение Va, Vb. (порядок важен)

    Если (скалярное произведение V3, Vn) отрицательно, тета отрицательно. В противном случае тета положительна.

7 голосов
/ 04 марта 2011

Вы можете получить угол до знака, используя точечное произведение . Чтобы получить знак угла, возьмите знак Vn * (Va x Vb). В особом случае плоскости XY это сокращается до Va_x*Vb_y - Va_y*Vb_x.

2 голосов
/ 04 марта 2011

Пересеките один вектор в другой и нормализуйте, чтобы получить единичный вектор.

Синус угла между двумя векторами равен величине перекрестного произведения, деленной на величины двух векторов:

http://mathworld.wolfram.com/CrossProduct.html

1 голос
/ 16 июня 2015

Продвинутый клиент предоставил следующее решение (изначально редактирование вопроса):

SOLUTION:

sina = |Va x Vb| / ( |Va| * |Vb| )
cosa = (Va . Vb) / ( |Va| * |Vb| )

angle = atan2( sina, cosa )

sign = Vn . ( Va x Vb )
if(sign<0)
{
    angle=-angle
}
1 голос
/ 17 ноября 2012

Предположим, Vx - это ось X, учитывая нормальное Vn, вы можете получить ось Y по перекрестному произведению, вы можете проецировать вектор Vb на Vx и Vy (по точечному произведению вы можете получить длину проекции).Vb на Vx и Vy), учитывая координаты (x, y) на плоскости, вы можете использовать atan2 (y, x), чтобы получить угол в диапазоне [-pi, + pi]

1 голос
/ 04 марта 2011

Пусть тета - угол между векторами. Пусть C = Va - перекрестное произведение Vb. Тогда

sin theta = длина (C) / (длина (Va) * длина (Vb))

Чтобы определить, является ли тета положительным или отрицательным, помните, что C перпендикулярен Va и Vb, указывая в направлении, определяемом правилом правой руки . Так, в частности, C параллелен Vn. В вашем случае, если C указывает в том же направлении, что и Vn, то тэта отрицательна, так как вы хотите вращение левой рукой. Вероятно, самый простой вычислительный способ быстро проверить, указывают ли Vn и C в одном и том же направлении, это просто взять их точечное произведение; если оно положительное, они указывают в одном направлении.

Все это следует из элементарных свойств кросс-произведения .

0 голосов
/ 17 декабря 2016

Это код Matlab для вычисления угла со знаком между двумя векторами u, v в 2D или 3D.Код не требует пояснений.Соглашение о знаке таково, что между ix и iy выводится положительное + 90 ° ([1,0,0], [0,1,0]) или iy и iz ([0,1,0], [0,0,1])

function thetaDEG = angDist2Vecs(u,v)

if length(u)==3
    %3D, can use cross to resolve sign
    uMod = sqrt(sum(u.^2));
    vMod = sqrt(sum(v.^2));
    uvPr = sum(u.*v);
    costheta = min(uvPr/uMod/vMod,1);

    thetaDEG = acos(costheta)*180/pi;

    %resolve sign
    cp=(cross(u,v));
    idxM=find(abs(cp)==max(abs(cp)));
    s=sign(cp(idxM(1)));
    if s < 0
        thetaDEG = -thetaDEG;
    end
elseif length(u)==2
    %2D use atan2
    thetaDEG = (atan2(v(2),v(1))-atan2(u(2),u(1)))*180/pi;
else
    error('u,v must be 2D or 3D vectors');
end
...