瀏覽代碼

feat(chapt13): 完成I2C通信实验

鱼香ROS 2 年之前
父節點
當前提交
fa0091dd11

+ 2 - 2
docs/_sidebar.md

@@ -248,8 +248,8 @@
     - 进阶篇-学会使用第三方库
     - 进阶篇-学会使用第三方库
       - [1.学会安装第三方开源库](humble/chapt13/advance/1.学会安装第三方开源库.md)
       - [1.学会安装第三方开源库](humble/chapt13/advance/1.学会安装第三方开源库.md)
       - [2.使用开源库驱动IMU](humble/chapt13/advance/2.使用开源库驱动IMU.md)
       - [2.使用开源库驱动IMU](humble/chapt13/advance/2.使用开源库驱动IMU.md)
-      - 2.使用SSD1306驱动OLED
-      - 3.通讯协议小课堂-I2C通信
+      - [3.学会面向对象编程-封装IMU驱动](humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动.md)
+      - [4.使用开源库驱动OLED](humble/chapt13/advance/4.使用开源库驱动OLED.md)
       - 4.学会面向对象编程-封装OLED驱动
       - 4.学会面向对象编程-封装OLED驱动
   - 第 14 章 接入ROS2-MicroROS
   - 第 14 章 接入ROS2-MicroROS
     - 基础篇-MicoRos介绍与安装
     - 基础篇-MicoRos介绍与安装

+ 273 - 1
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动.md

@@ -6,4 +6,276 @@
 
 
 ## 一、理论介绍
 ## 一、理论介绍
 
 
-欧拉角转四元数
+### 1.1.欧拉角转四元数
+
+欧拉角转四元数的公式我们在第六章入门篇第三节有介绍,这里回顾一下
+
+![image-20230119171251550](3.%E5%AD%A6%E4%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B-%E5%B0%81%E8%A3%85IMU%E9%A9%B1%E5%8A%A8/imgs/image-20230119171251550.png)
+
+根据公式我们可以写出代码
+
+```c++
+typedef struct
+{
+    float w;
+    float x;
+    float y;
+    float z;
+} quaternion_t;
+
+Euler2Quaternion(float roll, float pitch, float yaw, quaternion_t &q)
+{
+    double cr = cos(roll * 0.5);
+    double sr = sin(roll * 0.5);
+    double cy = cos(yaw * 0.5);
+    double sy = sin(yaw * 0.5);
+    double cp = cos(pitch * 0.5);
+    double sp = sin(pitch * 0.5);
+    q.w = cy * cp * cr + sy * sp * sr;
+    q.x = cy * cp * sr - sy * sp * cr;
+    q.y = sy * cp * sr + cy * sp * cr;
+    q.z = sy * cp * cr - cy * sp * sr;
+}
+```
+
+### 1.2  坐标系校准
+
+我们采用右手坐标系,接着我们依次来校准角度数据的方向。
+
+打开终端,点击RST,查看IMU数据。
+
+首先是X轴,我们让开发板上爱神丘比特的剑头指向自己,然后从右侧往左侧倾斜。
+
+![image-20230119174618988](3.%E5%AD%A6%E4%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B-%E5%B0%81%E8%A3%85IMU%E9%A9%B1%E5%8A%A8/imgs/image-20230119174618988.png)
+
+可以看到此时X轴为正值,符合右手坐标系法则。
+
+接着是Y轴,平放,将箭头朝向自己的胸口,接着抬高板子,让箭头指向自己的头部,观察Y轴的变化。
+
+![image-20230119174800571](3.%E5%AD%A6%E4%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B-%E5%B0%81%E8%A3%85IMU%E9%A9%B1%E5%8A%A8/imgs/image-20230119174800571.png)
+
+Y轴为负值,不符合右手坐标系法则,所以Y的值应该取一次负,使其为正。
+
+
+
+接着是Z轴,平放,将箭头朝向自己的胸口,然后逆时针旋转板子,观察数值变化。
+
+![image-20230119174952495](3.%E5%AD%A6%E4%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B-%E5%B0%81%E8%A3%85IMU%E9%A9%B1%E5%8A%A8/imgs/image-20230119174952495.png)
+
+值为正,表示符合右手坐标系法则。
+
+
+
+> 你可以会问小鱼怎么确认怎么旋转是正,怎么旋转是负,首先要确认轴向,我们开发板的Z轴朝上,X轴朝前,此时Y轴应该朝左。接着摊开右手手掌,用大拇指朝向轴的方向,比如朝向X轴,然后握起手掌,那么你握的方向就是正方向。
+
+## 二、开始写代码
+
+新建工程`example07_mpu6050_oop`
+
+![image-20230119175601688](3.%E5%AD%A6%E4%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B-%E5%B0%81%E8%A3%85IMU%E9%A9%B1%E5%8A%A8/imgs/image-20230119175601688.png)
+
+接着为其添加依赖
+
+修改`platformio.ini`
+
+```ini
+[env:featheresp32]
+platform = espressif32
+board = featheresp32
+framework = arduino
+lib_deps = 
+    https://ghproxy.com/https://github.com/rfetick/MPU6050_light.git
+```
+
+接着在`lib`下新建`IMU`文件夹,并在文件夹下新建`IMU.h`和`IMU.cpp`
+
+![image-20230119175935960](3.%E5%AD%A6%E4%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B-%E5%B0%81%E8%A3%85IMU%E9%A9%B1%E5%8A%A8/imgs/image-20230119175935960.png)
+
+IMU.h
+
+```c++
+#ifndef __IMU_H__
+#define __IMU_H__
+#include "Wire.h"
+#include "MPU6050_light.h"
+
+typedef struct
+{
+    float w;
+    float x;
+    float y;
+    float z;
+} quaternion_t; // 四元数结构体
+
+typedef struct
+{
+    float x;
+    float y;
+    float z;
+} vector_3d_t; // 通用3D点结构体
+
+typedef struct
+{
+    quaternion_t orientation;
+    vector_3d_t angle_euler;
+    vector_3d_t angular_velocity;
+    vector_3d_t linear_acceleration;
+} imu_t; // IMU数据结构体
+
+class IMU
+{
+private:
+    MPU6050 *mpu_; // mpu6050指针
+
+public:
+    /**
+     * @brief 同u哦NPU6050构造一个新的IMU对象
+     *
+     * @param mpu
+     */
+    IMU(MPU6050 &mpu);
+    
+    ~IMU() = default;
+    /**
+     * @brief 初始化函数
+     *
+     * @param sda 引脚编号
+     * @param scl 引脚编号
+     * @return true
+     * @return false
+     */
+    bool begin(int sda, int scl);
+    /**
+     * @brief 欧拉角转四元数
+     *
+     * @param roll 输入X
+     * @param pitch 输入y
+     * @param yaw 输入Z
+     * @param q  返回的四元数引用
+     */
+    static void Euler2Quaternion(float roll, float pitch, float yaw, quaternion_t &q);
+    /**
+     * @brief 获取IMU数据函数
+     *
+     * @param imu
+     */
+    void getImuData(imu_t &imu);
+    /**
+     * @brief 更新IMU数据,同上一节中的mou.update
+     *
+     */
+    void update();
+};
+
+#endif // __IMU_H__
+```
+
+IMU.cpp
+
+```c++
+#include "IMU.h"
+
+IMU::IMU(MPU6050 &mpu)
+{
+    mpu_ = &mpu;
+};
+
+bool IMU::begin(int sda, int scl)
+{
+    Wire.begin(sda, scl);
+    byte status = mpu_->begin();
+    Serial.print(F("MPU6050 status: "));
+    Serial.println(status);
+    if (status != 0)
+    {
+        return false;
+    } // stop everything if could not connect to MPU6050
+
+    Serial.println(F("Calculating offsets, do not move MPU6050"));
+    delay(1000);
+    // mpu.upsideDownMounting = true; // uncomment this line if the MPU6050 is mounted upside-down
+    mpu_->calcOffsets(); // gyro and accelero
+    Serial.println("Done!\n");
+    return true;
+}
+
+void IMU::Euler2Quaternion(float roll, float pitch, float yaw, quaternion_t &q)
+{
+    double cr = cos(roll * 0.5);
+    double sr = sin(roll * 0.5);
+    double cy = cos(yaw * 0.5);
+    double sy = sin(yaw * 0.5);
+    double cp = cos(pitch * 0.5);
+    double sp = sin(pitch * 0.5);
+    q.w = cy * cp * cr + sy * sp * sr;
+    q.x = cy * cp * sr - sy * sp * cr;
+    q.y = sy * cp * sr + cy * sp * cr;
+    q.z = sy * cp * cr - cy * sp * sr;
+}
+
+void IMU::getImuData(imu_t &imu)
+{
+    imu.angle_euler.x = mpu_->getAngleX();
+    imu.angle_euler.y = -mpu_->getAngleY();
+    imu.angle_euler.z = mpu_->getAngleZ();
+
+    imu.angular_velocity.x = mpu_->getAccAngleX();
+    imu.angular_velocity.y = -mpu_->getAccAngleY();
+    imu.angular_velocity.z = mpu_->getGyroZ();
+
+    imu.linear_acceleration.x = mpu_->getAccX();
+    imu.linear_acceleration.y = mpu_->getAccY();
+    imu.linear_acceleration.z = mpu_->getAccZ();
+
+    IMU::Euler2Quaternion(imu.angle_euler.x, imu.angle_euler.y, imu.angle_euler.z,
+                          imu.orientation);
+}
+
+void IMU::update()
+{
+    mpu_->update();
+}
+```
+
+main.cpp
+
+```c++
+#include <Arduino.h>
+#include "IMU.h"
+
+MPU6050 mpu(Wire); // 初始化MPU6050对象
+IMU imu(mpu);      // 初始化IMU对象
+
+imu_t imu_data;
+unsigned long timer = 0;
+
+void setup()
+{
+  Serial.begin(115200);
+  imu.begin(18, 19); // 初始化IMU,使用18,19引脚
+}
+
+void loop()
+{
+  imu.update();
+  if ((millis() - timer) > 100)
+  {
+    imu.getImuData(imu_data); // 获取IMU数据结构体
+    Serial.printf("imu:\teuler(%f,%f,%f)\n",
+                  imu_data.angle_euler.x, imu_data.angle_euler.y, imu_data.angle_euler.z);
+    Serial.printf("imu:\torientation(%f,%f,%f,%f)\n",
+                  imu_data.orientation.w, imu_data.orientation.x, imu_data.orientation.y, imu_data.orientation.z);
+    timer = millis();
+  }
+}
+```
+
+对于代码的解释已经放到了注释之中。
+
+编译下载后,你将看到
+
+![image-20230119215017374](3.%E5%AD%A6%E4%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B-%E5%B0%81%E8%A3%85IMU%E9%A9%B1%E5%8A%A8/imgs/image-20230119215017374.png)
+
+## 三、总结
+
+本节我们通过对MPU6050驱动的封装,学习了如何在嵌入式上使用面向对象编程的方法,下一节我们继续尝试使用开源库来驱动OLED模块,让我们的显示器亮起来。.

二進制
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动/imgs/image-20230119171251550.png


二進制
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动/imgs/image-20230119174618988.png


二進制
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动/imgs/image-20230119174800571.png


二進制
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动/imgs/image-20230119174952495.png


二進制
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动/imgs/image-20230119175601688.png


二進制
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动/imgs/image-20230119175935960.png


二進制
docs/humble/chapt13/advance/3.学会面向对象编程-封装IMU驱动/imgs/image-20230119215017374.png