制作基于stm32f401的平衡小车(cubemx+hal库)

玩单片机,制作小车是亘古不变的课题,平衡车更是重中之重

在学习单片机的早期我就一直有做平衡车的想法,包括上一篇文章也提到了,正好最近接触了一个关于单片机编写mpu6050的项目,就准备运用手头的各种模块制作一个平衡车,狠狠补档。

一,确定方案,模块选定:

制作平衡车就一点,通过调节pid完成平衡车直立,前进后退,左转右转。这些分别对应了pid的直立环,速度环和转向环,这几个环也是循序渐进环环相扣的。既然如此,我们就需要得到小车的实时速度,角度等信息,用来编写调节pid算法。

因此,获取角度选用mpu6050,这款imu是六轴陀螺仪加速度计,最大的优点就是可以调用dmp库直接稳定得到角度,不需要自己编写那些复杂的算法。

获取速度直接使用带霍尔编码器的减速直流电机,只需要使用定时器的计数器模式就可以很简单的计算出速度。

除此之外,主控芯片使用stm32f401ccu6就已经足够,还使用了oled(方便调试代码以及让数据更加直观)和hc05蓝牙模块(连接手机控制小车行动)

由于以上都是现成模块(自己pcb模块还要画和焊接各种外设实在麻烦)在设计小车时可以直接使用洞洞板自己焊接电路,比较优雅的方式是pcb一块全是排母的板子,直接插上模块就能用,当然面包板加杜邦线直接连接也可以,只是不太美观也不太稳定doge。

二,编写代码:

经历完上述准备工作,就要开始最重要的代码编写(摘抄)环节了。

首先打开cubemx,根据需要设置时钟,打开串口,设置各个gpio口然后创建文件。

然后就是几大重要模块的编写。

1,几大模块的基本编写

这里主要是说明使用到的几个模块具体如何获取数据,正确的数据获取是pid调试的基础。

(1)mpu6050

比较有参考价值的文章有很多,网上一搜一大把,下面我列举一二

stm32的陀螺仪芯片MPU6050的初始化寄存器配置_mpu6050初始化-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_60521516/article/details/123757041这篇文章对于mpu6050模块的讲解比较细致

基于STM32F103C8T6CbueMX-HAL库硬件IIC通信的MPU6050DMP库移植记录_stm32dmp库-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/wu_jiu_xin/article/details/133418810这篇文章就比较适合只是想做着玩的朋友,已经是现成的了,能够直接得到mpu6050原始数据和dmp库计算得到的角度。但是真正要学习这方面的盆友还是最好去学习一下原理,自己调一遍。

关于mpu6050方面的东西细讲要讲太多,这里就不赘述了,现在已有的文章能解决很多问题了,实在不行还能去看mpu6050官方手册,也能提高对此的理解

(2)霍尔编码器电机

简单来说就是开两个定时器的计数器模式,然后经过一系列代码计算得到速度,还能得到当前电机的角度,具体直接参考我以前的文章就好了stm32f401搭配带霍尔编码器的电机测速(hal库)_霍尔编码器hal-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/keil5_god/article/details/135572909

(3)OLED屏幕

这个也是一样,只需要去看一下网上的教程就好了,具体就是调用官方u8g2库,主要是自己写驱动太麻烦了,当然也可以去尝试一下,这里用的ssd1306的128x72的屏幕,确定型号就可以调用库的时候把多余文件全部删了。

STM32F103基于HAL库移植U8g2库驱动I2C SSD1306 Oled_hal库 ssd1306-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_42880082/article/details/130027593?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171585659616800188588271%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=171585659616800188588271&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-130027593-null-null.142%5Ev100%5Epc_search_result_base5&utm_term=%E8%B0%83%E7%94%A8u8g2%E5%BA%93%E9%A9%B1%E5%8A%A8oled&spm=1018.2226.3001.4187在我的这篇文章里面,也包括了一些我对这个库的见解,希望对你有帮助。

关于学校校初选任务参考和自己能力的测评-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/keil5_god/article/details/135094733?spm=1001.2014.3001.5501在这个网页里面看显示模块那里,oled就搞定了。

当然,如果想使用大一点的lcd屏幕,这个库就不那么适用了,可以直接上网上去找别人写好的驱动,也可以自己学着写一下,这里推荐一篇文章STM32驱动ST7735彩色屏幕(任意分辨率),驱动不了你顺着网线来打我-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Xiaowu12345678910/article/details/129038133

 2,pid算法编写和调试

(1)代码编写

首先,定义每个环的变量

float PID_Upright_Kp = 0; //直立环Kp
float PID_Upright_Kd = 0;//直立环Kd
 
float PID_Speed_Kp = 0;//速度环Kp
float PID_Speed_Ki = 0;//速度环Ki

float PID_Steering_Kp = 0; //转向环Kp
float PID_Steering_Kd = 0; //转向环Kd

然后直接编写代码

1.直立环

/*
expect:期望角度
angle:真实角度
gyro:真实角速度
 */
int PID_Upright(float expect, float angle, float gyro)//直立环(位置式PD)
{
    int PWM_out;
    PWM_out = PID_Upright_Kp * (angle - expect) + PID_Upright_Kd * gyro;
    return PWM_out;
}

2,速度环

int Speed_Integral=0, Speed_Err_Last;

/*
expect:期望速度
speed:当前速度
 */
int PID_Speed(int16_t expect, int16_t speed)//速度环(位置式PI)
{
    int PWM_out, Speed_Err;
    float a = 0.7;

    Speed_Err = speed - expect;                           //误差计算
    Speed_Err = (1 - a) * Speed_Err + a * Speed_Err_Last; //低通滤波
    Speed_Err_Last = Speed_Err;                           //更新上次误差

    Speed_Integral += Speed_Err; //积分计算

    if (Speed_Integral > 10000)
        Speed_Integral = 10000;
    if (Speed_Integral < -10000)
        Speed_Integral = -10000; //积分限幅

    PWM_out = PID_Speed_Kp * Speed_Err + PID_Speed_Ki * Speed_Integral; //计算输出
    return PWM_out;
}

3,转向环(后续再补充)

关于各个环的原理,网上有许多很好的文章讲解,下面举例一个我觉得不错的

PID深度解析(基于STM32平衡小车)_gyro,accel,quat-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Carbon6/article/details/106580443然后就直接开始调试了

(2)PID调试

网上很多讲pid如何调试的,我就大概讲一下吧。

首先,全部置零,然后我们开始确定直立环kp的极性。先给kp一个任意数,将车往一个方向倾斜,如果轮子朝着那个方向转极性就正确了,然后调节kp,当放平小车出现低频振荡时kp就差不多调好了。

然后,注释kp,给kd任意一个数值,然后向某方向给小车一个大一点的角速度,轮子向那个方向转,则极性正确。取消kp注释,然后调节kd大小,直到小车出现高频振荡就差不多调好了。

最后,我们一般会适当增大kp和kd,然后同时乘上0,6作为最终值,到此直立环就调节差不多了,此时我们的小车应该可以保持直立,但是会朝着一个方向前进,这是因为我们没有加上速度环。

...........

速度环我这里就不再赘述,具体可以看下面一篇文章和一个手把手的视频

PID平衡小车调参教程_哔哩哔哩_bilibili

【STM32】平衡小车(附带PID等代码及调参方法) - 知乎 (zhihu.com)

这两篇都很好,看完以后就差不多都会了。

3,平衡小车完全倒下后起立

这个是我突然想到的方法,不知道网上有没有更优解,但是就是有点伤车,下面直接放代码。

   			if(flag1==1){
				Set_Motor_PWM(MY_L, -4000);
				Set_Motor_PWM(MY_R, -4000);
				flag1=2;
			}
			else if(flag1==2){
				T_4++;
			}

			if(T_4>=84*10&&T_4<841){			
				
				Set_Motor_PWM(MY_L, 3000);
				Set_Motor_PWM(MY_R, 3000);
			}
			else if(T_4>=841){
				
				Set_Motor_PWM(MY_L, 0);
				Set_Motor_PWM(MY_R, 0);
				flag1=0;
				init_OK=1;
				T_4=0;
			}

//Set_Motor_PWM是给电机pwm的一个函数
//这里的大概思路就是在倒下后通过蓝牙发送信息给小车改变flag值,
//然后给予小车一个反向的速度,
//再在一段时间后给予小车一瞬间的正向速度,
//然后马上开启pid,只要卡好时间,小车就能成功起立

当然,以上代码也只是一个方向倒下的代码,另一个方向如何改变就看你如何diy咯。

当然,这个代码需要配合一个pid倒地和拿起的检测代码,其实就是在角度大于一定值时关闭pid

如下是我写的代码。

  if (init_OK == 1)
  {
    if (PWM_Upright + PWM_Speed > 4000||PWM_Upright + PWM_Speed < -4000)
      T_2++;
    else
    {
      if (roll > 50|| roll < -50)
        T_2++;
      else
        T_2 = 0;
    }

    if (T_2 > 100)
    {
      init_OK = 0;
      Set_Motor_PWM(MY_R, 0);
      Set_Motor_PWM(MY_L, 0);
			Speed_Integral=0;
			T_2 = 0;
    }
  }

文章到此就结束了,我的平衡车由于硬件问题就不放出来了(速度环怎么调都会动,要不然就是直接爆炸,估计是重心或者电机的问题),转向环以后再来补档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值