当前位置:   article > 正文

ADIS16465 加速度和陀螺仪数据融合及调试(kalman滤波)

adis16465

先通过SPI读取加速度和角速度,然后在单片机里通过通过kalman滤波进行数据融合,输出X轴角度,同时通过CAN口将融合前的原始数据和融合的角度发送给上位机,上位机借用基于ROS的Plotjuggler绘图工具,进行数据的图形化显示,极大的方便了滤波参数的调整。

 

下面贴出调试好的kalman滤波部分代码,以及ROS部分代码。

kalman滤波代码(借用他人代码修改了极少部分,原作者看到请留意我好署名原作者):

其中参数acc_x,y,z为三轴加速度值,有的代码只取了x轴,考虑非理想状态,最好是三轴都取

  1. #define DEG2RAD 0.017453292519943295769236907684886f // 角度转弧度
  2. #define RAD2DEG 57.295779513082320876798154814105f // 弧度转角度
  3. float Q_angle = 0.0008f; // 陀螺仪噪声的协方差
  4. float R_measure = 0.05f; // 加速度计的协方差
  5. float Q_bias = 0.0008f; // Q_bias为陀螺仪漂移
  6. float dt = 0.005f;
  7. float bias = 0;
  8. float k_0 = 0,k_1 = 0; // 卡尔曼增益
  9. float pdot[4] = { 0.0f,0.0f,0.0f,0.0f };
  10. float p[2][2] = { {1.0f,0.0f},{0.0f,1.0f} };
  11. float kalman(float acc_x,float acc_y,float acc_z,float gyro_x)
  12. {
  13. static float angle = 0.0f; // 卡尔曼滤波器的输岀值,最优估计的角度
  14. float angle_acc,acc1_z,acc1_x,acc1_y;
  15. acc1_z = acc_z * 0.00025;
  16. acc1_x = acc_x * 0.00025;
  17. acc1_y = acc_y * 0.00025;
  18. angle_acc = atan2f(acc1_x,sqrtf(acc1_z*acc1_z + acc1_y*acc1_y)) * RAD2DEG;
  19. //1、卡尔曼第一个公式(状态预测):X(k|k-1)=AX(k-1|k-1)+BU(k)
  20. angle += (gyro_x/40.0 - bias) * dt;
  21. //2、卡尔曼第二个公式(计算误差协方差):AP(k-1|k-1)A' + Q
  22. pdot[0] = Q_angle - p[0][1] + p[1][0];
  23. pdot[1] = -p[1][1];
  24. pdot[2] = -p[1][1];
  25. pdot[3] = Q_bias;
  26. p[0][0] += pdot[0] * dt;
  27. p[0][1] += pdot[1] * dt;
  28. p[1][0] += pdot[2] * dt;
  29. p[1][1] += pdot[3] * dt;
  30. //3、卡尔曼第三个公式(计算卡尔曼增益):Kg(k) = P(k|k-1)H' / (HP(k|k-1)H' + R)
  31. //R --->系统测量噪声协方差
  32. k_0 = p[0][0]/(p[0][0]+R_measure);
  33. k_1 = p[1][0]/(p[0][0]+R_measure);
  34. //4、卡尔曼第四个公式(修正估计):X(k|k) = X(k|k-1) + Kg(k)(Z(k) - HX(k|k-1))
  35. angle += k_0 * (angle_acc - angle);
  36. bias += k_1 * (angle_acc - angle);
  37. //5、卡尔曼第五个公式(更新误差协方差):P(k|k) = (1 - Kg(k)H) P(k|k-1)
  38. p[0][0] = (1 - k_0) * p[0][0];
  39. p[0][1] = (1 - k_0) * p[0][1];
  40. p[1][0] = (1 - k_0) * p[1][0];
  41. p[1][1] = (1 - k_0) * p[1][1];
  42. return angle;
  43. }

ROS下CAN部分代码(使用的珠海创芯科技的USB转CAN),main部分主要是进行can设备的初始化,以及初始化话题等,在主循环进行can消息的发送、IMU数据话题发布,IMU数据在后面的can主代码里通过读取can口获取。其中bms结构为发布的的IMU数据(修改了之前代码,懒得重命名,将就着看)将在后面的Plotjuggler里订阅。

  1. int main(int argc, char** argv)
  2. {
  3. pthread_t tid;
  4. ros::init(argc, argv, "abc");
  5. ros::NodeHandle n,nh,ns;
  6. std::string can_device;
  7. ros::Publisher bat_pub = n.advertise<std_msgs::Float32MultiArray>("bms", 1000);
  8. std_msgs::Float32MultiArray bms;
  9. bms.layout.dim.push_back(std_msgs::MultiArrayDimension());
  10. bms.layout.dim[0].size = 6;
  11. bms.layout.dim[0].stride = 6;
  12. bms.layout.dim[0].label = "bms";
  13. bms.data.resize(6);
  14. signal(SIGALRM, sigHandler);
  15. signal(SIGINT, sigHandler);
  16. if(initCAN(VCI_USBCAN2) == 0) // Initialization device
  17. exit(0);
  18. getCANInfo(VCI_USBCAN2); // get device information
  19. if(pthread_create(&tid,NULL,receiveFrame,(void *)&run) != 0)
  20. ROS_INFO("Thread: Create can1 thread failed!");
  21. frame = (PVCI_CAN_OBJ)malloc(sizeof(VCI_CAN_OBJ));
  22. memset(frame,0x00,sizeof(VCI_CAN_OBJ));
  23. ros::Rate loop_rate(100);
  24. while (ros::ok())
  25. {
  26. frame->id = 0x361;
  27. frame->dlc = 4;
  28. frame->data[0] = 0;
  29. frame->data[1] = 200;
  30. frame->data[2] = 0;
  31. frame->data[3] = 0;
  32. frame->is_extended = false;
  33. frame->is_rtr = false;
  34. sendCTLFrame(frame);
  35. bms.data[0] = s_bms.acc_x;
  36. bms.data[1] = s_bms.acc_y;
  37. bms.data[2] = s_bms.acc_z;
  38. bms.data[3] = s_bms.gyro;
  39. bms.data[4] = s_bms.angle_acc;
  40. bms.data[5] = s_bms.angle_kalman;
  41. bat_pub.publish(bms);
  42. ros::spinOnce();
  43. loop_rate.sleep();
  44. }
  45. pthread_join(tid,NULL);
  46. }

can驱动部分代码,receiveFrame函数读取所有帧,通过帧ID 0x333 跳转到adis16450()
函数进行IMU原始数据以及融合数据的格式转换,并写入s_bms结构,用于消息发布,注意变量类型(此处必须为short 因为涉及负数数据):

  1. #include <ros/ros.h>
  2. #include <memory>
  3. #include <string>
  4. #include <csignal>
  5. #include <geometry_msgs/Twist.h>
  6. #include "can/can.h"
  7. #include "std_msgs/UInt8MultiArray.h"
  8. #include "std_msgs/MultiArrayDimension.h"
  9. #define S_FLAG 0
  10. #define R_FLAG 1
  11. double g_csb_dis[4] = {5.0,5.0};
  12. static union {
  13. char c[4];
  14. unsigned long l;
  15. }endian_test;
  16. #define ENDIANNESS ((char)endian_test.l)
  17. PVCI_CAN_OBJ frame;
  18. Quaternion q;
  19. S_BMS s_bms;
  20. S_MOTOR s_motor;
  21. void adis16450(PVCI_CAN_OBJ frame)
  22. {
  23. float acc_x,acc_y,acc_z,gyro,k_angle,a_angle;
  24. //short gryo_x_r = (frame->data[0]<<8) | frame->data[1];
  25. short acc_x_r = (frame->data[0]<<8) | frame->data[1];
  26. // short acc_y_r = (frame->data[2]<<8) | frame->data[3];
  27. short acc_z_r = (frame->data[2]<<8) | frame->data[3];
  28. // short gyro_r = (frame->data[6]<<8) | frame->data[7];
  29. union{
  30. float ram;
  31. unsigned char bytes[4];
  32. }thing;
  33. thing.bytes[0] = frame->data[4];
  34. thing.bytes[1] = frame->data[5];
  35. thing.bytes[2] = frame->data[6];
  36. thing.bytes[3] = frame->data[7];
  37. acc_x = acc_x_r * 0.00025;
  38. // acc_y = acc_y_r * 0.00025;
  39. acc_z = acc_z_r * 0.00025;
  40. // gyro = gyro_r/40.0 + 0.124;
  41. s_bms.acc_x = acc_x;
  42. // s_bms.acc_y = acc_y;
  43. s_bms.acc_z = acc_z;
  44. s_bms.gyro = gyro;
  45. s_bms.angle_acc = a_angle;
  46. s_bms.angle_kalman = thing.ram;
  47. //ROS_INFO("Q: %f %f",thing.ram,acc_x);
  48. }
  49. void c620InfoProc(PVCI_CAN_OBJ frame)
  50. {
  51. s_motor.l_current = frame->data[1] | (frame->data[0]<<8);
  52. s_motor.r_current = frame->data[3] | (frame->data[2]<<8);
  53. s_motor.l_rpm = frame->data[5] | (frame->data[4]<<8);
  54. s_motor.r_rpm = frame->data[7] | (frame->data[6]<<8);
  55. // ROS_INFO("RPM: %d %d",s_motor.l_rpm,s_motor.r_rpm);
  56. }
  57. void bldcInfoProc(PVCI_CAN_OBJ frame)
  58. {
  59. int i = 0;
  60. double vl,vr,vxy;
  61. vl = frame->data[0] | (frame->data[1]<<8);
  62. vr = frame->data[2] | (frame->data[3]<<8);
  63. if(vl > 20000)
  64. vl -= 65536;
  65. if(vr > 20000)
  66. vr -= 65536;
  67. vl = vl/600 * 0.439822964; // pi*d = 0.439822964
  68. vr = vr/600 * 0.439822964;
  69. vxy = (vr-vl) / 2.0 * -1.0;
  70. vth = (vr+vl) / 0.327 * -1.0;
  71. vx = cos(vth) * vxy;
  72. vy = -sin(vth) * vxy;
  73. }
  74. void csbInfoProc(PVCI_CAN_OBJ frame)
  75. {
  76. g_csb_dis[0] = (frame->data[0]<<8 | frame->data[1]) / 1000.0;
  77. g_csb_dis[1] = (frame->data[2]<<8 | frame->data[3]) / 1000.0;
  78. }
  79. void batInfoProc(PVCI_CAN_OBJ frame)
  80. {
  81. s_bms.coulomb = ((frame->data[0]<<8) | frame->data[1]) / 56470.0;
  82. s_bms.voltage = (70.8 * (frame->data[2]<<8 | frame->data[3]) / 65535.0);
  83. s_bms.current = frame->data[4]<<8 | frame->data[5];
  84. s_bms.current = 64.0 * (s_bms.current - 32767.0) / 32767.0;
  85. s_bms.temperature = 510.0 * (frame->data[6]<<8 | frame->data[7]) / 65535.0 - 273.0;
  86. }
  87. int printFrame(PVCI_CAN_OBJ frame,unsigned char flag)
  88. {
  89. int i = 0;
  90. if(flag)
  91. ROS_INFO("RX(0x%08X): ",frame->id);
  92. else
  93. ROS_INFO("TX(0x%08X): ",frame->id);
  94. if(frame->is_extended==0) ROS_INFO(" Standard ");
  95. if(frame->is_extended==1) ROS_INFO(" Extend ");
  96. if(frame->is_rtr==0) ROS_INFO(" Data ");
  97. if(frame->is_rtr==1) ROS_INFO(" Remote ");
  98. ROS_INFO("DLC:0x%02X ",frame->dlc);
  99. for(i=0;i<(frame->dlc);i++)
  100. printf("%02X ",frame->data[i]);
  101. printf("\n");
  102. return 1;
  103. }
  104. void *receiveFrame(void *arg)
  105. {
  106. int r_len = 0;
  107. int j = 0;
  108. int *run = (int*)arg;
  109. VCI_CAN_OBJ rec[2500]; // 接收缓存,设为3000为佳
  110. if('b' == ENDIANNESS) // big endian
  111. ROS_INFO("Big endian");
  112. else
  113. ROS_INFO("Little endian");
  114. while(*run & 0xff)
  115. {
  116. if((r_len = VCI_Receive(VCI_USBCAN2,0,0,rec,2500,100))>0) //调用接收函数,如果有数据,进行数据处理显示。
  117. {
  118. for(j=0; j<r_len; j++)
  119. {
  120. switch(rec[j].id)
  121. {
  122. case 0x382:
  123. bldcInfoProc(&rec[j]);
  124. break;
  125. case 0x383:
  126. csbInfoProc(&rec[j]);
  127. // printFrame(&rec[j],R_FLAG);
  128. break;
  129. case 0x377:
  130. batInfoProc(&rec[j]);
  131. break;
  132. case 0x332:
  133. c620InfoProc(&rec[j]);
  134. break;
  135. case 0x333:
  136. adis16450(&rec[j]);
  137. break;
  138. default:
  139. break;
  140. }
  141. }
  142. }
  143. usleep(10000);
  144. }
  145. pthread_exit(0);
  146. }
  147. int initFrame(PVCI_CAN_OBJ frame) // 需要发送的帧,结构体设置
  148. {
  149. int i = 0;
  150. frame->id = 0x381;
  151. frame->sendType = 0;
  152. frame->is_rtr = 0;
  153. frame->is_extended = 0;
  154. frame->dlc=8;
  155. for(i=0; i<(frame->dlc); i++)
  156. frame->data[i] = 0;
  157. return 1;
  158. }
  159. int getCANInfo(unsigned int DevType) //获取CAN设备信息
  160. {
  161. int i = 0;
  162. VCI_BOARD_INFO pInfo;
  163. if(VCI_ReadBoardInfo(DevType,0,&pInfo)==1)//读取设备序列号、版本等信息。
  164. {
  165. ROS_INFO("Get device info success!");
  166. /*
  167. printf("Serial Number:");
  168. for(i=0; i<20; i++)
  169. printf("%c", pInfo.str_Serial_Num[i]);
  170. printf("\n");
  171. printf("HW Type:");
  172. for(i=0; i<10; i++)
  173. printf("%c", pInfo.str_hw_Type[i]);
  174. printf("\n");
  175. */
  176. return 1;
  177. }
  178. else
  179. {
  180. ROS_INFO("Get can info error!");
  181. return 0;
  182. }
  183. }
  184. int sendCTLFrame(PVCI_CAN_OBJ frame)
  185. {
  186. if(VCI_Transmit(VCI_USBCAN2, 0, 0, frame, 1) == 1)
  187. {
  188. // printFrame(frame,S_FLAG);
  189. return 1;
  190. }
  191. else
  192. return 0;
  193. }
  194. int initCAN(unsigned int DevType)
  195. {
  196. if(VCI_OpenDevice(VCI_USBCAN2,0,0) == 1) //打开设备
  197. {
  198. ROS_INFO("Open CAN1 success!");
  199. }
  200. else
  201. {
  202. ROS_INFO("Open CAN1 error!");
  203. return 0;
  204. }
  205. //初始化参数,严格参数二次开发函数库说明书
  206. VCI_INIT_CONFIG config;
  207. config.AccCode=0x80000000;
  208. config.AccMask=0xFFFFFFFF;
  209. config.Filter=1; //接收所有帧
  210. config.Timing0=0x00; //波特率125 Kbps 0x03 0x1C
  211. config.Timing1=0x1C;
  212. config.Mode=0; //正常模式
  213. if(VCI_InitCAN(DevType,0,0,&config)!=1)
  214. {
  215. ROS_INFO("Init CAN1 error!");
  216. VCI_CloseDevice(DevType,0);
  217. return 0;
  218. }
  219. if(VCI_InitCAN(DevType,0,1,&config)!=1)
  220. {
  221. ROS_INFO("Init CAN2 error!");
  222. VCI_CloseDevice(DevType,0);
  223. return 0;
  224. }
  225. if(VCI_StartCAN(DevType,0,0)!=1)
  226. {
  227. ROS_INFO("Start CAN1 error!");
  228. VCI_CloseDevice(DevType,0);
  229. return 0;
  230. }
  231. if(VCI_StartCAN(DevType,0,1)!=1)
  232. {
  233. ROS_INFO("Start CAN2 error!");
  234. VCI_CloseDevice(DevType,0);
  235. return 0;
  236. }
  237. return 1;
  238. }
  239. void closeCAN(PVCI_CAN_OBJ frame)
  240. {
  241. VCI_ResetCAN(VCI_USBCAN2, 0, 0); // 复位CAN1通道
  242. usleep(100000);// delay 100ms
  243. VCI_ResetCAN(VCI_USBCAN2, 0, 1);
  244. usleep(100000);
  245. VCI_CloseDevice(VCI_USBCAN2,0); // 关闭设备
  246. free(frame);
  247. }

头文件:

  1. #ifndef CAN_H
  2. #define CAN_H
  3. //接口卡类型定义
  4. #define VCI_PCI5121 1
  5. #define VCI_PCI9810 2
  6. #define VCI_USBCAN1 3
  7. #define VCI_USBCAN2 4
  8. #define VCI_PCI9820 5
  9. #define VCI_CAN232 6
  10. #define VCI_PCI5110 7
  11. #define VCI_CANLite 8
  12. #define VCI_ISA9620 9
  13. #define VCI_ISA5420 10
  14. //CAN错误码
  15. #define ERR_CAN_OVERFLOW 0x0001 //CAN控制器内部FIFO溢出
  16. #define ERR_CAN_ERRALARM 0x0002 //CAN控制器错误报警
  17. #define ERR_CAN_PASSIVE 0x0004 //CAN控制器消极错误
  18. #define ERR_CAN_LOSE 0x0008 //CAN控制器仲裁丢失
  19. #define ERR_CAN_BUSERR 0x0010 //CAN控制器总线错误
  20. //通用错误码
  21. #define ERR_DEVICEOPENED 0x0100 //设备已经打开
  22. #define ERR_DEVICEOPEN 0x0200 //打开设备错误
  23. #define ERR_DEVICENOTOPEN 0x0400 //设备没有打开
  24. #define ERR_BUFFEROVERFLOW 0x0800 //缓冲区溢出
  25. #define ERR_DEVICENOTEXIST 0x1000 //此设备不存在
  26. #define ERR_LOADKERNELDLL 0x2000 //装载动态库失败
  27. #define ERR_CMDFAILED 0x4000 //执行命令失败错误码
  28. #define ERR_BUFFERCREATE 0x8000 //内存不足
  29. //函数调用返回状态值
  30. #define STATUS_OK 1
  31. #define STATUS_ERR 0
  32. #define USHORT unsigned short int
  33. #define BYTE unsigned char
  34. #define CHAR char
  35. #define UCHAR unsigned char
  36. #define UINT unsigned int
  37. #define DWORD unsigned int
  38. #define PVOID void*
  39. #define ULONG unsigned int
  40. #define INT int
  41. #define UINT32 UINT
  42. #define LPVOID void*
  43. #define BOOL BYTE
  44. #define TRUE 1
  45. #define FALSE 0
  46. #if 1
  47. //1.ZLGCAN系列接口卡信息的数据类型。
  48. typedef struct _VCI_BOARD_INFO{
  49. USHORT hw_Version;
  50. USHORT fw_Version;
  51. USHORT dr_Version;
  52. USHORT in_Version;
  53. USHORT irq_Num;
  54. BYTE can_Num;
  55. CHAR str_Serial_Num[20];
  56. CHAR str_hw_Type[40];
  57. USHORT Reserved[4];
  58. }VCI_BOARD_INFO,*PVCI_BOARD_INFO;
  59. //2.定义CAN信息帧的数据类型。
  60. typedef struct _VCI_CAN_OBJ{
  61. UINT id;
  62. UINT timeStamp;
  63. BYTE timeFlag;
  64. BYTE sendType;
  65. BYTE is_rtr; // 是否是远程帧
  66. BYTE is_extended; // 是否是扩展帧
  67. BYTE dlc; // data len
  68. BYTE data[8];
  69. BYTE reserved[3];
  70. }VCI_CAN_OBJ,*PVCI_CAN_OBJ;
  71. //3.定义CAN控制器状态的数据类型。
  72. typedef struct _VCI_CAN_STATUS{
  73. UCHAR ErrInterrupt;
  74. UCHAR regMode;
  75. UCHAR regStatus;
  76. UCHAR regALCapture;
  77. UCHAR regECCapture;
  78. UCHAR regEWLimit;
  79. UCHAR regRECounter;
  80. UCHAR regTECounter;
  81. DWORD Reserved;
  82. }VCI_CAN_STATUS,*PVCI_CAN_STATUS;
  83. //4.定义错误信息的数据类型。
  84. typedef struct _ERR_INFO{
  85. UINT ErrCode;
  86. BYTE Passive_ErrData[3];
  87. BYTE ArLost_ErrData;
  88. }VCI_ERR_INFO,*PVCI_ERR_INFO;
  89. //5.定义初始化CAN的数据类型
  90. typedef struct _INIT_CONFIG{
  91. DWORD AccCode;
  92. DWORD AccMask;
  93. DWORD Reserved;
  94. UCHAR Filter;
  95. UCHAR Timing0;
  96. UCHAR Timing1;
  97. UCHAR Mode;
  98. }VCI_INIT_CONFIG,*PVCI_INIT_CONFIG;
  99. struct Quaternion
  100. {
  101. float w, x, y, z;
  102. float vx, vy, vz;
  103. float ax, ay, az;
  104. };
  105. typedef struct bms{
  106. float_t voltage;
  107. float_t current;
  108. float_t temperature;
  109. float_t coulomb;
  110. float_t acc_x;
  111. float_t acc_y;
  112. float_t acc_z;
  113. float_t gyro;
  114. float_t angle_acc;
  115. float_t angle_kalman;
  116. }S_BMS;
  117. typedef struct motor{
  118. short l_rpm;
  119. short r_rpm;
  120. short l_current;
  121. short r_current;
  122. }S_MOTOR;
  123. struct EulerAngles
  124. {
  125. float roll, pitch, yaw;
  126. };
  127. extern Quaternion q;
  128. extern P_V_INFO pVinfo;
  129. extern S_BMS s_bms;
  130. extern S_MOTOR s_motor;
  131. extern void framePrint(unsigned int cid,unsigned char *buf);
  132. extern int initCAN(unsigned int DevType);
  133. extern void closeCAN(PVCI_CAN_OBJ frame);
  134. extern int getCANInfo(unsigned int DevType);
  135. extern void *receiveFrame(void *arg);
  136. extern void *can2RecvFrame(void *arg);
  137. extern int sendCTLFrame(PVCI_CAN_OBJ frame);
  138. extern double g_csb_dis[];
  139. extern PVCI_CAN_OBJ frame;
  140. extern double vx,vy,vth;
  141. #ifdef __cplusplus
  142. extern "C" {
  143. #endif
  144. DWORD VCI_OpenDevice(DWORD DeviceType,DWORD DeviceInd,DWORD Reserved);
  145. DWORD VCI_CloseDevice(DWORD DeviceType,DWORD DeviceInd);
  146. DWORD VCI_InitCAN(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_INIT_CONFIG pInitConfig);
  147. DWORD VCI_ReadBoardInfo(DWORD DeviceType,DWORD DeviceInd,PVCI_BOARD_INFO pInfo);
  148. DWORD VCI_ReadErrInfo(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_ERR_INFO pErrInfo);
  149. DWORD VCI_ReadCANStatus(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_STATUS pCANStatus);
  150. DWORD VCI_GetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);
  151. DWORD VCI_SetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);
  152. ULONG VCI_GetReceiveNum(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
  153. DWORD VCI_ClearBuffer(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
  154. DWORD VCI_StartCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
  155. DWORD VCI_ResetCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
  156. ULONG VCI_Transmit(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pSend,UINT Len);
  157. ULONG VCI_Receive(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pReceive,UINT Len,INT WaitTime);
  158. #ifdef __cplusplus
  159. }
  160. #endif
  161. #endif
  162. #endif

具体编译的时候请参考硬件厂家提供的资料,还有引入对应的库等,有的版本硬件驱动库还存在bug,本文章主要用作自己资料记录,仅贴出部分代码,如有技术方面问题可留言共同交流探讨,仅此而已。

最终效果图:

0cc900adee094481abc360e7cca0f97a.jpeg

 ec67b2d56b53472ba83173dd3dd383da.jpeg

31587efa7e644cdb96d579ed748d69c3.jpeg

完全调好之后的忘了拍照,贴几个随便拍的,最终效果在静止状态角度在正负0.005之间变化 (好像是的)。

 

 

 

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

闽ICP备14008679号