12下一页
返回列表 发新帖我要提问本帖赏金: 10.00元(功能说明)

[STM32F4] 基于NUCLEOf411的两轮平衡车制作

[复制链接]
4841|28
 楼主| lianqiang 发表于 2016-2-18 15:11 | 显示全部楼层 |阅读模式
本帖最后由 lianqiang 于 2016-2-18 15:17 编辑

先上视频:
">
一:设计目标       一直想做一台平衡车玩,但是怕自己能力不够一直不敢动手去做,中间不断的收集各种资料,吸取别人的经验,了解PID算法等等。正好大三结课比较早,感觉自己已经了解大概了就开始动手做。
      先期的目标是使平衡车站立平稳,再就是使用2.4G遥控控制,最后添加其他的功能——自动巡航等等。
功能实现框图
硬件功能框图.jpg 平衡车成品图
平衡车成品.jpg
二硬件设计
首先确定平衡车主控,因为是第一次制作平衡车,选一款熟悉的开发平台十分重要,本人对于STM32软件开发也比较熟悉,并且以前申请得到一个NUCLEOF411板,主频高达100MHz,集成SPI接口等,完全能够胜任平衡车的要求。
F411主板.jpg
接下来是加速度陀螺仪传感器的选择,从价格开源方面来说MPU6050无疑是初次接触平衡车必选的模块,一块大概12元,经过测试,配合卡尔曼滤波得到的角度曲线还是比较好的。
电机参数.jpg 电机.jpg
因为贪便宜选择日本纳米奇(namiki)直流减速电机后来收到货才发现电机转速太慢,建议电机转速大一点的,不然到后来电机平衡常数不好调,而且小车的机动性能比较好。

再来就是电机驱动板,看过不少平衡车的制作方案,有的用L298N,也有用TB6612的,但是作为TI的粉丝,个人感觉TI的电机驱动芯片做的相当好,作为个人DIY感谢TI的样片申请制度,为学生党省了不少钱。以前用过TI的集成无刷驱动芯片DRV11873做硬盘时钟,也没用过TI的有刷芯片,去他的官网果然有一款芯片非常适合我的设计方案——DRV8881,电路简单,驱动方式也十分方便。
8881特性.jpg 8881系统图.jpg 8881应用原理图_改后.jpg
电机驱动板正面.jpg 电机驱动板背面.jpg
因为DRV8881的背面有散热PAD,但是市面上买的TTSOP转接板基本没有散热块。所以我采用反面焊接的方式,你也可以加焊一根粗一点的铜线加大散热,不过TI的芯片一般发热极少。

再来就是无线传输模块,因为在调试PID参数时,参数的整定是最耗时间的,基本都在几十次左右,如果通过重编译下载程序对于芯片的寿命也是一种影响,如果通过串口更改,线对于车的运动也会产生干扰。所以最好是通过无线传输进行PID整定。而且对于后续的遥控控制也必须采用无线传输。在以前的一个项目中使用过NRF24L01这个模块,对于它的传输机制也比较了解,故采用NRF24L01模块。
遥控正面.jpg 遥控背面.jpg
再来就是电源的选择,看过其他方案的平衡车,一般采用12V3S航模电池,但是如果采用航模电池就得用特定的充电器,这样总的成本比较大,所以我采用DC-DC升压模块加移动电源,方便充电也容易维护。
12V电源.jpg 5V主板电源.jpg
本方案采用双电源设计,原来使用一个12v电源,加7805降压给主控板,但是12v升压板的电源输出对光电编码器的影响比较大,所以采用双电源。而且在一次测试的过程中插错线把NUCLEO板给烧了
(好伤心的说),这更加坚定了我使用双电源的决心。
       以上就是主要的硬件部分,在制作过程主要注意以下几点,
.12V5V电源分离并固定,做好防反接。
二.做好引线的标记,防止接错。
三.将各个模块固定结实,防止运动的时候脱落。

以上都是惨痛的经验希望大家不要放同样的错误。


三:软件主体
软件框图.jpg
以下介绍一下编写平衡车的主要过程
一MPU6050的DMP移植我采用的是平衡小车之家的DMP文件进行的移植。
DMP移植.jpg

将红色框中的文件移到工程文件中,因为他的工程是基于103创建的,而我是f411所以要修改其中的头文件,延时函数,IIC接口时钟等等。
对于在文件下的.h文件的包涵如果出现错误可以如下操作
  1. #include "filter/filter.h"
  2. #include "MiniBalance/MiniBalance.h"
  3. #include "ioi2c.h"


移植完成后发现的问题:
如果按照原来文件中的读法利用void Read_DMP(void)这个函数在main函数中读取角度,加速度会存在尖峰。
  1. void Read_DMP(void)
  2. {        
  3.           unsigned long sensor_timestamp;
  4.                 unsigned char more;
  5.                 long quat[4];

  6.                                 dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more);               
  7.                                 if (sensors & INV_WXYZ_QUAT )
  8.                                 {   
  9.                                          q0=quat[0] / q30;
  10.                                          q1=quat[1] / q30;
  11.                                          q2=quat[2] / q30;
  12.                                          q3=quat[3] / q30;
  13.                                          Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;         
  14.                                 }

  15. }


DMP角速度1.jpg DMP1.jpg
        加速度(visualscope)                   角度(visualscope)
而如果将MPU6050INT脚引出利用中断读取就不会出现尖峰。但是读取的角度的波形不如采用定时器读取时平滑,这我一直没搞懂,因为采用外部中断读取的频率大概也在200Hz左右但是读取的角度在PD直立环时存在很大的延迟,反应非常慢。车的抖动非常大。
所以我采用他给出的第二个方案——void Get_Angle(u8 way)这个函数进行读取加速度和角度。取得了不错的效果。
  1. void Get_Angle(u8 way)
  2. {
  3.             float Accel_Y,Accel_X,Accel_Z,Gyro_Y,Gyro_Z;
  4.             if(way==1)                                      //DMP没有涉及到严格的时序问题,在主函数读取
  5.                         {        
  6.                                 Read_DMP();
  7.                                 Angle_Balance=Pitch;             //===更新平衡倾角
  8.                                 GyroLimit(gyro[1]);
  9.                                 Gyro_Balance=LastGyro;            //===更新平衡角速度
  10.                                 Gyro_Turn=gyro[2];               //===更新转向角速度
  11.                         }                        
  12.       else
  13.       {
  14.                         Gyro_Y=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_YOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_YOUT_L);    //读取Y轴陀螺仪
  15.                         Gyro_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_L);    //读取Z轴陀螺仪
  16.                   Accel_X=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_XOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_XOUT_L); //读取X轴加速度记
  17.                   Accel_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_L); //读取Z轴加速度记
  18.                   if(Gyro_Y>32768)  Gyro_Y-=65536;     //数据类型转换
  19.                         if(Gyro_Z>32768)  Gyro_Z-=65536;     //数据类型转换
  20.                   if(Accel_X>32768) Accel_X-=65536;    //数据类型转换
  21.                   if(Accel_Z>32768) Accel_Z-=65536;    //数据类型转换
  22.                         Gyro_Balance=-Gyro_Y;                                  //更新平衡角速度
  23.                    Accel_Y=atan2(Accel_X,Accel_Z)*180/PI;                 //计算与地面的夹角        
  24.                   Gyro_Y=Gyro_Y/16.4;                                    //陀螺仪量程转换        
  25.       if(Way_Angle==2)                          Kalman_Filter(Accel_Y,-Gyro_Y);//卡尔曼滤波        
  26.                         else if(Way_Angle==3)   Yijielvbo(Accel_Y,-Gyro_Y);    //互补滤波
  27.             Angle_Balance=angle;                                   //更新平衡倾角
  28.                         Gyro_Turn=Gyro_Z;                                      //更新转向角速度
  29.                   }
  30. }


卡尔曼滤波角速度.jpg 卡尔曼滤波角度1.jpg
        角速度(visualscope)                    角度(visualscope)
二2.4G无线传输  
无线传输发送模块采用51单片机,电脑将PID参数发送到51单片机,51单片机通过一定的协议发送给STM32进行处理。
  功能
  
  
8个字节的有效数据
  
  CRC校验
  
共11个字节
三电机编码器器       
因为STM32集成编码器电路,通过简单的设置就可以使用。
  1. void EncoderConfig(void)
  2. {
  3.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  4.   TIM_ICInitTypeDef TIM_ICInitStructure;
  5.   GPIO_InitTypeDef GPIO_InitStructure;
  6. //  NVIC_InitTypeDef NVIC_InitStructure;
  7.   
  8.         //TIM3 clock
  9.   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);           
  10.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC , ENABLE);        

  11.         //TIM4 clock
  12.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);           
  13.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB , ENABLE);         
  14.   
  15.         //TIM3 GPIO
  16.         GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3);
  17.         GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3);
  18.         
  19.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  20.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  21.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  22.   GPIO_Init(GPIOC, &GPIO_InitStructure);
  23.         
  24.         //TIM4 GPIO
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);
  26.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);

  27.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  28.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  29.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  30.   GPIO_Init(GPIOB, &GPIO_InitStructure);
  31.         
  32.         /* Timer3 configuration in Encoder mode */                                
  33.   TIM_DeInit(TIM3);
  34.   TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  35.   
  36.   TIM_TimeBaseStructure.TIM_Prescaler =0;  
  37.   TIM_TimeBaseStructure.TIM_Period = 8000;
  38.   TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  
  39.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  40.   TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  41.   TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,
  42.                                                                                                                  TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);        
  43.   
  44.   TIM_ICStructInit(&TIM_ICInitStructure);
  45.         TIM_ICInitStructure.TIM_ICFilter =0;
  46. //        TIM_ICInitStructure.TIM_Channel=TIM_Channel_1|TIM_Channel_2;
  47.   TIM_ICInit(TIM3, &TIM_ICInitStructure);
  48.   
  49. // Clear all pending interrupts
  50. //  TIM_ClearFlag(TIM3, TIM_FLAG_Update);
  51. //  TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE);
  52.   //Reset counter
  53.   TIM_SetCounter(TIM3,5000);
  54.   TIM_Cmd(TIM3, ENABLE);
  55.         
  56.   /* Timer4 configuration in Encoder mode */                                
  57.   TIM_DeInit(TIM4);                                                           
  58.   TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  59.   
  60.   TIM_TimeBaseStructure.TIM_Prescaler =0;  
  61.   TIM_TimeBaseStructure.TIM_Period = 8000;
  62.   TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  
  63.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  64.   TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  65.   TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,
  66.                                                                                                                  TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);        
  67.   
  68.   TIM_ICStructInit(&TIM_ICInitStructure);
  69.         TIM_ICInitStructure.TIM_ICFilter =0;
  70. //        TIM_ICInitStructure.TIM_Channel=TIM_Channel_1|TIM_Channel_2;
  71.   TIM_ICInit(TIM4, &TIM_ICInitStructure);
  72.   
  73. // Clear all pending interrupts
  74. //  TIM_ClearFlag(TIM4, TIM_FLAG_Update);
  75. //  TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE);
  76.   //Reset counter
  77.   TIM_SetCounter(TIM4,5000);
  78.   TIM_Cmd(TIM4, ENABLE);  
  79. }

四PID算法编写
一直立环PD
  1. int16_t balance(float Angle,float Gyro)
  2. {  
  3.    float Bias;
  4.          int16_t balance;
  5.          Bias=Angle+0;              //===求出平衡的角度中值 和机械相关 +0意味着身重中心在0度附近 如果身重中心在5度附近 那就应该减去5
  6.          balance=140*Bias+Gyro*0.9;//===计算平衡控制的电机PWM  PD控制 215  0.75
  7.          return balance;
  8. }
二速度环PI环
  1. int16_t velocity(void)
  2. {  
  3.                 int16_t Encoder_Least,Velocity;
  4.         int8_t Movement;
  5.           //=============遥控前进后退部分=======================//
  6.                 if(Qian_Flag==1)        Movement=50;                     //===如果前进标志位置1 位移为
  7.                 else if(Hou_Flag==1)          Movement=-50;          //===如果后退标志位置1 位移
  8.           else  Movement=0;
  9.    //=============速度PI控制器======================//        
  10.                 Encoder_Least =Encoder_Left+Encoder_Right;     //===获取最新速度偏差
  11. //        printf("Encoder=%d\n",Encoder);
  12. //                Encoder *= 0.8;                                             //===一阶低通滤波器      
  13. //                Encoder += Encoder_Least;                         //===一阶低通滤波器   
  14.           Encoder_Integral +=Encoder_Least;                     //===积分出位移 积分时间:10ms
  15.                 Encoder_Integral+=Movement;
  16.         //防止站立抖动
  17.         if(Encoder_Least<3&&Encoder_Least>-3&&Movement==0)
  18.         {
  19.                 Encoder_Least=0;
  20.         }

  21.                 if(Encoder_Integral>1700)          Encoder_Integral=1700;          //===积分限幅
  22.                 if(Encoder_Integral<-1700)        Encoder_Integral=-1700;         //===积分限幅        
  23.                 Velocity=Encoder_Least*120+Encoder_Integral*0.9;              //===速度PI控制器        
  24.           return Velocity;
  25. }
三转向PID环
  1. int16_t TurnBalance(void)
  2. {
  3.         int16_t Encoder_Diff,Turn_PWM;
  4.         int8_t Turn_Move;
  5.         if(Zuo_Flag==1)        Turn_Move=20;                     //===如果前进标志位置1 位移为负
  6.         else if(You_Flag==1)          Turn_Move=-20;          //===如果后退标志位置1 位移为正
  7.         else Turn_Move=0;
  8.         Encoder_Diff=Encoder_Left-Encoder_Right;
  9.         Encoder_Diff_Integral+=Encoder_Diff;
  10.         Encoder_Diff_Integral+=Turn_Move;

  11. //        t++;
  12. //        if(t>10)
  13. //        {
  14. //                printf("Encoder_Diff_Integral=%d,Encoder_Diff=%d\n",Encoder_Diff_Integral,Encoder_Diff);
  15. //                t=0;
  16. //        }
  17.         
  18.         if(Encoder_Diff_Integral>2500)          Encoder_Diff_Integral=2500;          //===积分限幅
  19.         if(Encoder_Diff_Integral<-2500)                Encoder_Diff_Integral=-2500;         //===积分限幅        
  20.         Turn_PWM=Encoder_Diff*100+Encoder_Diff_Integral*0.9-Gyro_Turn*0.75;
  21.         return Turn_PWM;
  22. }





打赏榜单

21ic小喇叭 打赏了 10.00 元 2016-02-24

评分

参与人数 1威望 +3 收起 理由
justtest111 + 3 等级不够不能发链接的

查看全部评分

 楼主| lianqiang 发表于 2016-2-18 15:19 | 显示全部楼层
为什么我的优酷HTML链接一发表就没了,上不了视频
怎么发视频
wahahaheihei 发表于 2016-2-18 16:30 | 显示全部楼层
你放个文本里上传上来,我去欣赏欣赏
wtyt 发表于 2016-2-18 16:42 | 显示全部楼层
 楼主| lianqiang 发表于 2016-2-18 17:20 | 显示全部楼层
//v.youku.com/v_show/id_XMTQ3NDk5OTQ0MA==.html为什么没有权限???
360截图20160218172105702.jpg

 楼主| lianqiang 发表于 2016-2-18 17:22 | 显示全部楼层
wahahaheihei 发表于 2016-2-18 16:30
你放个文本里上传上来,我去欣赏欣赏

//v.youku.com/v_show/id_XMTQ3NDk5OTQ0MA==.html
史迪威将军 发表于 2016-2-18 20:04 | 显示全部楼层
楼主做的真好,麻烦问一下,平衡的传感器用的是哪款?
justtest111 发表于 2016-2-18 20:06 | 显示全部楼层

我帮你发一下
 楼主| lianqiang 发表于 2016-2-18 20:06 | 显示全部楼层
史迪威将军 发表于 2016-2-18 20:04
楼主做的真好,麻烦问一下,平衡的传感器用的是哪款?

MPU6050
绚紫飞鸥 发表于 2016-2-19 08:14 | 显示全部楼层
支持下楼主,最近也打算做一个玩玩:P
bkn1860 发表于 2016-2-19 09:05 | 显示全部楼层
这个不错
klffnj 发表于 2016-2-19 09:56 | 显示全部楼层
做的很好,请教一下 这个函数 Kalman_Filter(Accel_Y,-Gyro_Y); 怎么做的
大秦正声 发表于 2016-2-19 09:59 来自手机 | 显示全部楼层
Leeone 发表于 2016-2-19 10:04 | 显示全部楼层
MPU6050这块程序能否发我一份 Leeone1990@163.com
zxmxx 发表于 2016-2-19 10:44 | 显示全部楼层
学习了
ecoren 发表于 2016-2-19 14:08 | 显示全部楼层
不孬,
ecoren 发表于 2016-2-19 14:11 | 显示全部楼层
减速电机多少大洋?
michael_llh 发表于 2016-2-19 14:37 | 显示全部楼层
很棒,支持一下!!!
woosoo521 发表于 2016-2-19 15:04 | 显示全部楼层
楼主很厉害
 楼主| lianqiang 发表于 2016-2-19 15:21 | 显示全部楼层
ecoren 发表于 2016-2-19 14:11
减速电机多少大洋?

加轮子好像60多,不过不建议买转速太低,跑不快,120r\min
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

105

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部