1.智能泡茶机项目需求
1.1.目标项目需求(总体:机械结构 电控)
- 机械为主,电控为辅。
- 突出机械创新点。
- 项目主要机械部分需要ANSYS有限元分析。
- 符合时代智能家居的理念。
1.2.作品完成后现有功能
- 可以通过控制执行器完全泡茶。
- 语音控制,按钮输入功能。
- 设计一套符合型号的设计APP。
- 语音提示。
2.泡茶机组件选型
元器件名称 | 数量 | 作用 |
---|---|---|
MG995舵机 | 19 | 主要执行器控制泡茶机的动作 |
HC-05号蓝牙模块 | 1 | 在手机和单片机之间建立桥梁 |
跟踪避障模块 | 3 | 用于检测茶杯(人机交互) |
水泵(接食用水) | 1 | 抽水 |
继电器 | 1 | 用Arduino来控制水泵何时通电 |
手机 | 1 | 用于蓝牙连接、语音识别、触摸屏(按钮) |
降压器(220V转5V) | 1 | 给传感器和执行器供电 |
Arduino Uno板 | 2 | 用于控制手、茶壶机械手 |
Arduino Mega板 | 1 | 主控板用于通信两外两Arduino Uno板 |
16路舵机驱动板 | 1 | 通过IIC通信驱动舵机减少了大量IO占用 |
注意:
- Arduino供电问题:Arduino供电问题:因为在泡茶及运行的时候,我们不可能说是把Arduino板连接在电脑上运行,所以我们这里的解决方案是用充电插头给它Arduino板供电,或使用稳压模块Arduino来供电。
- Arduino通信问题:在上述组件选择中,我使用了两个Uno板和一个Mega板,在通信上,我的思路是让Mega板作为上位机,其他板Uno板作为下位机,在通信上还是有一些问题的,比如通信上会有延迟,我们可以在这里使用它Arduino软串口可以解决通信延迟问题,但这个前提是你占用的IO不然一个软串口会占两个IO口的位置。因为我在设计的时候,IO它被广泛使用,所以我没有使用这个解决方案。或者你用两个Mega板替换你的两个Uno板,因为Mega板有四个通信口。
3.项目中需要使用的软件
软件名称 | 用途 |
---|---|
Mixly(或中文称米思齐) | 这是图形编程,用于传感器检测会快很多(这个软件是给孩子学编程的,所以……很简单) |
Arduino | 主要的Arduino假如你,编译器VS编译器里有Arduino模块也行 |
App Inventor | 基于Java的一个APP制作网页(也有软件版,可以在中国大学慕课找到相关课程) |
Fritzing | 这是一个用于电路接线的软件 |
ANSYS 17.0 | 这是一个强大的有限元分析软件,用于材料应力分析等。(19版以上有中文,没学过专门课程很难用) |
注意: 在控制方面,我们主要使用它Arduino来编程。 App Inventor是图形编程,使用起来比较简单。使用百度语音时,记得让手机联网,否则无法唤醒。 每次都有蓝牙Arduino复位后可能需要重新连接。
4、Arduino函数主要用于编程
4.1.舵机控制函数
在泡茶机中,我们主要的执行器就是MG955舵机。舵机有两种驱动方式:一种是使用Arduino自带的Servo二是使用第三方库Adafruit_PWMServoDriver.h。注意:不管你是你使用哪一种方式,delay函数只是给舵机足够的时间,并不是控制舵机转动时长。如果你要控制舵机请看4.2。
4.1.1、Servo库
Servo函数的主要成员(以下是Uno板为例)
函数名 | 用途 |
---|---|
attach() | 设置舵机接口,注意Arduino 0016前只能收到9和10引脚 |
write() | 将舵机旋转到指定角度(只有0-180度) |
writeMicroseconds() | 写一个微秒在标准舵机中,参数设置为1000为完全逆时针方向,2000为完全顺时针方向,1500为中间。但不同的制造商可能会有所不同 |
read() | 读取舵机当前角度,返回0-180度 |
attached() | 传入一个Servo变量,判断是否附加在引脚上,然后返回true,否则返回false |
detach() | 将servo如果所有变量都与引脚分离,servo变量是分开的,第9和第10教会可以使用analogWrite()函数进行PWM输出 |
市面上的Arduino板的0-13引脚(共14路)可以接舵机。如果解决不了,可以参考4.1.2的16路du 注意:我们在这里主要使用的一些attach()、write()这两个函数。本项目不能使用其他函数。 例子:
- 在全球定义中创造Servo 变量:Servo servo_9; //创造全局Servo 变量
- 在Arduino的setup中初始化:servo_9.attach(9); //附加到Arduino的9号引脚
- 发送PWM数据给9号引脚:servo_9.write(60); ///让舵机转60度
- 给舵机足够的时间: delay(100); ///向舵机延迟100毫秒
4.1.2、Adafruit_PWMServoDriver.h库
左图为16路舵机驱动板。
在Adafruit_PWMServoDriver.h库中主要的函数如下
函数名 | 作用 |
---|---|
Adafruit_PWMServoDriver(uint8_t addr = 0x40) | Adafruit_PWMServoDriver库的构造函数,默认地址为0X40 |
begin(void) | 启动函数,无参数 |
reset(void) | 复位函数,无参数 |
setPWMFreq(float freq) | 设置刷新频率,我这里使用的是60Hz |
setPWM(uint8_t num, uint16_t on, uint16_t off) | 发送off(脉冲长度计数)给16路舵机的num接口 |
注意:如果使用舵机驱动板的话还需要用到#include <SPI.h>和#include <Wire.h>,这两个库是Arduino的自带库(不需要额外下载的),他们是用于IIC通信时使用的。 例子:
- 在全局变量中定义:Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0X40); //创建一个16路舵机驱动板变量
- 在Arduino的setup函数中:pwm.begin(); //驱动板开启
- 在Arduino的setup函数中:pwm.setPWMFreq(60); //模拟伺服系统以60赫兹的频率更新
- 启动舵机:pwm.setPWM(0, 0, 600);
- 给一定的时间延时:delay(100);
4.2、解决舵机运行速度和加速度的方式
对于不同厂家生产出来的舵机转速是有一定差异的,下面是我在一个淘宝卖家上的截图。
我们可以看到在4.8V和6.V时的空载速度是不同的(单位是秒/60°)。随着舵机的规格不同,速度也是有些差异的。就拿上面的9KG铜齿舵机来说,4.8V时的空载速度为140ms转完一圈,那么大概就是2.4ms转一度。这也说明你给他140ms它能转一圈,但是你给它120ms的话它是不会转一圈的(即转速不会加快),除非是提高电压!同理,也不能说是给200ms它就会慢慢转,他只会用140ms,剩下的的60ms它只会在那边干等着。
此外,舵机也算是电机,只是他是一个特殊的电机(我们这里就把他看成是一个小型的电机),根据《执行元件及控制》中电机的特性曲线可以只知道电机在开始工作的时侯转速的偏慢的,那我们把电机映射成舵机的话,我们可以得出舵机小幅度转动的话旋转的速度是会更慢一些的。
所以,我们可以根据这两个特性来对将要旋转的度数进行划分。
下面我就用一个demo来描述一下舵机转速主要解决方式,这里我使用的是上文中第一种控制方式(使用Servo.h库函数),舵机选用上面9KG扭矩的舵机,接上4.8V电压让其空转。
#include <Servo.h>
Servo servo_2; //创建一个全局的Servo 变量
int Degree = 0; //记录当前角度
int Target = 0; //记录将要旋转的目标角度
int Velocity = 20; //记录舵机每度的延时时间,可作为旋转速度
int Acceleration = 1; //加速度
int readnum = 0; //用于存储随机数
void setup(){
servo_2.attach(2); //这里我是使用2号引脚
}
void loop(){
Target = random(1, 180); //这里我随机产生一个随机数作为目标角度
Degree = Run_Servo(Degree ,Target,Velocity ,Acceleration); //获取当前角度
delay(1000); //延时1秒后进行下一次转动
}
//舵机执行函数,输入当前度数、指定度数,速度级别,加速度
int Run_Servo(int Deg,int Tar,int v,int a){
int flag = -1; //记录是要顺时针转还是逆时针
int Decode_Motor_Degree; //记录将要转动的度数
int Variable = 0; //两个度数差
//做差值,求两角度的差值
Variable = Deg - Tar;
//判断角度差是正值还是负值,并且设立标志位
if(Variable>=0){
flag = 1;
}else{
flag = -1;
}
for(int i = 1; i <= Variable*flag; i++){
Decode_Motor_Degree = Deg+i*flag;
servo_2.write(Decode_Motor_Degree); //这里就是让舵机一度一度的往上加
//下面为每度的延时时长
if(Velocity>3){
delay(Velocity-a*i);
}else{
delay(3);
}
}
}
注意:我们必须要给系统留一些裕度,所以在舵机每度延时方面不能小于2.4ms,为了保证系统稳定性,我们最少设置为3ms。上面对加速度的处理方面,我只是提供一个比较简陋的处理方式,没有考虑到过多的细节,大家可以对此进行优化。
5、三个Arduino之间的通信问题与解决方案
在项目初期,我原本打算使用单单一个Uno板来完成整个项目的控制,但是由于经验不足,对于16路舵机驱动板运用不够熟悉,还有在项目期间遇到很多无法解决的问题,所以就放弃了这个想法。相比之下,使用多块Arduino板来控制会相对简单很多。 到最后版本我选择使用一个Mege板外加两个Uno板,其中Mega板作为上位机,两个Uno板作为下位机。使用串口通信。
- 第一版解决方案:在一个Uno板在控制机械手的时候记录好自己所需要用的时间,Mega板作为上位机,给下位机发出信号后,自己进入延时来等待下位机完成动作。 遇到的问题:如果使用这一方案的话会需要花费大量的时间来记录Uno板每个动作的时间,且容错率低,这一方案会浪费大量时间。
- 第二版解决方案:给他们3个Arduino板一个共同的时间,来控制时间方面。 遇到的问题:对于这里方面没有花费过多的研究,但是让三个时间同步,可以使用Mega板IIC连接两个Uno下位机,但是对于Uno板的IIC地址和通信不是很懂,所以放弃了这一方案。
- 第二版解决方案:借用网络通信的三次握手理念,但是我这里有点改动,就是如果没有接收到下位机的回应就会一直阻塞等待。这样的一个好处就是防止上一个动作还没做完,其他机械手就开始工作了,这样很容易让机械手相撞。
6、注意事项和解决方法
- 有时候代码没错,但是舵机动不了 解决方法(5种情况):
- 用一个简单的舵机驱动测试一下那个舵机,是不是单独的那个舵机损坏问题(舵机明显发热等现象)。
- 上面的如果没问题看看是不是舵机的输入电压不够(一般都是这个问题)。
- 检查是不是16路舵机驱动板的问题(检查不动的那个舵机在舵机驱动板上的接口),看看那个16路舵机驱动板的输出口电压是否足够(5V左右的电压)?
- 检查这个舵机所在的舵机驱动板,看看这个驱动板上有没有那种执行起来比较奇怪的舵机(这种舵机它旋转起来转起来感觉很丝滑,不会发出正常舵机的转动的声音),这种舵机会影响16路多级驱动板上其他舵机的信号!
- 接线问题,看看是不是舵机的信号线接触不良引起的。
- 舵机没有被卡住,但是抖动严重 解决方法(检验与排除): 看看连接在同一16舵机驱动板(或者是面包板)上的其他舵机又没右哪个卡住了,某一个卡住的话也会影响其他舵机,这明显的现象就是有的舵机剧烈抖动,但是没有卡住现象。
最后,我在这里总结一下,毕竟有的同学是和我一样使用220V转5V的,一般这种加压器的接线旁边是可以调节输出电压的(一个可以用螺丝刀调节的三极管),所以在使用之前一定一定要先测一下输出电压再接上电路。舵机短时间正负极接错是没有多大问题的,但是接个30s多半就废了。