驴友花雕 发表于 2025-6-17 11:36:20

【Arduino 动手做】Arduino SELF-BALANCE 机器人

什么是自平衡机器人?
自平衡机器人是一种两轮机器人,它可以平衡自己以防止自己跌倒。

自平衡机器人如何工作?
自平衡机器人使用“闭环反馈控制”系统;这意味着来自运动传感器的实时数据用于控制电机并快速补偿任何倾斜运动,以保持机器人直立。类似的自平衡反馈控制系统可以在许多其他应用中看到。

自平衡机器人用在什么地方?
在轮式机器人中,Segway 和 Ninebot 这两款自平衡机器人已经流行起来,用于通勤或作为巡逻运输车。此外,Anybots QB 等自平衡轮式机器人目前被用作服务机器人平台。

需要哪些组件,为什么?
在这部分,我们将讨论组件,我们为什么选择它们,并且我们还将并排制作机器人。

为了更好地解释,我将其分为三个不同的部分——

机械部分:- 在这里,我们将学习以非常简单易行的方式制作机器人本体。
电子部分:- 在这里我们将学习电子元件、电路设计和组装所有组件。这部分结束,我们的机器人将准备好进行下一个也是最后一个级别,即 Arduino 编码和校准。
Arduino编码和校准:- 在最后一部分,我们将讨论Arduino代码以及如何校准机器人以实现自我平衡。
完成这三个步骤后,我们的机器人就可以展示了。

MECHANICAL PART
我们将首先开始制作我们的机械零件。为此,我使用了一些 PVC 板和 4 支石墨铅笔来制作机器人本体。请将机器人做成多层的,因为我们将使用 mpu6050,如果它放置在比底座高出某个高度,效果最好。我们将在本文电子部分的后面部分详细讨论 mpu6050。现在,我们将只讨论机械部分。

之后,我们将需要两个齿轮电机、两个电机支架和两个轮子。

我用热胶将所有部件连接在一起。只需不超过 20-25 分钟即可使其看起来像这样完美。我们的机械部分已经准备好了,现在我们将进行电子部分。

ELECTRONICS PART
在这一部分中,我们将讨论我们用来制造这个机器人的电子元件。我们还将了解为什么选择所有这些组件。在这里,我们还将根据我已经附在本文中的电路图将所有组件连接在一起。请在将所有组件连接在一起之前下载它。

ARDUINO 纳米
Arduino NANO 是机器人的大脑。在这里我选择它,因为它是一个完美的微控制器,可以学习业余电子和编程,而且它的尺寸使其非常适合构建到需要小尺寸的项目中。

L298n 电机驱动器
L298N 电机驱动器是一款使用 H 桥轻松控制多达 2 个直流电机的方向和速度的控制器。L298N 是一款双 H 桥电机驱动器,可同时控制两个直流电机的速度和方向。该模块可以驱动电压在 5 到 35V 之间、峰值电流高达 2A 的直流电机。

MPU6050
MPU6050 是一个微型机电系统 (MEMS),它由一个三轴加速度计和一个三轴陀螺仪组成。这意味着它给出了 6 个值作为 output:

来自加速度计的三个值
陀螺仪的三个
它帮助我们测量速度、方向、加速度、位移和其他类似运动的特征。它非常准确,因为它包含每个通道的 16 位模数转换硬件。因此,它会同时捕获 x、y 和 z 通道。该传感器使用 I2C 总线与 Arduino 连接。MPU6050 可以使用其片上陀螺仪测量角度旋转,该陀螺仪具有 ±250°/s、±500°/s、±1000°/s 和 ±2000°/s 四个可编程满量程范围。

这些是我在这个机器人中使用的组件的基本细节。还有更多组件,如摇杆开关、跳线等,还有更多不需要解释的东西。现在我要进行下一步

将组件组装在一起
正如我之前已经提到的,我已经在这篇文章中附上了一个电路图,所以请在将它们全部连接在一起之前下载它。就我而言,我从 JLCPCB 制作了一个定制的 PCB 板,以跳过几个接线步骤,并在所有组件之间轻松连接.2 个 PCB 5 美元,PCBA 0 美元起,在此处注册以获取免费优惠券:https://jlcpcb.com/IYB。 我真的很喜欢 PCB 板,因为它们的质量非常高。

我将 Arduino nano 和 MPU6050 放在同一个电路板上,并且还为 HC-05 蓝牙模块单独指定了点,以便我可以在下一个项目中使用相同的 PCB 板轻松地将我的自平衡机器人升级为蓝牙控制自平衡机器人。我刚刚从我的定制 PCB 板中排除了 l298n 电机驱动器模块。该部分只需要布线。

在这里你可以看到我已经完成了组件的接线。下一步非常简单,就是将机械部分和电子部分拼贴在一起,还必须将电池座开关和 18650 锂离子电池与机器人本体和电路连接起来。让我们这样做......

通过将所有组件完美地安装在机器人本体中,我们完成了机器人的制造。现在只剩下最后一步,即将 Arduino 代码上传到 Arduino Nano。

ARDUINO CODING AND CALIBRATION
必须遵循几个步骤来校准和上传代码...

为了更好地理解,我将这个 Arduino 编码和校准部分分为一些不同的部分。

使用加速度计测量倾角
MPU6050 有一个 3 轴加速度计和一个 3 轴陀螺仪。加速度计测量沿三个轴的加速度,陀螺仪测量沿三个轴的角速率。为了测量机器人的倾斜角度,我们需要沿 y 轴和 z 轴的加速度值。atan2(y, z) 函数给出平面的正 z 轴与该平面上的坐标 (z, y) 给出的点之间的弧度角度(以弧度为单位),逆时针角度为正号(右半平面,y > 0),顺时针角度为负号(左半平面,y < 0)。我们使用 Jeff Rowberg 编写的这个库从 MPU6050 读取数据。上传下面给出的代码,看看倾斜角度是如何变化的。


尝试前后移动机器人,同时保持其倾斜某个固定角度。您将观察到串行监视器中显示的角度突然发生变化。这是由于加速度的水平分量干扰了 y 轴和 z 轴的加速度值。

使用陀螺仪测量倾角
MPU6050 的 3 轴陀螺仪测量沿三个轴的角速率(旋转速度)。对于我们的自平衡机器人,仅沿 x 轴的角速度就足以测量机器人的下落速率。在下面给出的代码中,我们读取关于 x 轴的陀螺仪值,将其转换为每秒度数,然后将其乘以循环时间以获得角度的变化。我们将其添加到前一个角度以获得当前角度。程序开始运行时MPU6050的位置是零倾角点。将相对于该点测量倾斜角度。

将机器人稳定在固定角度,您会观察到角度会逐渐增大或减小。它不会保持稳定。这是由于陀螺仪固有的漂移造成的。

在上面给出的代码中,循环时间是使用 Arduino IDE 中内置的 millis() 函数计算的。在后续步骤中,我们将使用 timer interrupts 来创建精确的采样间隔。此采样周期也将用于使用 PID 控制器生成输出。

















驴友花雕 发表于 2025-6-17 11:37:35

【Arduino 动手做】Arduino 自平衡机器人

项目代码

//Self Balancing Robot
#include<Arduino.h>
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif

#define MIN_ABS_SPEED 30

MPU6050 mpu;

// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer; // FIFO storage buffer

// orientation/motion vars
Quaternion q; // quaternion container
VectorFloat gravity; // gravity vector
float ypr; // yaw/pitch/roll container and gravity vector

//PID
double originalSetpoint = 172.50;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.1;
double input, output;

//adjust these values to fit your own design
double Kp = 60;
double Kd = 2.2;
double Ki = 270;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

double motorSpeedFactorLeft = 0.6;
double motorSpeedFactorRight = 0.5;

//MOTOR CONTROLLER
int ENA = 5;
int IN1 = 6;
int IN2 = 7;
int IN3 = 9;
int IN4 = 8;
int ENB = 10;
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, motorSpeedFactorLeft, motorSpeedFactorRight);

volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady()
{
mpuInterrupt = true;
}


void setup()
{
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif

mpu.initialize();

devStatus = mpu.dmpInitialize();

// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip

// make sure it worked (returns 0 if so)
if (devStatus == 0)
{
    // turn on the DMP, now that it's ready
    mpu.setDMPEnabled(true);

    // enable Arduino interrupt detection
    attachInterrupt(0, dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();

    // set our DMP Ready flag so the main loop() function knows it's okay to use it
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();

    //setup PID
    pid.SetMode(AUTOMATIC);
    pid.SetSampleTime(10);
    pid.SetOutputLimits(-255, 255);
}
else
{
    // ERROR!
    // 1 = initial memory load failed
    // 2 = DMP configuration updates failed
    // (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed (code "));
    Serial.print(devStatus);
    Serial.println(F(")"));
}
}


void loop()
{
// if programming failed, don't try to do anything
if (!dmpReady) return;

// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize)
{
    //no mpu data - performing PID calculations and output to motors
    pid.Compute();
    motorController.move(output, MIN_ABS_SPEED);

}

// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();

// get current FIFO count
fifoCount = mpu.getFIFOCount();

// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024)
{
    // reset so we can continue cleanly
    mpu.resetFIFO();
    Serial.println(F("FIFO overflow!"));

    // otherwise, check for DMP data ready interrupt (this should happen frequently)
}
else if (mpuIntStatus & 0x02)
{
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);

    // track FIFO count here in case there is > 1 packet available
    // (this lets us immediately read more without waiting for an interrupt)
    fifoCount -= packetSize;

    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    input = ypr * 180 / M_PI + 180;
}
}

驴友花雕 发表于 2025-6-17 11:41:42

【Arduino 动手做】Arduino 自平衡机器人

【Arduino 动手做】Arduino 自平衡机器人
项目链接:https://www.hackster.io/marketingmanagerofdattabanur/arduino-self-balancing-robot-e23f9c
项目作者:Make Your Own Creation

项目代码:https://www.hackster.io/code_files/600668/download
https://hacksterio.s3.amazonaws.com/uploads/attachments/1460531/i2cdev_X7d34PBJ4x.cpp



页: [1]
查看完整版本: 【Arduino 动手做】Arduino SELF-BALANCE 机器人