0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何构建两轮自平衡机器人

454398 来源:wv 2019-10-18 09:41 次阅读

步骤1:

1。框架:我的框架只是将两个铝制伺服支架用螺栓固定到两个垂直的胶合板上,并与伺服支架固定在一起。框架的构成或配置方式实际上并不重要。您可能应该将其调高一点,然后将电池放在顶部-多少钱总是一个问题,太高了,电动机将没有足够的扭矩来使车轮足够快地旋转,过低又可能使电动机太慢而无法转动抓住机器人的倾斜。一块水平的胶合板底部装有Arduino Uno和电机控制器。

2。马达:我使用了两个无处不在的黄色齿轮马达和车轮,每个到处都可以找到,价格分别为几美元。它们的转速约为110 rpm,足以平衡,但如果转速约为200或300 rpm,那就太好了。它们的齿轮倾斜度很小,因此机器人总是会有点摆动。在将它们连接到电机控制器之前,您可能应该将两个电机引线互相缠绕,以防止杂散电磁场干扰Arduino。在电动机引线两端连接几个电容器也是一个好主意。我用几个拉链把电动机固定在车架上,效果很好。

3。马达控制器:我使用了L293D迷你控制器,我非常喜欢它,因为我可以使用一个2s锂电池为控制器供电,该控制器还可以为Arduino Uno供电-无需第二个电池。轻巧的重量减轻器和轻巧的重量,意味着机器人更容易平衡。

4。 MPU6050陀螺仪/加速度计:这是一个不错的小模块,用于测量机器人的倾斜角度。调用函数非常简单。我将我的机器人安装在arduino和机器人的倾斜轴上方。有些人说应该更高些,有些人说应该更低些,但是可以找到它在哪里。

5。 Arduino Uno:神经网络将轻松以2k运行。

6。电源开关:连接电源开关以打开和关闭电池真的很值得。使机器人的使用变得比每次都要插入电池更容易。

7。 LIPO电池:我使用800mah 2s电池为所有电池供电。电池寿命通常约为连续运行20分钟或更长时间。足够用于测试和玩耍。

8。原理图:最后一张照片是我连接所有模块和电机的示意图。

步骤2:加载并运行Arduino草图

1。 MPU6050校准:在实际运行机器人之前,首先需要进行的是陀螺仪/加速度计的校准。下载位于以下位置的校准草图:http://forum.arduino.cc/index.php?action = dlattach; 。..在执行之前,将您的机器人笔直站立,并在校准程序运行时不要移动它。除非您碰巧将MPU6050移动到机器人上的新位置,否则您只需运行一次校准例程。

运行时,它将向Arduino串行监视器输出6个值需要三个才能放入草图。

2。 NeuralNet-SelfBalancingRobot草图:将以下草图加载到Arduino Uno。您需要将GYRO/ACC参数更改为校准运行中的参数。然后运行草图,查看机器人是否平衡。我的机器人会在地毯或床上保持相当不错的平衡,但会四处运行,然后掉落在光滑的地板上。

我为我的机器人设置了PID代码,其平衡与Neuro Net略有不同但是使用NN基本上没有调整,只需加载草图即可平衡。 PID例程需要大量的操作。

我可以将我的PID控制器上传到SB机器人,而无需进行任何修改即可比较PID与NN软件。 NN会在平衡点附近以较小的振荡获胜,但会在受到干扰的情况下输给PID。但是我还没有真正调整NN。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//神经网络程序,使用S型函数并应用于简单的自平衡机器人

//由商洛大学Jim Demello创建,2018年5月

//改编自Sean Hodgins神经网络代码:https://www.instructables.com/id/Arduino-Neural-Ne。

/修改了midhun_s自平衡机器人代码:https://www.instructables.com/id/Arduino-Self-Bala.。.

/构建了我自己的自平衡机器人

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include“ MPU6050.h”

#包括“ math.h”

/**************************************** **********************************

网络配置-为每个网络自定义

************************************************** ****************/

const int PatternCount = 2;

const int InputNodes = 1;

const int Hidd enNodes = 3;

const int OutputNodes = 1;

const float LearningRate = 0.3;

const float Momentum = 0.9;

const float InitialWeightMax = 0.5;

const float Success = 0.0015;

float Input [PatternCount] [InputNodes] = {

{0},//左倾斜

{1}//倾斜

//{-1}//倾斜

//{0,1,1,0} ,//左右左右发光

//{0,1,0,0},//左右左右发光

//{1,1,1,0} ,//顶部,左侧和右侧的灯光

};

const float Target [PatternCount] [OutputNodes] = {

{0,},////左倾斜

{1,}//右倾斜

//{-1,}//左移动

//{0.65, 0.55},//LEFT MOTOR SLOW

//{0.75,0.5},//LEFT MOTOR FASTER

};

/***** ************************************************** ***********

终端网络配置

********************** ***************/

int i,j,p,q,r;

int ReportEvery1000;

int RandomizedIndex [PatternC ount];

长时间训练周期;

浮动Rando;

浮动误差= 2;

浮动累积;

float Hidden [HiddenNodes];

float Output [OutputNodes];

float HiddenWeights [InputNodes + 1] [HiddenNodes];

float OutputWeights [HiddenNodes + 1] [OutputNodes];

float HiddenDelta [HiddenNodes];

float OutputDelta [OutputNodes];

float ChangeHiddenWeights [InputNodes + 1] [HiddenNodes] ;

float ChangeOutputWeights [HiddenNodes +1] [OutputNodes];

#define leftMotorPWMPin 6

#define leftMotorDirPin 7

#define rightMotorPWMPin 5

#define rightMotorDirPin 4

#define sampleTime 0.005

MPU6050 mpu;

int16_t accY,accZ,gyroX;

int motorPower,gyroRate;

float accAngle,gyroAngle,currentAngle,prevAngle = 0,error,prevError = 0,errorSum = 0;

字节数= 0;

long previousMillis = 0;

unsigned long currentMillis;

long loopTimer = 4;

void setMotors(int leftMotorSpeed,int rightMotorSpeed){

//串行.print(“ leftMotorSpeed =”); Serial.print(leftMotorSpeed); Serial.print(“ rightMotorSpeed =”); Serial.println(rightMotorSpeed);

if(leftMotorSpeed》 = 0){

AnalogWrite(leftMotorPWMPin,leftMotorSpeed);

digitalWrite(leftMotorDirPin,LOW);

}

else {//如果leftMotorSpeed为《0,则将dir设置为反向

AnalogWrite(leftMotorPWMPin, 255 + leftMotorSpeed);

digitalWrite(leftMotorDirPin,HIGH);

}

if(rightMotorSpeed》 = 0){

AnalogWrite (rightMotorPWMPin,rightMotorSpeed);

digitalWrite(rightMotorDirPin,LOW);

}

else {

AnalogWrite(rightMotorPWMPin,255 + rightMotorSpeed);

digitalWrite(rightMotorDirPin,HIGH);

}

}

void setup(){

Serial.begin(115200);

Serial.println(“启动程序”);

randomSeed(analogRead(A1));//收集一个随机ADC样本以进行随机化。

ReportEvery1000 = 1;

for(p = 0; p

RandomizedIndex [ p] = p;

}

Serial.println(“ do train_nn”);

train_nn();

delay( 1000);

//将电动机控制和PWM引脚设置为输出模式

pinMode(leftMotorPWMPin,OUTPUT);

pinMode(leftMotorDirPin,OUTPUT);

pinMode(rightMotorPWMPin,OUTPUT);

pinMode(rightMotorDirPin,OUTPUT);

//初始化MPU6050并设置偏移值

mpu.initialize();

mpu.setYAccelOffset(2113);//通过校准例程

mpu.setZAccelOffset(1122);

mpu.setXGyroOffset(7);

Serial.print(“ End在以下位置初始化MPU: “); Serial.println(米利斯());

}

///////////////

/主循环/

/////////////

void loop(){

drive_nn();

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////

/使用了训练有素的神经网络要驱动机器人

void drive_nn()

{

Serial.println(“ Running NN Drive”);

while(Error 《成功){

currentMillis = millis();

float TestInput [] = {0,0};

if(currentMillis-previousMillis》 loopTimer) {//每5毫秒或更长时间进行一次计算

Serial.print(“ currentMillis =”); Serial.println(currentMillis);

/////////////////////////////////////

//计算incli的角度国家//

//////////////////////////////////////////

accY = mpu.getAccelerationY();

accZ = mpu.getAccelerationZ();

gyroX = mpu.getRotationX();

accAngle = atan2(accY,accZ)* RAD_TO_DEG;

gyroRate = map(gyroX,-32768,32767 ,-250,250);

gyroAngle =(float)gyroRate * sampleTime;

///////////////////////////////////////////////////////////////////

//补充过滤器///////////////////////////////////////////

////////////////////////////////////////////////////////////////////

currentAngle = 0.9934 *(prevAngle + gyroAngle)+ 0.0066 *(accAngle);

//Serial.print(“currentAngle=“); Serial.print(currentAngle); Serial.print(“ error =”); Serial.println(error);

//错误= currentAngle-targetAngle;//不使用

float nnInput = currentAngle;

//Serial.print(“ nnInput =”); Serial.println(nnInput);

nnInput = map(nnInput,-30,30,0,100);//将倾斜角度范围映射到0到100

TestInput [0] = float(nnInput)/100;//转换为0到1

//Serial.print(“ testinput =”); Serial.println(TestInput [0]);

InputToOutput(TestInput [0]) ;//输入到ANN以获取输出

//Serial.print(”output =“); Serial.println(Output [0]);

///////////////////////////////////////////

//在之后设置电动机功率约束它//

///////////////////////////////////////////

motorPower =输出[0] * 100;//从0转换为1

//如果(motorPower 《50)motorPower = motorPower * -1;

motorPower = map(motorPower,0,100,-300,300 );

motorPower = motorPower +(motorPower * 6.0);//需要乘数以使车轮在接近平衡点时足够快地旋转

//Serial.print(“motorPower =“); Serial.println(motorPower);

motorPower = constrain(motorPower,-255,255);

prevAngle = currentAngle;

previousMillis = currentMillis;

}//结束毫秒循环

//如果(abs(error)》 30)motorPower = 0;//如果跌落则关闭电动机

//motorPower = motorPower + error;

setMotors(motorPower,motorPower);

}

}//drive_nn()函数的结尾

///在培训时显示信息

无效到Terminal()

{

for(p = 0; p

Serial.println();

Serial.print(“ Training Pattern:”);

Serial.println(p);

Serial.print(“ Input”);

for(i = 0; i

Serial.print(Input [p] [i],DEC);

Serial.print(“”);

}

Serial.print (“ Target”);

for(i = 0; i

Serial.print(Target [p] [i],DEC);

Serial.print(“”);

}

/********************* **************

计算隐藏层激活

***************************************** *********************************/

for(i = 0; i

Accum = HiddenWeights [InputNodes] [i];

for(j = 0; j

累计+ =输入[p] [j] * HiddenWeights [j] [i];

}

隐藏[i] = 1.0/(1.0 + exp(-Accum));//激活功能

}

/****************************** ******************************************

计算输出层激活并计算错误

******************************************* ***************************/

用于(i = 0; i

累计= OutputWeights [HiddenNodes] [i];

for(j = 0; j 《隐藏节点; j ++){

累计+ =隐藏[j] * OutputWeights [j] [i];

}

输出[i] = 1.0/(1.0 + exp(-Accum));

}

Serial.print(“ Output”);

for(i = 0; i

Serial.print(Output [i],5);

Serial.print(“”);

}

}

}

无效InputToOutput(float In1 )

{

float TestInput [] = {0};

TestInput [0] = In1;

//TestInput [ 1] = In2;//未使用

//TestInput [2] = In3;//未使用

//TestInput [3] = In4;//不使用

/****************************************** ****************************

计算隐藏层激活

**** ************************************************** ************/

for(i = 0; i

Accum = HiddenWeights [InputNodes] [i];

for(j = 0; j

累计+ = TestInput [j] * HiddenWeights [j] [i];

}

隐藏[i] = 1.0/(1.0 + exp(-Accum));

}

/********* ************************************************** *******

计算输出层激活并计算错误

********************** ***************/

for(i = 0; i

Accum = OutputWeights [HiddenNodes] [i];

for(j = 0; j

累计+ =隐藏[j] * OutputWeights [j] [i];

}

输出[i] = 1.0/(1.0 + exp(-Accum));

}

//#ifdef调试

Serial.print(“输出”);

对于(i = 0 ;我

Serial.print(Output [i],5);

Serial.print(“”);

}

//#endif

}

//训练神经网络

void train_nn(){

/*** ************************************************** *************

初始化HiddenWeights和ChangeHiddenWeights

******************* ***************/

int prog_start = 0;

Serial.println(“开始培训。..”);

//digitalWrite(LEDYEL,LOW);

for(i = 0; i

for(j = 0; j 《= InputNodes; j ++){

ChangeHiddenWeights [j] [i ] = 0.0;

Rando = float(random(100))/100;

HiddenWeights [j] [i] = 2.0 *(Rando-0.5)* InitialWeightMax;

}

}

//digitalWrite(LEDYEL,HIGH);

/************ ************************************************** ****

初始化OutputWeights和ChangeOutputWeights

**************************** ******************************************/

//digitalW rite(LEDRED,LOW);

for(i = 0;我

for(j = 0; j 《= HiddenNodes; j ++){

ChangeOutputWeights [j] [i] = 0.0;

Rando = float(random(100))/100;

OutputWeights [j] [i] = 2.0 *(Rando-0.5)* InitialWeightMax;

}

}

//digitalWrite(LEDRED,HIGH);

//SerialUSB.println(”Initial/Untrained Outputs:“);

//toTerminal();

/****************************************** ****************************

开始训练

****** ************************************************** **********/

用于(TrainingCycle = 1; TrainingCycle 《2147483647; TrainingCycle ++){

/*********** ************************************************** *****

随机分配训练模式的顺序

************************** ********************************************/

用于( p = 0; p

q = random(PatternCount);

r = RandomizedIndex [p];

RandomizedIndex [p] = RandomizedIndex [q];

RandomizedIndex [q] = r;

}

错误= 0.0;

/*************************************** **************************************

以随机顺序遍历每种训练模式

************************************************** ********************/

为(q = 0; q

p = RandomizedIndex [q];

/************************* **********************************************

隐藏计算层激活

********************************************* *****************************/

//digitalWrite(LEDYEL,LOW);

表示(i = 0; i

累计= HiddenWeights [InputNodes] [i];

for(j = 0; j

累计+ =输入[p] [j] *隐藏重量[j] [i];

}

隐藏[i] = 1.0/(1.0 + exp(-Accum));

}

//digitalWrite(LEDYEL,HIGH);

/*********** ************************************************** *****

计算输出层激活并计算错误

************************ *************/

//digitalWrite(LEDRED,LOW);

for(i = 0; i

Accum = OutputWeights [HiddenNodes] [i];

for(j = 0; j

累计+ =隐藏[j] * OutputWeights [j] [i];

}

Output [i] = 1.0/(1.0 + exp(-Accum));

OutputDelta [i] =(Target [p] [i]-Output [ i])*输出[i] *(1.0-输出[i]);

错误+ = 0.5 *(目标[p] [i]-输出[i])*(目标[p] [i]-Output [i]);

}

//Serial.println(Output [0] * 100);

//digitalWrite( LEDRED,HIGH);

/***************************************** *********************************

向后传播到隐藏层的错误

** ************************************************** **************/

//digitalWrite(LEDYEL,LOW);

for(i = 0;我

累计= 0.0;

对于(j = 0; j

累计+ = OutputWeights [i] [j ] * OutputDelta [j];

}

HiddenDelta [i] =累积*隐藏[i] *(1.0-隐藏[i]);

}

//digitalWrite(LEDYEL,HIGH);

/************************* **********************************************

更新内部-》隐藏重量

****************************************** ********************************/

//digitalWrite(LEDRED,LOW);

for(i = 0; i

ChangeHiddenWeights [InputNodes] [i] = LearningRate * HiddenDelta [i] +动量* ChangeHiddenWeights [InputNodes] [i];

HiddenWeights [InputNodes] [i] + = ChangeHiddenWeights [InputNodes] [i];

for(j = 0; j

ChangeHiddenWeights [ j] [i] =学习率*输入[p] [j] * HiddenDelta [i] +动量* ChangeHiddenWeights [j] [i];

HiddenWeights [j] [i] + = ChangeHiddenWeights [j ] [i];

}

}

//digitalWrite(LEDRED,HIGH);

/************************************************* *********************

隐藏更新-》输出权重

******** ************************************************** ********/

//digitalWrite(LEDYEL,LOW);

for(i = 0;我

ChangeOutputWeights [HiddenNodes] [i] =学习率* OutputDelta [i] +动量* ChangeOutputWeights [HiddenNodes] [i];

OutputWeights [HiddenNodes] [i] ] + = ChangeOutputWeights [HiddenNodes] [i];

for(j = 0; j

ChangeOutputWeights [j] [i] = LearningRate * Hidden [ j] * OutputDelta [i] +动量* ChangeOutputWeights [j] [i];

OutputWeights [j] [i] + = ChangeOutputWeights [j] [i];

}

}

//digitalWrite(LEDYEL,HIGH);

}

/********** ************************************************** ******

每100个周期将数据发送到终端进行显示并在OLED上绘制图形

*************** ************************************************** */

ReportEvery1000 = ReportEvery1000-1;

如果(ReportEvery1000 == 0)

{

int graphNum = TrainingCycle/100 ;

int graphE1 =错误* 1000;

int graphE = map(graphE1,3,80,47,0);

Serial.print(“ TrainingCycle:“);

Se rial.print(TrainingCycle);

Serial.print(“ Error =”);

Serial.println(Error,5);

toTerminal() ;

if(TrainingCycle == 1)

{

ReportEvery1000 = 99;

}

否则

{

ReportEvery1000 = 100;

}

}

/******* ************************************************** *********

如果错误率小于预定阈值,则结束

*************** ************************************************** */

如果(错误《成功)中断;

}

}

步骤3:最终注释

1。这些参数可能只需要一点点就可以播放,尤其是可以增加NN输出值的乘法器。当电动机接近平衡时,必须使用该倍增器来提高电动机的转速。事实证明,这几乎迫使机器人成为爆炸式,平衡式机器人。如果在平衡点附近的电动机的值不够高,则机器人将在电动机具有足够的rpm来捕捉下降之前倒下。

2。也许可以使用比S形函数更好的激活函数。有人说tanf函数更有用。我认为真正需要的只是一个简单的f(x)函数。对这个领域的任何人都会真正感兴趣。

3。这是一个简单的单输入,多个隐藏节点和单个输出神经网络,而且肯定会产生过大的杀伤力,因为PID控制器会更简单,并且您实际上可以使用仅一行代码的简单P控制器来达到平衡。但是,我不必像PID控制器那样对这个NN进行调整,所以这很酷。使用更多的输入将很有趣,您可以简单地将陀螺仪的值设置为两个输入,而将加速度计设置为三个输入神经网络的另一个。然后,您将不需要补充过滤器,因为神经网络将充当过滤器。不确定如何操作,但尝试可能很有趣。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 机器人
    +关注

    关注

    206

    文章

    26943

    浏览量

    201198
  • Arduino
    +关注

    关注

    184

    文章

    6425

    浏览量

    184749
收藏 人收藏

    评论

    相关推荐

    基于ACM32 MCU的两轮车充电桩方案,打造高效安全的电池管理

    随着城市化进程的加快、人们生活水平的提高和节能环保理念的普及,越来越多的人选择了电动车作为代步工具,而两轮电动车的出行半径较短,需要频繁充电,因此在城市中设置两轮车充电桩就非常有必要了。城市中的充电
    发表于 03-06 15:10

    开源项目!教你如何复刻平衡赛车机器人、智能家居中控、竞技机器人先进模糊控制器等

    开源项目作品 为了方便大家更好提升自己,电子发烧友小编为大家整理了一些工程师大佬设计的开源项目作品,供大家可以参考学习,希望对广大工程师有所帮助。 1.用全志R128复刻平衡赛车机器人,还实现
    发表于 12-26 09:17

    用全志R128复刻平衡赛车机器人,还实现了三种不同的操控方式

    手柄上的菜单栏UI可以选择不同的机器人基础参数设置和进行机器人操控。 在平衡机器人启动后,可以通过面包板上的个手柄去遥控
    发表于 12-20 10:22

    高动态人形机器人“夸父”通过OpenHarmony 3.2 Release版本兼容性测评

    鸿联合乐聚(深圳)机器人技术有限公司(简称“乐聚”)联合打造,通过KaihongOS构建了“人形机器人+”开放生态平台。近日,夸父人形机器人正式上市。 夸父人形
    发表于 12-20 09:31

    机器人编程需要什么软件?

    机器人编程需要什么软件
    发表于 11-01 07:34

    两轮平衡小车是用51单片机好还是stm32好?

    两轮平衡小车是用51单片机好,还是stm32好?
    发表于 10-08 08:27

    Arduino教学机器人的使用教程

    本文档的主要内容详细介绍的是Arduino教学机器人的使用教程
    发表于 09-27 06:53

    ai人工智能机器人

    的运营成本、人力成本还在不断提高(如:办公场地的租金、员工的工资、社保公积金、节假日福利等)。 如今的智能电话机器人,每天的电话拨打量可达800-1000通,相比人工提高了3-5倍,大大缩短了名单的筛选
    发表于 09-21 11:09

    电机转子动平衡机的常见的问题有哪些

    在电机运行过程中,转子动平衡是确保电机稳定运行和减少振动噪声的关键步骤。电机转子动平衡机作为专用设备广泛应用于电机制造、维修和调试等领域。杭州集智本文将深入探讨电机转子动平衡机的原理和应用,并解析在实际使用过程中可能遇到的相关问
    发表于 08-07 10:12 616次阅读

    使用Arduino制作两轮机器人

    电子发烧友网站提供《使用Arduino制作两轮机器人.zip》资料免费下载
    发表于 06-19 14:28 0次下载
    使用Arduino制作<b class='flag-5'>两轮机器人</b>

    二轮自平衡机器人开源设计

    电子发烧友网站提供《二轮自平衡机器人开源设计.zip》资料免费下载
    发表于 06-12 10:43 2次下载
    二轮自<b class='flag-5'>平衡机器人</b>开源设计

    如何快速地让机器人投入生产

    机器人也是一种设备,它主要内容是完成一个制造的其中一个功能。 制造一个产品,可以分为几个部件。和装配几个部件。 一个部件,可以是一个模具。或者由机器人完成组成一个模具。 机器人主要完成的是一些模具
    发表于 06-06 16:18

    机器人如何计算简单的运动

    模型要具备齿轮,导轨几种基本的运动原理。 要分辨基本图形,视觉模型要能分辨圆形,方形和三角形。 有了这点,机器人就基本上能计算开模和注塑这种简单的工作了。要替代人类的工作,还要进一步学习。
    发表于 05-19 20:40

    浅谈儿童陪护机器人

    儿童陪护机器人越来越受到人们的关注,其中转动控制是其重要组成部分之一。步进电机芯片作为一种常用的控制芯片,被广泛应用于儿童陪护机器人中的转动控制。本文将从步进电机芯片的工作原理、优势和应用场景等方面
    发表于 05-11 15:12

    由于S32k146有128KB RAM,那么我们可以将它用于两轮车VCU应用吗?

    由于 S32k146 有 128KB RAM,那么我们可以将它用于两轮车 VCU 应用吗?或者是否需要增加 RAM 大小?
    发表于 04-20 09:20