下一步:无需三角函数或浮点数即可计算反正切 (arctan)

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

我正在此 Nextion 显示屏上编写界面。具体来说,我正在尝试编写一个围绕中心点旋转的数字表盘。

这些屏幕很棒,但它们没有任何三角函数,没有浮点,也不能旋转图像。只是分享这些规格作为我的限制背景,不用担心屏幕。

我需要一种将 X,Y 坐标转换为 0-359 输出的方法。

有人知道使用整数数学来近似角度的任何技巧或窍门吗?

我现在所拥有的只是针对每个四分位数调整的梯度,但它不是线性的,因为我只是在做上升/运行。

integer trigonometry angle integer-division nextion
2个回答
1
投票

有一篇论文反正切函数的高效近似描述了快速

atan
函数:

double FastArcTan(double x)
{
    return M_PI_4*x - x*(fabs(x) - 1)*(0.2447 + 0.0663*fabs(x));
}

这当然使用

double
数据类型。

用比例因子 10000 的定点算术重写(因此“(int) 10000”实际上意味着“1.0”),您可以编写一个函数

int IntArcTan(int x_scaled)
,这里是完整的小测试程序:

#include <stdio.h>
#include <math.h>

// -10000 < x_scaled < 10000; integer arithmetic i.e. (int)10000 means (float)1.0000
int IntArcTan(int x_scaled)
{
   int abs_x_scaled = x_scaled >= 0 ? x_scaled : -x_scaled;
   int a = 7854/* PI/4 */ * x_scaled / 10000;
   int b = x_scaled*(abs_x_scaled - 10000) / 10000;
   int c = b * 2447 / 10000;
   int d = b * 663 / 10000 * abs_x_scaled / 10000;

   return a - c - d;
}

int main()
{
   for (double x = -1.; x < 1.; x+=.01)
   {
      double   atan_std           = atan(x);
      int      atan_int_scaled    = IntArcTan((int)(x * 10000));
      double   atan_int           = (double)atan_int_scaled / 10000;
      printf("x = %10.3f, atan_std(x) = %10.3f, atan_int(x) = %10.3f, error = %10.3f%%\n", x, atan_std, atan_int, atan_int/atan_std*100-100);
   }
   
}

您只需要记住缩放定点算术变量(1.0 -> 10000),并且

atan
的最终输出以弧度为单位,以便获得度数
atan_int_scaled * 45 / 7854


0
投票

谢谢@jhole 为我指出反正切近似公式的方向。我最终对其进行了一些修改以适应我的 Nextion 屏幕所需的内容,包括 X 和 Y 坐标的输入、以度为单位的输出,并且不要使变量太大而溢出(选择 x10 乘数以允许 1 位数字进行舍入) ).

我能够使用这个公式启动并运行它(来自 Desmos 的屏幕截图):

'a' 是 Y 坐标,'b' 是 X 坐标。我在图片中包含了基准三角函数,您可以看到它非常接近。

实施该公式的一个关键部分是它仅在 RISE 的情况下有效 <= RUN, which translates to working only in the 45 degree segments along the x axis. To get a measurement in the top and bottom four 45 degree segments I had to flip around the RISE/RUN variables. From there I was able to add/subtract from the 0,180,270,360 values depending on what quadrant you are touching.

这是已实现的 Nextion 代码:

RUN.val=tch0-400
RISE.val=tch1-240
if(RISE.val<0)
{
  RISE.val*=-1
}
if(RUN.val<0)
{
  RUN.val*=-1
}
if(RISE.val>=RUN.val) // Switch around rise and run cus over 45 degrees
{
  Quad.val=1
  temp.val=RISE.val
  RISE.val=RUN.val
  RUN.val=temp.val
}else
{
  Quad.val=0
}
RISEscaled.val=RISE.val*10
M.val=RISEscaled.val/RUN.val
if(M.val>=0)
{
  absM.val=M.val
}else
{
  absM.val=M.val*-1
}
absMneg.val=absM.val-10
DoubleM.val=20+absM.val
A.val=573*RISE.val
A.val*=DoubleM.val
A.val*=absMneg.val
A.val/=RUN.val
A.val/=1000
C.val=4500*RISE.val
C.val/=RUN.val
C.val/=10
Theta.val=C.val-A.val
ThetaCheck.val=Theta.val%10
if(ThetaCheck.val>=5)
{
  Theta.val/=10
  Theta.val++
}else
{
  Theta.val/=10
}
if(tch0>=400)
{
  if(tch1<=240) // Greater than halfway
  {
    if(Quad.val==1)
    {
      AngleAdj.val=90-Theta.val
    }else
    {
      AngleAdj.val=Theta.val
    }
  }else
  {
    if(Quad.val==1)
    {
      AngleAdj.val=270+Theta.val
    }else
    {
      AngleAdj.val=360-Theta.val
    }
  }
}else // Left half of the screen
{
  if(tch1<=240) // Greater than halfway
  {
    if(Quad.val==1)
    {
      AngleAdj.val=90+Theta.val
    }else
    {
      AngleAdj.val=180-Theta.val
    }
  }else
  {
    if(Quad.val==1)
    {
      AngleAdj.val=270-Theta.val
    }else
    {
      AngleAdj.val=180+Theta.val
    }
  }
}
if(AngleAdj.val==360)
{
  AngleAdj.val=0
}
p1.pic=403-AngleAdj.val
© www.soinside.com 2019 - 2024. All rights reserved.