[PIC®/AVR®/dsPIC®产品] Arduino 两台自平衡机器人

[复制链接]
幸福小强 发表于 2025-8-11 12:24 | 显示全部楼层 |阅读模式





首先,让我们解释一下所使用的组件及其功能。

- 采用微机电系统(MEMS)技术的MPU6050加速度计和陀螺仪传感器,用于检测沿X、Y、Z轴的倾斜角度或倾斜角度。

- 来自该传感器的信息被传输到微控制器,在我们的例子中它是 Arduino Uno 板。该板是机器人的大脑,处理传感器数据并确定保持机器人直立所需的作。

- 来自 Arduino 的处理数据被发送到电机驱动板,在我们的例子中是 L298N 5AD 类型。电机驱动电路控制电机的方向和速度。

- 该机器人使用两个直流电机,每个电机都连接到一个轮子上。这些电机调节车轮的速度以纠正机器人的倾斜。

- 和电源,特别是串联的两节锂电池,为电机和电子设备提供能量。所有组件都安装在由 5 毫米厚的 PVC 板制成的合适支撑结构上。



现在,我们简单解释一下工作原理:加速度计测量机器人相对于重力的方向,陀螺仪检测倾斜率。Arduino 使用 PID(比例、积分、微分)控制器来处理来自传感器的数据。


PID 控制器计算保持机器人平衡所需的必要速度和方向调整。如果机器人向前倾斜,电机会旋转得更快,使机器人向前移动并防止跌倒。如果它向后倾斜,电机会向后移动机器人以恢复平衡。当机器人检测到不平衡时,它会通过调整轮速、移动质心或向前或向后移动以保持其位置来进行补偿。

关于 Arduino 代码的几句话。安装代码的方法之前已经描述过很多次了,所以现在我只关注由于所用组件的不同特性以及它们的放置方法,需要针对每种情况单独进行的修改。

现在是有趣的部分,即专门为我们构建的设备自定义代码。根据组件的布局,机器人的重心会发生变化,并且每个MPU6050板的差异很小,需要在部分代码中进行补偿。如果我们更详细地查看代码,我们会看到有一个位置设置了陀螺仪偏移量。


在代码的这一部分中,在MPU6050芯片的制造过程中纠正了错误。对于每个单独的芯片,这些值都是不同的。它们的值是使用专门为此目的制作的 Arduino 代码确定的,您可以在本文末尾下载它。

接下来,我们需要继续设置加速度计。在“双设定点”行中,我们需要输入一个与我们的设备相对应的值。



为了获得这个值,我们需要将机器人保持在垂直于表面的理想垂直位置,并在串行监视器上读取所需的值。就我而言,大约是 182
最后,我们需要通过实验确定PID控制所需的Kp、Kd和Ki值。

为此,我们将所有三个值都设置为零。现在我们为 Kp 设置一些值并执行测试。Kp 太少会使机器人摔倒。过多的 Kp 会让机器人来回疯狂。一个好的 Kp 会让机器人稍微来回移动。接下来我们需要设置 Kd。gooD Kd 值将减少振荡,直到机器人几乎稳定,并使机器人保持站立。最后设置气。正确的 Ki 值将缩短机器人稳定所需的时间。我想强调的是,表面需要足够粗糙,以增加它与车轮之间的摩擦力。

  1. #include "I2Cdev.h"
  2. #include <PID_v1.h> //From https://github.com/br3ttb/Arduino-PID-Library/blob/master/PID_v1.h
  3. #include "MPU6050_6Axis_MotionApps20.h" //https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050
  4. MPU6050 mpu;
  5. // MPU control/status vars
  6. bool dmpReady = false;  // set true if DMP init was successful
  7. uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
  8. uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
  9. uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
  10. uint16_t fifoCount;     // count of all bytes currently in FIFO
  11. uint8_t fifoBuffer[64]; // FIFO storage buffer
  12. // orientation/motion vars
  13. Quaternion q;           // [w, x, y, z]         quaternion container
  14. VectorFloat gravity;    // [x, y, z]            gravity vector
  15. float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
  16. /*********Tune these 4 values for your BOT*********/
  17. double setpoint= 182; //set the value when the bot is perpendicular to ground using serial monitor.
  18. //Read the project documentation on circuitdigest.com to learn how to set these values
  19. double Kp = 15; //21 Set this first
  20. double Kd = 0.9; //0.8 Set this secound
  21. double Ki = 140; //140 Finally set this
  22. /******End of values setting*********/
  23. double input, output;
  24. PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
  25. volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
  26. void dmpDataReady()
  27. {
  28.     mpuInterrupt = true;
  29. }
  30. void setup() {
  31.   Serial.begin(115200);
  32.   // initialize device
  33.     Serial.println(F("Initializing I2C devices..."));
  34.     mpu.initialize();
  35.      // verify connection
  36.     Serial.println(F("Testing device connections..."));
  37.     Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
  38.     // load and configure the DMP
  39.     devStatus = mpu.dmpInitialize();
  40.     // supply your own gyro offsets here, scaled for min sensitivity
  41.     mpu.setXGyroOffset(-479);
  42.     mpu.setYGyroOffset(84);
  43.     mpu.setZGyroOffset(15);
  44.     mpu.setZAccelOffset(1638);
  45.       // make sure it worked (returns 0 if so)
  46.     if (devStatus == 0)
  47.     {
  48.         // turn on the DMP, now that it's ready
  49.         Serial.println(F("Enabling DMP..."));
  50.         mpu.setDMPEnabled(true);
  51.         // enable Arduino interrupt detection
  52.         Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
  53.         attachInterrupt(0, dmpDataReady, RISING);
  54.         mpuIntStatus = mpu.getIntStatus();
  55.         // set our DMP Ready flag so the main loop() function knows it's okay to use it
  56.         Serial.println(F("DMP ready! Waiting for first interrupt..."));
  57.         dmpReady = true;
  58.         // get expected DMP packet size for later comparison
  59.         packetSize = mpu.dmpGetFIFOPacketSize();
  60.         //setup PID
  61.         pid.SetMode(AUTOMATIC);
  62.         pid.SetSampleTime(10);
  63.         pid.SetOutputLimits(-255, 255);
  64.     }
  65.     else
  66.     {
  67.         // ERROR!
  68.         // 1 = initial memory load failed
  69.         // 2 = DMP configuration updates failed
  70.         // (if it's going to break, usually the code will be 1)
  71.         Serial.print(F("DMP Initialization failed (code "));
  72.         Serial.print(devStatus);
  73.         Serial.println(F(")"));
  74.     }
  75. //Initialise the Motor outpu pins
  76.     pinMode (6, OUTPUT);
  77.     pinMode (9, OUTPUT);
  78.     pinMode (10, OUTPUT);
  79.     pinMode (11, OUTPUT);
  80. //By default turn off both the motors
  81.     analogWrite(6,LOW);
  82.     analogWrite(9,LOW);
  83.     analogWrite(10,LOW);
  84.     analogWrite(11,LOW);
  85. }
  86. void loop() {
  87.     // if programming failed, don't try to do anything
  88.     if (!dmpReady) return;
  89.     // wait for MPU interrupt or extra packet(s) available
  90.     while (!mpuInterrupt && fifoCount < packetSize)
  91.     {
  92.         //no mpu data - performing PID calculations and output to motors     
  93.         pid.Compute();
  94.         //Print the value of Input and Output on serial monitor to check how it is working.
  95.         Serial.print(input); Serial.print(" =>"); Serial.println(output);
  96.         if (input>150 && input<200){//If the Bot is falling
  97.         if (output>0) //Falling towards front
  98.         Forward(); //Rotate the wheels forward
  99.         else if (output<0) //Falling towards back
  100.         Reverse(); //Rotate the wheels backward
  101.         }
  102.         else //If Bot not falling
  103.         Stop(); //Hold the wheels still
  104.     }
  105.     // reset interrupt flag and get INT_STATUS byte
  106.     mpuInterrupt = false;
  107.     mpuIntStatus = mpu.getIntStatus();
  108.     // get current FIFO count
  109.     fifoCount = mpu.getFIFOCount();
  110.     // check for overflow (this should never happen unless our code is too inefficient)
  111.     if ((mpuIntStatus & 0x10) || fifoCount == 1024)
  112.     {
  113.         // reset so we can continue cleanly
  114.         mpu.resetFIFO();
  115.         Serial.println(F("FIFO overflow!"));
  116.     // otherwise, check for DMP data ready interrupt (this should happen frequently)
  117.     }
  118.     else if (mpuIntStatus & 0x02)
  119.     {
  120.         // wait for correct available data length, should be a VERY short wait
  121.         while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
  122.         // read a packet from FIFO
  123.         mpu.getFIFOBytes(fifoBuffer, packetSize);
  124.         // track FIFO count here in case there is > 1 packet available
  125.         // (this lets us immediately read more without waiting for an interrupt)
  126.         fifoCount -= packetSize;
  127.         mpu.dmpGetQuaternion(&q, fifoBuffer); //get value for q
  128.         mpu.dmpGetGravity(&gravity, &q); //get value for gravity
  129.         mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); //get value for ypr
  130.         input = ypr[1] * 180/M_PI + 180;
  131.    }
  132. }
  133. void Forward() //Code to rotate the wheel forward
  134. {
  135.     analogWrite(6,output);
  136.     analogWrite(9,0);
  137.     analogWrite(10,output);
  138.     analogWrite(11,0);
  139.     Serial.print("F"); //Debugging information
  140. }
  141. void Reverse() //Code to rotate the wheel Backward  
  142. {
  143.     analogWrite(6,0);
  144.     analogWrite(9,output*-1);
  145.     analogWrite(10,0);
  146.     analogWrite(11,output*-1);
  147.     Serial.print("R");
  148. }
  149. void Stop() //Code to stop both the wheels
  150. {
  151.     analogWrite(6,0);
  152.     analogWrite(9,0);
  153.     analogWrite(10,0);
  154.     analogWrite(11,0);
  155.     Serial.print("S");
  156. }



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 幸福小强 发表于 2025-8-11 12:26 | 显示全部楼层
看起来不错,大家可以参考这个代码试试。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

143

主题

1720

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部

143

主题

1720

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部