在C++代码中处理角度包裹

问题描述 投票:0回答:8

有没有一种方法既安全又简单地用最少的 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 度。

在这些场景中有处理角度包裹的通用方法吗?

c++ math
8个回答
78
投票

为了完整起见,我将同时包含

[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);
}

这应该平分“较小”一侧的角度。 (警告:尚未完全测试)


24
投票

我发现使用数学库中的

remainder()
很方便。给定一个角度
a
,将其限制为 -180, 180,你可以这样做:

remainder(a, 360.0);

并将弧度的

360.0
更改为
2.0 * M_PI


9
投票

将角度标准化为范围
[-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;

参见:http://en.cppreference.com/w/cpp/numeric/math/floor


5
投票

所以,如果找到一种方法来有效地做我想做的事情,使用神秘的方法来限制角度。这是:

enter image description here

这似乎适用于我能想到的任何例子。


0
投票

我知道这是一个旧线程,但试试这个:

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++ 格式错误,它不是我的母语。


0
投票

自动新角度 = atan2(sin(旧角度), cos(旧角度));


0
投票
#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


-1
投票

将角度(+PI ~ -PI)映射到有符号整型值(或短整型值):

angle_signed_short = angle_float / PI * 0x7FFFF;

然后您可以正常添加或减少值。然后映射回来:

angle_float = angle_signed_short * PI / 0x7FFFF;
© www.soinside.com 2019 - 2024. All rights reserved.