stm32平衡小车制作遇到的问题和解决方案分享

您所在的位置:网站首页 为什么抖音卡住不动了呢 stm32平衡小车制作遇到的问题和解决方案分享

stm32平衡小车制作遇到的问题和解决方案分享

2024-07-10 05:27| 来源: 网络整理| 查看: 265

问题总览

        1、电机带负载所引起的死区补偿问题

        2、利用MPU6050传感器进行某一轴的角度测量问题

        3、stm32串口通讯得到的数据用DMA转运问题

一、电机带负载所引起的死区补偿问题

        我选用的电机为直流减速电机,型号为GA25-370,是12V的电机。

        电机运行分为带负载和不带负载的两种情况:不带负载的电机转速呈现饱和特性,带负载的电机转速呈现有死区的饱和特性。制作平衡车的时候不会让电机满速运行,所以我们可以近似地看做为线性特性和死区特性。平衡车一定是带负载的,那我们如何补偿呢?这里分享我的方法。

        直流电机调速方案比较简单,就是用PWM波进行调速。一个最简单粗暴的方法就是直接在PID控制的结果中,加上一个数,这个数就是死区的△值。这样的话测量死区△值也很简单,只需将小车平放在地上,失能PID运算,并逐渐提高PWM值,小车轮胎即将运动的时刻所输出的PWM占空比值即为我们所要的死区△的值,记录并写进程序即可。但上述办法有一个弊端,电机的死区主要来源是轮胎的摩擦力(其他的力比如空气阻力可忽略不计),轮胎的摩擦力并不是一成不变的,也就是说△的值是一个时变的量,这种方法补偿的实际效果是小车有一个低频抖动且无法通过改变PID值进行消除。

        第二种方法比较复杂,需要修改直立环的程序逻辑,我们在用第一种方法得到死区△的值后,减去适量的数a进行补偿(此时补偿值为△-a),或者不补偿也可(此时补偿值为0)。之后在直立环的函数里,当角度偏差小的时候(比如±3°),kp选用较大的数,同时结合补偿(△-a或者0)保证小幅偏差可以让小车启动;当角度偏差大的时候,kp选用较小的数,同时结合补偿(△)能保证小车回正即可。这种方法补偿的实际效果可以减轻上一个方法的低频抖动问题,但是小车的抗干扰能力不会太强。

二、利用MPU6050传感器进行某一轴的角度测量问题

        我们计算偏角普遍是用两种方法:一种是获得重力加速度在x轴和z轴(根据传感器安装不同,选取的两个轴也会有所不同)上分量,通过反正切函数获得,我们设为angle1;另一种方法是获得y轴角速度,对角速度积分获得角度,我们设为angle2。但这两种方法各有利弊:第一种方法适合在静止的时候使用,第二种方法适合在运动的时候使用。

        针对上述两种方法的利弊,我们有两种滤波算法可以使用。

        // 第一种算法为一阶滤波算法,在平衡小车上应用就是:k1*angle1 + (1-k1) *angle2,k1为滤波系数,k1∈[0,1]。对于k1的值,我们通常选用0.03,也就是说我们更偏向于angle2所计算的角度,而不是angle1。为什么呢,是因为小车在平衡的过程中,是不断运动着的,angle2精确度比angle1要高,所以我们更置信于angle2所计算的结果。当然,如果你在编程调试并未使用电机时(也就是说只是看程序运行结果还未上实物),这个滤波算法肯定是不准的,因为它只适合运动的时候进行计算。

        // 第二种算法为卡尔曼滤波算法,这个算法较为复杂,我这里不展开解释其算法过程,只简单介绍原理。卡尔曼滤波算法其实本质上就是猜,是一种有根据地猜,如果你的小车在几乎静止的状态下,卡尔曼滤波更置信于angle1,你的小车在运动的情况下,卡尔曼滤波更置信于angle2,也就是说,卡尔曼滤波在小车的应用上算是更先进的一阶滤波算法,因为它可以根据实际情况调整k1的值。

        根据我的实际应用,这两种算法在平衡小车的上结果几乎一样,分不出哪一个更优劣。不过卡尔曼滤波在只进行数据调试的时候更好用一些。

三、stm32串口通讯得到的数据用DMA转运问题

stm32的DMA外设可以一定程度上减少CPU的运算量,避免中断过多而引起的bug,这里我分享我的USART3的DMA配置(库函数)。

#include "stm32f10x.h" // Device header char Usart_DRData; //串口数据接收变量 void MyUSART_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStruture; GPIO_InitStruture.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruture.GPIO_Pin = GPIO_Pin_10; GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStruture); GPIO_InitStruture.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStruture.GPIO_Pin = GPIO_Pin_11; GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStruture); //选用USART3的RX、TX接口 USART_InitTypeDef USART_InitStruture; USART_InitStruture.USART_BaudRate = 9600; USART_InitStruture.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruture.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //或运算可以同时配置RX与TX USART_InitStruture.USART_Parity = USART_Parity_No; USART_InitStruture.USART_StopBits = USART_StopBits_1; USART_InitStruture.USART_WordLength = USART_WordLength_8b; USART_Init(USART3,&USART_InitStruture); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); DMA_InitTypeDef DMA_InitStruture; DMA_InitStruture.DMA_PeripheralBaseAddr = (uint32_t)&USART3 -> DR;//外设接收寄存器地址 DMA_InitStruture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStruture.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruture.DMA_MemoryBaseAddr = (uint32_t)(&Usart_DRData); //传输目标寄存器地址 DMA_InitStruture.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruture.DMA_MemoryInc = DMA_MemoryInc_Disable; DMA_InitStruture.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStruture.DMA_BufferSize = 1; //转运的数的个数,我这里就转一个数,所以这里的值可以任意写,如果你转运带包头包尾的数, //这个就不能任意写,而且传输目标寄存器地址就得是数组变量,然后DMA_MemoryInc为Enable //也就是地址自增,数组的地址都是紧挨着的 DMA_InitStruture.DMA_Mode = DMA_Mode_Circular; //循环模式 DMA_InitStruture.DMA_M2M = DMA_M2M_Disable; DMA_InitStruture.DMA_Priority = DMA_Priority_Medium; //优先级,这里就一个DMA通道,可以任意选 DMA_Init(DMA1_Channel3,&DMA_InitStruture); //USART3的RX的DMA通道为DMA1的3通道 //可以通过查找数据手册的DMA1请求映像表得到 DMA_Cmd(DMA1_Channel3,ENABLE); USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); //使能USART3的RX的DMA转运 USART_Cmd(USART3,ENABLE); } /* 之后Usart_DRData的值即为串口通讯的值 */



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3