Вычисление синуса, косинуса и угла между 3 точками

Я написал функцию для вычисления косинуса, синуса и градусов угла между тремя точками, у которых у меня есть координаты x и y - точка 1 (x1, y1), точка 2 (x2, y2) и точка 3 (x3 , у3). Я написал функцию и пытался ее протестировать, но я не совсем уверен в ее точности. Кто-нибудь знает, не ошибся ли я в своих расчетах?

def path_angle_degree(x1, y1, x2, y2, x3, y3):
    u = (x2 - x1, y2 - y1)
    v = (x3 - x2, y3 - y2)
    norm_u = math.sqrt(u[0] * u[0] + u[1] * u[1])
    norm_v = math.sqrt(v[0] * v[0] + v[1] * v[1])

    # this conditional is to check there has been movement between the points
    if norm_u < 0.001 or norm_v < 0.001:
        return (None, None, None)
    prod_n = norm_u * norm_v
    dot_uv = u[0] * v[0] + u[1] * v[1]
    cos_uv = dot_uv / prod_n

    # fixes floating point rounding
    if cos_uv > 1.0 or cos_uv < -1.0:
        cos_uv = round(cos_uv)
    radians = math.acos(cos_uv)
    sin_uv = math.sin(radians)
    degree = math.degrees(radians)
    return (cos_uv, sin_uv, degree)

Примером вызова этой функции на прямом пути может быть:

print(path_angle_degree(6,6,7,6,8,6))

Большое спасибо!


person Jack Simpson    schedule 16.04.2015    source источник
comment
извините, как найти угол между тремя коллинеарными точками? в планарной геометрии угол — это фигура, образованная двумя лучами. и вы не можете пройти 2 луча из этой точки.   -  person marmeladze    schedule 16.04.2015
comment
@marmeladze, не зная, что подразумевает OP, вы можете просто построить два вектора из трех точек, предполагая, что одна точка является общей для обоих векторов - например, точки (a, b, c) могут быть использованы для построения одного вектора ( a-›b) и второй (b-›c). (или (a-›b) и (a-›c) и т. д.)   -  person jedwards    schedule 16.04.2015
comment
Я не совсем уверен в ее точности – почему бы и нет? Почему бы вам не рассчитать несколько примеров самостоятельно и не сравнить результаты?   -  person jonrsharpe    schedule 16.04.2015
comment
Вас волнует знак вашего угла? Например, хотели бы вы, чтобы path_angle_degree(6,6,7,6,8,7) и path_angle_degree(6,6,7,6,8,5) давали разные результаты? (Или, что то же самое, вы хотите иметь возможность различать поворот налево и поворот на ту же величину направо?)   -  person Mark Dickinson    schedule 16.04.2015
comment
Между прочим, вы могли бы упростить расчет нормы: norm_u = math.hypot(*u). Возможно, вы захотите округлить значения cos_uv, когда abs(cos_uv)>1-EPSILON, где EPSILON — что-то маленькое, например 5.0E-12.   -  person PM 2Ring    schedule 16.04.2015
comment
Для угла все, что вам нужно, это один вызов math.atan2: никаких квадратных корней или других триггерных вызовов не требуется. math.atan2(u[0]*v[1]-u[1]*v[0], u[0]*v[0]+u[1]*v[1]) должен это сделать.   -  person Mark Dickinson    schedule 16.04.2015
comment
Большое спасибо за ответы, ребята, поэтому я отслеживал помеченное насекомое для эксперимента, и поэтому типичный путь (снятый со скоростью 25 кадров в секунду) будет иметь несколько тысяч координат x и y, где было обнаружено насекомое. Я пытаюсь увидеть, как часто она меняла направление, потому что тогда я могу отделить экстенсивную деятельность от интенсивной. Таким образом, каждые 3 точки (или более, если я попытаюсь сгладить путем подвыборки) я вычислю изменение угла, косинуса и синуса. Надеюсь, это проясняет причину, по которой я это делаю :)   -  person Jack Simpson    schedule 16.04.2015
comment
Да, я пытался выяснить, как различать случаи, когда насекомое полностью меняет направление, как вы показали Марку, - я расстраивался, когда не мог их различить.   -  person Jack Simpson    schedule 16.04.2015
comment
@JackSimpson: вы в безопасности для изменения направления только с помощью метода acos: он даст беззнаковый угол между 0 и пи. Только если вы хотите иметь возможность отличать повороты по часовой стрелке от поворотов против часовой стрелки, вам нужно больше, чем скалярный продукт. А можно я просто скажу: математика насекомых! Прохладный!   -  person Mark Dickinson    schedule 16.04.2015


Ответы (2)


Помимо дороговизны, sin_uv нестабилен и не справляется с многочисленными пограничными случаями. Вместо этого используйте векторное произведение (вам нужна только z-компонента).

Кроме того, вы обнаружите, что проще и дешевле нормализовать u и v перед вычислением произведений.

Когда у вас есть cos_uv и sin_uv, используйте atan2, чтобы получить угол.

person Marcelo Cantos    schedule 16.04.2015
comment
В качестве уточнения, аналог перекрестного произведения в 2D обычно известен как перпендикулярное скалярное произведение или скалярное произведение. - person Sneftel; 16.04.2015
comment
Однако перекрестное произведение само по себе не дает достаточной информации: например, оно не может различить угол в 45° и 135°. - person Mark Dickinson; 16.04.2015
comment
@MarkDickinson: вместе с точечным произведением это так, но я должен был указать это. Я изменил свой ответ. - person Marcelo Cantos; 17.04.2015

Возможно, вы захотите взглянуть на функцию arctan2().

person user50619    schedule 16.04.2015
comment
Вы имели в виду math.atan2? В Python нет функции arctan2 (хотя в NumPy она есть). - person Mark Dickinson; 16.04.2015