Iterating toward a specific angle on a circle - math

I'm building a robot vehicle that has an on-board compass. Starting from the current orientation of the vehicle in degrees, I would like to rotate it 90 degrees in either direction, using the compass.
I'm supposing that the best way to do this is to rotate the vehicle in increments within a "while" loop and test after each rotational increment if it has moved 90 degrees.
However, while dealing with transitions between two positive points is simple, it becomes challenging with transitions that involve the transition from 0 to 360.
In other words, this code for left rotation fails for obvious reasons:
let startingPoint = 30 // in degrees
let endPoint = startingPoint - 90
while currentPoint > endPoint {
rotateLeft()
}
Is there an equation that will enable this comparison when crossing the 360/0 boundary?

You can check difference, not absolute value.
To find whether difference is outside 90 range, you can use formula that accounts for arbitrary angles including transitions through 0 (don't forget angles should be in radians)
if Cos(startingangle - currentangle) <=0 then
absolute difference is equal or more than Pi/2 (90 degrees)
Here thick arrow shows (ignore axis labels or divide them by 4) Cos of difference of zero start angle with +-30 degrees angles (works for any start angle)
Python demo:
import math
def AngleInRange(value, central, arange):
value = math.radians(value)
central = math.radians(central)
arange = math.radians(arange)
return (math.cos(value - central) >= math.cos(arange))
for a in range (100, 220, 15): #through 180
print(a, AngleInRange(a, 150, 45))
for a in range (-40, 40, 10): #through 0
print(a, AngleInRange(a, -10, 20))
100 False
115 True
130 True
145 True
160 True
175 True
190 True
205 False
-40 False
-30 True
-20 True
-10 True
0 True
10 True
20 False
30 False

Related

r - find the side of turning to achieve smallest angle between two bearings

This question is building on Difference (angle) between two bearings.
Basically, if we can find the difference between two bearings, can we find the side on which the object needs to turn to achieve the smallest amount of rotation.
Using the solution/function in R, from the previous question:
angle_diff <- function(ber1, ber2){
theta <- abs(ber1 - ber2) %% 360
return(ifelse(theta > 180, 360 - theta, theta))
}
To show what is needed here are 2 examples:
First ex.: if we have ber1 = - 175 and ber2 = 175, in order for the object to go from bearing -175 to bearing 175 it needs to turn counter clockwise for 10 degrees.
Second ex.: if we have ber1 = - 10 and ber2 = 50, in order for the object to go from bearing -10 to bearing 50 it needs to turn clockwise for 60 degrees.
Finding the amount of degrees to make the shortest term is answered in the question mentioned above, but is it possible to find whether the turn needs to be made clockwise or counter clockwise ?
Perhaps something like this?
bearing_diff <- function(b1, b2) {
angle <- b2 - b1
clockwise <- angle %% 360
counter_clockwise <- 360 - clockwise
if(abs(clockwise) < abs(counter_clockwise)) {
paste(abs(clockwise), "degrees clockwise")
} else {
paste(abs(counter_clockwise), "degrees counter-clockwise")
}
}
bearing_diff(175, -175)
#> [1] "10 degrees clockwise"
bearing_diff(0, 180)
#> [1] "180 degrees counter-clockwise"
bearing_diff(0, 185)
#> [1] "175 degrees counter-clockwise"
Created on 2020-08-15 by the reprex package (v0.3.0)

Measure rotation around one axis with Gyroscope

The gyroscope of a device returns values in the following range:
Alpha: 0 - 360 around the Z-axis
Beta: -180 - 180 around the X-axis
Gamma: -90 - 90 around the Y-axis
When I rotate the device around the Y-axis, the value 'flips' at a certain point from -90 to 90.
I know that this is due to the fact that the gyroscope functions like a gimbal, where Alpha is the outer ring, Beta is the middle ring and Gamma is the inner ring.
My question is: how do I measure the rotation JUST around the Y-axis, without the 'flipping' effect?
Is there a mathematical way to process the alpha/beta/gamma values and get more 'useful' values for this case, so that is easier to get the amount of rotation around the X, Y or Z axis?

Working with degrees of a circle in R

I have a bearing of X degrees and I wish to calculate other bearings either side of X. We all know a circle has 360 degrees but if I'm given a bearing of 0 degrees and I wish to calculate the bearings + and - 30 degrees of 0 how can I program this such that I am looking for 0 - 30 = 330 degs and 0 + 30 = 30 degrees?
The modulus operator (%%) can help you with that
x <- 0
(x+30) %% 360
# [1] 30
(x-30) %% 360
# [1] 330
In this case it will keep all values in the range [0, 360). See the ?"%%" help page for more information or even the wikipedia page for these types of operations

Calculating if an angle is between two angles

So I am making a little game where I am checking if a character can "see" another where character A can see character B if A is within a certain distance of B, and the direction in degrees of A is +/- 45 degrees of the angle B is facing.
Currently, I do a little calculation where I'm checking if
(facingAngle - 45) =< angleOfTarget =< (facingAngle + 45)
This works fine except for when we cross the 360 degree line.
Let's say facingAngle = 359, angleOfTarget = 5. In this situation, the target is only 6 degrees off center, so I want my function to return true. Unfortunately, 5 is not between 314 and 404.
Just try
anglediff = (facingAngle - angleOfTarget + 180 + 360) % 360 - 180
if (anglediff <= 45 && anglediff>=-45) ....
The reason is that the difference in angles is facingAngle - angleOfTarget although due to wrapping effects, might be off by 360 degrees.
The add 180+360 then modulo 360 then subtract 180, effectively just converts everything to the range -180 to 180 degrees (by adding or subtracting 360 degrees).
Then you can check the angle difference easily, whether it is within -45 to 45 degrees.
Here is a simple function I found online, and modified. It works correctly for any angles (can be outside of 0-360). (This function is made to work in c, works in Xcode.)
Remember, it checks COUNTER-CLOCKWISE from angle A to angle B. It returns YES (true) if the angle is between :)
First, a simple conversion function to make all angles 1-360
//function to convert angle to 1-360 degrees
static inline double angle_1to360(double angle){
angle=((int)angle % 360) + (angle-trunc(angle)); //converts angle to range -360 + 360
if(angle>0.0)
return angle;
else
return angle + 360.0;
}
Check if angle is between :)
//check if angle is between angles
static inline BOOL angle_is_between_angles(float N,float a,float b) {
N = angle_1to360(N); //normalize angles to be 1-360 degrees
a = angle_1to360(a);
b = angle_1to360(b);
if (a < b)
return a <= N && N <= b;
return a <= N || N <= b;
}
Eg. To check if the angle 300 is between 180 and 10 degrees:
BOOL isBetween=angle_is_between_angles( 300, 180,10);
//RETURNS YES
There is a trigonometric solution that avoids the wrapping problem.
I'm assuming that you have (x, y) coordinates for both characters P1 and P2. You've already specified that you know the distance between the two which you presumably calculated using Pythagoras' theorem.
You can use the dot product of two vectors to calculate the angle between them:
A . B = |A| . |B| . cos(theta).
If you take A as the facingAngle vector it will be [cos(fA), sin(fA)], and will have magnitude |A| of 1.
If you take B as the vector between the two characters, and your distance above you get:
cos(theta) = (cos(fA) * (P2x - P1x) + sin(fA) * (P2y - P1y)) / |B|
where |B| is the distance you've already calculated.
You don't need to actually take the inverse cosine to find theta, since for range of -45 to +45 you just need to check for cos(theta) >= 0.70710678 (i.e. 1 / sqrt(2)).
This might seem slightly complicated, but the chances are that you've already got all of the required variables hanging around in your program anyway.
A simple solution to handle wrapping at the low end (into negative values), is just to add 360 to all your values:
(facingAngle + 315) =< (angleOfTarget + 360) =< (facingAngle + 405)
That way, the subtraction of 45 can never go negative, because it no longer happens.
To handle wrapping at the top end, you need to check again, adding another 360 to the angleOfTarget value:
canSee = (facingAngle + 315 <= angleOfTarget + 360) &&
(angleOfTarget + 360 <= facingAngle + 405);
canSee |= (facingAngle + 315 <= angleOfTarget + 720) &&
(angleOfTarget + 720 <= facingAngle + 405);
Another way using always minimum positive difference and comparing with threshold:
anglediff = Math.min(Math.abs(facingAngle - angleOfTarget), 360 - Math.abs(angleOfTarget - allowDirection));
if (anglediff <= 45)
Restating Alnitak's answer in a different way, a solution that avoids the angle wrap at 360 degrees is to restate the problem in a different coordinate system where the angles are always small. Here is the code:
def inside_angle(facing, target):
dot = cos(facing)*cos(target) + sin(facing)*sin(target)
angle = acos(dot)
return angle <= pi/4
This is done using vector projection. Assuming the vectors |facing> = [cos(facing) sin(facing)] and |target> = [cos(target) sin(target)], when projecting the target into the facing vector, the angle will range from zero, when the target is exactly at the facing vector or will increase to either side. This way we can just compare it to pi/4 (45 degrees). The formula for the angle is the following:
cos(angle) = <facing|target> / <target|target> <facing|facing>
That is, the cosine of the angle is the dot product between the vectors |facing> and |target> divided their modules, which is 1 in this case, which becomes:
angle = acos(<facing|target>)
Reference:
https://en.wikipedia.org/wiki/Vector_projection

Calculating reflex and obtuse angles

Given two lines of common origin point, whose rotational angles I have in degrees, what's the best way in Lua to calculate the reflex and obtuse angles involved?
Assuming that you want the reflex angle that complements the obtuse angle, and don't worry about getting values of 90 or 180 degrees for the obtuse value:
function obtuse_reflex(a, b)
local diff = (a - b) % 180
if diff < 90 then diff = 180 - diff end
return diff, 360 - diff
end

Resources