旭日x3开发板测试IMU40609D(六轴传感器),包含ros节点发布
最终数据
1、环境安装
可以根据指导文档进行搭建:[https://developer.horizon.cc/documents_rdk/category/linux_development](https://developer.horizon.cc/documents_rdk/category/linux_development)
有一些地方需要注意:
2、测试代码
2.1、imu_test.cpp文件main函数:
#include "ICM40609D.hpp"
#include <iostream>
int main(int argc, char* argv[]){
ICM40609D imu1;
imu1.SPIinit(1,0,aMode_LN,gMode_LN,AFS_32G,GFS_31_25DPS,AODR_1kHz,GODR_1kHz);
long start = micro_time();
long end = micro_time();
for (int i=0;i<1000;++i){
imu1.imuDataGet();
std::cout<<"Accel_X:"<<imu1.stAccelRawData.s16X<< "\tAccel_Y:"<<imu1.stAccelRawData.s16Y<<"\tAccel_Z:" << imu1.stAccelRawData.s16Z<<std::endl;
std::cout<<"gyro_X:"<<imu1.stGyroRawData.s16X<< "\tgyro_Y:"<<imu1.stGyroRawData.s16Y<<"\tgyro_Z:" << imu1.stGyroRawData.s16Z<<std::endl;
usleep(2000);
}
imu1.Close();
}
下面对上面的步骤进行简单描述:
2.2、ICM40609D.cpp
#include "ICM40609D.hpp"
uint64_t micro_time() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * (uint64_t)1000000 + tv.tv_usec;
}
ICM40609D::ICM40609D(){
time_interval=0.005f;
ex_i = 0;
ey_i = 0;
ez_i = 0;
Kp=3.0;
Ki=0.5;
}
void ICM40609D::SPIinit(int spinum,int csnum, uint8_t aMode, uint8_t gMode, uint8_t Ascale,uint8_t Gscale,uint8_t AODR,uint8_t GODR)
{
fd = SPIInit(spinum,csnum);
fd_address=&fd;
gstGyroOffset ={0,0,0};
state_check bRet = ICM40609D::Check();
if( true == bRet)
{
std::cout<< "Motion sersor is ICM-40609D\n"<<std::endl;
ICM40609D::Init(aMode,gMode, Ascale,Gscale,AODR,GODR);
}
else
{
std::cout<< "Motion sersor NULL\n"<<std::endl;
}
q0 = 1.0f;
q1 = 0.0f;
q2 = 0.0f;
q3 = 0.0f;
}
state_check ICM40609D::Check(void)
{
state_check bRet = false;
if(VAL_WHO_AM_I == SPI_ReadOneByte(fd,ICM40609D_WHO_AM_I))
{
SPI_WriteOneByte(fd,ICM40609D_REG_BANK_SEL, 0x00);
bRet=true;
}
return bRet;
}
void ICM40609D::Init(uint8_t aMode, uint8_t gMode, uint8_t Ascale,uint8_t Gscale,uint8_t AODR,uint8_t GODR)
{
SPI_WriteOneByte(fd,ICM40609D_REG_BANK_SEL, 0x00); // select register bank 0
SPI_WriteOneByte(fd, ICM40609D_PWR_MGMT0, gMode << 2 | aMode); // set accel and gyro modes
SPI_WriteOneByte(fd,ICM40609D_ACCEL_CONFIG0, Ascale << 5 | AODR); // set accel ODR and FS
SPI_WriteOneByte(fd,ICM40609D_GYRO_CONFIG0, Gscale << 5 | GODR); // set gyro ODR and FS
SPI_WriteOneByte(fd,ICM40609D_GYRO_ACCEL_CONFIG0, 0x44); // set gyro and accel bandwidth to ODR/10
switch (Gscale)
{
// Possible gyro scales (and their register bit settings) are:
case GFS_15_625DPS:
Gscale_factor=GFS_15_625DPS_SCALE_FACTOR;
break;
case GFS_31_25DPS:
Gscale_factor=GFS_31_25DPS_SCALE_FACTOR;
break;
case GFS_62_50DPS:
Gscale_factor=GFS_62_50DPS_SCALE_FACTOR;
break;
case GFS_125DPS:
Gscale_factor=GFS_125DPS_SCALE_FACTOR;
break;
case GFS_250DPS:
Gscale_factor=GFS_250DPS_SCALE_FACTOR;
break;
case GFS_500DPS:
Gscale_factor=GFS_500DPS_SCALE_FACTOR;
break;
case GFS_1000DPS:
Gscale_factor=GFS_1000DPS_SCALE_FACTOR;
break;
case GFS_2000DPS:
Gscale_factor=GFS_2000DPS_SCALE_FACTOR;
break;
}
switch (Ascale)
{
// Possible accelerometer scales (and their register bit settings) are:
case AFS_4G:
Ascale_factor= AFS_4G_SCALE_FACTOR;
break;
case AFS_8G:
Ascale_factor= AFS_8G_SCALE_FACTOR;
break;
case AFS_16G:
Ascale_factor= AFS_16G_SCALE_FACTOR;
break;
case AFS_32G:
Ascale_factor= AFS_32G_SCALE_FACTOR;
break;
}
usleep(5000);
/* offset */
// bais 会随证白噪声的积分发生改变 因此这里计算的偏置感觉意义不大?
/*
ICM40609D::GyroOffset();
*/
return;
}
void ICM40609D::Close(void)
{
SPIClose(fd_address);
std::cout<< "imu sensor close"<<std::endl;
}
void ICM40609D::GyroRead(int16_t& s16X, int16_t& s16Y, int16_t& s16Z)
{
uint8_t u8Buf[2];
int16_t s16Buf[3] = {0};
u8Buf[0]=SPI_ReadOneByte(fd,ICM40609D_GYRO_DATA_X0);
u8Buf[1]=SPI_ReadOneByte(fd,ICM40609D_GYRO_DATA_X1);
s16Buf[0]= (u8Buf[1]<<8)|u8Buf[0];
u8Buf[0]=SPI_ReadOneByte(fd,ICM40609D_GYRO_DATA_Y0);
u8Buf[1]=SPI_ReadOneByte(fd,ICM40609D_GYRO_DATA_Y1);
s16Buf[1]= (u8Buf[1]<<8)|u8Buf[0];
u8Buf[0]=SPI_ReadOneByte(fd,ICM40609D_GYRO_DATA_Z0);
u8Buf[1]=SPI_ReadOneByte(fd,ICM40609D_GYRO_DATA_Z1);
s16Buf[2]= (u8Buf[1]<<8)|u8Buf[0];
// s16X = s16Buf[0] - gstGyroOffset.s16X;
// s16Y = s16Buf[1] - gstGyroOffset.s16Y;
// s16Z = s16Buf[2] - gstGyroOffset.s16Z;
s16X = s16Buf[0];
s16Y = s16Buf[1];
s16Z = s16Buf[2];
return;
}
void ICM40609D::AccelRead(int16_t& s16X, int16_t& s16Y, int16_t& s16Z)
{
uint8_t u8Buf[2];
int16_t s16Buf[3] = {0};
u8Buf[0]=SPI_ReadOneByte(fd,ICM40609D_ACCEL_DATA_X0);
u8Buf[1]=SPI_ReadOneByte(fd,ICM40609D_ACCEL_DATA_X1);
s16Buf[0]= (u8Buf[1]<<8)|u8Buf[0];
u8Buf[0]=SPI_ReadOneByte(fd,ICM40609D_ACCEL_DATA_Y0);
u8Buf[1]=SPI_ReadOneByte(fd,ICM40609D_ACCEL_DATA_Y1);
s16Buf[1]= (u8Buf[1]<<8)|u8Buf[0];
u8Buf[0]=SPI_ReadOneByte(fd,ICM40609D_ACCEL_DATA_Z0);
u8Buf[1]=SPI_ReadOneByte(fd,ICM40609D_ACCEL_DATA_Z1);
s16Buf[2]= (u8Buf[1]<<8)|u8Buf[0];
s16X = s16Buf[0];
s16Y = s16Buf[1];
s16Z = s16Buf[2];
return;
}
void ICM40609D::GyroOffset(void)
{
uint8_t i;
int16_t s16Gx = 0, s16Gy = 0, s16Gz = 0;
int32_t s32TempGx = 0, s32TempGy = 0, s32TempGz = 0;
for(i = 0; i < 64; i ++)
{
ICM40609D::GyroRead(s16Gx, s16Gy, s16Gz);
s32TempGx += s16Gx;
s32TempGy += s16Gy;
s32TempGz += s16Gz;
usleep(5000);
}
gstGyroOffset.s16X = s32TempGx >> 6; //等价 s32TempGx / 64
gstGyroOffset.s16Y = s32TempGy >> 6;
gstGyroOffset.s16Z = s32TempGz >> 6;
return;
}
void ICM40609D::imuDataGet()
{
float MotionVal[6];
int16_t s16Gyro[3], s16Accel[3];
ICM40609D::AccelRead(s16Accel[0], s16Accel[1], s16Accel[2]);
ICM40609D::GyroRead(s16Gyro[0], s16Gyro[1], s16Gyro[2]);
/*
MotionVal[0]=(float)s16Gyro[0]/Gscale_factor;
MotionVal[1]=(float)s16Gyro[1]/Gscale_factor;
MotionVal[2]=(float)s16Gyro[2]/Gscale_factor;
MotionVal[3]=s16Accel[0];
MotionVal[4]=s16Accel[1];
MotionVal[5]=s16Accel[2];
*/
/* 计算旋转四元数 */
/*
mahonyAHRSupdate((float)MotionVal[0] * 0.01745, (float)MotionVal[1] * 0.01745, (float)MotionVal[2] * 0.01745,
(float)MotionVal[3], (float)MotionVal[4], (float)MotionVal[5]);
stQuaternion.q0=q0;
stQuaternion.q1=q1;
stQuaternion.q2=q2;
stQuaternion.q3=q3;
*/
/* 计算姿态角 */
/*
pstAngles.fPitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.295; // pitch
pstAngles.fRoll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.295; // roll
pstAngles.fYaw = atan2(-2 * q1 * q2 - 2 * q0 * q3, 2 * q2 * q2 + 2 * q3 * q3 - 1) * 57.295;
*/
stAccelRawData.s16X = (s16Accel[0] * 9.801) / Ascale_factor;
stAccelRawData.s16Y = (s16Accel[1] * 9.801) / Ascale_factor;
stAccelRawData.s16Z = (s16Accel[2] * 9.801) / Ascale_factor;
stGyroRawData.s16X = s16Gyro[0] / Gscale_factor;
stGyroRawData.s16Y = s16Gyro[1] / Gscale_factor;
stGyroRawData.s16Z = s16Gyro[2] / Gscale_factor;
}
void ICM40609D::mahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az){
float Norm;
float vx, vy, vz;
float ex, ey, ez;
float qa, qb, qc;
// Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
// Normalise accelerometer measurement
Norm = invSqrt(ax * ax + ay * ay + az * az);
ax *= Norm;
ay *= Norm;
az *= Norm;
// Estimated direction of gravity and vector perpendicular to magnetic flux
vx = 2*(q1 * q3 - q0 * q2);
vy = 2*(q0 * q1 + q2 * q3);
vz = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;
// Error is sum of cross product between estimated and measured direction of gravity
ex = (ay * vz - az * vy);
ey = (az * vx - ax * vz);
ez = (ax * vy - ay * vx);
ex_i += ex*time_interval;
ey_i += ey*time_interval;
ez_i += ez*time_interval;
gx += Kp * ex + Ki * ex_i;
gy += Kp * ey + Ki * ey_i;
gz += Kp * ez + Ki * ez_i;
}
qa = q0;
qb = q1;
qc = q2;
q0 += 0.5f*(-qb * gx - qc * gy - q3 * gz)*time_interval;
q1 += 0.5f*(qa * gx + qc * gz - q3 * gy)*time_interval;
q2 += 0.5f*(qa * gy - qb * gz + q3 * gx)*time_interval;
q3 += 0.5f*(qa * gz + qb * gy - qc * gx)*time_interval;
// Normalise quaternion
Norm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 *= Norm;
q1 *= Norm;
q2 *= Norm;
q3 *= Norm;
}
/*
std::thread ICM40609D::imuDataGet_thread(void){
return std::thread([this]{imuDataGet();});
}
*/
2.3、ICM40609D.h
#include "IMU_spi.h"
#include <iostream>
#include <thread>
uint64_t micro_time();
class ICM40609D
{
public:
ICM40609D();
void imuDataGet();
void SPIinit(int spinum,int csnum, uint8_t aMode, uint8_t gMode, uint8_t Ascale,uint8_t Gscale,uint8_t AODR,uint8_t GODR);
void Close(void);
std::thread imuDataGet_thread(void);
float time_interval;
float ex_i;
float ey_i;
float ez_i;
float Kp;
float Ki;
float q0,q1,q2,q3;
IMU_ST_QUATERNION_DATA stQuaternion;
IMU_ST_SENSOR_DATA stGyroRawData;
IMU_ST_SENSOR_DATA stAccelRawData;
private:
state_check Check(void);
void Init(uint8_t aMode, uint8_t gMode, uint8_t Ascale,uint8_t Gscale,uint8_t AODR,uint8_t GODR);
void GyroRead(int16_t& s16X, int16_t& s16Y, int16_t& s16Z);
void AccelRead(int16_t& s16X, int16_t& s16Y, int16_t& s16Z);
void GyroOffset(void);
void mahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az);
float Ascale_factor;
float Gscale_factor;
IMU_ST_SENSOR_DATA gstGyroOffset;
int fd;
int *fd_address;
};
2.4、IMU_spi.c
#include "IMU_spi.h"
#ifdef __cplusplus
extern "C" {
#endif
static uint8_t mode = 0;
static uint8_t bits = 8;
static uint8_t count = 2;
static uint32_t speed = 12000000;
static uint16_t delay = 0;
static void pabort(const char *s) {
perror(s);
abort();
}
int SPIInit(int spi_num,int cs_num){
int ret = 0;
int fd;
char spi_dev[30];
sprintf(spi_dev,"/dev/spidev%d.%d",spi_num,cs_num);
fd = open(spi_dev, O_RDWR);
if (fd < 0) {
pabort("can't open device");
}
// SPI mode
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
pabort("can't set spi mode");
}
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1) {
pabort("can't get spi mode");
}
// bits per word
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1) {
pabort("can't set bits per word");
}
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1) {
pabort("can't get bits per word");
}
// max speed Hz
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
pabort("can't set max speed hz");
}
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1) {
pabort("can't get max speed hz");
}
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
return fd;
}
void SPIClose(int *fd_address){
close(*fd_address);
}
uint8_t SPI_ReadOneByte(int fd, uint8_t reg){
int8_t *tx_buffer;
int8_t rx_buffer[2]={0,0};
uint8_t readBuf;
int ret;
tx_buffer = (char *)calloc(count, sizeof(char));
if (tx_buffer == 0) {
pabort("failed to malloc tx_buffer");
}
tx_buffer[0] = 0x80 | reg; // setting msb to 1 makes this a "read" operation
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx_buffer,
.rx_buf = (unsigned long)&rx_buffer,
.len = count,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
pabort("can't send spi message");
}
free(tx_buffer);
readBuf = rx_buffer[1];
return readBuf;
}
void SPI_WriteOneByte(int fd, uint8_t reg, uint8_t value){
int8_t *tx_buffer;
int8_t rx_buffer[2]={0,0};
int ret;
tx_buffer = (char *)calloc(count, sizeof(char));
if (tx_buffer == 0) {
pabort("failed to malloc tx_buffer");
}
tx_buffer[0] = reg; // setting msb to 0 makes this a "write" operation
tx_buffer[1] = value;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx_buffer,
.rx_buf = (unsigned long)&rx_buffer,
.len = count,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
pabort("can't write spi message");
}
free(tx_buffer);
return;
}
float invSqrt(float x){
float halfx = 0.5f * x;
float y = x;
long i = *(long*)&y; //get bits for floating value
i = 0x5f3759df - (i >> 1); //gives initial guss you
y = *(float*)&i; //convert bits back to float
y = y * (1.5f - (halfx * y * y)); //newtop step, repeating increases accuracy
return y;
}
#ifdef __cplusplus
}
#endif
2.5、IMU_spi.h
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <stddef.h>
#include <math.h>
#include <sys/time.h>
typedef uint8_t state_check;
#define true 1
#define false 0
#define VAL_WHO_AM_I 0x3B
// User Bank 0
#define ICM40609D_DEVICE_CONFIG 0x11
#define ICM40609D_DRIVE_CONFIG 0x13
#define ICM40609D_INT_CONFIG 0x14
#define ICM40609D_FIFO_CONFIG 0x16
#define ICM40609D_TEMP_DATA1 0x1D
#define ICM40609D_TEMP_DATA0 0x1E
#define ICM40609D_ACCEL_DATA_X1 0x1F
#define ICM40609D_ACCEL_DATA_X0 0x20
#define ICM40609D_ACCEL_DATA_Y1 0x21
#define ICM40609D_ACCEL_DATA_Y0 0x22
#define ICM40609D_ACCEL_DATA_Z1 0x23
#define ICM40609D_ACCEL_DATA_Z0 0x24
#define ICM40609D_GYRO_DATA_X1 0x25
#define ICM40609D_GYRO_DATA_X0 0x26
#define ICM40609D_GYRO_DATA_Y1 0x27
#define ICM40609D_GYRO_DATA_Y0 0x28
#define ICM40609D_GYRO_DATA_Z1 0x29
#define ICM40609D_GYRO_DATA_Z0 0x2A
#define ICM40609D_TMST_FSYNCH 0x2B
#define ICM40609D_TMST_FSYNCL 0x2C
#define ICM40609D_INT_STATUS 0x2D
#define ICM40609D_FIFO_COUNTH 0x2E
#define ICM40609D_FIFO_COUNTL 0x2F
#define ICM40609D_FIFO_DATA 0x30
#define ICM40609D_APEX_DATA0 0x31
#define ICM40609D_APEX_DATA1 0x32
#define ICM40609D_APEX_DATA2 0x33
#define ICM40609D_APEX_DATA3 0x34
#define ICM40609D_APEX_DATA4 0x35
#define ICM40609D_APEX_DATA5 0x36
#define ICM40609D_INT_STATUS2 0x37
#define ICM40609D_INT_STATUS3 0x38
#define ICM40609D_SIGNAL_PATH_RESET 0x4B
#define ICM40609D_INTF_CONFIG0 0x4C
#define ICM40609D_INTF_CONFIG1 0x4D
#define ICM40609D_PWR_MGMT0 0x4E
#define ICM40609D_GYRO_CONFIG0 0x4F
#define ICM40609D_ACCEL_CONFIG0 0x50
#define ICM40609D_GYRO_CONFIG1 0x51
#define ICM40609D_GYRO_ACCEL_CONFIG0 0x52
#define ICM40609D_ACCEL_CONFIG1 0x53
#define ICM40609D_TMST_CONFIG 0x54
#define ICM40609D_APEX_CONFIG0 0x56
#define ICM40609D_SMD_CONFIG 0x57
#define ICM40609D_FIFO_CONFIG1 0x5F
#define ICM40609D_FIFO_CONFIG2 0x60
#define ICM40609D_FIFO_CONFIG3 0x61
#define ICM40609D_FSYNC_CONFIG 0x62
#define ICM40609D_INT_CONFIG0 0x63
#define ICM40609D_INT_CONFIG1 0x64
#define ICM40609D_INT_SOURCE0 0x65
#define ICM40609D_INT_SOURCE1 0x66
#define ICM40609D_INT_SOURCE3 0x68
#define ICM40609D_INT_SOURCE4 0x69
#define ICM40609D_FIFO_LOST_PKT0 0x6C
#define ICM40609D_FIFO_LOST_PKT1 0x6D
#define ICM40609D_SELF_TEST_CONFIG 0x70
#define ICM40609D_WHO_AM_I 0x75 // should return 0x47
#define ICM40609D_REG_BANK_SEL 0x76
// User Bank 1
#define ICM40609D_SENSOR_CONFIG0 0x03
#define ICM40609D_GYRO_CONFIG_STATIC2 0x0B
#define ICM40609D_GYRO_CONFIG_STATIC3 0x0C
#define ICM40609D_GYRO_CONFIG_STATIC4 0x0D
#define ICM40609D_GYRO_CONFIG_STATIC5 0x0E
#define ICM40609D_GYRO_CONFIG_STATIC6 0x0F
#define ICM40609D_GYRO_CONFIG_STATIC7 0x10
#define ICM40609D_GYRO_CONFIG_STATIC8 0x11
#define ICM40609D_GYRO_CONFIG_STATIC9 0x12
#define ICM40609D_GYRO_CONFIG_STATIC10 0x13
#define ICM40609D_XG_ST_DATA 0x5F
#define ICM40609D_YG_ST_DATA 0x60
#define ICM40609D_ZG_ST_DATA 0x61
#define ICM40609D_TMSTAL0 0x63
#define ICM40609D_TMSTAL1 0x64
#define ICM40609D_TMSTAL2 0x62
#define ICM40609D_INTF_CONFIG4 0x7A
#define ICM40609D_INTF_CONFIG5 0x7B
#define ICM40609D_INTF_CONFIG6 0x7C
// User Bank 2
#define ICM40609D_ACCEL_CONFIG_STATIC2 0x03
#define ICM40609D_ACCEL_CONFIG_STATIC3 0x04
#define ICM40609D_ACCEL_CONFIG_STATIC4 0x05
#define ICM40609D_XA_ST_DATA 0x3B
#define ICM40609D_YA_ST_DATA 0x3C
#define ICM40609D_ZA_ST_DATA 0x3D
// User Bank 4
#define ICM40609D_APEX_CONFIG1 0x40
#define ICM40609D_APEX_CONFIG2 0x41
#define ICM40609D_APEX_CONFIG3 0x42
#define ICM40609D_APEX_CONFIG4 0x43
#define ICM40609D_APEX_CONFIG5 0x44
#define ICM40609D_APEX_CONFIG6 0x45
#define ICM40609D_APEX_CONFIG7 0x46
#define ICM40609D_APEX_CONFIG8 0x47
#define ICM40609D_APEX_CONFIG9 0x48
#define ICM40609D_ACCEL_WOM_X_THR 0x4A
#define ICM40609D_ACCEL_WOM_Y_THR 0x4B
#define ICM40609D_ACCEL_WOM_Z_THR 0x4C
#define ICM40609D_INT_SOURCE6 0x4D
#define ICM40609D_INT_SOURCE7 0x4E
#define ICM40609D_INT_SOURCE8 0x4F
#define ICM40609D_INT_SOURCE9 0x50
#define ICM40609D_INT_SOURCE10 0x51
#define ICM40609D_OFFSET_USER0 0x77
#define ICM40609D_OFFSET_USER1 0x78
#define ICM40609D_OFFSET_USER2 0x79
#define ICM40609D_OFFSET_USER3 0x7A
#define ICM40609D_OFFSET_USER4 0x7B
#define ICM40609D_OFFSET_USER5 0x7C
#define ICM40609D_OFFSET_USER6 0x7D
#define ICM40609D_OFFSET_USER7 0x7E
#define ICM40609D_OFFSET_USER8 0x7F
#define AFS_4G 0x03 // 011: ±4g
#define AFS_8G 0x02 // 010: ±8g
#define AFS_16G 0x01 // 001: ±16g
#define AFS_32G 0x00 // 000: ±32g (default)
#define GFS_2000DPS 0x00 // default
#define GFS_1000DPS 0x01
#define GFS_500DPS 0x02
#define GFS_250DPS 0x03
#define GFS_125DPS 0x04
#define GFS_62_50DPS 0x05
#define GFS_31_25DPS 0x06
#define GFS_15_625DPS 0x07
// Low Noise mode
#define AODR_32kHz 0x01
#define AODR_16kHz 0x02
#define AODR_8kHz 0x03
#define AODR_4kHz 0x04
#define AODR_2kHz 0x05
#define AODR_1kHz 0x06 // default
//Low Noise or Low Power modes
#define AODR_500Hz 0x0F
#define AODR_200Hz 0x07
#define AODR_100Hz 0x08
#define AODR_50Hz 0x09
#define AODR_25Hz 0x0A
#define AODR_12_5Hz 0x0B
// Low Power mode
#define AODR_6_25Hz 0x0C
#define AODR_3_125Hz 0x0D
#define AODR_1_5625Hz 0x0E
#define GODR_32kHz 0x01
#define GODR_16kHz 0x02
#define GODR_8kHz 0x03
#define GODR_4kHz 0x04
#define GODR_2kHz 0x05
#define GODR_1kHz 0x06 // default
#define GODR_500Hz 0x0F
#define GODR_200Hz 0x07
#define GODR_100Hz 0x08
#define GODR_50Hz 0x09
#define GODR_25Hz 0x0A
#define GODR_12_5Hz 0x0B
#define aMode_OFF 0x01
#define aMode_LP 0x02
#define aMode_LN 0x03
#define gMode_OFF 0x00
#define gMode_SBY 0x01
#define gMode_LN 0x03
#define AFS_4G_SCALE_FACTOR 8192.0f
#define AFS_8G_SCALE_FACTOR 4096.0f
#define AFS_16G_SCALE_FACTOR 2048.0f
#define AFS_32G_SCALE_FACTOR 1024.0f
#define GFS_2000DPS_SCALE_FACTOR 16.4
#define GFS_1000DPS_SCALE_FACTOR 32.8
#define GFS_500DPS_SCALE_FACTOR 65.5
#define GFS_250DPS_SCALE_FACTOR 131.0f
#define GFS_125DPS_SCALE_FACTOR 262.0f
#define GFS_62_50DPS_SCALE_FACTOR 524.3
#define GFS_31_25DPS_SCALE_FACTOR 1048.6
#define GFS_15_625DPS_SCALE_FACTOR 2097.2
#ifdef __cplusplus
extern "C" {
#endif
typedef struct imu_st_quaternion_data_tag{
float q0;
float q1;
float q2;
float q3;
}IMU_ST_QUATERNION_DATA;
typedef struct imu_st_angles_data_tag
{
float fYaw;
float fPitch;
float fRoll;
}IMU_ST_ANGLES_DATA;
typedef struct imu_st_sensor_data_tag
{
float s16X;
float s16Y;
float s16Z;
}IMU_ST_SENSOR_DATA;
int SPIInit(int spi_num,int cs_num);
void SPIClose(int *fd_address);
uint8_t SPI_ReadOneByte(int fd, uint8_t reg);
void SPI_WriteOneByte(int fd, uint8_t reg,uint8_t value);
float invSqrt(float x);
#ifdef __cplusplus
}
#endif
3、ros发布数据节点
将上面的imu_test.cpp进行更改,使其成为ros中的一个节点,将读取到的传感器数据发布至话题:/data,供其它节点使用。
3.1、创建ros工作空间
下面给出具体创建流程:
①mkdir catkin_ws
②cd catkin_ws
③mkdir src
④cd src
⑤catkin_init_workspace //在src中生成CmakeLists.txt
⑥cd …
⑦catkin_make //编译,会生成build和devel文件夹,编译源码生产的可执行文件会放入devel/lib中。
⑧Catkin_make install //生成install空间
至此工作空间创建完毕,这样就可以在工作空间中创建功能包了:必须创建在src中,并且在同一个工作空间中不允许同名的功能包:
下面就是功能包创建完成之后里面包含的内容:
3.2、数据发布节点代码
#include "ros/ros.h"
#include "ICM40609D.hpp"
#include <iostream>
#include <sensor_msgs/Imu.h>
int main(int argc, char **argv) {
ros::init(argc, argv, "imu_test_node");// 初始化ROS,设置节点名称为"imu_test_node"。
ros::NodeHandle nh;// 创建节点句柄。管理ros API资源,比如后面的nh.advertise
// 创建一个IMU数据发布者
ros::Publisher imu_pub = nh.advertise<sensor_msgs::Imu>("/data", 10);//设置了一个名为imu_pub的发布者,用于在/data主题上发布sensor_msgs::Imu类型的消息,消息队列大小为10。
// 设置循环频率
ros::Rate loop_rate(10);
ICM40609D imu1;
imu1.SPIinit(1,0,aMode_LN,gMode_LN,AFS_32G,GFS_31_25DPS,AODR_1kHz,GODR_1kHz);
while (ros::ok()) {
sensor_msgs::Imu imu_msg;
imu1.imuDataGet();
// 填充IMU数据
imu_msg.header.stamp = ros::Time::now();
imu_msg.header.frame_id = "imu_frame";
// 设置其他IMU数据字段
imu_msg.angular_velocity.x = imu1.stGyroRawData.s16X;
imu_msg.angular_velocity.y = imu1.stGyroRawData.s16Y;
imu_msg.angular_velocity.z = imu1.stGyroRawData.s16Z;
imu_msg.linear_acceleration.x = imu1.stAccelRawData.s16X;
imu_msg.linear_acceleration.y = imu1.stAccelRawData.s16Y;
imu_msg.linear_acceleration.z = imu1.stAccelRawData.s16Z;
// 发布IMU数据
imu_pub.publish(imu_msg);//在"/data"主题上发布IMU数据。
ros::spinOnce();
loop_rate.sleep();
}
imu1.Close();
return 0;
}
***在cpp文件写好之后要在CmakeLists.txt文件中加入以下两行代码(cpp源代码需要放在功能包的src目录下):
***运行之前先把环境变量设置一下·,以免找不到工作空间。或者直接添加进.bashrc的最后一行:source/home/sunrise/Documents/workspace/catkin_ws/devel/setup.bash
执行ros节点的命令:
①开启节点控制器:roscore
②开始运行发布节点:rosrun ICM40609D imu_test_node
至此测试和发布数据节点全部完成。