【问题标题】:Determining the average angle确定平均角度
【发布时间】:2013-08-06 00:47:21
【问题描述】:

我正在开发一个涉及在游戏中获取相机角度的应用程序。角度可以是 0-359 之间的任何位置。 0 是北,90 是东,180 是南,等等。我正在使用一个 API,它在 Camera 类中有一个 getAngle() 方法。

我如何找到不同摄像机角度之间的平均值。 0 和 359 的实际平均值为 179.5。作为一个摄影机角度,应该是南方,但显然 0 和 359 都非常接近北方。

【问题讨论】:

  • 提示:基本上,你试图找到一个向量的角度,它是代表你的方向的向量的总和。
  • 是我的数学错了,还是 1-359 不够圈?
  • 谈论“平均”角度是没有意义的。如果你有两个角度,你可以平分它们之间的弧线,但平均 3 个或更多角度是毫无意义的,除非它们相对靠近。
  • 是的,要平均的角度都在90度以内。

标签: math angle


【解决方案1】:

您可以从向量的角度来考虑它。让θ<sub>1</sub>θ<sub>2</sub> 成为您在radians 中表达的两个角度。然后我们可以确定在这些角度的单位向量的 x 和 y 分量:

x1 = sin(θ1) y1 = cos(θ1) x2 = sin(θ2) y2 = cos(θ2)

然后您可以将这两个向量相加,并确定结果的 x 和 y 分量:

x* = x1 + x2 y* = y1 + y2

最后,您可以确定这个结果向量的角度:

θavg = tan-1(y*/x*)

或者,更好的是,使用atan2(许多语言支持的功能):

θavg = atan2(y*, x*)

您可能必须分别处理y<sup>*</sup> = 0x<sup>*</sup> = 0 的情况,因为这意味着这两个向量指向完全相反的方向(那么“平均值”应该是多少? )。

【讨论】:

  • +1,这是最“数学”的解决问题的方法,但是你不讨论分母为0时该怎么做。
  • @jxh Mathy,真的。但是如果你在做图形动画,你应该尽量避免超越函数。它们所花费的时间是简单算术的数倍,而这确实是解决这个问题所需要的。
  • @Gene:正如 OP 所要求的,当将解决方案推广到多个角度时,算术的“简单”部分并不那么简单。
  • @jxh 分母为 0 为有效情况;您将收到atan(inf),即pi/2。但是,当您拥有0/0 时,就会出现问题。
  • @jxh 这并不难。查看我的解决方案。
【解决方案2】:

这取决于您所说的“平均”是什么意思。但正常的定义是夹锐角的平分线。您必须将两者置于 180 度以内。有很多方法可以做到这一点,但一种简单的方法是增加或减少一个角度。如果角度是ab,那么就可以了:

if (a < b)
  while (abs(a - b) > 180) a = a + 360
else
  while (abs(a - b) > 180) a = a - 360

现在您可以计算简单平均值:

avg = (a + b) / 2

当然你可能想再标准化一次:

while (avg < 0) avg  = avg + 360
while (avg >= 360) avg = avg - 360

在您的示例中,您将有 a=0,b=359。第一个循环将 a 增加到 360。平均值为 359.5。当然,如果您愿意,您可以将其四舍五入为整数。如果四舍五入到 360,那么最后一组循环将减少到 0。

请注意,如果您的角度始终归一化为 [0..360),则这些循环都不会执行超过一次。但它们可能是一种很好的做法,因此狂野的参数不会导致您的代码失败。

【讨论】:

    【解决方案3】:

    您想要平分角度而不是平均它们。首先获取它们之间的距离,采用最短的方式,然后将其分成两半并添加到其中一个角度。例如:

    A = 355
    B = 5
    
    if (abs(A - B) < 180) {
        Distance = abs(A - B)
        if (A < B) {
            Bisect = A + Distance / 2
        }
        else {
            Bisect = B + Distance / 2
        }
    }
    else {
        Distance = 360 - abs(A - B)
        if (A < B) {
            Bisect = A - Distance / 2
        }
        else {
            Bisect = B - Distance / 2
        }
    }
    

    或者类似的东西——对于给定的输入,“Bisect”应该为零。可能有一些聪明的方法可以使算术产生更少的 if 和 abs 操作。

    【讨论】:

    • (Sashkello 之前的评论是看待同一事物的另一种更通用的方法——计算由角度表示的向量的向量和并计算该向量的角度。这将适用于超过2 个角度。)
    【解决方案4】:

    在评论中,您提到要平均的所有“角度”都在 90 度以内。我猜实际上只有一个摄像头,但它会四处移动,而您正在为摄像头 POV 创建某种图像稳定性机制。

    在任何情况下,只有相机可能在 270-359 象限和 0-89 象限的特殊情况。对于所有其他情况,您可以取一个简单的平均值。因此,您只需要检测这种特殊情况,当它发生时,将 270-359 象限中的角度视为 -90 到 -1。然后,在计算简单平均值后,如有必要,将其调整回 270-359 象限。

    在 C 代码中:

    int quadrant (int a) {
        assert(0 <= a && a < 360);
        return a/90;
    }
    
    double avg_rays (int rays[], int num) {
        int i;
        int quads[4] = { 0, 0, 0, 0 };
        double sum = 0;
        /* trivial case */
        if (num == 1) return rays[0];
        for (i = 0; i < num; ++i) ++quads[quadrant(rays[i])];
        if (quads[0] == 0 || quads[3] == 0) {
            /* simple case */
            for (i = 0; i < num; ++i) sum += rays[i];
            return sum/num;
        }
        /* special case */
        for (i = 0; i < num; ++i) {
            if (quadrant(rays[i]) == 3) rays[i] -= 360;
            sum += rays[i];
        }
        return sum/num + (sum < 0) * 360;
    }
    

    此代码可以以目的明确为代价进行优化。当您检测到特殊情况时,您可以在事后修复sum。因此,您可以计算 sum 并找出特殊情况并一次性完成修复。

    double avg_rays_opt (int rays[], int num) {
        int i;
        int quads[4] = { 0, 0, 0, 0 };
        double sum = 0;
        /* trivial case */
        if (num == 1) return rays[0];
        for (i = 0; i < num; ++i) {
            ++quads[quadrant(rays[i])];
            sum += rays[i];
        }
        if (quads[0] == 0 || quads[3] == 0) {
            /* simple case */
            return sum/num;
        }
        /* special case */
        sum -= quads[3]*360;
        return sum/num + (sum < 0) * 360;
    }
    

    我相信它可以进一步优化,但它应该给你一个开始。

    【讨论】:

      猜你喜欢
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 1970-01-01
      • 2018-07-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多