举几个例子,角度范围为0~360度,0度和360度是重合的,不妨先算法下面角度的均值,
[10, 30] = 20 结果正确
[20, 100] = 60 结果正确
[160, 200]= 180 结果正确
[0, 360] = 180 因为360度和0度重合,这个结果貌似不是我们想要的,结果360度才合理
[20, 300] = 160 要能求得均值为340度就好了
不知看出什么问题没有——角度求均值不再是简单的算术均值!
从上面的结果也可以知道,有些情况下角度均值就是算术平均值,那什么情况下不是求算术平均值呢?
如上图,当α<β时,当β-α > 180时,(α+β)/2不再是要求的结果;同理,α<β时,α-β > 180时不再是求算术平均值。
综合这两种情况,当|α-β|>180情况下不是求算术平均值。这种情况下期望的角度均值为(以α小于β为例):
同理,因为上面的表达式中的α与β对称,所以α大于β计算的结果也一样。因此可知,非算术平均值的情况只要在算术平均值的基础上加180就可以了,因此归纳如下:
上面仅是两个角度值的平均,通过两个角度的差值做判别条件确定是否是算术均值,然而,多个的呢?
多个的角度均值显得有些复杂,这需要用户对输入输出角度范围提供约束,这里不妨将命题变成:考虑到相邻角度在时间上的相关性,设相邻的两个输入角度值偏移值不会大于180度,在这种情况下,只要考虑0~360度的突变情况就可以了。
直接看matlab程序吧(参考资料2),
function [avg] = avg_degree(angles)
% 计算角度平均值,注意0-360度情况
last = angles(1);
sum = angles(1);
for i=2:length(angles)
diff = mod(angles(i)-angles(i-1)+180,360)-180;
last = last + diff;
sum = sum + last;
end
avg = mod(sum/length(angles), 360);
end
给个自己使用的C程序也有,只不过求模用条件运算替换了,
#define ANGLE_DIFF(diff, x2, x1) \
do { \
diff = x2-x1+180; \
if (diff < 0) { \
diff = diff + 360 - 180; \
} else if (diff > 360) { \
diff = diff - 360 - 180; \
} else { \
diff = diff - 180; \
} \
} while(0)
float angle_avg(float *angle, uint32_t n)
{
float diff = 0;
float last = angle[0];
float sum = angle[0];
uint32_t i = 0;
for (i=1; i<n; i++) {
/* diff = mod(angle[i]-angle[i-1]+180,360)-180 */
ANGLE_DIFF(diff, angle[i], angle[i-1]);
last += diff;
sum += last;
}
return sum/n;
}
其中的DIFF宏定义用于计算两个角度差值。
维基百科角度出现循环的变量为圆周量,如上面的角度值、一天的24小时等。
Wikipedia中计算平均角度的方法:
参考文献1中给出了关于圆周变量更细致的数学分析与描述,是一份难得的资料,参考文献2是StackOverflow上关于圆周角度的探讨,作为对该问题的理解很有帮助。