【问题标题】:Calculate difference between two angles计算两个角度之间的差异
【发布时间】:2016-03-15 00:06:22
【问题描述】:

我意识到这可能是有史以来最菜鸟的问题,但我的想法完全被卡住了。

我有两个角度,可以说是两个单位的罗盘方向

单元 1:90.0

单元 2:45.0

如果它们相差超过 20 度,则 unit2 需要采用 unit1 的方向,+/-20 度,这样

unit1:90.0

unit2:70.0

我可以使用

计算出这两者(有符号)之间的度数差异
angle = 180 - abs(abs(unit1 - unit2) - 180)

但我需要知道是调整 +20 还是 -20。

例如,对于这个集合:

单元 1:270

unit2:350

unit2需要变成290(unit1加20度)

我很确定可能有一个 python 函数可以为我做这件事,但我完全卡住了,不记得我 20 年前学过的任何数学。

我完全希望有人能愚弄我,但无论如何我都会很感激。

【问题讨论】:

  • 为什么unit2 变成了70?不应该是20 上面的65 吗?
  • @zondo 最多需要距离 20。对OP来说,指南针的特征不就是360度角吗?为什么是 180?
  • 我是这么理解的。所以如果 A=90 和 B=20,be 必须增加到 70。但是如果 A=90 和 B=140,那么它必须下降到 B=110。这就是OP想要弄清楚的。无论是 B=A+20 还是 B=A-20。一个简单的比较就足够了。
  • 您需要补偿整圈吗?例如unit1=30; unit2=360 会生成新角度50,因为unit2>unit1,这是一个问题吗?

标签: python math


【解决方案1】:

我明白你为什么会坚持下去。另一个解决方案并没有真正解决像 unit1 处于 30 度和 unit2 处于 340 度这样的情况,它会在 360 度旋转到 0 度。代码可以进行一些清理,但我希望这会有所帮助。

import math

# calculate and return the distance unit2 
# needs to move to reach unit1
def get_distance(unit1, unit2):
    phi = abs(unit2-unit1) % 360
    sign = 1
    # used to calculate sign
    if not ((unit1-unit2 >= 0 and unit1-unit2 <= 180) or (
            unit1-unit2 <= -180 and unit1-unit2 >= -360)):
        sign = -1
    if phi > 180:
        result = 360-phi
    else:
        result = phi

    return result*sign

# distance unit2 needs to move to reach unit1
print get_distance(90,45)  # output 45 
print get_distance(270, 350) # output -80 
print get_distance(350, 30) # output -40 (unit2 moves -40 degrees)   
print get_distance(30, 350) # output 40  

unit1 = 30
unit2 = 350
# then calculate distance to move by taking 20 from distance
distance_to_move = (get_distance(unit1, unit2) - 20)

print (unit2 + distance_to_move)%360 # new position 10

正如Tadhg 所指出的那样,我对其进行了一些更改,使其可以工作超过 360 度。 20 度的方差也没有那么硬编码。

import math

# calculate and return the distance unit2 
# needs to move to reach unit1
def get_distance(unit1, unit2):
    phi = (unit2-unit1) % 360
    sign = -1
    # used to calculate sign
    if not ((phi >= 0 and phi <= 180) or (
            phi <= -180 and phi >= -360)):
        sign = 1
    if phi > 180:
        result = 360-phi
    else:
        result = phi
    return (result*sign), sign

def get_sign(unit1, unit2):
    phi = (unit2-unit1) % 360
    sign = 1
    if ((phi >= 0 and phi <= 180) or (
            phi <= -180 and phi >= -360)):
        sign = -1
    return sign

def new_position(unit1, unit2, variance = 20):
    distance_to_move, sign = get_distance(unit1, unit2)
    variance*=sign
    # %360 to keep it within the circle
    return (unit2+distance_to_move-variance)%360

# distance unit2 needs to move to reach unit1
assert (get_distance(90,45) == (45, 1))
assert (get_distance(270, 350) == (-80, -1))
assert (get_distance(350, 30) == (-40, -1))   
assert (get_distance(30, 350) == (40, 1))
assert (get_distance(50, 360*4) == (50, 1))
assert (get_distance(360*4, 50) == (-50, -1))
print "----------"
assert (new_position(90,45) == 70)
assert (new_position(270, 350) == 290) 
assert (new_position(350, 30) == 10)
assert (new_position(30, 350) == 10)
assert (new_position(50, 360*4) == 30)
assert (new_position(360*4, 50) == 20)

【讨论】:

  • 我不确定这实际上是在回答原始问题...您在-20 中进行了硬编码,而不是根据符号来计算...
  • 另外,这对get_distance(1, 360*4)get_distance(360*4, 1)) 给出了相同的结果,因此您没有正确检查符号,我建议在计算phi 时删除abs 但是然后这个只是my answer
  • 干杯,我修好了一点,现在检查符号更好了,get_distance(1, 360*4) 得到 +1,get_distance(360*4, 1) 得到 -1。
  • 这样就完美解决了。标记为答案。非常感谢你!我 100% 被困在这里。
【解决方案2】:

如果两个方向之间的距离大于 360,则当前计算两个方向之间角度的方法将分崩离析:

unit1 = 360 * 4
unit2 = 50
angle = 180 - abs(abs(unit1 - unit2) - 180)
print(angle) #outputs -1050

我强烈建议使用模数% 来计算角度:

#this will calculate the angle so that 0<=angle<360
angle = (unit2 - unit1) % 360

#this alone will give you the angle such that:
assert 0<= angle <360
assert (unit1 + angle) %360 == unit2 %360

虽然使用 -180 到 180 之间的值而不是 0 到 360 之间的值会更容易,因此您可以将 180 以上的值减少为负数,如下所示:

angle = (unit2 - unit1) % 360
if angle>180:
    angle -=360

# or using @PM2Ring suggestion in comments
angle = 180 - (180 - unit2 + unit1) % 360

#now these two statements will always be True
assert -180< angle<=180
assert (unit1 + angle) %360 == unit2 %360

那么您可以只检查大于 20 或小于 -20 的值:

if angle>20:
    unit2 = unit1 + 20
elif angle<-20:
    unit2 = unit1 - 20
#else leave unit2 unchanged

编辑: 如果您不介意使用 0 到 360 之间的角度,您仍然可以使用 340 作为截止值进行等效计算:

if 20<angle<=180:
    unit2 = unit1 + 20
elif angle<340: #elif angle in range(181,340):
    unit2 = unit1 - 20
#else: assert angle in range(21) or angle in range(340,360) #no change

这篇文章和原帖使用 3 次检查来完成任务,但这种方法没有减少 angle 的可能额外步骤,因此这将执行得更快,但可能会更令人困惑。

【讨论】:

  • FWIW,你可以从角度计算中去掉if测试:angle = 180 - (180 - unit2 + unit1) % 360
  • angle = (unit2 - unit1 + 180) %360 -180 尽管它提供了 -180&lt;=angle&lt;180 或者根本不改变截止值,正如我在编辑中显示的那样,但我仍然会使用我原来的解决方案,因为 OP 使用的是 python。 Readability counts.
  • 我的角度是罗盘方位,所以它们总是在 0..360 以内
  • 当然,但重点仍然是使用模数更好地完成此任务,并且您接受的答案的工作方式相同,但有更多不必要的步骤。
  • @PM2Ring,抱歉有点侮辱你的建议,起初这似乎是黑客魔法,但我意识到它只是抵消了模数的截止值,这真的很聪明!
【解决方案3】:

从概念上讲,您正在寻找这个伪代码:

if angle_difference > 20:
    if unit1 > unit2:
        unit2 = unit1 - 20
    else:
        unit2 = unit1 + 20

也就是说,例如,如果unit1 更大,那么unit2 将增加,直到它比unit1 少20。但是,我最初错过了一个技巧。如果角度差大于 180,则它们的补码小于 180,因此unit2 从另一个方向接近unit1 的时间更短,符号计算切换。

def adjust_angle(a, b):
    angle = abs(a - b)
    if 340 > angle > 20:
        adjustment = -20 if angle < 180 else 20
        b = (a + adjustment if a > b else a - adjustment) % 360
    return a, b

这里要解释几点。同理,如果角度差小于 20,则无需执行任何操作,大于 340 (360-20) 的情况也是如此。

sn-p

if 340 > angle > 20:
    adjustment = -20 if angle < 180 else 20
    b = (a + adjustment if a > b else a - adjustment) % 360

在逻辑上等价于以下

if 340 > angle > 20:
    if angle < 180:
        b = (a - 20 if a > b else a + 20) % 360
    else:
        b = (a + 20 if a > b else a - 20) % 360

通过使用adjustment 变量,我们简单地消除了代码复制。

最后,使用模运算是因为有时在纯计算中unit2 可能超过值 360。例如,对于unit1 = 50unit2 = 340unit2 必须增加 50,实际上变成 390。但由于我们被限制在 0 到 360 度,结果会变成 30。

【讨论】:

  • angle 来自哪里?
  • @TadhgMcDonald-Jensen 在 OP 中计算。
  • 谢谢!但是,如果 unit1 为 350 而 unit2 为 15,就我所见,这将不起作用。
  • @Trondh 对于这么晚的更新,我深表歉意。我知道你已经得到了你的答案,但我正在劝你看看另一种方法。
【解决方案4】:

我知道这是一篇旧帖子,但只是想分享这个漂亮的小单线来比较角度。实现使用 numpy 并利用了一些循环数学。您可能会发现的唯一问题是它会为绝对边缘返回 False。例如unit1 = 90 和 unit2 = 70。但如果 unit2 = 70.01 将返回 True。

import math
import numpy

if math.cos(np.deg2rad(unit2) - np.deg2rad(unit1)) >= math.cos(np.deg2rad(20)):
    unit2=unit1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-10
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多