两个角度之间的最小差值是多少?

8

我正在尝试计算两个角度之间的最小差异。

这是我的当前代码(稍微变化了一下从网上找到的东西):

float a1 = MathHelper.ToDegrees(Rot);
float a2 = MathHelper.ToDegrees(m_fTargetRot);

float dif = (float)(Math.Abs(a1 - a2);

if (dif > 180)
  dif = 360 - dif;

dif = MathHelper.ToRadians(dif);

除了在圆的边缘情况下,它都能正常工作。例如,如果当前角度为355,目标角度为5,则计算出的差值为-350而不是10,因为365度等于5度。

有什么想法可以让这个工作吗?


这看起来是正确的。而且你不需要将其转换为度数再转回弧度 - 你可以直接用弧度进行计算。 - Petar Ivanov
4
答案中没有提及MathHelper.WrapAngle的爱 :( - Andrew Russell
太好了,@Andrew。我对MathHelper一无所知,但WrapAngle似乎是正确的解决方案。希望你能发表一个解决方案,但既然你没有,我会更新我的答案。 - Prestaul
5个回答

8
你基本上已经理解了。在检查是否大于180之前,只需对dif进行360的模运算即可:
float a1 = MathHelper.ToDegrees(Rot);
float a2 = MathHelper.ToDegrees(m_fTargetRot);

float dif = (float)Math.Abs(a1 - a2) % 360;

if (dif > 180)
    dif = 360 - dif;

dif = MathHelper.ToRadians(dif);

编辑:@Andrew Russell在您的问题的评论中提出了一个很好的观点,下面的解决方案利用了他建议的MathHelper.WrapAngle方法:

diff = Math.Abs(MathHelper.WrapAngle(a2 - a1));

我该如何知道是增加还是减少差异? - meds
抱歉,我不太确定你在问什么... 这段代码应该是完整的,并且始终会返回介于0和180(含)之间的正值。 - Prestaul
2
请注意,MathHelper.WrapAngle 使用弧度制,因此如果您使用它,请勿进行度数转换。(实际上,这从未是必要的:2π 弧度 = 360 度。别忘了我们的好朋友 MathHelper.TwoPi!) - Andrew Russell
非常好的观点,@Andrew。我应该指出将其转换为度数是不必要的事实。 - Prestaul

4
您需要扩展角度越界检查:
if (dif < 0) dif = dif + 360;
if (dif > 180) dif = 360 - dif;

2
为什么要点踩?如果你不解释你认为哪里有问题,那么答案就无法得到改进。 - Guffa

2
我不喜欢使用case语句来处理零包装。相反,我使用点积的定义来计算两个角度之间的(无符号)角度:

vec(a) . vec(b) = ||a|| ||b|| cos(theta)

我们只需要让a和b成为单位向量,因此||a|| == ||b|| == 1

由于vec(x) = [cos(x),sin(x)],所以我们得到:

unsigned_angle_theta(a,b) = acos(cos(a)cos(b) + sin(a)sin(b))

(注:所有角度均为弧度)

1

您可以将结果归一化为 0 <= theta < 360:

while(theta < 0) { theta += 360; }

如果您想保留弧度作为答案(建议):

const Double TwoPi = 2 * Math.Pi;
while(theta < 0) { theta += TwoPi; }

1
我们可以使用欧拉公式:exp(iA) = cos A + i sin A。
在两个角度之间的差异情况下,变成了:
exp(i(A-B))
使用指数法则:
= exp(iA).exp(-iB)。
-iB是iB的共轭因此:
= exp(iA).exp(conjugate(iB))。
复杂指数可以通过泰勒级数计算得出:
taylor_e(P={cplx,_,_}) ->
    taylor_e(
      #{sum => to_complex(1),
    term => 0,
    term_value => to_complex(1),
    min_terms => 3,
    quadrature => 1,
    error_term => 1.0e-4,
    domain => P}
       );

taylor_e(P=#{sum := Sum,
       term := Term,
       term_value := TermValue0,
       min_terms := MinTerms, 
       domain := Dom, 
       quadrature := Q,
       error_term := ErrorTerm
      }) 
  when ((Term =< MinTerms) or (abs(1-Q) > ErrorTerm)) and
       (Term < 20) ->
    NewTerm = Term+1,
    TermValue1 = scalar_divide(multiply(TermValue0,Dom),NewTerm),
    PartialSum = add(Sum,TermValue1),
    taylor_e(P#{sum := PartialSum,
        term :=  Term+1,
        term_value := TermValue1,
        quadrature := quadrance(PartialSum)
          });
taylor_e(#{sum := Result}) ->
    Result.

角度差是结果复数的参数(方向),通过atan2检索得到。
当然,您需要一些基本的复数例程。此方法在0/360度周围没有不连续性,并且结果的符号给出了旋转方向。如果这是某个参考方向(例如自动驾驶仪)之间的差异,则只需要计算一次,然后存储,直到选择新的航向。然而,必须从每个样本中计算偏差。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接