15浏览
查看: 15|回复: 2

[项目] 【Arduino 动手做】使用编码器 AS5600 的自平衡小车

[复制链接]
I. 功能简介
日期:2024 年 8 月。
带有减速齿轮的有刷直流电机在齿轮之间不可避免地存在间隙,这可能导致低分辨率编码器(如霍尔效应传感器)的实际速度读数不准确。BLDC 电机、12 位 AS5600 编码器和磁场定向控制 (FOC) 可以解决这个问题。尽管如此,我还是使用级联 PID 到控制回路。
代码、CAD 模型 (STL) 和电路原理图位于 GitHub 上。

II. 使用 FOC 进行速度控制和位置控制
由于编码器 AS5600 和电机是分开的,我需要构建一些东西来将它们放在一起,所以我就开始构建自平衡机器人

III. 打印和组装

VI. 站起来
它静止不动,因为它卡在一些奇怪的位置。我猜是它后面的 USB 数据线固定着它。
我只是粗略地调整了 PID,它已经具有不错的稳定性。
第二天,我对 PID 进行了多次微调,它比上一个视频中展示的要稳定得多。
带位置控制的稳定性测试。后来我发现位置控制效果不佳,所以我在蓝牙控制上添加了一个开关来启用/禁用位置控制。此视频之后的所有视频都没有位置控制。
静止不动,几乎没有移动。
蓝牙控制测试静止不动
无位置控制的稳定性测试,
我猜这是由于钟摆的动力学特性,它需要倾斜才能加速,但要保持恒定速度,所以它有时会来回摆动。它掉落是因为车轮达到了最大速度,并且无法再加速。此限制可能来自 PID 输出限制和/或高速换向问题

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图3

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图2

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图1

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图4

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图5

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图6

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图7

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图9

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图8

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图10

【Arduino 动手做】使用编码器 AS5600 的自平衡小车图11

驴友花雕  中级技神
 楼主|

发表于 5 小时前

【Arduino 动手做】使用编码器 AS5600 的自平衡小车

项目代码

  1. ////////////////////////////////////////////////////////////// IMU //////////////////////////////////////////////////////////////
  2. #include <Adafruit_Sensor.h>
  3. #include <Adafruit_MPU6050.h>
  4. #include <Wire.h>
  5. #include <Kalman.h> // Source: https://github.com/TKJElectronics/KalmanFilter
  6. Adafruit_MPU6050 mpu;
  7. // accel
  8. float ax_float, ay_float, az_float;
  9. float ax_error, ay_error, az_error; // error of linear acceleration
  10. // gyro
  11. float wx, wy, wz; // rad/s
  12. float wx_error, wy_error, wz_error;// rad/s
  13. Kalman kalmanY; // Create the Kalman instances
  14. double p_kf=0;
  15. uint32_t timer_kf;
  16. ////////////////////////////////////////////////////////////// IMU //////////////////////////////////////////////////////////////
  17. ////////////////////////////////////////////////////////////// Encoder //////////////////////////////////////////////////////////////
  18. #include "AS5600.h"
  19. AS5600 as5600_0(&Wire);
  20. AS5600 as5600_1(&Wire1);
  21. ////////////////////////////////////////////////////////////// Encoder //////////////////////////////////////////////////////////////
  22. void setup() {
  23.   Serial.begin(460800);
  24.   Serial.println("开始初始化");
  25.   AS5600_setup();
  26.   mpu6050_setup();
  27.   Serial.println("初始化完成");
  28. }
  29. void loop() {
  30.   // 读取AS5600传感器的角度
  31.   float angle0 = float(as5600_0.readAngle())/4096*2*M_PI;
  32.   float angle1 = float(as5600_1.readAngle())/4096*2*M_PI;
  33.   // 输出数据到串口
  34.   Serial.print("Ang 1: ");
  35.   Serial.print(angle0);
  36.   Serial.print("\tAng 2: ");
  37.   Serial.print(angle1);
  38.   read_mpu6050_angle_loop();
  39.   Serial.println();
  40.   delay(4);
  41. }
  42. ////////////////////////////////////////////////////////////// Encoder //////////////////////////////////////////////////////////////
  43. void AS5600_setup(){
  44.   Wire.begin(21, 22);    // SDA=21, SCL=22
  45.   Wire1.begin(19, 23);   // SDA=19, SCL=23
  46.   if (!as5600_0.begin()) {
  47.     Serial.println("No 1 AS5600 Found");
  48.     while (1);
  49.   }
  50.   if (!as5600_1.begin()) {
  51.     Serial.println("No 2 AS5600 Found");
  52.     while (1);
  53.   }
  54. }
  55. ////////////////////////////////////////////////////////////// Encoder //////////////////////////////////////////////////////////////
  56. ////////////////////////////////////////////// IMU //////////////////////////////////////////////
  57. void mpu6050_setup(){
  58.   Serial.flush();
  59.   delay(2500);
  60.   mpu6050_start();
  61.   delay(2000);
  62.   read_accel_gyro_raw();
  63.   ax_float-=ax_error;
  64.   ay_float-=ay_error;
  65.   az_float-=az_error;
  66.   double pitch=atan(-1*ax_float/sqrt(sq(ay_float)+sq(az_float)));
  67.   kalmanY.setAngle(pitch); // Set starting angle
  68.   timer_kf = micros();
  69. }
  70. void mpu6050_start(){
  71.   // Try to initialize!
  72.   Serial.println("MPU6050 Start");delay(1000);
  73.   // DO NOT USE: if (!mpu.begin()) {
  74.   //        USE: ↓↓↓↓↓↓↓↓↓↓↓↓
  75.   if (!mpu.begin(0x68,&Wire)) {
  76.     Serial.println("Failed to find MPU6050 chip");
  77.     while (1) {
  78.       delay(10);
  79.     }
  80.   }
  81.   Serial.println("MPU6050 Found!");
  82.   mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  83.   Serial.print("Accelerometer range set to: ");
  84.   switch (mpu.getAccelerometerRange()) {
  85.   case MPU6050_RANGE_2_G:
  86.     Serial.println("+-2G");
  87.     break;
  88.   case MPU6050_RANGE_4_G:
  89.     Serial.println("+-4G");
  90.     break;
  91.   case MPU6050_RANGE_8_G:
  92.     Serial.println("+-8G");
  93.     break;
  94.   case MPU6050_RANGE_16_G:
  95.     Serial.println("+-16G");
  96.     break;
  97.   }
  98.   mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  99.   Serial.print("Gyro range set to: ");
  100.   switch (mpu.getGyroRange()) {
  101.   case MPU6050_RANGE_250_DEG:
  102.     Serial.println("+- 250 deg/s");
  103.     break;
  104.   case MPU6050_RANGE_500_DEG:
  105.     Serial.println("+- 500 deg/s");
  106.     break;
  107.   case MPU6050_RANGE_1000_DEG:
  108.     Serial.println("+- 1000 deg/s");
  109.     break;
  110.   case MPU6050_RANGE_2000_DEG:
  111.     Serial.println("+- 2000 deg/s");
  112.     break;
  113.   }
  114.   mpu.setFilterBandwidth(MPU6050_BAND_5_HZ);
  115.   Serial.print("Filter bandwidth set to: ");
  116.   switch (mpu.getFilterBandwidth()) {
  117.   case MPU6050_BAND_260_HZ:
  118.     Serial.println("260 Hz");
  119.     break;
  120.   case MPU6050_BAND_184_HZ:
  121.     Serial.println("184 Hz");
  122.     break;
  123.   case MPU6050_BAND_94_HZ:
  124.     Serial.println("94 Hz");
  125.     break;
  126.   case MPU6050_BAND_44_HZ:
  127.     Serial.println("44 Hz");
  128.     break;
  129.   case MPU6050_BAND_21_HZ:
  130.     Serial.println("21 Hz");
  131.     break;
  132.   case MPU6050_BAND_10_HZ:
  133.     Serial.println("10 Hz");
  134.     break;
  135.   case MPU6050_BAND_5_HZ:
  136.     Serial.println("5 Hz");
  137.     break;
  138.   }
  139. }
  140. void read_accel_gyro_raw(){
  141.   sensors_event_t a, g, temp;
  142.   mpu.getEvent(&a, &g, &temp);
  143.   ax_float=a.acceleration.x;
  144.   ay_float=a.acceleration.y;
  145.   az_float=a.acceleration.z;
  146.   wx=g.gyro.x;
  147.   wy=g.gyro.y;
  148.   wz=g.gyro.z;
  149. }
  150. void read_mpu6050_angle_loop(){
  151.   read_accel_gyro_raw();
  152.   ax_float-=ax_error;
  153.   ay_float-=ay_error;
  154.   az_float-=az_error;
  155.   wx-=wx_error;
  156.   wy-=wy_error;
  157.   wz-=wz_error;
  158.   double dt = (double)(micros() - timer_kf) / 1000000; // Calculate delta time
  159.   timer_kf = micros();
  160.   
  161.   double pitch=atan(ay_float/sqrt(sq(ax_float)+sq(az_float)))*180/M_PI;
  162.   p_kf = kalmanY.getAngle(pitch, wx*180/M_PI, dt); // pitch
  163.   // Serial.print(-50); // To freeze the lower limit
  164.   // Serial.print(" ");
  165.   // Serial.print(50); // To freeze the upper limit
  166.   // Serial.print(" ");
  167.   // Serial.print("Pitch: ");
  168.   // Serial.print(p_kf); Serial.print("\t");
  169.   Serial.print("p_kf:");Serial.print(p_kf); Serial.print("\t");
  170. }
  171. ////////////////////////////////////////////// IMU //////////////////////////////////////////////
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 5 小时前

【Arduino 动手做】使用编码器 AS5600 的自平衡小车

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail