i2c_mpu6050_angle.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h>
  4. #include <unistd.h>
  5. #include <sys/ioctl.h>
  6. #include <linux/i2c-dev.h>
  7. #include <stdint.h>
  8. #include <math.h>
  9. #include <sys/time.h>
  10. /************************ 宏定义 ************************/
  11. #define I2C_DEVICE_PATH "/dev/i2c-2" // RK3588 I2C2设备节点
  12. #define MPU6050_ADDR 0x68 // MPU6050从机地址(AD0=GND)
  13. // MPU6050寄存器地址
  14. #define PWR_MGMT_1 0x6B // 电源管理寄存器1
  15. #define SMPLRT_DIV 0x19 // 采样率分频寄存器
  16. #define CONFIG 0x1A // 配置寄存器
  17. #define GYRO_CONFIG 0x1B // 陀螺仪配置寄存器
  18. #define ACCEL_CONFIG 0x1C
  19. #define ACCEL_XOUT_H 0x3B // 加速度X轴高位数据寄存器
  20. #define GYRO_XOUT_H 0x43 // 陀螺仪X轴高位数据寄存器
  21. // 互补滤波系数(0.98偏向陀螺仪动态性能,0.02偏向加速度计静态稳定性)
  22. #define COMPLEMENTARY_COEFF 0.98f
  23. // 弧度转角度系数
  24. #define RAD_TO_DEG (180.0f / M_PI)
  25. /************************ 全局变量 ************************/
  26. int i2c_fd; // I2C设备文件描述符
  27. // 角度保存变量(静态变量,保持上次值用于互补滤波)
  28. static float roll_angle = 0.0f; // 横滚角(Roll)
  29. static float pitch_angle = 0.0f; // 俯仰角(Pitch)
  30. float roll_angle1 = 0.0f; // 横滚角(Roll)
  31. float pitch_angle1 = 0.0f; // 俯仰角(Pitch)
  32. /************************ 函数声明 ************************/
  33. // I2C设备初始化
  34. int i2c_init(const char *i2c_path, uint8_t slave_addr);
  35. // 向MPU6050单个寄存器写数据
  36. int mpu6050_write_reg(uint8_t reg_addr, uint8_t data);
  37. // 从MPU6050读取多个寄存器数据
  38. int mpu6050_read_regs(uint8_t reg_addr, uint8_t *buf, uint8_t len);
  39. // 初始化MPU6050
  40. int mpu6050_init(void);
  41. // 读取加速度和陀螺仪数据
  42. void mpu6050_read_data(int16_t *accel_data, int16_t *gyro_data);
  43. // 计算实际物理值
  44. void mpu6050_calc_phy(float *accel_phy, float *gyro_phy, int16_t *accel_data, int16_t *gyro_data);
  45. // 计算横滚角和俯仰角(互补滤波)
  46. void mpu6050_calc_angle(float *accel_phy, float *gyro_phy, double dt);
  47. /************************ 函数实现 ************************/
  48. /**
  49. * @brief I2C设备初始化(打开设备+设置从机地址)
  50. * @param i2c_path: I2C设备路径(如/dev/i2c-2)
  51. * @param slave_addr: 从机地址(如0x68)
  52. * @retval 0:成功, -1:失败
  53. */
  54. int i2c_init(const char *i2c_path, uint8_t slave_addr)
  55. {
  56. // 打开I2C设备
  57. i2c_fd = open(i2c_path, O_RDWR);
  58. if (i2c_fd < 0) {
  59. perror("open i2c device failed");
  60. return -1;
  61. }
  62. // 设置I2C从机地址
  63. if (ioctl(i2c_fd, I2C_SLAVE, slave_addr) < 0) {
  64. perror("set i2c slave address failed");
  65. close(i2c_fd);
  66. return -1;
  67. }
  68. printf("I2C init success (path: %s, slave addr: 0x%02X)\n", i2c_path, slave_addr);
  69. return 0;
  70. }
  71. /**
  72. * @brief 向MPU6050单个寄存器写数据
  73. * @param reg_addr: 寄存器地址
  74. * @param data: 要写入的数据
  75. * @retval 0:成功, -1:失败
  76. */
  77. int mpu6050_write_reg(uint8_t reg_addr, uint8_t data)
  78. {
  79. uint8_t buf[2];
  80. buf[0] = reg_addr; // 寄存器地址
  81. buf[1] = data; // 要写入的数据
  82. // 写入2字节(寄存器地址+数据)
  83. if (write(i2c_fd, buf, 2) != 2) {
  84. perror("write mpu6050 reg failed");
  85. return -1;
  86. }
  87. return 0;
  88. }
  89. /**
  90. * @brief 从MPU6050读取多个寄存器数据
  91. * @param reg_addr: 起始寄存器地址
  92. * @param buf: 数据接收缓冲区
  93. * @param len: 要读取的字节数
  94. * @retval 0:成功, -1:失败
  95. */
  96. int mpu6050_read_regs(uint8_t reg_addr, uint8_t *buf, uint8_t len)
  97. {
  98. // 先写入要读取的起始寄存器地址
  99. if (write(i2c_fd, &reg_addr, 1) != 1) {
  100. perror("write reg addr failed");
  101. return -1;
  102. }
  103. // 读取指定长度的数据
  104. if (read(i2c_fd, buf, len) != len) {
  105. perror("read mpu6050 regs failed");
  106. return -1;
  107. }
  108. return 0;
  109. }
  110. /**
  111. * @brief 初始化MPU6050(配置采样率、量程等)
  112. * @retval 0:成功, -1:失败
  113. */
  114. int mpu6050_init(void)
  115. {
  116. // 1. 唤醒MPU6050(PWR_MGMT_1=0x00,解除睡眠模式)
  117. if (mpu6050_write_reg(PWR_MGMT_1, 0x00) < 0) {
  118. return -1;
  119. }
  120. // 2. 设置采样率分频(SMPLRT_DIV=0x07,采样率=1000/(1+7)=125Hz)
  121. if (mpu6050_write_reg(SMPLRT_DIV, 0x07) < 0) {
  122. return -1;
  123. }
  124. // 3. 配置低通滤波(CONFIG=0x06,滤波频率=5Hz)
  125. if (mpu6050_write_reg(CONFIG, 0x06) < 0) {
  126. return -1;
  127. }
  128. // 4. 配置陀螺仪量程(GYRO_CONFIG=0x18,±2000°/s)
  129. if (mpu6050_write_reg(GYRO_CONFIG, 0x18) < 0) {
  130. return -1;
  131. }
  132. // 5. 配置加速度计量程(ACCEL_CONFIG=0x18,±16g)
  133. if (mpu6050_write_reg(ACCEL_CONFIG, 0x18) < 0) {
  134. return -1;
  135. }
  136. printf("MPU6050 init success\n");
  137. return 0;
  138. }
  139. /**
  140. * @brief 读取加速度(3轴)和陀螺仪(3轴)原始数据
  141. * @param accel_data: 加速度原始数据缓冲区([0]=X, [1]=Y, [2]=Z)
  142. * @param gyro_data: 陀螺仪原始数据缓冲区([0]=X, [1]=Y, [2]=Z)
  143. */
  144. void mpu6050_read_data(int16_t *accel_data, int16_t *gyro_data)
  145. {
  146. uint8_t buf[14]; // 加速度(6字节)+温度(2字节)+陀螺仪(6字节)=14字节
  147. // 从ACCEL_XOUT_H开始读取14字节数据
  148. mpu6050_read_regs(ACCEL_XOUT_H, buf, 14);
  149. // 拼接16位原始数据(高位在前,低位在后)
  150. accel_data[0] = (int16_t)(buf[0] << 8 | buf[1]); // 加速度X
  151. accel_data[1] = (int16_t)(buf[2] << 8 | buf[3]); // 加速度Y
  152. accel_data[2] = (int16_t)(buf[4] << 8 | buf[5]); // 加速度Z
  153. gyro_data[0] = (int16_t)(buf[8] << 8 | buf[9]); // 陀螺仪X
  154. gyro_data[1] = (int16_t)(buf[10] << 8 | buf[11]); // 陀螺仪Y
  155. gyro_data[2] = (int16_t)(buf[12] << 8 | buf[13]); // 陀螺仪Z
  156. }
  157. /**
  158. * @brief 将原始数据转换为实际物理值
  159. * @param accel_phy: 加速度物理值(单位:g)
  160. * @param gyro_phy: 陀螺仪物理值(单位:°/s)
  161. * @param accel_data: 加速度原始数据
  162. * @param gyro_data: 陀螺仪原始数据
  163. */
  164. void mpu6050_calc_phy(float *accel_phy, float *gyro_phy, int16_t *accel_data, int16_t *gyro_data)
  165. {
  166. // 加速度计量程±16g,对应原始数据范围±32768,灵敏度=16g/32768=0.00048828125 g/LSB
  167. accel_phy[0] = accel_data[0] * 0.00048828125f;
  168. accel_phy[1] = accel_data[1] * 0.00048828125f;
  169. accel_phy[2] = accel_data[2] * 0.00048828125f;
  170. // 陀螺仪量程±2000°/s,对应原始数据范围±32768,灵敏度=2000°/s/32768≈0.06103515625 °/s/LSB
  171. gyro_phy[0] = gyro_data[0] * 0.06103515625f;
  172. gyro_phy[1] = gyro_data[1] * 0.06103515625f;
  173. gyro_phy[2] = gyro_data[2] * 0.06103515625f;
  174. }
  175. /**
  176. * @brief 用互补滤波计算横滚角(Roll)和俯仰角(Pitch)
  177. * @param accel_phy: 加速度物理值(g)
  178. * @param gyro_phy: 陀螺仪物理值(°/s)
  179. * @param dt: 两次采样的时间差(s)
  180. */
  181. void mpu6050_calc_angle(float *accel_phy, float *gyro_phy, double dt)
  182. {
  183. // 1. 由加速度计计算静态角度(利用重力分量,仅静态有效)
  184. float accel_roll = atan2(accel_phy[1], accel_phy[2]) * RAD_TO_DEG;
  185. float accel_pitch = atan2(-accel_phy[0], sqrt(accel_phy[1]*accel_phy[1] + accel_phy[2]*accel_phy[2])) * RAD_TO_DEG;
  186. // 2. 由陀螺仪计算动态角度(积分得到,存在漂移)
  187. float gyro_roll = roll_angle + gyro_phy[0] * dt;
  188. float gyro_pitch = pitch_angle + gyro_phy[1] * dt;
  189. // 3. 互补滤波融合:兼顾动态响应和静态稳定性
  190. roll_angle = COMPLEMENTARY_COEFF * gyro_roll + (1 - COMPLEMENTARY_COEFF) * accel_roll;
  191. pitch_angle = COMPLEMENTARY_COEFF * gyro_pitch + (1 - COMPLEMENTARY_COEFF) * accel_pitch;
  192. }
  193. /************************ 主函数 ************************/
  194. int main(int argc, char *argv[])
  195. {
  196. int16_t accel_raw[3] = {0}; // 加速度原始数据
  197. int16_t gyro_raw[3] = {0}; // 陀螺仪原始数据
  198. float accel_phy[3] = {0.0f}; // 加速度物理值(g)
  199. float gyro_phy[3] = {0.0f}; // 陀螺仪物理值(°/s)
  200. struct timeval last_time, curr_time; // 时间戳(用于计算dt)
  201. double dt = 0.0; // 两次采样时间差(s)
  202. // 1. 初始化I2C
  203. if (i2c_init(I2C_DEVICE_PATH, MPU6050_ADDR) < 0) {
  204. exit(EXIT_FAILURE);
  205. }
  206. // 2. 初始化MPU6050
  207. if (mpu6050_init() < 0) {
  208. close(i2c_fd);
  209. exit(EXIT_FAILURE);
  210. }
  211. // 3. 获取初始时间戳
  212. gettimeofday(&last_time, NULL);
  213. // 4. 循环读取并打印数据
  214. printf("=========================================================================\n");
  215. printf("Accel(X/Y/Z) (g) | Gyro(X/Y/Z) (°/s) | Roll (°) | Pitch (°)\n");
  216. printf("=========================================================================\n");
  217. while (1) {
  218. // 获取当前时间戳
  219. gettimeofday(&curr_time, NULL);
  220. // 计算时间差dt(微秒转换为秒)
  221. dt = (curr_time.tv_sec - last_time.tv_sec) + (curr_time.tv_usec - last_time.tv_usec) / 1000000.0;
  222. last_time = curr_time; // 更新上一次时间戳
  223. // 读取原始数据
  224. mpu6050_read_data(accel_raw, gyro_raw);
  225. // 转换为物理值
  226. mpu6050_calc_phy(accel_phy, gyro_phy, accel_raw, gyro_raw);
  227. // 计算横滚角和俯仰角
  228. mpu6050_calc_angle(accel_phy, gyro_phy, dt);
  229. roll_angle1=roll_angle + 11.79;
  230. pitch_angle1=pitch_angle-2.39;
  231. // 打印数据(\r实现同一行刷新)
  232. printf("%.4f / %.4f / %.4f | %.4f / %.4f / %.4f | %.2f | %.2f\r",
  233. accel_phy[0], accel_phy[1], accel_phy[2],
  234. gyro_phy[0], gyro_phy[1], gyro_phy[2],
  235. roll_angle1, pitch_angle1);
  236. fflush(stdout); // 强制刷新输出缓冲区
  237. // 延时100ms(读取频率约10Hz,可调整)
  238. usleep(100000);
  239. }
  240. // 关闭I2C设备(实际循环中不会执行到)
  241. close(i2c_fd);
  242. return 0;
  243. }