有没有一种方法既安全又简单地用最少的 case 语句来处理角度包裹。
当使用特定的角度表示形式(0-360 度或 -180 - 180 度(或等效的弧度))并且包裹该角度时,就会发生角度包裹。例如,假设角度为 -170,则减去 50 度。数学上加起来是 -220,但实际上应该是 +140 度。
显然你可以使用以下方法检查:
if (deg < -180) { 180 - abs(deg + 180); }
或类似的。但首先你需要大量的检查,其次如果你包装两次就不起作用了。
第二种普遍存在的情况是两个角度之间的插值。
例如,假设我有一个 -170 度和 160 度的角度,我想要它们之间的中间位置。执行此操作的常见方法是
ang1 + 0.5(ang2-ang1)
,但在我提供的示例中,它将导致角度应为 175 度时变为 -5 度。
在这些场景中有处理角度包裹的通用方法吗?
为了完整起见,我将同时包含
[0, 360)
和 [-180, 180)
标准化。
您将需要
#include <math.h>
。
标准化为
[0,360)
:
double constrainAngle(double x){
x = fmod(x,360);
if (x < 0)
x += 360;
return x;
}
标准化为
[-180,180)
:
double constrainAngle(double x){
x = fmod(x + 180,360);
if (x < 0)
x += 360;
return x - 180;
}
该模式应该足够容易识别以推广到弧度。
角平分:
double angleDiff(double a,double b){
double dif = fmod(b - a + 180,360);
if (dif < 0)
dif += 360;
return dif - 180;
}
double bisectAngle(double a,double b){
return constrainAngle(a + angleDiff(a,b) * 0.5);
}
这应该平分“较小”一侧的角度。 (警告:尚未完全测试)
[-180, 180)
deg -= 360. * std::floor((deg + 180.) * (1. / 360.));
[0, 360)
deg -= 360. * std::floor(deg * (1. / 360.));
deg = -90
-> [0, 360)
:
deg -= 360. * std::floor(-90 / 360.);
deg -= 360. * -1;
deg = 270
deg = 270
-> [-180, 180)
:
deg -= 360. * std::floor((deg + 180.) / 360.);
deg -= 360. * std::floor(480. / 360.);
deg -= 360. * 1.;
deg = -90;
所以,如果找到一种方法来有效地做我想做的事情,使用神秘的方法来限制角度。这是:
这似乎适用于我能想到的任何例子。
我知道这是一个旧线程,但试试这个:
double angleRef(double thtIn, double thtRef){
tht = fmod(thtIn + (180-thtRef),360) + (thtRef-180);
return tht;
}
就像你的例子一样,如果 A=-170 且 B=160,那么它们之间的中间角度是
A + 0.5*(angleRef(B,A) - A) = -185
或者如果您更喜欢 A=160 和 B=-170
A + 0.5*(angleRef(B,A) - A) = 175
请原谅任何 C++ 格式错误,它不是我的母语。
自动新角度 = atan2(sin(旧角度), cos(旧角度));
#include <iostream>
#include <iomanip>
#include <numbers>
#include <vector>
#include <cmath>
// Normalize to [0,2PI):
double phaseNorm(double x)
{
x = fmod(x, 2*std::numbers::pi);
if (x < 0)
x += 2*std::numbers::pi;
return x;
};
// unwrap phase [-PI,PI]
std::vector<double> phaseUnwrap(std::vector<double> in)
{
// Normalize to [0,2PI):
std::transform(in.begin(),in.end(),in.begin(),[&](double d){ return phaseNorm(d); });
// unwrap iteration
for(size_t i = 0; i < in.size()-1; ++i)
{
int n2PiJump = in[i] / (2*std::numbers::pi);
in[i+1] += n2PiJump * 2*std::numbers::pi;
if(in[i]-in[i+1] > std::numbers::pi)
{
in[i+1] += 2*std::numbers::pi;
}
}
return in;
}
int main() {
// create phase vector
int n = 3;
std::vector<double> phase;
for(int h = 0; h < 3; ++h)
{
for(int i = n; i > -n; --i)
{
phase.push_back(-i*std::numbers::pi/n);
}
}
// print phase vector
std::cout << std::setw(25) << "Input vector: ";
for(auto& p : phase)
{
std::cout << std::setw(10) << p << " ";
}
std::cout << std::endl;
// normalize phase vector
std::cout << std::setw(25) << "Normalized vector: ";
for(auto& p : phase)
{
p = phaseNorm(p);
std::cout << std::setw(10) << p << " ";
}
std::cout << std::endl;
// unwrap phase vector
std::cout << std::setw(25) << "Unwraped norm. vector: ";
std::vector<double> phaseUnwraped = phaseUnwrap(phase);
for(auto& p : phaseUnwraped)
{
std::cout << std::setw(10) << p << " ";
}
std::cout << std::endl;
return 0;
}
Input vector: -3.14159 -2.0944 -1.0472 0 1.0472 2.0944 -3.14159 -2.0944 -1.0472 0 1.0472 2.0944 -3.14159 -2.0944 -1.0472 0 1.0472 2.0944
Normalized vector: 3.14159 4.18879 5.23599 0 1.0472 2.0944 3.14159 4.18879 5.23599 0 1.0472 2.0944 3.14159 4.18879 5.23599 0 1.0472 2.0944
Unwraped norm. vector: 3.14159 4.18879 5.23599 6.28319 7.33038 8.37758 9.42478 10.472 11.5192 12.5664 13.6136 14.6608 15.708 16.7552 17.8024 18.8496 19.8968 20.944
编译时不要忘记 c++20 标志
-std=c++20
,因为 std::numbers::pi
。
将角度(+PI ~ -PI)映射到有符号整型值(或短整型值):
angle_signed_short = angle_float / PI * 0x7FFFF;
然后您可以正常添加或减少值。然后映射回来:
angle_float = angle_signed_short * PI / 0x7FFFF;