Arduino论坛
直播中

antimatter2333

9年用户 93经验值
擅长:控制/MCU
私信 关注
[经验]

【Landzo C1试用体验】+ 第四篇 ☞头部运动追踪第一步:MPU6050控制舵机

        做飞思卡尔智能车比赛的时候看到平衡组的用过这个6050,当时就很感兴趣,一直想玩一下。
        后来我就想这个东西是不是可以用来做头部运动的追踪,没想到经过一天的查资料,在别的论坛这些早都被玩过了。
        经过一天的学习,我实现了我想要的功能,完整的头追要等开学以后才能发帖,我回老家只带了这么点东西回来。
       下面进入正题,
        1.什么是MPU6050?
                6050.png
        MPU6050是一种非常流行的空间运动传感器芯片,可以获取器件当前的三个加速度分量和三个旋转角速度。由于其体积小巧,功能强大,精度较高,不仅被广泛应用于工业,同时也是航模爱好者的神器,被安装在各类飞行器上驰骋蓝天。
  2.所使用套件中的蓝色舵机简介
         舵机.png
        蓝宙的PDF里已经给的很全了,这里不做赘述。
  3.接线示意图
         接线.png
        另外的舵机按照上一节图片中相应去接就可以了。
  4.俯仰角和横滚角
         关于两个角度.png 默认串口显示.png 坐标系.png
        通过阅读上述资料,以及具体的实验观察现象,其对应关系为pitch(俯仰角)和roll(横滚角)具体方法是使用卡尔曼滤波算法中的例程《MPU6050》,这个例程我会上传到附件中。把程序下载到arduino,再打开串口监视器,(这里注意要把波特率修改为115200,和程序中的设定同步,不然你看到的将会是乱码),我们就可以看到第二张图中定义的几个量了。如果需要显示acc和gyro则把程序中的0改为1。
  5.具体实现代码
        通过对第四节和第二节的学习,我们知道舵机的角度是0~180度,而我们通过串口监视器观察到的roll大概是在-90度~90度之间。为了实现同步,在舵机赋值的时候我们把roll加个90再附进去就差不多了。手上硬件全的话,你们就根据实际情况矫正的精准一些,我这里暂时没有这个必要了。我在附件中上传了实验效果的视频,有兴趣的同学可以查看。再添加舵机语句的时候要注意放的位置,不要放到某一个if判断的末尾。添加的语句已经标注为红色。
  1. /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.

  2. This software may be distributed and modified under the terms of the GNU
  3. General Public License version 2 (GPL2) as published by the Free Software
  4. Foundation and appearing in the file GPL2.TXT included in the packaging of
  5. this file. Please note that GPL2 Section 2[b] requires that all works based
  6. on this software must also be made publicly available under the terms of
  7. the GPL2 ("Copyleft").

  8. Contact information
  9. -------------------

  10. Kristian Lauszus, TKJ Electronics
  11. Web      :  http://www.tkjelectronics.com
  12. e-mail   :  kristianl@tkjelectronics.com
  13. */
  14. #include
  15. #include
  16. #include // Source: https://github.com/TKJElectronics/KalmanFilter

  17. Servo servo_pin_10;

  18. #define RESTRICT_PITCH // Comment out to restrict roll to ±90deg instead - please read: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf

  19. Kalman kalmanX; // Create the Kalman instances
  20. Kalman kalmanY;

  21. /* IMU Data */
  22. double accX, accY, accZ;
  23. double gyroX, gyroY, gyroZ;
  24. int16_t tempRaw;

  25. double gyroXangle, gyroYangle; // Angle calculate using the gyro only
  26. double compAngleX, compAngleY; // Calculated angle using a complementary filter
  27. double kalAngleX, kalAngleY; // Calculated angle using a Kalman filter

  28. uint32_t timer;
  29. uint8_t i2cData[14]; // Buffer for I2C data

  30. // TODO: Make calibration routine

  31. void setup() {
  32.    servo_pin_10.attach(10);
  33.   Serial.begin(115200);
  34.   Wire.begin();
  35. #if ARDUINO >= 157
  36.   Wire.setClock(400000UL); // Set I2C frequency to 400kHz
  37. #else
  38.   TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
  39. #endif

  40.   i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
  41.   i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
  42.   i2cData[2] = 0x00; // Set Gyro Full Scale Range to ±250deg/s
  43.   i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to ±2g
  44.   while (i2cWrite(0x19, i2cData, 4, false)); // Write to all four registers at once
  45.   while (i2cWrite(0x6B, 0x01, true)); // PLL with X axis gyroscope reference and disable sleep mode

  46.   while (i2cRead(0x75, i2cData, 1));
  47.   if (i2cData[0] != 0x68) { // Read "WHO_AM_I" register
  48.     Serial.print(F("Error reading sensor"));
  49.     while (1);
  50.   }

  51.   delay(100); // Wait for sensor to stabilize

  52.   /* Set kalman and gyro starting angle */
  53.   while (i2cRead(0x3B, i2cData, 6));
  54.   accX = (i2cData[0] << 8) | i2cData[1];
  55.   accY = (i2cData[2] << 8) | i2cData[3];
  56.   accZ = (i2cData[4] << 8) | i2cData[5];

  57.   // Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
  58.   // atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2
  59.   // It is then converted from radians to degrees
  60. #ifdef RESTRICT_PITCH // Eq. 25 and 26
  61.   double roll  = atan2(accY, accZ) * RAD_TO_DEG;
  62.   double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
  63. #else // Eq. 28 and 29
  64.   double roll  = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;
  65.   double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
  66. #endif

  67.   kalmanX.setAngle(roll); // Set starting angle
  68.   kalmanY.setAngle(pitch);
  69.   gyroXangle = roll;
  70.   gyroYangle = pitch;
  71.   compAngleX = roll;
  72.   compAngleY = pitch;

  73.   timer = micros();
  74. }

  75. void loop() {
  76.   /* Update all the values */
  77.   while (i2cRead(0x3B, i2cData, 14));
  78.   accX = ((i2cData[0] << 8) | i2cData[1]);
  79.   accY = ((i2cData[2] << 8) | i2cData[3]);
  80.   accZ = ((i2cData[4] << 8) | i2cData[5]);
  81.   tempRaw = (i2cData[6] << 8) | i2cData[7];
  82.   gyroX = (i2cData[8] << 8) | i2cData[9];
  83.   gyroY = (i2cData[10] << 8) | i2cData[11];
  84.   gyroZ = (i2cData[12] << 8) | i2cData[13];


  85.   double dt = (double)(micros() - timer) / 1000000; // Calculate delta time
  86.   timer = micros();

  87.   // Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
  88.   // atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2
  89.   // It is then converted from radians to degrees
  90. #ifdef RESTRICT_PITCH // Eq. 25 and 26
  91.   double roll  = atan2(accY, accZ) * RAD_TO_DEG;
  92.   double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
  93. #else // Eq. 28 and 29
  94.   double roll  = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;
  95.   double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
  96. #endif

  97.     servo_pin_10.write(roll+ 90);

  98.   double gyroXrate = gyroX / 131.0; // Convert to deg/s
  99.   double gyroYrate = gyroY / 131.0; // Convert to deg/s

  100. #ifdef RESTRICT_PITCH
  101.   // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  102.   if ((roll < -90 && kalAngleX > 90) || (roll > 90 && kalAngleX < -90)) {
  103.     kalmanX.setAngle(roll);
  104.     compAngleX = roll;
  105.     kalAngleX = roll;
  106.     gyroXangle = roll;
  107.   } else
  108.     kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter

  109.   if (abs(kalAngleX) > 90)
  110.     gyroYrate = -gyroYrate; // Invert rate, so it fits the restriced accelerometer reading
  111.   kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt);
  112. #else
  113.   // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  114.   if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) {
  115.     kalmanY.setAngle(pitch);
  116.     compAngleY = pitch;
  117.     kalAngleY = pitch;
  118.     gyroYangle = pitch;
  119.   } else
  120.     kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt); // Calculate the angle using a Kalman filter

  121.   if (abs(kalAngleY) > 90)
  122.     gyroXrate = -gyroXrate; // Invert rate, so it fits the restriced accelerometer reading
  123.   kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter
  124. #endif

  125.   gyroXangle += gyroXrate * dt; // Calculate gyro angle without any filter
  126.   gyroYangle += gyroYrate * dt;
  127.   //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate
  128.   //gyroYangle += kalmanY.getRate() * dt;

  129.   compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll; // Calculate the angle using a Complimentary filter
  130.   compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;

  131.   // Reset the gyro angle when it has drifted too much
  132.   if (gyroXangle < -180 || gyroXangle > 180)
  133.     gyroXangle = kalAngleX;
  134.   if (gyroYangle < -180 || gyroYangle > 180)
  135.     gyroYangle = kalAngleY;
  136.       

  137.   /* Print Data */
  138. #if 0 // Set to 1 to activate
  139.   Serial.print(accX); Serial.print("t");
  140.   Serial.print(accY); Serial.print("t");
  141.   Serial.print(accZ); Serial.print("t");

  142.   Serial.print(gyroX); Serial.print("t");
  143.   Serial.print(gyroY); Serial.print("t");
  144.   Serial.print(gyroZ); Serial.print("t");

  145.   Serial.print("t");
  146. #endif

  147.   Serial.print(roll); Serial.print("t");
  148.   Serial.print(gyroXangle); Serial.print("t");
  149.   Serial.print(compAngleX); Serial.print("t");
  150.   Serial.print(kalAngleX); Serial.print("t");

  151.   Serial.print("t");

  152.   Serial.print(pitch); Serial.print("t");
  153.   Serial.print(gyroYangle); Serial.print("t");
  154.   Serial.print(compAngleY); Serial.print("t");
  155.   Serial.print(kalAngleY); Serial.print("t");

  156. #if 0 // Set to 1 to print the temperature
  157.   Serial.print("t");

  158.   double temperature = (double)tempRaw / 340.0 + 36.53;
  159.   Serial.print(temperature); Serial.print("t");
  160. #endif

  161.   Serial.print("rn");
  162.   delay(2);
  163. }
  6.应用展望
        如果我手上有两个舵机,那么水平面和竖直面就都可以转动,配合上程序,我们就可以远程的更加人性化的控制摄像头的转动,再把图像显示在头盔中,从而改善现有的监控设备;
        如果我把它装在一辆联网的载具上,就可以远程看到正常人坐在载具中驾驶室的图像,配合上远程操控设备从而时间远程驾驶;
        如果我们在人的鞋子里装上加速度计,通过不同的值来换算成小车的移动速度以及轨迹,再配合上其他传感器,那么,是不是就有可能做出一台跟着人同步走的车?进而我们就可以把书包什么的都丢到车身上,然后我们就可以跑步回家锻炼身体而不是挤公交了。






回帖(29)

antimatter2333

2016-8-9 20:02:08
担心给读者的感觉不够直接,上图,
举报
  • QQ图片20160809200100.jpg

yuanrk810

2016-9-23 08:32:52
学习了,感谢分享!!
举报

雷翌仁

2016-10-31 14:33:36
謝謝樓主分享!
举报

好的够了没有

2016-11-2 22:32:07
我只想把这个装在hmz t1上把它改造成vr,要怎么没实现呢,需要什么硬件和软件呢
举报

更多回帖

发帖
×
20
完善资料,
赚取积分