资讯详情

稚晖君教你制作全球最迷你的自平衡机器人

06262af1365e40c1f3101eb81c087c8a.png

:Nano它是一个身高约10厘米的小而可爱的机器人,具有良好的平衡感,非常白色和可爱。作为世界上最迷你的自平衡机器人,Nano虽然身材小,但配有丰富的传感器陀螺仪、超声波,Motion sensor,如果喜欢的话你可以让它自主巡线,跟踪,避障…更重要的是,它是完全开源的,从硬件到软件的数据将在下面下载。

自平衡站立

自平衡行走转向

手机APP遥控及交互

超声波感应

跟踪摄像头目标

卖萌

Nano与蛋黄

Nano的创意始于2013年暑假,那时候它还叫“蛋黄”,当时的想法是制作一个入门级的自平衡小车(因为赶上学校飞思卡尔比赛,当时报的平衡组,当预习功课了),初步的设想是:

事实上,这也是我第一次接触和使用它Arduino,当时少年穷…买国产的mini pro裸板,感觉有点开心,没多久就被我瞎接电源给霍霍了…

b7f623aca7c35a91359eb9f66c4785fe.png

蛋黄的初始版本版本mini pro因为当时没有3D打印机可供送货,所以所有部件的连接基本上都是通用胶,整体结构非常粗糙,怎么说,这可能是蒸汽朋克风(不是)。

当时制作的一个小视频《蛋黄故事》:

蛋黄数据发布在论坛后引起了很多关注,许多学生成功地进入了自平衡汽车的坑…一年多来,人们一直在问我相关的问题。正因为如此,我羞于用这么多干货来处理每个人很长一段时间。此外,我对自平衡系统有了更深入的了解…因此,我们决定改代蛋黄的诸多不足,开始设计和制作蛋黄2.0版本。

当时,对第一代的不满主要体现在外观(毕竟,外观水平是战斗力)、速度控制(几乎没有速度环,只能通过手动调整平衡点移动)、扩展(第一代外观太简单,没有充分发挥处理器的性能)、外观(我真的很关心…)。

最后就有了Nano啦~

7b2e289fe5629941133aaf4b293c6e9d.png

制作教程

好吧,接下来将介绍制作一个Nano的详细教程,其中包括一些有关自动控制的原理和个人遇到的一些问题和经验总结。另外值得说明的是,实现自平衡机器人的完整控制需要大量的参数调试过程,因此本教程会尽量以通俗的方式介绍原理和调试方法,但还是需要您拥有一定的电子制作基础、熟悉Arduino祝成功:-)

原理篇

自平衡车是典型的倒立摆控制模型。什么是倒立摆?相信大家都见过普通钟摆。

97a052c0b9e092947ef25ca9996d4391.png

当物体离开垂直平衡位置时,它会受到重力和悬挂的作用,驱动重物回复平衡位置,称为回复力,其大小

偏移角度θ很小的情况下,sinθ ≈ θ,因此,回复力与偏移角度的大小成正比,方向相反。在这种恢复力的作用下,周期运动是单摆便。

考虑到在空气中移动的单个位置,由于空气的阻尼力,单个位置最终会停止在垂直平衡位置。空气的阻尼力与单个位置的角速成正比,方向相反。阻尼力越大,单个位置就越能尽快稳定在垂直位置。

现在看这样一个等效模型

4bc8c7ff8874006b80c74bcbd0b845c9.png

事实上,我们的车型相当于一个倒立的钟摆。可以看出,此时重力对物体的作用是向下的,也就是说,当物体偏移一个小角度时,重力的作用将与偏移角度的方向相同。如果车轮不移动,摆动将很快下降。

我们有两种方法可以使倒立摆像单摆一样稳定在垂直位置:

改变重力的方向 2,增加额外的力,使恢复力与位移方向相反。

显然,只有第二种方法可以做到。因此,我们根据摆动的偏移角度控制车轮的加减速运动,使摆动在汽车坐标系(非惯性系统)中受到额外惯性力的影响,最终使摆动平衡。

一般来说,当我们发现汽车向前倾斜时,让它的轮子向前加速,当我们发现汽车向后倾斜时,让它的轮子向后加速。只要这个过程足够准确和快速,就能实现汽车的自平衡。

硬件篇

原则上说,我们应该根据汽车偏移的角度来控制车轮的加减速,所以根据需要使用的模块包括:

模块 说明
Arduino主控板 选择你熟悉的任何一块,推荐nano,小巧,下载方便
陀螺仪加速度计模块 推荐测量倾角MPU6050便宜,使用方便
减速电机 尺寸自定,但最终输出速度为300rpm左右会更合适。值得注意的是,电机必须配备编码器或码盘来测量速度。单相或两相都可以
电机驱动 推荐普通尺寸的电机TB6612驱动芯片,比L298效率高,不易发热(平均电流1.2A请选择左右功率较大的L298或其他驱动);使用迷你电机L9110s模块可以,便宜又小
蓝牙模块 可用于与手机通信,从模块或主从一体
按键 任何两只脚的按钮都可以用来设置一些
电池 若使用高于5V锂电池供电可直接使用,但如果是迷你车3.7v对于小电池供电,需要注意额外添加一个DC否则,升压模块Arduino16可能无法正常工作MHz
超声波模块 可用于测距避障,SR04更常用,更小一点RCW-0001,当然更小的也可以自己买收发DIY
距离传感器 夏普的一系列传感器比超声模块贵,但效果更好
OLED显示屏 当然,屏幕是显示状态数据不可或缺的。.96寸的分辨率128×64效果很好,注意最好买SPI因为I2C可能跟MPU6050有冲突(可能是个例,地址不冲突,具体原因不深入)
蜂鸣器 让汽车发声往往比盯着一个更好LED效果更好。建议使用有源蜂鸣器
摄像头 Motion Sensor 准确地说,它是红外光传感器,因为Arduino性能不足以进行图像处理,因此不能使用普通摄像头

另外,一把热熔胶枪除了一般的焊接工具和手工工具外,还会成为你DIY得力助手。至于结构件,有3个D打印机的学生可以直接下载以下内容STL文件自行打印,没有打印机的学生也可以在通用橙色网站上找到3D打印服务店。还有一点需要解释的是,我当时用的小电机是我自己改装的。它最初是一台数码相机的变焦电机,所以它非常小,可以安装一个代码盘。在改变减速比并增加出轴后,它被用作汽车,但现在似乎没有这个…

然而,我无意中找到了一个更合适的N20减速小电机,性能比自己改装的好很多,虚位和摩擦损失会更好,轴也可以直接连接到车轮,并配备霍尔测速,长这样:

4b330ba4fd35adee1208288f878f4d71.png

有兴趣做超迷你平衡车的同学可以选择这款。TB链接不接…橙网搜 “N20减速电机 迷你

软件篇

主要介绍软件篇PID算法,可以说PID它是整个项目程序的核心,它的使用决定了你的车是否能自平衡,以及它是否稳定。PID算法和理论分析网络上有很多介绍,这里没有详细说明你可以自己搜索。基于数学模型的介绍有点难以理解,本文文从控制学的角度简单讲解一下PID及其使用方法。

所谓PID就是比例-积分-微分的英文缩写,但并不是必须同时具备这三种算法,也可以是 PD, PI,甚至只有 P算法控制,下面分别介绍每个参数的含义:

首先需要明确一个事实就是,要实现PID算法,必须在硬件上具有闭环控制,就是得有反馈。比如控制一个电机的转速,就得有一个测量转速的传感器,并将结果反馈到控制器中,而在自平衡系统中,常用的有三个控制环 — 角度环、速度环、转向环

大家可以想象出每个闭环的反馈元件分别是什么吗,对就是上面元件清单里面包含的 IMU(陀螺仪+加速度计)、编码器、摄像头(或者其他可以确定方位的元件比如陀螺仪,磁场计等)

P(比例):以小车巡线为例,现在需要让小车跟随一条轨迹前进,用PID算法控制方向环,反馈传感器就假设为摄像头。那么小车行进中有这么几种情况:

1、车通过摄像头发现自己处在轨迹的左边,位置误差值为正,那么就需要向右转向,转向值为正2、车通过摄像头发现自己处在轨迹的右边,位置误差值为负,那么就需要向左转向,转向值为负3、车通过摄像头发现自己处在轨迹的正中间,位置误差值为0,很欢快地笔直前行,转向值为0

于是我们发现,小车转向值的输出可以简单地通过把位置误差乘以一个系数就得到了,而且显然,误差越大,得到的转向值也越大,符合需求。这里面这个系数,就是P了,而系数具体的大小,需要根据实际情况调试确定。

我们有了第一个公式:

D_term = kD* (error- last_error)

如果上面的例子还是不好理解的话,考虑前面的单摆模型:

P相当于重力的作用,让摆左右往复运动,而D则相当于空气阻力,让摆慢慢停在中点。D的大小很理想的情况下,应该是大概摆动左右各一下之后就停在中点,想象把摆放在水中摆动的情况。

I(积分):有的时候我们会发现,系统中存在一些固定的阻力,例如,我们用PID控制一个电机的转速,当给定的目标速度很小的时候,就会出现这样的情况:

根据P_term = kP * error,由于error很小,P的输出也很小,而由于摩擦力的存在,此时并不能让电机转动起来;又由D_term = kD* (error- last_error),由于电机没有转动,显然(error- last_error)始终为0于是D输出也为0,那么问题来了,除非改变目标值,否则电机就永远转不起来了…

I的作用就是消除这样的静态误差,它会将每次的误差都积累起来,然后同样也是乘以一个系数之后作为输出。比如上面的情况,虽然误差很小,但却不是0,于是在每一轮的计算中,I项把error逐渐累积,直到超过临界值让电机转起来;而在误差为0的情况下,I项却又不会帮倒忙。

第三个公式:I_term = kI*(I_term + error)

以上就是PID的全部计算了,最后三者加起来就得到了:

PID_output = P_term + I_term + D_term

每隔一段固定时间把它运行一遍,就是PID算法了。

可以看出,PID的算法实现其实非常简单,不过只有几行代码而已,所以非常建议自己实现一遍PID代码。Arduino平台上也是有PID库的,但库的名字叫什么我不告诉你,自己去找哦。

制造篇

如果上面的都可以理解的话就可以开始动手制作啦,这里会以Nano的制作过程为例,但是大家可以根据实际情况自行调整。

网盘老是链接失效,

对里面文件说明一下:

1、STL文件是Nano头部和身体的结构件,底座由于大家使用电机不同需要自己确定,按照自己买到的电机的情况制作一个带两个电机的底座就行,有热熔胶枪的帮助应该挺简单的

2、源码建议用1.6.5版本的IDE编译,旧版本的库文件有些区别

原理图

ca44afd1d794dbb28636941aae9dc5e7.png

需要说明的是图里面的电源线是没有连接的(一正一负大家都知道),另外IO大家可自己调换,只要在程序里改一下宏定义就行;另外编码器分为单相和AB相的,我这里用的是单相,只能测转速不能测方向,所以其实方向的检测是通过给电机的PWM方向近似模拟的,有条件的话还是用双向的更好。单相编码器的话直接把信号线接到D2,D3脚,分别属于中断0和中断1;AB相的话则是一根线接到D2或D3另一根线接到一个普通IO用来判断方向(这时候需要修改程序最后的ENCODER_L()和 ENCODER_R()函数内容)。

然后就开始按图连接模块到外壳里

a10682f36b2fe990a580d9b0fdb8e1be.png

文件打印好之后按原理图组合,头部里面装了超声波,Arduino nano板(不带排针),蓝牙模块,两个LED,摄像头以及蜂鸣器,注意把所有的IO都用导线引出到脖子部位

be5d0fcb94a9f5a68e3b6e8b76f9952a.png

正面的样子

14e6ada7c703167edf2b0a385a9b6ca3.png

头部模块都塞完之后先别急着粘合,之后还有身体的导线要连接到主控板,里面的部件可以先用热熔胶固定一下。对了导线选择的话如果技术好可以用漆包线最省空间,没有把握的话就用这种FC排线,比较软去皮容易,比较细而且是整排一起的会美观一些,杜邦线真的很不好用…

2fd933b7e1d5a56df621e2260976e979.png

身体部分主要放舵机(如果要用的话),OLED,电机驱动板及升压板,OLED塞的时候注意不要用蛮力不然屏幕玻璃易碎(不要问我怎么知道的…)

e9fac88d80dc5baf515ef28a00cf27cd.png 93696301129625479abacd987497fc5b.png

如果上面装头部的时候有把导线引出这时就可以焊在屏幕上了。MPU6050在底部,也用502或者热熔胶固定。

729df676d61ac5b50e6618184d2df1a2.png

然后是舵机,舵机用的是最常见的9g舵机,虽然也有更小的但是怕力矩不够,可以看到由舵机摆臂推动脖子的连接杆就可以让头部运动了。舵机不是很建议采用,一个是小机器人的电池功率比较小,舵机在堵转的时候容易造成死机,另一个头部重量还是相对比较大,所以运动的时候重心变化容易干扰平衡,参数设置会更复杂。

034151ef568cdafee57acd5aec601f36.png

然后是装身体部分侧面,先把升压板用热熔胶固定在一边,然后盖上盖板用502粘合(红色圈住的是一块DC-DC升压板)

01b9b8ed200a908c7aac23d3ca88faf4.png

这时候主体就已经快完成啦

9910cd32695b8782c678b905a3635738.png

这时可以开始制作电机底座了,一个参考方案是,用一张废弃的电话卡或者银行卡剪裁一下作为基板,然后把两个电机用热熔胶固定在基板上,这样基板就可以用胶和身体粘合起来了。

e11337f9b967fb6947e29d2b89e6783a.png

现在可以装电机驱动了,我用的是L9110S,直接盖在身体背面

2bb38f3c2bbccd9daecd841324a212c0.png

装上背盖整个身体部分就完成啦!

616854c9074d85f3181bbbcae0c2dd47.png

最后是电池包,找块轻薄一点的电池直接粘在背部就行,也可以加上电池盖

20ad8b2266da809397db36ea87337271.png

侧面的样子

202156b498f7e3943eae423f030a24e8.png 96070608ecc8982d89d9a1d5316ef56e.png

小Nano完成了!

当然现在它还不会动,这时候还是先别急着把头部粘合,先写几个小程序逐个测试一下每个模块的工作状态,比如超声波是否工作,6050有没有数据,这些程序直接去各个库里面找到例程改一下定义的引脚运行看效果就行。

当确定没有问题之后,用热熔胶仔细地固定好每个部位,准备开始程序调试工作了。

打开源程序文件夹里任意一个文件,就可以看到整个工程代码。

c6b40358cc967358f9595a61b59d1c70.png

首先需要修改的是引脚定义,把引脚改成你实际连接的情况

/*********************引脚定义*********************/#define LFT 0#define RHT 1#define BUZZER 4 //蜂鸣器#define BUTTON 5 //按钮#define LED 11 //脸颊LED//#define SERVO 13 //舵机,小机器人不推荐使用,电流易过载#define TRIG_PIN 8 //超声波模块触发脚#define ECHO_PIN 7 //超声波模块接收脚

然后再最上面的调试选项中,取消IMU_OUTPUT的注释,像这样

/*****************************调试选项********************************///#define TIMING_DEBUG   //PID周期调试,开启后打印时间信息//#define PARAM_DEBUG    //PID参数调试,关闭后用宏取代变量值节省动态内存#define IMU_OUTPUT  //输出6050数据//#define SONIC_OUTPUT  //输出超声波数据//#define SPEED_LOOP       //速度环开关//#define MOTOR_ENABLE     //电机使能//#define SONIC_ENABLE     //超声波使能//#define CAMERA_ENABLE  //摄像头使能

下载程序到主控里,打开串口监视器,按一下按键,就可以看到输出的角度数据了。

如果数据不正常的话需要检查前面哪里出问题了,这一步是为了获取机器人平衡的自然角度:手动把机器人放正,就是大概在自然重心的平衡角度,读取串口的角度数据,记录下这个值就是angle_setpoint的值了,把66行的angle_setpoint = 0改成你得到的值。

下一步把IMU_OUTPUT重新注释掉,开启MOTOR_ENABLE的注释,然后用 Motor(char LR, int SPEED)这个函数放在setup()的最后面来检测电机的正负极是否正确,也就是当给Motor(LFT,100)时左轮正转,Motor(LFT,-100)时左轮反转,Motor(RHT,100)时右轮正转,Motor(RHT,-100)时右轮反转。

上一步也检测成功之后,就可以开始调节角度环的PID参数了

/***************PID变量定义**********************/#ifdef PARAM_DEBUG  //角度环数据double  P_angle = 0, I_angle = 0, D_angle = 0;#else#define P_angle  0#define I_angle  0#define D_angle  0#endifdouble  angle_setpoint = 0, angle_output, angle_integral;uint32_t angle_PID_timer;#ifdef PARAM_DEBUG  //速度环数据double  P_speed = 0, I_speed = 0;#else#define P_speed  0#define I_speed  0#endifdouble  speed_setpoint = 0, speed_output, speed_integral;uint32_t speed_PID_timer;

说明一下这里用宏定义了一遍参数的原因是,如果用的mega328p的芯片的话由于整个程序代码量还是挺大的所以会提示动态内存紧张,所以在调试完参数之后就把它们用宏固化了节省SRAM。

总体的参数整定原则是:

1、先PD,如果电机响应慢(比如减速级很多的电机),再调I,如果PD效果足够好的话则不需要I

2、单一变量法,即调整一个参数的时候其他参数都固定不变

3、先定量级再定数值,比如调整P的时候,先从0.0001开始,查看小车反应,没有效果的话改为0.001,以此类推,直到确定一个合适的数量级,然后才开始在这个数量级里微调,这样其实就已经把调整的范围缩小到很小了

4、先超调再减小,即所有参数都先尽量加大,直到系统震荡,然后再取这个值的小一点的值作为最合适参数

5、调试过程中尽量让车处于自然状态没有额外的力作用,即尽量用无线调试,有线的话找根软一些的线

好那么调节过程中小车什么表现才算是“好”呢?

在角度环中,当P逐渐增大,小车会开始有恢复力作用,也就是当手扶着往前倒的时候小车也能大概跟着往前走,但是还是走的很“软”,再逐渐增大参数,恢复力越来越大,直到大到一定程度,小车开始前后自主剧烈抖动,电机性能好一点的小车即使手不扶着也能大概站立了。

稍微减小刚刚震荡的参数,作为P值固定在程序里,开始调节D值,依然是确定量级之后逐渐增大,在增大的过程中会发现小车的震荡频率逐渐降低了,增大到一定程度小车基本不再震荡,这个值就是需要的D值

也会有一部分情况下由于电机性能原因上面PD的调整过程中始终无法达到很好的效果,那么此时需要加入I,在调完P值之后,D值置0,增加 I值,直到小车的恢复力变得比较“硬”,然后稍微减小P值,直到出现比较理想的直立效果;最后再加D,视效果增加到震荡为止,再减小到70%左右,这样角度环PID所有参数就整定完成了。

角度环调好了,小车可以稳定平衡了,可是为啥它一直往一边跑呢?

因为角度环的任务就是维持小车的角度,除此之外就是它能力范围之外了,角度环是不关心小车是静止着平衡的,还是边跑边平衡的 — 如果恰好目标平衡点和小车重心平衡点重合,那么小车可以大概静止,而如果不是,那小车就会在平衡中不断加速,直到轮子的速度超出了电机所能提供的转速,于是小车还是会倒下。

所以我们需要添加一个速度环,用编码器测量速度来作为反馈

在速度环中,首先确定编码器获取的数值是正确的,在程序中分别是count_Lcount_R储存计数,打印输出一下转动轮子看数据对不对,正确的话,在调节好角度环的基础上,就可以添加速度环了。

在调试选项中取消SPEED_LOOP的注释,然后这次我们不需要D,速度环单纯靠PI调节,而且先调 I再调P,对应的表现如下:

先给一个比较小的P值(因为I是P的累加,如果P为0的话I也就没有意义了),随着I值逐渐增大,用手轻推小车,小车会前进之后慢慢后退,就说明参数起作用了。此时你可以决定到底给一个更大的 I值还是小的I值,越大的I值对应了更快的恢复速度,在偏离之后会更剧烈地后退,在更短的距离内回到原点,但是当然这样也会降低小车的稳定性,而小的参数则于此相反,需要在前进更长的距离之后才会慢慢回到原点,但是也使得抗干扰能力增强,也就是不会被轻易推倒。

只用I值的话小车的回复会是往复的,逐渐逼近原点,加上P值则可以消除这种来回震荡,与角度环调节D值的过程一样,逐渐增加直到推了之后可以在前后各摆动一次就回复到原点静止,此时这个P值便是对应于那个 I值的最合适参数。

这里有个我之前做的一个大车的抗干扰视频,该车的电机性能非常暴力,因此可以看到其平衡性能非常好

PID参数的调整过程大概如此,总之这是一项既需要细心调整,但自由度也很大的工作,所谓“大胆假设,小心求证”,在多尝试几种参数组合之后,你会找到适合你的小车的magic point的~

关于参数的调整本来还有很多可以说,比如调整的形式上,大家可以在小车上加上几个电位器analogRead读取后作为参数值,这样就可以方便而直观地观察到参数连续变化带来的影响;又比如无线调试的话使用SSH终端的串口协议方式控制会比使用串口助手方便很多等等。但我想对于参数理解最有效的方式还是亲手去操作,多尝试多对比。


专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「」获取学习资料网盘链接。

标签: 电机动平衡传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台