STM32平衡小车制作问题及解决方案分享

问题总览

        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的值即为串口通讯的值 */

物联沃分享整理
物联沃-IOTWORD物联网 » STM32平衡小车制作问题及解决方案分享

发表评论