赞
踩
先通过SPI读取加速度和角速度,然后在单片机里通过通过kalman滤波进行数据融合,输出X轴角度,同时通过CAN口将融合前的原始数据和融合的角度发送给上位机,上位机借用基于ROS的Plotjuggler绘图工具,进行数据的图形化显示,极大的方便了滤波参数的调整。
下面贴出调试好的kalman滤波部分代码,以及ROS部分代码。
kalman滤波代码(借用他人代码修改了极少部分,原作者看到请留意我好署名原作者):
其中参数acc_x,y,z为三轴加速度值,有的代码只取了x轴,考虑非理想状态,最好是三轴都取
- #define DEG2RAD 0.017453292519943295769236907684886f // 角度转弧度
- #define RAD2DEG 57.295779513082320876798154814105f // 弧度转角度
- float Q_angle = 0.0008f; // 陀螺仪噪声的协方差
- float R_measure = 0.05f; // 加速度计的协方差
- float Q_bias = 0.0008f; // Q_bias为陀螺仪漂移
- float dt = 0.005f;
- float bias = 0;
- float k_0 = 0,k_1 = 0; // 卡尔曼增益
- float pdot[4] = { 0.0f,0.0f,0.0f,0.0f };
- float p[2][2] = { {1.0f,0.0f},{0.0f,1.0f} };
-
- float kalman(float acc_x,float acc_y,float acc_z,float gyro_x)
- {
- static float angle = 0.0f; // 卡尔曼滤波器的输岀值,最优估计的角度
- float angle_acc,acc1_z,acc1_x,acc1_y;
- acc1_z = acc_z * 0.00025;
- acc1_x = acc_x * 0.00025;
- acc1_y = acc_y * 0.00025;
- angle_acc = atan2f(acc1_x,sqrtf(acc1_z*acc1_z + acc1_y*acc1_y)) * RAD2DEG;
-
- //1、卡尔曼第一个公式(状态预测):X(k|k-1)=AX(k-1|k-1)+BU(k)
- angle += (gyro_x/40.0 - bias) * dt;
-
- //2、卡尔曼第二个公式(计算误差协方差):AP(k-1|k-1)A' + Q
- pdot[0] = Q_angle - p[0][1] + p[1][0];
- pdot[1] = -p[1][1];
- pdot[2] = -p[1][1];
- pdot[3] = Q_bias;
-
- p[0][0] += pdot[0] * dt;
- p[0][1] += pdot[1] * dt;
- p[1][0] += pdot[2] * dt;
- p[1][1] += pdot[3] * dt;
-
- //3、卡尔曼第三个公式(计算卡尔曼增益):Kg(k) = P(k|k-1)H' / (HP(k|k-1)H' + R)
- //R --->系统测量噪声协方差
- k_0 = p[0][0]/(p[0][0]+R_measure);
- k_1 = p[1][0]/(p[0][0]+R_measure);
-
- //4、卡尔曼第四个公式(修正估计):X(k|k) = X(k|k-1) + Kg(k)(Z(k) - HX(k|k-1))
- angle += k_0 * (angle_acc - angle);
- bias += k_1 * (angle_acc - angle);
-
- //5、卡尔曼第五个公式(更新误差协方差):P(k|k) = (1 - Kg(k)H) P(k|k-1)
- p[0][0] = (1 - k_0) * p[0][0];
- p[0][1] = (1 - k_0) * p[0][1];
- p[1][0] = (1 - k_0) * p[1][0];
- p[1][1] = (1 - k_0) * p[1][1];
- return angle;
- }
ROS下CAN部分代码(使用的珠海创芯科技的USB转CAN),main部分主要是进行can设备的初始化,以及初始化话题等,在主循环进行can消息的发送、IMU数据话题发布,IMU数据在后面的can主代码里通过读取can口获取。其中bms结构为发布的的IMU数据(修改了之前代码,懒得重命名,将就着看)将在后面的Plotjuggler里订阅。
- int main(int argc, char** argv)
- {
- pthread_t tid;
- ros::init(argc, argv, "abc");
- ros::NodeHandle n,nh,ns;
- std::string can_device;
-
- ros::Publisher bat_pub = n.advertise<std_msgs::Float32MultiArray>("bms", 1000);
-
- std_msgs::Float32MultiArray bms;
- bms.layout.dim.push_back(std_msgs::MultiArrayDimension());
- bms.layout.dim[0].size = 6;
- bms.layout.dim[0].stride = 6;
- bms.layout.dim[0].label = "bms";
- bms.data.resize(6);
-
-
- signal(SIGALRM, sigHandler);
- signal(SIGINT, sigHandler);
-
- if(initCAN(VCI_USBCAN2) == 0) // Initialization device
- exit(0);
-
- getCANInfo(VCI_USBCAN2); // get device information
-
- if(pthread_create(&tid,NULL,receiveFrame,(void *)&run) != 0)
- ROS_INFO("Thread: Create can1 thread failed!");
-
- frame = (PVCI_CAN_OBJ)malloc(sizeof(VCI_CAN_OBJ));
- memset(frame,0x00,sizeof(VCI_CAN_OBJ));
-
- ros::Rate loop_rate(100);
-
- while (ros::ok())
- {
- frame->id = 0x361;
- frame->dlc = 4;
- frame->data[0] = 0;
- frame->data[1] = 200;
- frame->data[2] = 0;
- frame->data[3] = 0;
- frame->is_extended = false;
- frame->is_rtr = false;
- sendCTLFrame(frame);
-
- bms.data[0] = s_bms.acc_x;
- bms.data[1] = s_bms.acc_y;
- bms.data[2] = s_bms.acc_z;
- bms.data[3] = s_bms.gyro;
- bms.data[4] = s_bms.angle_acc;
- bms.data[5] = s_bms.angle_kalman;
- bat_pub.publish(bms);
-
-
- ros::spinOnce();
- loop_rate.sleep();
-
- }
- pthread_join(tid,NULL);
- }
can驱动部分代码,receiveFrame函数读取所有帧,通过帧ID 0x333 跳转到adis16450()
函数进行IMU原始数据以及融合数据的格式转换,并写入s_bms结构,用于消息发布,注意变量类型(此处必须为short 因为涉及负数数据):
- #include <ros/ros.h>
- #include <memory>
- #include <string>
- #include <csignal>
- #include <geometry_msgs/Twist.h>
-
- #include "can/can.h"
- #include "std_msgs/UInt8MultiArray.h"
- #include "std_msgs/MultiArrayDimension.h"
-
- #define S_FLAG 0
- #define R_FLAG 1
-
- double g_csb_dis[4] = {5.0,5.0};
-
- static union {
- char c[4];
- unsigned long l;
- }endian_test;
- #define ENDIANNESS ((char)endian_test.l)
-
- PVCI_CAN_OBJ frame;
- Quaternion q;
- S_BMS s_bms;
- S_MOTOR s_motor;
-
- void adis16450(PVCI_CAN_OBJ frame)
- {
- float acc_x,acc_y,acc_z,gyro,k_angle,a_angle;
-
- //short gryo_x_r = (frame->data[0]<<8) | frame->data[1];
- short acc_x_r = (frame->data[0]<<8) | frame->data[1];
- // short acc_y_r = (frame->data[2]<<8) | frame->data[3];
- short acc_z_r = (frame->data[2]<<8) | frame->data[3];
- // short gyro_r = (frame->data[6]<<8) | frame->data[7];
- union{
- float ram;
- unsigned char bytes[4];
- }thing;
- thing.bytes[0] = frame->data[4];
- thing.bytes[1] = frame->data[5];
- thing.bytes[2] = frame->data[6];
- thing.bytes[3] = frame->data[7];
-
-
- acc_x = acc_x_r * 0.00025;
- // acc_y = acc_y_r * 0.00025;
- acc_z = acc_z_r * 0.00025;
- // gyro = gyro_r/40.0 + 0.124;
-
- s_bms.acc_x = acc_x;
- // s_bms.acc_y = acc_y;
- s_bms.acc_z = acc_z;
- s_bms.gyro = gyro;
- s_bms.angle_acc = a_angle;
- s_bms.angle_kalman = thing.ram;
-
- //ROS_INFO("Q: %f %f",thing.ram,acc_x);
- }
-
- void c620InfoProc(PVCI_CAN_OBJ frame)
- {
- s_motor.l_current = frame->data[1] | (frame->data[0]<<8);
- s_motor.r_current = frame->data[3] | (frame->data[2]<<8);
- s_motor.l_rpm = frame->data[5] | (frame->data[4]<<8);
- s_motor.r_rpm = frame->data[7] | (frame->data[6]<<8);
- // ROS_INFO("RPM: %d %d",s_motor.l_rpm,s_motor.r_rpm);
- }
-
- void bldcInfoProc(PVCI_CAN_OBJ frame)
- {
- int i = 0;
- double vl,vr,vxy;
- vl = frame->data[0] | (frame->data[1]<<8);
- vr = frame->data[2] | (frame->data[3]<<8);
- if(vl > 20000)
- vl -= 65536;
- if(vr > 20000)
- vr -= 65536;
- vl = vl/600 * 0.439822964; // pi*d = 0.439822964
- vr = vr/600 * 0.439822964;
- vxy = (vr-vl) / 2.0 * -1.0;
- vth = (vr+vl) / 0.327 * -1.0;
- vx = cos(vth) * vxy;
- vy = -sin(vth) * vxy;
- }
-
- void csbInfoProc(PVCI_CAN_OBJ frame)
- {
- g_csb_dis[0] = (frame->data[0]<<8 | frame->data[1]) / 1000.0;
- g_csb_dis[1] = (frame->data[2]<<8 | frame->data[3]) / 1000.0;
- }
-
- void batInfoProc(PVCI_CAN_OBJ frame)
- {
- s_bms.coulomb = ((frame->data[0]<<8) | frame->data[1]) / 56470.0;
- s_bms.voltage = (70.8 * (frame->data[2]<<8 | frame->data[3]) / 65535.0);
- s_bms.current = frame->data[4]<<8 | frame->data[5];
- s_bms.current = 64.0 * (s_bms.current - 32767.0) / 32767.0;
- s_bms.temperature = 510.0 * (frame->data[6]<<8 | frame->data[7]) / 65535.0 - 273.0;
- }
-
- int printFrame(PVCI_CAN_OBJ frame,unsigned char flag)
- {
- int i = 0;
- if(flag)
- ROS_INFO("RX(0x%08X): ",frame->id);
- else
- ROS_INFO("TX(0x%08X): ",frame->id);
-
- if(frame->is_extended==0) ROS_INFO(" Standard ");
- if(frame->is_extended==1) ROS_INFO(" Extend ");
- if(frame->is_rtr==0) ROS_INFO(" Data ");
- if(frame->is_rtr==1) ROS_INFO(" Remote ");
- ROS_INFO("DLC:0x%02X ",frame->dlc);
-
- for(i=0;i<(frame->dlc);i++)
- printf("%02X ",frame->data[i]);
- printf("\n");
- return 1;
- }
-
- void *receiveFrame(void *arg)
- {
- int r_len = 0;
- int j = 0;
- int *run = (int*)arg;
-
- VCI_CAN_OBJ rec[2500]; // 接收缓存,设为3000为佳
- if('b' == ENDIANNESS) // big endian
- ROS_INFO("Big endian");
- else
- ROS_INFO("Little endian");
- while(*run & 0xff)
- {
- if((r_len = VCI_Receive(VCI_USBCAN2,0,0,rec,2500,100))>0) //调用接收函数,如果有数据,进行数据处理显示。
- {
- for(j=0; j<r_len; j++)
- {
- switch(rec[j].id)
- {
- case 0x382:
- bldcInfoProc(&rec[j]);
- break;
- case 0x383:
- csbInfoProc(&rec[j]);
- // printFrame(&rec[j],R_FLAG);
- break;
- case 0x377:
- batInfoProc(&rec[j]);
- break;
- case 0x332:
- c620InfoProc(&rec[j]);
- break;
- case 0x333:
- adis16450(&rec[j]);
- break;
- default:
- break;
- }
- }
- }
- usleep(10000);
- }
- pthread_exit(0);
- }
-
- int initFrame(PVCI_CAN_OBJ frame) // 需要发送的帧,结构体设置
- {
- int i = 0;
- frame->id = 0x381;
- frame->sendType = 0;
- frame->is_rtr = 0;
- frame->is_extended = 0;
- frame->dlc=8;
- for(i=0; i<(frame->dlc); i++)
- frame->data[i] = 0;
- return 1;
- }
-
- int getCANInfo(unsigned int DevType) //获取CAN设备信息
- {
- int i = 0;
- VCI_BOARD_INFO pInfo;
- if(VCI_ReadBoardInfo(DevType,0,&pInfo)==1)//读取设备序列号、版本等信息。
- {
- ROS_INFO("Get device info success!");
- /*
- printf("Serial Number:");
- for(i=0; i<20; i++)
- printf("%c", pInfo.str_Serial_Num[i]);
- printf("\n");
- printf("HW Type:");
- for(i=0; i<10; i++)
- printf("%c", pInfo.str_hw_Type[i]);
- printf("\n");
- */
- return 1;
- }
- else
- {
- ROS_INFO("Get can info error!");
- return 0;
- }
- }
-
- int sendCTLFrame(PVCI_CAN_OBJ frame)
- {
- if(VCI_Transmit(VCI_USBCAN2, 0, 0, frame, 1) == 1)
- {
- // printFrame(frame,S_FLAG);
- return 1;
- }
- else
- return 0;
- }
-
- int initCAN(unsigned int DevType)
- {
- if(VCI_OpenDevice(VCI_USBCAN2,0,0) == 1) //打开设备
- {
- ROS_INFO("Open CAN1 success!");
- }
- else
- {
- ROS_INFO("Open CAN1 error!");
- return 0;
- }
- //初始化参数,严格参数二次开发函数库说明书
- VCI_INIT_CONFIG config;
- config.AccCode=0x80000000;
- config.AccMask=0xFFFFFFFF;
- config.Filter=1; //接收所有帧
- config.Timing0=0x00; //波特率125 Kbps 0x03 0x1C
- config.Timing1=0x1C;
- config.Mode=0; //正常模式
-
-
- if(VCI_InitCAN(DevType,0,0,&config)!=1)
- {
- ROS_INFO("Init CAN1 error!");
- VCI_CloseDevice(DevType,0);
- return 0;
- }
- if(VCI_InitCAN(DevType,0,1,&config)!=1)
- {
- ROS_INFO("Init CAN2 error!");
- VCI_CloseDevice(DevType,0);
- return 0;
- }
- if(VCI_StartCAN(DevType,0,0)!=1)
- {
- ROS_INFO("Start CAN1 error!");
- VCI_CloseDevice(DevType,0);
- return 0;
- }
- if(VCI_StartCAN(DevType,0,1)!=1)
- {
- ROS_INFO("Start CAN2 error!");
- VCI_CloseDevice(DevType,0);
- return 0;
- }
- return 1;
- }
-
- void closeCAN(PVCI_CAN_OBJ frame)
- {
- VCI_ResetCAN(VCI_USBCAN2, 0, 0); // 复位CAN1通道
- usleep(100000);// delay 100ms
- VCI_ResetCAN(VCI_USBCAN2, 0, 1);
- usleep(100000);
- VCI_CloseDevice(VCI_USBCAN2,0); // 关闭设备
- free(frame);
- }
-
头文件:
- #ifndef CAN_H
- #define CAN_H
-
- //接口卡类型定义
- #define VCI_PCI5121 1
- #define VCI_PCI9810 2
- #define VCI_USBCAN1 3
- #define VCI_USBCAN2 4
- #define VCI_PCI9820 5
- #define VCI_CAN232 6
- #define VCI_PCI5110 7
- #define VCI_CANLite 8
- #define VCI_ISA9620 9
- #define VCI_ISA5420 10
-
- //CAN错误码
- #define ERR_CAN_OVERFLOW 0x0001 //CAN控制器内部FIFO溢出
- #define ERR_CAN_ERRALARM 0x0002 //CAN控制器错误报警
- #define ERR_CAN_PASSIVE 0x0004 //CAN控制器消极错误
- #define ERR_CAN_LOSE 0x0008 //CAN控制器仲裁丢失
- #define ERR_CAN_BUSERR 0x0010 //CAN控制器总线错误
-
- //通用错误码
- #define ERR_DEVICEOPENED 0x0100 //设备已经打开
- #define ERR_DEVICEOPEN 0x0200 //打开设备错误
- #define ERR_DEVICENOTOPEN 0x0400 //设备没有打开
- #define ERR_BUFFEROVERFLOW 0x0800 //缓冲区溢出
- #define ERR_DEVICENOTEXIST 0x1000 //此设备不存在
- #define ERR_LOADKERNELDLL 0x2000 //装载动态库失败
- #define ERR_CMDFAILED 0x4000 //执行命令失败错误码
- #define ERR_BUFFERCREATE 0x8000 //内存不足
-
-
- //函数调用返回状态值
- #define STATUS_OK 1
- #define STATUS_ERR 0
-
- #define USHORT unsigned short int
- #define BYTE unsigned char
- #define CHAR char
- #define UCHAR unsigned char
- #define UINT unsigned int
- #define DWORD unsigned int
- #define PVOID void*
- #define ULONG unsigned int
- #define INT int
- #define UINT32 UINT
- #define LPVOID void*
- #define BOOL BYTE
- #define TRUE 1
- #define FALSE 0
-
-
- #if 1
- //1.ZLGCAN系列接口卡信息的数据类型。
- typedef struct _VCI_BOARD_INFO{
- USHORT hw_Version;
- USHORT fw_Version;
- USHORT dr_Version;
- USHORT in_Version;
- USHORT irq_Num;
- BYTE can_Num;
- CHAR str_Serial_Num[20];
- CHAR str_hw_Type[40];
- USHORT Reserved[4];
- }VCI_BOARD_INFO,*PVCI_BOARD_INFO;
-
- //2.定义CAN信息帧的数据类型。
- typedef struct _VCI_CAN_OBJ{
- UINT id;
- UINT timeStamp;
- BYTE timeFlag;
- BYTE sendType;
- BYTE is_rtr; // 是否是远程帧
- BYTE is_extended; // 是否是扩展帧
- BYTE dlc; // data len
- BYTE data[8];
- BYTE reserved[3];
- }VCI_CAN_OBJ,*PVCI_CAN_OBJ;
-
- //3.定义CAN控制器状态的数据类型。
- typedef struct _VCI_CAN_STATUS{
- UCHAR ErrInterrupt;
- UCHAR regMode;
- UCHAR regStatus;
- UCHAR regALCapture;
- UCHAR regECCapture;
- UCHAR regEWLimit;
- UCHAR regRECounter;
- UCHAR regTECounter;
- DWORD Reserved;
- }VCI_CAN_STATUS,*PVCI_CAN_STATUS;
-
- //4.定义错误信息的数据类型。
- typedef struct _ERR_INFO{
- UINT ErrCode;
- BYTE Passive_ErrData[3];
- BYTE ArLost_ErrData;
- }VCI_ERR_INFO,*PVCI_ERR_INFO;
-
- //5.定义初始化CAN的数据类型
- typedef struct _INIT_CONFIG{
- DWORD AccCode;
- DWORD AccMask;
- DWORD Reserved;
- UCHAR Filter;
- UCHAR Timing0;
- UCHAR Timing1;
- UCHAR Mode;
- }VCI_INIT_CONFIG,*PVCI_INIT_CONFIG;
-
-
- struct Quaternion
- {
- float w, x, y, z;
- float vx, vy, vz;
- float ax, ay, az;
- };
-
- typedef struct bms{
- float_t voltage;
- float_t current;
- float_t temperature;
- float_t coulomb;
- float_t acc_x;
- float_t acc_y;
- float_t acc_z;
- float_t gyro;
- float_t angle_acc;
- float_t angle_kalman;
- }S_BMS;
-
- typedef struct motor{
- short l_rpm;
- short r_rpm;
- short l_current;
- short r_current;
- }S_MOTOR;
-
-
- struct EulerAngles
- {
- float roll, pitch, yaw;
- };
-
- extern Quaternion q;
- extern P_V_INFO pVinfo;
- extern S_BMS s_bms;
- extern S_MOTOR s_motor;
-
- extern void framePrint(unsigned int cid,unsigned char *buf);
- extern int initCAN(unsigned int DevType);
- extern void closeCAN(PVCI_CAN_OBJ frame);
- extern int getCANInfo(unsigned int DevType);
- extern void *receiveFrame(void *arg);
- extern void *can2RecvFrame(void *arg);
- extern int sendCTLFrame(PVCI_CAN_OBJ frame);
- extern double g_csb_dis[];
- extern PVCI_CAN_OBJ frame;
- extern double vx,vy,vth;
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- DWORD VCI_OpenDevice(DWORD DeviceType,DWORD DeviceInd,DWORD Reserved);
- DWORD VCI_CloseDevice(DWORD DeviceType,DWORD DeviceInd);
- DWORD VCI_InitCAN(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_INIT_CONFIG pInitConfig);
-
- DWORD VCI_ReadBoardInfo(DWORD DeviceType,DWORD DeviceInd,PVCI_BOARD_INFO pInfo);
- DWORD VCI_ReadErrInfo(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_ERR_INFO pErrInfo);
- DWORD VCI_ReadCANStatus(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_STATUS pCANStatus);
-
- DWORD VCI_GetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);
- DWORD VCI_SetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);
-
- ULONG VCI_GetReceiveNum(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
- DWORD VCI_ClearBuffer(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
-
- DWORD VCI_StartCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
- DWORD VCI_ResetCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
-
- ULONG VCI_Transmit(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pSend,UINT Len);
- ULONG VCI_Receive(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pReceive,UINT Len,INT WaitTime);
-
- #ifdef __cplusplus
- }
- #endif
- #endif
-
- #endif
具体编译的时候请参考硬件厂家提供的资料,还有引入对应的库等,有的版本硬件驱动库还存在bug,本文章主要用作自己资料记录,仅贴出部分代码,如有技术方面问题可留言共同交流探讨,仅此而已。
最终效果图:
完全调好之后的忘了拍照,贴几个随便拍的,最终效果在静止状态角度在正负0.005之间变化 (好像是的)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。