|
@@ -1,422 +0,0 @@
|
|
|
-/*******************************************************************
|
|
|
- * Copyright:2016-2021 www.corvin.cn ROS小课堂
|
|
|
- * Description:使用串口方式读取和控制IMU模块信息.
|
|
|
- * Author: corvin
|
|
|
- * History:
|
|
|
- * 20211122:init this file.
|
|
|
-******************************************************************/
|
|
|
-#include"../include/imu_data.h"
|
|
|
-
|
|
|
-using namespace std;
|
|
|
-
|
|
|
-#define BYTE_CNT 55 //每次从串口中读取的字节数
|
|
|
-#define ACCE_CONST 0.000488281 //用于计算加速度的常量16.0/32768.0
|
|
|
-#define ANGULAR_CONST 0.061035156 //用于计算角速度的常量2000.0/32768.0
|
|
|
-#define ANGLE_CONST 0.005493164 //用于计算欧拉角的常量180.0/32768.0
|
|
|
-
|
|
|
-static unsigned char r_buf[BYTE_CNT]; //一次从串口中读取的数据存储缓冲区
|
|
|
-static int port_fd = -1; //串口打开时的文件描述符
|
|
|
-static float acce[3],angular[3],angle[3],quater[4];
|
|
|
-
|
|
|
-static struct termios initial_settings, new_settings;
|
|
|
-
|
|
|
-void init_keyboard(void)
|
|
|
-{
|
|
|
- tcgetattr(0,&initial_settings);
|
|
|
- new_settings = initial_settings;
|
|
|
- new_settings.c_lflag &= ~ICANON;
|
|
|
- new_settings.c_lflag &= ~ECHO;
|
|
|
- new_settings.c_lflag &= ~ISIG;
|
|
|
- new_settings.c_cc[VMIN] = 1;
|
|
|
- new_settings.c_cc[VTIME] = 0;
|
|
|
- tcsetattr(0, TCSANOW, &new_settings);
|
|
|
-}
|
|
|
-void close_keyboard(void)
|
|
|
-{
|
|
|
- tcsetattr(0, TCSANOW, &initial_settings);
|
|
|
-}
|
|
|
-
|
|
|
-/**************************************
|
|
|
- * Description:linux下kbhit的实现.
|
|
|
- *************************************/
|
|
|
-int kbhit(void)
|
|
|
-{
|
|
|
- char ch;
|
|
|
- int nread;
|
|
|
- new_settings.c_cc[VMIN]=0;
|
|
|
- tcsetattr(0, TCSANOW, &new_settings);
|
|
|
- nread = read(0,&ch,1);
|
|
|
- new_settings.c_cc[VMIN]=1;
|
|
|
- tcsetattr(0, TCSANOW, &new_settings);
|
|
|
- if(nread == 1)
|
|
|
- {
|
|
|
- return 1;
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/**************************************
|
|
|
- * Description:关闭串口文件描述符.
|
|
|
- *************************************/
|
|
|
-int closeSerialPort(void)
|
|
|
-{
|
|
|
- int ret = close(port_fd);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-/*****************************************************************
|
|
|
- * Description:向IMU模块发送解锁命令,发送完命令需要延迟10ms.
|
|
|
- ****************************************************************/
|
|
|
-static int send_unlockCmd(int fd)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
- unsigned char unLockCmd[5] = {0xFF, 0xAA, 0x69, 0x88, 0xB5};
|
|
|
- ret = write(fd, unLockCmd, sizeof(unLockCmd));
|
|
|
- if(ret != sizeof(unLockCmd))
|
|
|
- {
|
|
|
- cout << "发送IMU模块解锁命令失败!!!" << endl;
|
|
|
- return -1;
|
|
|
- }
|
|
|
- usleep(10 * 1000);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*****************************************************************
|
|
|
- * Description:向IMU模块发送保存命令,发送完命令需要延迟100ms.
|
|
|
- *****************************************************************/
|
|
|
-static int send_saveCmd(int fd)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
- unsigned char saveCmd[5] = {0xFF, 0xAA, 0x00, 0x00, 0x00};
|
|
|
- ret = write(fd, saveCmd, sizeof(saveCmd));
|
|
|
- if(ret != sizeof(saveCmd))
|
|
|
- {
|
|
|
- cout << "发送IMU模块保存命令失败!!!" << endl;
|
|
|
- return -1;
|
|
|
- }
|
|
|
- usleep(100 * 1000);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*****************************************************************
|
|
|
- * Description:向IMU模块的串口中发送数据,第一步就是先解锁,其次才能
|
|
|
- * 发送控制命令,最后就是需要发送两遍解锁命令、保存命令的操作.
|
|
|
- *****************************************************************/
|
|
|
-static int send_data(int fd, unsigned char *send_buffer, int length)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
-
|
|
|
- #if 0 //打印发送给IMU模块的数据,用于调试
|
|
|
- printf("Send %d byte to IMU:", length);
|
|
|
- for(int i=0; i<length; i++)
|
|
|
- {
|
|
|
- printf("0x%02x ", send_buffer[i]);
|
|
|
- }
|
|
|
- printf("\n");
|
|
|
- #endif
|
|
|
- send_unlockCmd(fd); //发送解锁命令
|
|
|
-
|
|
|
- //发送完控制命令,需要延时100ms
|
|
|
- ret = write(fd, send_buffer, length*sizeof(unsigned char));
|
|
|
- if(ret != length)
|
|
|
- {
|
|
|
- cout << "发送IMU模块控制命令失败!!!" << endl;
|
|
|
- return -2;
|
|
|
- }
|
|
|
- usleep(100 * 1000);
|
|
|
- send_saveCmd(fd); //发送保存命令
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/**************************************************************
|
|
|
- * Description:根据串口数据协议来解析有效数据,这里共有4种数据.
|
|
|
- * 解析读取其中的44个字节,正好是4帧数据,每一帧数据都是11个字节.
|
|
|
- * 有效数据包括加速度输出(0x55 0x51),角速度输出(0x55 0x52)
|
|
|
- * 角度输出(0x55 0x53),四元素输出(0x55 0x59).
|
|
|
- *************************************************************/
|
|
|
-static void parse_serialData(unsigned char chr)
|
|
|
-{
|
|
|
- static unsigned char chrBuf[BYTE_CNT];
|
|
|
- static unsigned char chrCnt = 0; //记录读取的第几个字节
|
|
|
-
|
|
|
- signed short sData[4]; //save 8 Byte有效信息
|
|
|
- unsigned char i = 0; //用于for循环
|
|
|
- unsigned char frameSum = 0; //存储数据帧的校验和
|
|
|
-
|
|
|
- chrBuf[chrCnt++] = chr; //保存当前字节,字节计数加1
|
|
|
-
|
|
|
- //判断是否读取满一个完整数据帧11个字节,若没有则返回不进行解析
|
|
|
- if(chrCnt < 11)
|
|
|
- return;
|
|
|
-
|
|
|
- //读取满完整一帧数据,计算数据帧的前十个字节的校验和,累加即可得到
|
|
|
- for(i=0; i<10; i++)
|
|
|
- {
|
|
|
- frameSum += chrBuf[i];
|
|
|
- }
|
|
|
-
|
|
|
- //找到数据帧第一个字节是0x55,同时判断校验和是否正确,若两者有一个不正确,
|
|
|
- //都需要移动到最开始的字节,再读取新的字节进行判断数据帧完整性
|
|
|
- if ((chrBuf[0] != 0x55)||(frameSum != chrBuf[10]))
|
|
|
- {
|
|
|
- memcpy(&chrBuf[0], &chrBuf[1], 10); //将有效数据往前移动1字节位置
|
|
|
- chrCnt--; //字节计数减1,需要再多读取一个字节进来,重新判断数据帧
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- #if 0 //打印出完整的带帧头尾的数据帧
|
|
|
- for(i=0; i<11; i++)
|
|
|
- printf("0x%02x ",chrBuf[i]);
|
|
|
- printf("\n");
|
|
|
- #endif
|
|
|
-
|
|
|
- memcpy(&sData[0], &chrBuf[2], 8);
|
|
|
- switch(chrBuf[1]) //根据数据帧不同类型来进行解析数据
|
|
|
- {
|
|
|
- case 0x51: //x,y,z轴 加速度输出
|
|
|
- acce[0] = ((short)(((short)chrBuf[3]<<8)|chrBuf[2]))*ACCE_CONST;
|
|
|
- acce[1] = ((short)(((short)chrBuf[5]<<8)|chrBuf[4]))*ACCE_CONST;
|
|
|
- acce[2] = ((short)(((short)chrBuf[7]<<8)|chrBuf[6]))*ACCE_CONST;
|
|
|
- break;
|
|
|
- case 0x52: //角速度输出
|
|
|
- angular[0] = ((short)(((short)chrBuf[3]<<8)|chrBuf[2]))*ANGULAR_CONST;
|
|
|
- angular[1] = ((short)(((short)chrBuf[5]<<8)|chrBuf[4]))*ANGULAR_CONST;
|
|
|
- angular[2] = ((short)(((short)chrBuf[7]<<8)|chrBuf[6]))*ANGULAR_CONST;
|
|
|
- break;
|
|
|
- case 0x53: //欧拉角度输出, roll, pitch, yaw
|
|
|
- angle[0] = ((short)(((short)chrBuf[3]<<8)|chrBuf[2]))*ANGLE_CONST;
|
|
|
- angle[1] = ((short)(((short)chrBuf[5]<<8)|chrBuf[4]))*ANGLE_CONST;
|
|
|
- angle[2] = ((short)(((short)chrBuf[7]<<8)|chrBuf[6]))*ANGLE_CONST;
|
|
|
- break;
|
|
|
- case 0x59: //四元素输出
|
|
|
- quater[0] = ((short)(((short)chrBuf[3]<<8)|chrBuf[2]))/32768.0;
|
|
|
- quater[1] = ((short)(((short)chrBuf[5]<<8)|chrBuf[4]))/32768.0;
|
|
|
- quater[2] = ((short)(((short)chrBuf[7]<<8)|chrBuf[6]))/32768.0;
|
|
|
- quater[3] = ((short)(((short)chrBuf[9]<<8)|chrBuf[8]))/32768.0;
|
|
|
- //printf("%f %f %f %f\n", quater[0], quater[1], quater[2], quater[3]);
|
|
|
- break;
|
|
|
- default:
|
|
|
- cout << "转换IMU数据帧错误!!!" << endl;
|
|
|
- break;
|
|
|
- }
|
|
|
- chrCnt = 0;
|
|
|
-}
|
|
|
-
|
|
|
-/********************************************************************
|
|
|
- * Description:将yaw角度归零,这里需要通过串口发送的命令如下所示,
|
|
|
- * 发送z轴角度归零的固定命令:0xFF 0xAA 0x01 0x04 0x00;
|
|
|
- *******************************************************************/
|
|
|
-int makeYawZero(void)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
- unsigned char yawZeroCmd[5] = {0xFF, 0xAA, 0x01, 0x04, 0x00};
|
|
|
-
|
|
|
- ret = send_data(port_fd, yawZeroCmd, sizeof(yawZeroCmd));
|
|
|
- if(ret != 0) //通过串口发送命令失败
|
|
|
- {
|
|
|
- cout << "发送串口归零命令失败!!!" << endl;
|
|
|
- return -1;
|
|
|
- }
|
|
|
- cout << "发送串口归零命令成功!" << endl;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/******************************************************************
|
|
|
- * Description:更新IMU模块的IIC地址,这里通过发送串口命令方式来更新IIC地址,
|
|
|
- * 完整的控制命令为:0xFF 0xAA 0x1A 0x** 0x00,更新的IIC地址为第4个字节.
|
|
|
- *****************************************************************/
|
|
|
-int updateIICAddr(std::string input)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
- std::stringstream num;
|
|
|
- int addr = 0;
|
|
|
- const char *iicAddr = NULL;
|
|
|
- unsigned char updateIICAddrCmd[5] = {0xFF, 0xAA, 0x1A, 0x00, 0x00};
|
|
|
-
|
|
|
- iicAddr = input.c_str();
|
|
|
- num<<std::hex<<iicAddr+2<<std::endl;
|
|
|
- num>>addr;
|
|
|
- updateIICAddrCmd[3] = addr;
|
|
|
-
|
|
|
- #if 0
|
|
|
- for (int i=0; i<5; i++)
|
|
|
- {
|
|
|
- printf("0x%02x ", updateIICAddrCmd[i]);
|
|
|
- }
|
|
|
- printf("\n\n");
|
|
|
- #endif
|
|
|
-
|
|
|
- ret = send_data(port_fd, updateIICAddrCmd, sizeof(updateIICAddrCmd));
|
|
|
- if(ret != 0)
|
|
|
- {
|
|
|
- cout << "更新IMU模块IIC地址失败!!!" << endl;
|
|
|
- return -1;
|
|
|
- }
|
|
|
- cout << "更新IMU模块IIC地址成功!" << endl;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/********************************************************************
|
|
|
- * Description:显示IMU模块获取的数据。
|
|
|
- *******************************************************************/
|
|
|
-void show_imu_data(void)
|
|
|
-{
|
|
|
- float yaw, pitch, roll;
|
|
|
- float degree2Rad = 3.1415926 / 180.0;
|
|
|
- float acc_factor = 9.806;
|
|
|
- init_keyboard();
|
|
|
- while (true)
|
|
|
- {
|
|
|
- if (getImuData() < 0)
|
|
|
- {
|
|
|
- break;
|
|
|
- }
|
|
|
- if (kbhit())
|
|
|
- {
|
|
|
- close_keyboard();
|
|
|
- break;
|
|
|
- }
|
|
|
- roll = getAngle(0) * degree2Rad;
|
|
|
- pitch = getAngle(1) * degree2Rad;
|
|
|
- yaw = getAngle(2) * degree2Rad;
|
|
|
- if (yaw >= 3.1415926)
|
|
|
- yaw -= 6.2831852;
|
|
|
-
|
|
|
- cout << endl;
|
|
|
- cout.setf(std::ios::right);
|
|
|
- cout << "roll: " << setw(12) << roll << " pitch: " << setw(12) << pitch << " yaw: " << setw(12) << yaw << endl;
|
|
|
- cout << "q_x: " << setw(12) << getQuat(1) << " q_y: " << setw(12) << getQuat(2) << " q_z: " << setw(12) << getQuat(3) << " q_w: " << setw(12) << getQuat(0) << endl;
|
|
|
- cout << "a_v_x: " << setw(12) << getAngular(0) * degree2Rad << " a_v_y: " << setw(12) << getAngular(1) * degree2Rad << " a_v_z: " << setw(12) << getAngular(2) * degree2Rad << endl;
|
|
|
- cout << "l_acc_x: " << setw(12) << -getAcc(0) * acc_factor << " l_acc_y: " << setw(12) << -getAcc(1) * acc_factor << " l_acc_z: " << setw(12) << -getAcc(2) * acc_factor << endl;
|
|
|
-
|
|
|
- //usleep(10 * 1000);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*****************************************************************
|
|
|
- * Description:根据串口配置信息来连接IMU串口,准备进行数据通信,
|
|
|
- * 两个输入参数的意义如下:
|
|
|
- * const char* path:IMU设备的挂载地址;
|
|
|
- ****************************************************************/
|
|
|
-int initSerialPort(const char* path)
|
|
|
-{
|
|
|
- struct termios terminfo;
|
|
|
- bzero(&terminfo, sizeof(terminfo));
|
|
|
-
|
|
|
- port_fd = open(path, O_RDWR|O_NOCTTY);
|
|
|
- if (-1 == port_fd)
|
|
|
- {
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- //判断当前连接的设备是否为终端设备
|
|
|
- if (isatty(STDIN_FILENO) == 0)
|
|
|
- {
|
|
|
- cout << "IMU dev isatty error !" << endl;
|
|
|
- return -2;
|
|
|
- }
|
|
|
-
|
|
|
- /*设置串口通信波特率-57600bps*/
|
|
|
- cfsetispeed(&terminfo, B57600);
|
|
|
- cfsetospeed(&terminfo, B57600);
|
|
|
-
|
|
|
- //设置数据位 - 8 bit
|
|
|
- terminfo.c_cflag |= CLOCAL|CREAD;
|
|
|
- terminfo.c_cflag &= ~CSIZE;
|
|
|
- terminfo.c_cflag |= CS8;
|
|
|
-
|
|
|
- //不设置奇偶校验位 - N
|
|
|
- terminfo.c_cflag &= ~PARENB;
|
|
|
-
|
|
|
- //设置停止位 - 1
|
|
|
- terminfo.c_cflag &= ~CSTOPB;
|
|
|
-
|
|
|
- //设置等待时间和最小接收字符
|
|
|
- terminfo.c_cc[VTIME] = 0;
|
|
|
- terminfo.c_cc[VMIN] = 1;
|
|
|
-
|
|
|
- //清除串口缓存的数据
|
|
|
- tcflush(port_fd, TCIFLUSH);
|
|
|
-
|
|
|
- if((tcsetattr(port_fd, TCSANOW, &terminfo)) != 0)
|
|
|
- {
|
|
|
- cout << "set imu serial port attr error !" << endl;
|
|
|
- return -3;
|
|
|
- }
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-/*****************************************
|
|
|
- * Description:得到三轴加速度信息,输入
|
|
|
- * 参数可以为0,1,2分别代表x,y,z轴的
|
|
|
- * 加速度信息.
|
|
|
- *****************************************/
|
|
|
-float getAcc(int flag)
|
|
|
-{
|
|
|
- return acce[flag];
|
|
|
-}
|
|
|
-
|
|
|
-/******************************************
|
|
|
- * Description:得到角速度信息,输入参数可
|
|
|
- * 以为0,1,2,分别代表x,y,z三轴的角速度
|
|
|
- * 信息.
|
|
|
- *****************************************/
|
|
|
-float getAngular(int flag)
|
|
|
-{
|
|
|
- return angular[flag];
|
|
|
-}
|
|
|
-
|
|
|
-/********************************************
|
|
|
- * Description:获取yaw,pitch,roll,输入参数0,
|
|
|
- * 1,2可以分别获取到roll,pitch,yaw的数据.
|
|
|
- *******************************************/
|
|
|
-float getAngle(int flag)
|
|
|
-{
|
|
|
- return angle[flag];
|
|
|
-}
|
|
|
-
|
|
|
-/********************************************
|
|
|
- * Description:输入参数0,1,2,3可以分别获取到
|
|
|
- * 四元素的q0,q1,q2,q3.但是这里对应的ros中
|
|
|
- * 的imu_msg.orientation.w,x,y,z的顺序,
|
|
|
- * 这里的q0是对应w参数,1,2,3对应x,y,z.
|
|
|
- ********************************************/
|
|
|
-float getQuat(int flag)
|
|
|
-{
|
|
|
- return quater[flag];
|
|
|
-}
|
|
|
-
|
|
|
-/*************************************************************
|
|
|
- * Description:从串口读取数据,然后解析出各有效数据段,这里
|
|
|
- * 一次性从串口中读取88个字节,然后需要从这些字节中进行
|
|
|
- * 解析读取44个字节,正好是4帧数据,每一帧数据是11个字节.
|
|
|
- * 有效数据包括加速度输出(0x55 0x51),角速度输出(0x55 0x52)
|
|
|
- * 角度输出(0x55 0x53),四元素输出(0x55 0x59).
|
|
|
- *************************************************************/
|
|
|
-int getImuData(void)
|
|
|
-{
|
|
|
- int data_len = 0;
|
|
|
- bzero(r_buf, sizeof(r_buf)); //存储新数据前,将缓冲区清零
|
|
|
-
|
|
|
- //开始从串口中读取数据,并将其保存到r_buf缓冲区中
|
|
|
- data_len = read(port_fd, r_buf, sizeof(r_buf));
|
|
|
- if(data_len <= 0) //从串口中读取数据失败
|
|
|
- {
|
|
|
- cout << "读串口数据失败\n" << endl;
|
|
|
- closeSerialPort();
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- //printf("recv data:%d byte\n", data_len); //一次性从串口中读取的总数据字节数
|
|
|
- for (int i=0; i<data_len; i++) //将读取到的数据进行解析
|
|
|
- {
|
|
|
- //printf("0x%02x ", r_buf[i]);
|
|
|
- parse_serialData(r_buf[i]);
|
|
|
- }
|
|
|
- //printf("\n\n");
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|