当前位置:   article > 正文

C语言正余弦函数定点查表算法原理及实现_sin查表c程序

sin查表c程序

如果用数学库中的sin和cos函数计算,可能涉及浮点、乘法、除法运算,运行速率较低。这里介绍一种正余弦查表算法。

  • 参考代码:SMT32 FOC电机库的mc_math.c

首先我们知道正弦和余弦的函数值差了90°,所以查表数据只需要保存正弦或者余弦的结果即可。待计算的角度范围为[0,2π],我们只需要保存[0,\frac{\pi}{2}]的角度,其它象限的角度通过三角函数公式变换一下即可。 

1 原理与实现步骤

(1)正余弦表格生成

    第一步当然是保存[0,\frac{\pi}{2}]范围内正余弦函数的值,保存的越多计算结果越精确,但又消耗更多的存储空间。在STM32电机库中采用int16类型表示角度,其中(0,32767]表示(0°,180°],[-32768,0]表示(180°,360°],同时为了避免浮点运算,这里采用Q15格式表示角度。之所以是Q15是因为保存的是 [0,\frac{\pi}{2}]范围内正余弦函数的值,后续转换到别的象限还有正负号,故最后求出来的正余弦值也可以用int16范围变量表示。

    这里将[0,\frac{\pi}{2}]范围内的角度细分为256份,整个周期就细分了1024份。故将\sin(\frac{\pi}{2} * \frac{i}{256})\times 2^{15}保存为表格,正余弦结果表格生成程序如下

  1. #include <stdio.h>
  2. #include <math.h>
  3. #define NUM 256
  4. #define PI 3.1415926535898f
  5. int main()
  6. {
  7. int i;
  8. for(i = 0; i < NUM; i++)
  9. {
  10. printf("0x%04X,", (unsigned short)((sin(PI/2*i/NUM)) * 32768));
  11. if((i+1) % 8==0)
  12. {
  13. putchar('\n');
  14. }
  15. }
  16. return 0;
  17. }

(2)索引获取

输入的角度为int16范围的hAngle,作如下处理,求对应角度的索引:

  1. int32_t shindex;
  2. uint16_t uhindex;
  3. shindex = ( ( int32_t )32768 + ( int32_t )hAngle );
  4. uhindex = ( uint16_t )shindex;
  5. uhindex /= ( uint16_t )64;

shindex是一个包含四个象限范围的值,将四个象限共细分为2^{16}=65536份,每个象限有2^{14}个数,但基于存储空间考虑,在前面生成的正余弦表中,我们将一个象限细分为256份,则四个象限共细分为1024份,所以除以2^{6}=64,这样就把用户输入的int16_t范围的角度转到了1024内。此时可以根据最高两位判断角度所在的象限,同时将这个数取余256,就是该角度所对应在正余弦表中的索引。

(3)判断角度范围

前面代码中,将int16的角度转为正数,是因为后面要根据我们实际定义的数组大小缩放得到索引,但这样四个象限的顺序乱了,所以需要判断角度的范围。此时[0,32768)表示(180°,360°],(32768,65535]表示[0,180°) ,前面又除以了一个64,故十进制数范围和其对应的角度范围如下表所示:

十进制数范围角度范围
(0,256)(180°,270°)
(256,512)(270°,360°)
(512,768)(0°,90°)
(768,1024)(90°,180°)

故角度和SIN_MASK相与,判断最高两位就可以知道输入角度的范围。

  1. #define SIN_MASK 0x0300u
  2. #define U0_90 0x0200u
  3. #define U90_180 0x0300u
  4. #define U180_270 0x0000u
  5. #define U270_360 0x0100u

(4)查表求角度

由于前面建立的表中角度是(0°,90°)范围内的,所以根据三角函数公式转换其它象限的角度到这个范围内即可求出。下面代码中将uint16类型的变量uhindex强制转化为uint8范围,实际上就是对256取余,等价于uhindex % 256,就是所求角度在数组中的索引。

  1. switch ( ( uint16_t )( uhindex ) & SIN_MASK )
  2. {
  3. case U0_90:
  4. Local_Components.hSin = hSin_Cos_Table[( uint8_t )( uhindex )];
  5. Local_Components.hCos = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  6. break;
  7. case U90_180:
  8. Local_Components.hSin = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  9. Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( uhindex )];
  10. break;
  11. case U180_270:
  12. Local_Components.hSin = -hSin_Cos_Table[( uint8_t )( uhindex )];
  13. Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  14. break;
  15. case U270_360:
  16. Local_Components.hSin = -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  17. Local_Components.hCos = hSin_Cos_Table[( uint8_t )( uhindex )];
  18. break;
  19. default:
  20. break;
  21. }

2 完整代码

  1. #define int16_t short
  2. #define uint8_t unsigned char
  3. #define uint16_t unsigned short
  4. #define int32_t unsigned int
  5. #define SIN_COS_TABLE {\
  6. 0x0000,0x00C9,0x0192,0x025B,0x0324,0x03ED,0x04B6,0x057F,\
  7. 0x0648,0x0711,0x07D9,0x08A2,0x096A,0x0A33,0x0AFB,0x0BC4,\
  8. 0x0C8C,0x0D54,0x0E1C,0x0EE3,0x0FAB,0x1072,0x113A,0x1201,\
  9. 0x12C8,0x138F,0x1455,0x151C,0x15E2,0x16A8,0x176E,0x1833,\
  10. 0x18F9,0x19BE,0x1A82,0x1B47,0x1C0B,0x1CCF,0x1D93,0x1E57,\
  11. 0x1F1A,0x1FDD,0x209F,0x2161,0x2223,0x22E5,0x23A6,0x2467,\
  12. 0x2528,0x25E8,0x26A8,0x2767,0x2826,0x28E5,0x29A3,0x2A61,\
  13. 0x2B1F,0x2BDC,0x2C99,0x2D55,0x2E11,0x2ECC,0x2F87,0x3041,\
  14. 0x30FB,0x31B5,0x326E,0x3326,0x33DF,0x3496,0x354D,0x3604,\
  15. 0x36BA,0x376F,0x3824,0x38D9,0x398C,0x3A40,0x3AF2,0x3BA5,\
  16. 0x3C56,0x3D07,0x3DB8,0x3E68,0x3F17,0x3FC5,0x4073,0x4121,\
  17. 0x41CE,0x427A,0x4325,0x43D0,0x447A,0x4524,0x45CD,0x4675,\
  18. 0x471C,0x47C3,0x4869,0x490F,0x49B4,0x4A58,0x4AFB,0x4B9D,\
  19. 0x4C3F,0x4CE0,0x4D81,0x4E20,0x4EBF,0x4F5D,0x4FFB,0x5097,\
  20. 0x5133,0x51CE,0x5268,0x5302,0x539B,0x5432,0x54C9,0x5560,\
  21. 0x55F5,0x568A,0x571D,0x57B0,0x5842,0x58D3,0x5964,0x59F3,\
  22. 0x5A82,0x5B0F,0x5B9C,0x5C28,0x5CB3,0x5D3E,0x5DC7,0x5E4F,\
  23. 0x5ED7,0x5F5D,0x5FE3,0x6068,0x60EB,0x616E,0x61F0,0x6271,\
  24. 0x62F1,0x6370,0x63EE,0x646C,0x64E8,0x6563,0x65DD,0x6656,\
  25. 0x66CF,0x6746,0x67BC,0x6832,0x68A6,0x6919,0x698B,0x69FD,\
  26. 0x6A6D,0x6ADC,0x6B4A,0x6BB7,0x6C23,0x6C8E,0x6CF8,0x6D61,\
  27. 0x6DC9,0x6E30,0x6E96,0x6EFB,0x6F5E,0x6FC1,0x7022,0x7083,\
  28. 0x70E2,0x7140,0x719D,0x71F9,0x7254,0x72AE,0x7307,0x735E,\
  29. 0x73B5,0x740A,0x745F,0x74B2,0x7504,0x7555,0x75A5,0x75F3,\
  30. 0x7641,0x768D,0x76D8,0x7722,0x776B,0x77B3,0x77FA,0x783F,\
  31. 0x7884,0x78C7,0x7909,0x794A,0x7989,0x79C8,0x7A05,0x7A41,\
  32. 0x7A7C,0x7AB6,0x7AEE,0x7B26,0x7B5C,0x7B91,0x7BC5,0x7BF8,\
  33. 0x7C29,0x7C59,0x7C88,0x7CB6,0x7CE3,0x7D0E,0x7D39,0x7D62,\
  34. 0x7D89,0x7DB0,0x7DD5,0x7DFA,0x7E1D,0x7E3E,0x7E5F,0x7E7E,\
  35. 0x7E9C,0x7EB9,0x7ED5,0x7EEF,0x7F09,0x7F21,0x7F37,0x7F4D,\
  36. 0x7F61,0x7F74,0x7F86,0x7F97,0x7FA6,0x7FB4,0x7FC1,0x7FCD,\
  37. 0x7FD8,0x7FE1,0x7FE9,0x7FF0,0x7FF5,0x7FF9,0x7FFD,0x7FFE}
  38. const int16_t hSin_Cos_Table[256] = SIN_COS_TABLE;
  39. #define SIN_MASK 0x0300u
  40. #define U0_90 0x0200u
  41. #define U90_180 0x0300u
  42. #define U180_270 0x0000u
  43. #define U270_360 0x0100u
  44. typedef struct
  45. {
  46. int16_t hCos;
  47. int16_t hSin;
  48. } Trig_Components;
  49. Trig_Components MCM_Trig_Functions( int16_t hAngle )
  50. {
  51. int32_t shindex;
  52. uint16_t uhindex;
  53. Trig_Components Local_Components;
  54. /* 10 bit index computation */
  55. shindex = ( ( int32_t )32768 + ( int32_t )hAngle );
  56. uhindex = ( uint16_t )shindex;
  57. uhindex /= ( uint16_t )64;
  58. switch ( ( uint16_t )( uhindex ) & SIN_MASK )
  59. {
  60. case U0_90:
  61. Local_Components.hSin = hSin_Cos_Table[( uint8_t )( uhindex )];
  62. Local_Components.hCos = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  63. break;
  64. case U90_180:
  65. Local_Components.hSin = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  66. Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( uhindex )];
  67. break;
  68. case U180_270:
  69. Local_Components.hSin = -hSin_Cos_Table[( uint8_t )( uhindex )];
  70. Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  71. break;
  72. case U270_360:
  73. Local_Components.hSin = -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
  74. Local_Components.hCos = hSin_Cos_Table[( uint8_t )( uhindex )];
  75. break;
  76. default:
  77. break;
  78. }
  79. return ( Local_Components );
  80. }

注意事项

首先由于这个函数是基于Q15格式表示的角度来求正余弦的结果的,如果你想用上面的函数计算一个我们正常逻辑中0~360°定义的角度的正余弦的话,还需要将这个角度按比例转为定点的,这个过程又涉及到浮点,但我们这样做的目的本来就是为了避免这些浮点运算,因为CPU做浮点运算效率很低。所以在你的程序中,应该使用int16_t格式表示角度,即我们要有定点运算的思维,用定点的原因是浮点计算效率太低了。

比如在FOC中采用int16_t表示电角度,后续如果我想控制电机转到它的电角度45°的话,我们只需要设置位置环的目标值为8192即可,用定点并不会影响我们的应用效果。

另外需要注意的是,如果你想求一个数乘以cos或sin的结果的话,比如你想求`10*cos0 = 10`,在这个函数里得到的结果就是327670,所以得出来的结果应该右移15位才是你预期的结果。当然这里32767右移15位等于除以32768是有误差的,包括我们生成的表格中也有误差,但实际应用中,这个误差完全可以忽略。而且你表示角度的值越大,这个误差是越小的,所以对于你传进来的数,也可以考虑定点化为int16_t类型,这样两个数相乘的结果可以用int32_t保存,而且误差很小。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/454612
推荐阅读
相关标签
  

闽ICP备14008679号