迷你掌上平衡车
为了深入学PID,必须自己实践,因此看到这个迷你掌上平衡车miniBot项目比较容易制作,则开始行动。
硬件部分
按照清单购买了元件,嘉立创白嫖打板,自己焊接。焊接完成后能下载程序,但是电机不输出。贴片焊接难度大,出了问题不知道是哪里虚焊,排查比较麻烦。
▲施展不开的书桌。好想有个自己的书房,可以放各种设备折腾自己的事情,无奈房子太小了。
为了焊接两个串联的18650电池,又折腾点焊机,使用率极低,得不偿失o(╥﹏╥)o还是应该使用电池盒,下次其它项目电池还可以使用。
▲点焊机
▲焊接完成电机不能输出
最终自己又按照原理图利用模块化拼装思路,重新绘制了PCB板,这样出问题的概率就低很多。过程中入门了嘉立创PCB的绘制,PCB在线绘制简单易用。又重新白嫖打板,购买模块化元件。
嘉立创专业版 miniBot模块化PCB文件:ProProject_miniBot模块化_2023-11-03.zip
▲模块化的PCB
▲模块化的miniBot,成功点亮和输出。
程序部分
硬件部分已经做完很长时间,用作者的程序调试始终都不太稳定。准备自己好好研究源程序及相应的库对其做优化,深入理解PID代码及姿态控制算法,可一直没花时间去做这个事情。后续计划看什么时候把这个坑补上。。。。
未完待续。。。
20240229更新
自己按照这个博主的教程重新整理了代码,在PID调试时始终不太平稳。自己评估一是可能重心太高且重量太重,二是3.3V供给选型的N20电机转速也偏小。后续可改成一节18650供电配升压模块。
用以下程序需交换两个电机的插头。
蓝牙调试工具(Android):BtSerialDebugAndControl.zip
代码如下
/*
* pwm+为前进,pwm+-为后退
* 左马达IN1 IN2,OUT1 OUT2 , pwm 9起转。
* 右马达IN3 IN4,OUT3 OUT4, PWM 9起转。
*
*/
#include <Arduino.h>
//#include<analogWrite.h>
#include "BluetoothSerial.h"
#include <MPU6050_tockn.h>
#include <Wire.h>
#define IN1 32 //AIN1
#define IN2 33 //AIN2
#define IN3 21 //BIN1
#define IN4 27 //BIN2
int sda_pin = 18, scl_pin = 19; //自定义eps32的I2C引脚
/*---调试和预设定值的变量--------*/
float Balance_Angle_raw = -4; //测试出的静态机械平衡角度。
int leftMotorPwmOffset = 9, rightMotorPwmOffset = 9; //测试出的左右轮的启动pwm值,pwm达到一定电压马达才开始转动。
float kp = 14, ki = 1.2, kd = 1.0; //根据调试测试得到设置kp ki kd的值
int pwm = 0;
/*---调试和控制变量--------*/
float Keep_Angle, bias, integrate; //保持角度,角度偏差,偏差积分变量
float AngleY, GyroY, GyroZ; // mpu6050输出的角度值为浮点数,两位有效小数
int vertical_PWM, turn_PWM, PWM, L_PWM, R_PWM; //各种PWM计算值
BluetoothSerial SerialBT; //实例化esp32的蓝牙串口对象
MPU6050 mpu6050(Wire); //实例化mpu6050对象
/*---电机启动输出--------*/
void motor(int left_EN, int right_EN)
{
if (left_EN == 0)
{
analogWrite(IN1, 0);
analogWrite(IN2, 0);
}
if (left_EN < 0) //左边反转
{
if (left_EN < -255)
left_EN = -255;
analogWrite(IN1, 0);
analogWrite(IN2, 0 - left_EN);
}
if (left_EN > 0)//左边正转
{
if (left_EN > 255)
left_EN = 255;
analogWrite(IN1, left_EN);
analogWrite(IN2, 0);
}
if (right_EN == 0)
{
analogWrite(IN3, 0);
analogWrite(IN4, 0);
}
if (right_EN < 0)
{
if (right_EN < -255)
right_EN = -255;
analogWrite(IN3, 0);
analogWrite(IN4, 0 - right_EN);
}
if (right_EN > 0)
{
if (right_EN > 255)
right_EN = 255;
analogWrite(IN3, right_EN);
analogWrite(IN4, 0);
}
}
void serial_debug()
{
if (SerialBT.available() > 0)
{
char DATA = SerialBT.read();
delay(5);
switch (DATA)
{
/*直立环调试*/
case '0':
kp -= 1; //调节直立环 比例kp项-
break;
case '1':
kp += 1; //调节直立环 比例kp项+
break;
case '2':
ki -= 0.1; //调节直立环 积分项ki-
break;
case '3':
ki += 0.1; //调节直立环 积分项ki+
break;
case '4':
kd -= 0.1; //调节直立环 微分项kd-
break;
case '5':
kd += 0.1; //调节直立环 微分项kd+
break;
}
/*调试时PID极性限制*/
if (kp < 0)kp = 0;
if (ki < 0)ki = 0;
if (kd < 0)kd = 0;
/*串口打印输出显示*/
SerialBT.print("Keep_Angle: ");
SerialBT.println(Keep_Angle);
SerialBT.print("kp:");
SerialBT.print(kp);
SerialBT.print(" ki:");
SerialBT.print(ki);
SerialBT.print(" kd:");
SerialBT.println(kd);
SerialBT.println("--------------------");
}
}
void vertical_pwm_calculation() //直立PMW计算
{
AngleY = mpu6050.getAngleY();
GyroY = mpu6050.getGyroY();
bias = AngleY - Keep_Angle; // 计算角度偏差。bias为小车角度是静态平衡角度的差值。
integrate += bias; //偏差的积分,integrate为全局变量,一直积累。
integrate = constrain(integrate, -1000, 1000); //限定误差积分的最大和最小值
/*==直立PID计算PWM==通过陀螺仪返回数据计算,前倾陀螺仪Y轴为正,后仰陀螺仪Y轴为负。
前倾车前进,后仰车后退,保持直立。但可能为了直立,车会随时移动。*/
vertical_PWM = kp * bias + ki * integrate + kd * GyroY;
}
void motor_control() //马达PWM控制函数
{
/*---【补偿右轮pwm差值】------------*/
if (PWM >= 0)
{
L_PWM = PWM + leftMotorPwmOffset; //左右轮的启动pwm达到一定电压马达才开始转动。这里为补偿值。
R_PWM = PWM + rightMotorPwmOffset; //左右轮的启动pwm达到一定电压马达才开始转动。这里为补偿值。
}
if (PWM < 0)
{
L_PWM = PWM - leftMotorPwmOffset; //左右轮的启动pwm达到一定电压马达才开始转动。这里为补偿值。
R_PWM = PWM - rightMotorPwmOffset; //左右轮的启动pwm达到一定电压马达才开始转动。这里为补偿值。
}
/*---【控制马达输出】-------------*/
// L_PWM = constrain(L_PWM, -255, 255); //计算出来的PWM限定大小。255为输出上限。
// R_PWM = constrain(R_PWM, -255, 255);
motor(L_PWM, R_PWM);
/*--------判断是否小车倒下,此时停止马达和编码器计数-----*/
if (AngleY > 45 || AngleY < -45) //倾角过大(车倒下时),停止马达输出
{
motor(0, 0);
}
}
void setup()
{
SerialBT.begin("ESP32_car"); // Bluetooth device name
Wire.begin(sda_pin, scl_pin);
mpu6050.begin();
mpu6050.calcGyroOffsets(false);
Keep_Angle = Balance_Angle_raw; //平衡角度初始化为静态平衡时的陀螺仪角度。Keep_Angle可以改变,才可以控制前进后退。
motor(0, 0); //机器启动时马达确保停止。
delay(10); //循环前延时,确保各种初始和准备完成
}
void loop()
{
serial_debug();
mpu6050.update(); //陀螺仪刷新
vertical_pwm_calculation(); //直立环PWM计算
PWM = vertical_PWM;
motor_control();// 电机输出
}