【问题标题】:How can I find the difference between two angles?如何找到两个角度之间的差异?
【发布时间】:2010-12-25 03:13:54
【问题描述】:

给定 -PI -> PI 范围内的 2 个角度围绕一个坐标,它们之间的 2 个角度中最小的值是多少?

考虑到 PI 和 -PI 之间的差异不是 2 PI 而是零。

示例:

想象一个圆,有 2 条线从中心出来,这些线之间有 2 个角度,它们在内侧形成的角度也称为 较小的角度,它们在外面,也就是更大的角度。两个角加起来就是一个完整的圆。鉴于每个角度都可以适应一定的范围,考虑到翻转,较小的角度值是多少

【问题讨论】:

  • 我读了 3 遍才明白你的意思。请添加示例,或更好地解释...
  • 想象一个圆,有两条线从中心向外延伸,这些线之间有两个角度,它们在内部形成的角度也就是较小的角度,它们在外部形成的角度,也就是更大的角度。两个角加起来就是一个完整的圆。假设每个角度都可以在一定范围内拟合,那么较小的角度值是多少,考虑到翻转
  • @JimG。这不是同一个问题,在这个问题中,另一个问题中使用的角度 P1 将是不正确的答案,它将是另一个较小的角度。另外,不能保证角度与水平轴一致
  • 如果你使用Unity c#脚本,你可以使用Mathf.DeltaAngle函数。

标签: language-agnostic geometry angle


【解决方案1】:

适用于任何角度的高效 C++ 代码:弧度和度数是:

inline double getAbsoluteDiff2Angles(const double x, const double y, const double c)
{
    // c can be PI (for radians) or 180.0 (for degrees);
    return c - fabs(fmod(fabs(x - y), 2*c) - c);
}

看到它在这里工作: https://www.desmos.com/calculator/sbgxyfchjr

【讨论】:

    【解决方案2】:

    我在 C++ 中使用的一个简单方法是:

    double deltaOrientation = angle1 - angle2;
    double delta =  remainder(deltaOrientation, 2*M_PI);
    

    【讨论】:

    • 这是错误的,恐怕。考虑如果角度1 = 0 和角度2 = pi+c,对于某些c>0。正确答案应该是 -(pi-c),但你的答案是 pi+c。请记住,OP 明确要求较小的角度,并且较小的角度应始终小于或等于 pi。
    【解决方案3】:

    对于 UnityEngine 用户,简单的方法就是使用Mathf.DeltaAngle

    【讨论】:

    • 虽然没有签名输出
    【解决方案4】:

    如果你的两个角度是 x 和 y,那么它们之间的角度之一是 abs(x - y)。另一个角度是 (2 * PI) - abs(x - y)。所以 2 个角中最小的一个值为:

    min((2 * PI) - abs(x - y), abs(x - y))
    

    这会为您提供角度的绝对值,并假设输入已标准化(即:在[0, 2π) 范围内)。

    如果您想保留角度的符号(即:方向)并接受[0, 2π) 范围之外的角度,您可以概括上述内容。这是通用版本的 Python 代码:

    PI = math.pi
    TAU = 2*PI
    def smallestSignedAngleBetween(x, y):
        a = (x - y) % TAU
        b = (y - x) % TAU
        return -a if a < b else b
    

    请注意,% 运算符在所有语言中的行为并不相同,尤其是在涉及负值时,因此在移植时可能需要进行一些符号调整。

    【讨论】:

    • @bradgonesurfing 这是/是真的,但公平地说,您的测试检查了原始问题中未指定的内容,特别是非规范化输入和符号保留。编辑后答案中的第二个版本应该通过您的测试。
    • 第二个版本也不适合我。例如尝试 350 和 0。它应该返回 -10 但返回 -350
    • @kjyv 我无法重现您描述的行为。你能发布确切的代码吗?
    • 啊,对不起。我已经在 python 中再次用 rad 和 degree 准确地测试了你的版本,它工作得很好。所以在我翻译成 C# 时一定是一个错误(不再有它了)。
    • 请注意,从 Python 3 开始,您实际上可以在本地使用 tau!只需写from math import tau
    【解决方案5】:

    这给出了任何角度的符号角度:

    a = targetA - sourceA
    a = (a + 180) % 360 - 180
    

    请注意,在许多语言中,modulo 操作会返回一个与被除数符号相同的值(如 C、C++、C#、JavaScript、full list here)。这需要一个自定义的mod 函数,如下所示:

    mod = (a, n) -> a - floor(a/n) * n
    

    左右:

    mod = (a, n) -> (a % n + n) % n
    

    如果角度在 [-180, 180] 范围内,这也有效:

    a = targetA - sourceA
    a += (a>180) ? -360 : (a<-180) ? 360 : 0
    

    以更详细的方式:

    a = targetA - sourceA
    a -= 360 if a > 180
    a += 360 if a < -180
    

    【讨论】:

    • 虽然人们可能想做一个 % 360,例如如果我有角度0和目标角度721,正确答案是1,上面给出的答案是361
    • a -= 360*sgn(a)*(abs(a) &gt; 180) 是一种更简洁但可能更昂贵的方法,相当于后一种方法的第二条语句。 (想想看,如果你有 sgnabs 的无分支实现,那么这个特性实际上可能开始补偿需要两次乘法。)
    • “任何角度的有符号角度”示例似乎在大多数情况下都有效,但有一个例外。在场景double targetA = 2; double sourceA = 359; 'a' 将等于 -357.0 而不是 3.0
    • 在 C++ 中,您可以使用 std::fmod(a,360) 或 fmod(a,360) 来使用浮点取模。
    • 如果% 运算符在您的语言中表现得像余数(保留符号),您可以简单地添加一个额外的 360 而不是定义模函数:a = (a + 540) % 360 - 180 如上所述,这仅适用于彼此之间的 360 度以内的角度,这可能经常是这种情况。否则:a = ((a % 360) + 540) % 360 - 180
    【解决方案6】:

    无需计算三角函数。 C语言的简单代码是:

    #include <math.h>
    #define PIV2 M_PI+M_PI
    #define C360 360.0000000000000000000
    double difangrad(double x, double y)
    {
    double arg;
    
    arg = fmod(y-x, PIV2);
    if (arg < 0 )  arg  = arg + PIV2;
    if (arg > M_PI) arg  = arg - PIV2;
    
    return (-arg);
    }
    double difangdeg(double x, double y)
    {
    double arg;
    arg = fmod(y-x, C360);
    if (arg < 0 )  arg  = arg + C360;
    if (arg > 180) arg  = arg - C360;
    return (-arg);
    }
    

    让 dif = a - b ,以弧度为单位

    dif = difangrad(a,b);
    

    让 diff = a - b , 以度为单位

    dif = difangdeg(a,b);
    
    difangdeg(180.000000 , -180.000000) = 0.000000
    difangdeg(-180.000000 , 180.000000) = -0.000000
    difangdeg(359.000000 , 1.000000) = -2.000000
    difangdeg(1.000000 , 359.000000) = 2.000000
    

    没有罪,没有cos,没有棕褐色,......只有几何!!!

    【讨论】:

    • 错误!由于您#define PIV2 为“M_PI+M_PI”,而不是“(M_PI+M_PI)”,arg = arg - PIV2; 行扩展为 arg = arg - M_PI + M_PI,因此什么也不做。
    【解决方案7】:

    算术(相对于算法)解决方案:

    angle = Pi - abs(abs(a1 - a2) - Pi);
    

    【讨论】:

    【解决方案8】:

    x 是目标角度。 y 是源或起始角度:

    atan2(sin(x-y), cos(x-y))
    

    它返回带符号的增量角。请注意,根据您的 API,atan2() 函数的参数顺序可能会有所不同。

    【讨论】:

    • x-y 为您提供角度差异,但可能超出预期范围。想想这个角度定义了单位圆上的一个点。该点的坐标是(cos(x-y), sin(x-y))atan2 返回该点的角度(相当于x-y),但其范围是 [-PI, PI]。
    • 一个简单的解决方案并为我解决(不是选定的答案;))。但 tan inverse 是一个代价高昂的过程。
    • 对我来说,最优雅的解决方案。可惜它的计算成本可能很高。
    • 对我来说也是最优雅的解决方案!完美解决了我的问题(希望有一个公式,可以为我提供 signed 转角,这是两个可能的转角/角度中较小的一个)。
    【解决方案9】:

    我接受了提供签名答案的挑战:

    def f(x,y):
      import math
      return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)
    

    【讨论】:

    • 啊……顺便说一句,答案是 Python 函数。抱歉,我暂时处于 Python 模式。希望没关系。
    • 我将把新公式插入我楼上的代码中,看看结果如何! (谢谢^_^)
    • 我很确定 PeterB 的回答也是正确的。和邪恶的hackish。 :)
    • 但是这个不包含三角函数:)
    • java的等价公式是什么?如果角度是度数。?
    猜你喜欢
    • 2018-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多