PS:本文需要的先行知识请自行补充

本文用linux的I2c_dev,去对I2C设备进行操作(open/read/write/close),实现部署于ROS中,并结合ROS特性完成于MPU9250的通讯

linux-I2C

代码需包含的头文件:

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

开启I2C并进行初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fd = open(I2C_DEV,O_RDWR);
if(fd < 0)
{
// printf("open %s failed\n",I2C_DEV);
std::stringstream ss;
ss << "Failed to open " << I2C_DEV << " device";
throw std::runtime_error(ss.str());
}
int ret = ioctl(fd,I2C_SLAVE,Device_Address);
if(ret < 0)
{
// printf("setenv address faile ret: %x \n", ret);
throw std::runtime_error("Failed to set I2C address");
}

读写操作:

1
2
3
ssize_t bytesRead ;
bytesRead = write(fd,addr,1);
bytesRead = read(fd,buf,6);

ROS实现I2C通讯 MPU9250

工程环境:RK3588 + Ubuntu 22.04.2 LTS + ROS2-humble

默认工作空间已建立,环境变量已导入脚本;

创建功能包:catkin_create_pkg myi2C_MPU9250_pkg roscpp rospy std_msgs

MPU9250 BSP代码设计

部分宏定义:

1
2
3
4
#define I2C_DEV "/dev/i2c-6"
#define Device_Address 0x68
#define gravity 9.8

定义MPU9250类:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MPU9250
{
public:
MPU9250();

void Init();
int8_t MPU9250_SetLPF(int16_t LPF);
void MPU9250_Accel_Gyro_Reda(int16_t *AccelGyroData);
void MPU9250_Temp_Read(int16_t *TempData);
void MPU9250_Set_ZeroBias();
private:
int fd;
};

部分函数封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MPU9250::MPU9250()
{
fd = open(I2C_DEV,O_RDWR);
if(fd < 0)
{
// printf("open %s failed\n",I2C_DEV);
std::stringstream ss;
ss << "Failed to open " << I2C_DEV << " device";
throw std::runtime_error(ss.str());
}
int ret = ioctl(fd,I2C_SLAVE,Device_Address);
if(ret < 0)
{
// printf("setenv address faile ret: %x \n", ret);
throw std::runtime_error("Failed to set I2C address");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void MPU9250::MPU9250_Accel_Gyro_Reda(int16_t *AccelGyroData)
{
char addr[1];
char buf[6];
ssize_t bytesRead ;


addr[0] = MPU_ACCEL_XOUTH_REG;
bytesRead = write(fd,addr,1);
bytesRead = read(fd,buf,6);

AccelGyroData[0] = (int16_t)((buf[0]<<8)|buf[1]);
AccelGyroData[1] = (int16_t)((buf[2]<<8)|buf[3]);
AccelGyroData[2] = (int16_t)((buf[4]<<8)|buf[5]);

addr[0] = MPU_GYRO_XOUTH_REG;
bytesRead = write(fd,addr,1);
bytesRead = read(fd,buf,6);

AccelGyroData[3] = (int16_t)((buf[0]<<8)|buf[1]);
AccelGyroData[4] = (int16_t)((buf[2]<<8)|buf[3]);
AccelGyroData[5] = (int16_t)((buf[4]<<8)|buf[5]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
void MPU9250::MPU9250_Temp_Read(int16_t *TempData)
{
char addr[1];
char buf[6];
short data;
ssize_t bytesRead ;

addr[0] = MPU_TEMP_OUTH_REG;
bytesRead = write(fd,addr,1);
bytesRead = read(fd,buf,6);
data = (int16_t)((buf[0] << 8) | buf[1]);
*TempData = 21.0 + ((float)data / 333.87f);
}

ROS节点设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/imu.hpp"
#include "mpu9250.h"

using namespace std::chrono_literals;

class ImuPublisher : public rclcpp::Node
{
public:
using ImuMsg = sensor_msgs::msg::Imu;
ImuPublisher()
: Node("imu_publisher")
{
mpu9250.Init();

imu_publisher_ = this->create_publisher<ImuMsg>("imu/data_raw", 3);

timer_ = this->create_wall_timer(1ms, std::bind(&ImuPublisher::timer_callback, this));
}
private:

void timer_callback()
{
ImuMsg imu_msg;
int16_t accelgyrodata[6] = {0};

rclcpp::Time Stamp = rclcpp::Node::now();

mpu9250.MPU9250_Accel_Gyro_Reda(accelgyrodata);

imu_msg.header.stamp = Stamp;
imu_msg.header.frame_id = "Imu_frame";

imu_msg.angular_velocity.x = accelgyrodata[3] / 16.4;
imu_msg.angular_velocity.y = accelgyrodata[4] / 16.4;
imu_msg.angular_velocity.z = accelgyrodata[5] / 16.4;

imu_msg.linear_acceleration.x = accelgyrodata[0] / 8192.0 * gravity;
imu_msg.linear_acceleration.y = accelgyrodata[1] / 8192.0 * gravity;
imu_msg.linear_acceleration.z = accelgyrodata[2] / 8192.0 * gravity;

imu_publisher_->publish(imu_msg);
}
MPU9250 mpu9250;

rclcpp::Publisher<ImuMsg>::SharedPtr imu_publisher_;

rclcpp::TimerBase::SharedPtr timer_;

};


int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ImuPublisher>());

rclcpp::shutdown();
return 0;
}

工程概述

在/dev/ 中可以查询设备的/dev/i2c-* ,选择需要的I2C即可

本例程中使用/dev/i2c-6

功能:
ROS2从MPU9250传感器读取加速度计和陀螺仪数据,并以IMU消息的格式通过话题发布这些数据