
本文共 11547 字,大约阅读时间需要 38 分钟。
无刷有感电机测速、速度闭环控制
1.直流无刷有感电机介绍
无刷直流 (Brushless Direct Current, BLDC)电机是一种正快速普及的电机类型,在移动机器人领域也有诸多应用,这里我们将对无刷直流电机的常见问题进行描述。
(1)工作原理
首先,我们来看一下无刷电机的工作原理,下图为三电极、二磁极、内转子无刷电机演示。
无刷电机不是传统的直流电机,模型虽然是直流电池供电,但通过无刷电调之后就转变为了三相交变电流传输到了三个极性上。通过上图可以看出,无刷电机是没有碳刷的,和有刷相反,无刷电机的磁铁成了转子。
(2)内转子与外转子
上面无刷电机工作原理图所示的是内转子无刷电机,顾名思义,磁铁在里边。而外转子无刷电机则相反,它的磁铁“包”在外面。而A、B、C电极在里边。这样的设计可以让电机的扭力更大,但转速却上不来。因为这个原因,一般四旋翼无人机上常见外转子无刷电机(需要扭矩大),而模型车和模型船一般都使用内转子无刷电机(需要转速高)。
(3)有感无刷和无感无刷
要说明这个问题首先要弄懂感是啥,有感无刷中的感是指“霍尔传感器”,那么什么是“霍尔”呢?霍尔是指的霍尔效应,这一现象是美国物理学家霍尔于1879年在研究金属的导电机构时发现的。当电流垂直于外磁场通过导体时,在导体的垂直于磁场和电流方向的两个端面之间会出现电势差,这一现象便是霍尔效应。这个电势差也被叫做霍尔电势差。
那么我们为什么要感应这玩意?要说清这个问题就必须从无感无刷的一个缺点说起。无刷电机的转速是靠交流电频率决定的,那么电调要想方设法弄明白目前电机的转速以及当前电机的状态。其实这对已经正常运转的电机来说很容易,但对于一个刚刚起步或者运行速度很慢的电机来说就显得比较麻烦了(很难较准确的测出电机转速的状态),所以无感无刷电机会在低速时线性不好甚至可能会颤抖,而起步的扭力也难以强过同等级有刷电机。
但是人们发现无论什么运行状态的无刷电机,它的霍尔效应都是明显的,所以通过霍尔效应电调可以很容易的知道无论高速还是低速电机的运行状态,从而解决了无感无刷电机的毛病!
(4)直流有感无刷电机驱动电路
驱动电路原理
一般有感直流无刷电机的驱动电路是由6个MOS管组成的桥式电路。
编程控制无刷直流电机运行,其最底层的工作就是控制着6个MOS管的两个状态**:导通和关断**。
根据相关的控制理论,我们可以清晰的得知:
(1)每次只能打开两个MOS管。保持任意两个电极上有电。
(2)驱动电路上面一排和下面一排的MOS管不会出现两个同时打开。只打开一行就只送了一个电极的电。
(3)同一列的MOS管也不会同时打开,同时打开会出现电源短路。
所以6个MOS管的开关状态只有六种,常见的无刷电机驱动器的电路原型就是这样,也就是六步换向法的由来。
无刷电机调速原理
如果我们对下面一排MOS管按驱动原理进行一次性的完全打开与关闭,上面一排MOS管按驱动原理通过单片机的PWM进行非完全一次性打开与关断的,也就是MOS不完全打开,MOS的G极达不到最大电压值,这样我们就实现调速了。
表示无刷电机正反转状态
通过三个霍尔传感器的六种状态表示无刷电机的正反转过程。一般无刷有感电机都安装3个霍尔传感器,关于霍尔传感器在无刷电机中有以下特点:
(1)霍尔信号不会出现全零(000)和全一(111)两种状态。所以,三个霍尔仅存6中状态。
(2)霍尔的6种状态是顺序变化的,往复循环。通过先后顺序即可判断电机正反转。
所以我们是不是可以理解为,任意三个霍尔的组合状态之后的状态只有两种,一个表示正转,一个表示反转。
(5)霍尔安装角度
常用的霍耳传感器安装方式有120 ° 安装和60° 安装2 种,2 种方法都可以输出6 个不同的霍耳信号,分别对应6 个不同的区域,当无刷直流电机转子转到某一区域时,对应的绕组通电,电机就可以正常工作,若霍耳信号与绕组关系错误,就无法正常工作,甚至会对电机或功率器件造成损坏。因此,确定霍耳信号与定子绕组关系对于无刷直流电机而言非常重要。
霍耳传感器常用来检测无刷直流电机换相点。三相无刷直流电机需要3 个霍耳传感器来检测6 个不同的位置,霍耳传感器的安装有120° 安装和60° 安装2 种方式,120° 安装指3 个霍耳传感器互差120° (电角度),而60°安装指3 个霍耳传感器互差60° (电角度)。2 种安装方式最大的区别在于采用60 ° 安装时可以输出“000”和“111”信号,而120° 安装则不会输出这2 个信号,通过这一点可以判断霍耳传感器的安装方式。
霍尔的安装角度,对驱动器的选择有很大的影响,通常卖家都会问无刷电机的霍尔安装角度。
2.STM32无刷电机轮速测量
(1)测速方法:
单片机转速测量的算法很多,主要有频率测速法(M法)、周期测速法(T法)等。
M 测速法:通过在相同的时间T间隔内计算传感器输出的脉冲个数来算出转速;
设R为每转的脉冲信号数,T为间隔时间,M为T时间内测得的脉冲数。可见这种测速法的分辨率取决于电机转一周的输出R和测速周期T,极对数越多或测量周期越长,则分辨率越小,但一般电机的R不大,且测量周期不宜过大,因为测量周期过大为影响测速的反应速度,降低系统的实时性。所以这种方法是不可取的。
T法测速:通过测量传感器发出的相邻两个脉冲之间的T来算出转速;因为相邻两个脉冲对应轮子上的物理距离N是确定的。
用一个计数器对三个霍尔的脉冲信号进行采集;从一个脉冲触发开始计时,到下一个脉冲触发新的计时,测得每两个脉冲间的时间T;
所以,T法测速的电机速度speed=N/T;
注意,关于N的获得有两种方法,一、根据电机参数即可获得,用轮子周长除以轮子转一圈的脉冲数,二、可以测量10圈轮子累积脉冲,求平均值。
(2)测速的技术要求:
(1)尽量精细化速度分辨率。所以采用T法测速
(2)计算出轮子的实际线速度m/s,而不是电机的转速。所以要明确两个脉冲对应轮子转动的物理距离。
(3)用速度的正负值明确分辨轮子正反转。所以要根据判断前后霍尔组合状态来判断电机所谓的正反。
(3)实现方式:
首先,通过单片机的外部触发中断,获得同一触发时刻的三个霍尔信号状态并通过二进制组合成状态数字;根据下一次的状态数字,进行速度正反的区分。
其次,通过单片机的定时计数器,测算每两个脉冲之间的时间。即一个上升沿到一个下降沿的时间,或者一个下降沿到上升沿的时间,或者一个下降沿到一个下降沿的时间等等。
最后,进行速度计算,符号改变即可。
注意,这里说的每两个脉冲不是一个霍尔的每两个脉冲而是三个霍尔中的每两个脉冲,这个脉冲既包括上升沿又包括下降沿。
(4)使用设备以及软件:
STM32F103单片机 + 120度霍尔的无刷有感电机 + keil5。
(5)具体代码实现:
下面以一个电机为例,具体的源码工程文件,可以在公众号:小白学移动机器人,发送:无刷电机测速,既可获得。
外部中断配置代码:
//电机霍尔外部中断配置函数void Hall_EXIT_Config(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin){ GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; u8 EXTI_IRQn; uint8_t GPIO_PortSource,GPIO_PinSource; uint32_t EXTI_Line; //外部中断,需要使能AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能GPIO时钟 if(GPIOx == GPIOA) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE),GPIO_PortSource = GPIO_PortSourceGPIOA; else if(GPIOx == GPIOB) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE),GPIO_PortSource = GPIO_PortSourceGPIOB; else if(GPIOx == GPIOC) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE),GPIO_PortSource = GPIO_PortSourceGPIOC; else if(GPIOx == GPIOD) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE),GPIO_PortSource = GPIO_PortSourceGPIOD; else if(GPIOx == GPIOE) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE),GPIO_PortSource = GPIO_PortSourceGPIOE; else if(GPIOx == GPIOF) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE),GPIO_PortSource = GPIO_PortSourceGPIOF; else if(GPIOx == GPIOG) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE),GPIO_PortSource = GPIO_PortSourceGPIOG; //选择中断线,中断服务函数 if(GPIO_Pin == GPIO_Pin_0) GPIO_PinSource = GPIO_PinSource0, EXTI_Line = EXTI_Line0, EXTI_IRQn = EXTI0_IRQn; else if(GPIO_Pin == GPIO_Pin_1) GPIO_PinSource = GPIO_PinSource1, EXTI_Line = EXTI_Line1, EXTI_IRQn = EXTI1_IRQn; else if(GPIO_Pin == GPIO_Pin_2) GPIO_PinSource = GPIO_PinSource2, EXTI_Line = EXTI_Line2, EXTI_IRQn = EXTI2_IRQn; else if(GPIO_Pin == GPIO_Pin_3) GPIO_PinSource = GPIO_PinSource3, EXTI_Line = EXTI_Line3, EXTI_IRQn = EXTI3_IRQn; else if(GPIO_Pin == GPIO_Pin_4) GPIO_PinSource = GPIO_PinSource4, EXTI_Line = EXTI_Line4, EXTI_IRQn = EXTI4_IRQn; else if(GPIO_Pin == GPIO_Pin_5) GPIO_PinSource = GPIO_PinSource5, EXTI_Line = EXTI_Line5, EXTI_IRQn = EXTI9_5_IRQn; else if(GPIO_Pin == GPIO_Pin_6) GPIO_PinSource = GPIO_PinSource6, EXTI_Line = EXTI_Line6, EXTI_IRQn = EXTI9_5_IRQn; else if(GPIO_Pin == GPIO_Pin_7) GPIO_PinSource = GPIO_PinSource7, EXTI_Line = EXTI_Line7, EXTI_IRQn = EXTI9_5_IRQn; else if(GPIO_Pin == GPIO_Pin_8) GPIO_PinSource = GPIO_PinSource8, EXTI_Line = EXTI_Line8, EXTI_IRQn = EXTI9_5_IRQn; else if(GPIO_Pin == GPIO_Pin_9) GPIO_PinSource = GPIO_PinSource9, EXTI_Line = EXTI_Line9, EXTI_IRQn = EXTI9_5_IRQn; else if(GPIO_Pin == GPIO_Pin_10) GPIO_PinSource = GPIO_PinSource10,EXTI_Line = EXTI_Line10,EXTI_IRQn = EXTI15_10_IRQn; else if(GPIO_Pin == GPIO_Pin_11) GPIO_PinSource = GPIO_PinSource11,EXTI_Line = EXTI_Line11,EXTI_IRQn = EXTI15_10_IRQn; else if(GPIO_Pin == GPIO_Pin_12) GPIO_PinSource = GPIO_PinSource12,EXTI_Line = EXTI_Line12,EXTI_IRQn = EXTI15_10_IRQn; else if(GPIO_Pin == GPIO_Pin_13) GPIO_PinSource = GPIO_PinSource13,EXTI_Line = EXTI_Line13,EXTI_IRQn = EXTI15_10_IRQn; else if(GPIO_Pin == GPIO_Pin_14) GPIO_PinSource = GPIO_PinSource14,EXTI_Line = EXTI_Line14,EXTI_IRQn = EXTI15_10_IRQn; else if(GPIO_Pin == GPIO_Pin_15) GPIO_PinSource = GPIO_PinSource15,EXTI_Line = EXTI_Line15,EXTI_IRQn = EXTI15_10_IRQn; //引脚初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin; //端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOx, &GPIO_InitStructure); //根据设定参数初始化GPIO GPIO_EXTILineConfig(GPIO_PortSource,GPIO_PinSource); EXTI_InitStructure.EXTI_Line = EXTI_Line; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //上升下降都触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器 NVIC_InitStructure.NVIC_IRQChannel = EXTI_IRQn; //使能按键所在的外部中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2, NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 NVIC_Init(&NVIC_InitStructure); }
定时器配置代码:
void TIM4Init(u16 Timing,u16 Divis){ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); TIM_TimeBaseStructure.TIM_Period = Timing;//计数值 TIM_TimeBaseStructure.TIM_Prescaler = Divis; //预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //不分频72Mhz时钟频率 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //允许定时器3更新中断 TIM_Cmd(TIM4, ENABLE);//开启TIM4 NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn; //定时器4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x03; //抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; //子优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}
左轮三相霍尔处理函数:
//左轮三相霍尔处理函数void left_hall_deal(TIM_TypeDef* TIMx,char *motor_state,char *hall_state_last,char *hall_state_now,unsigned short int *count,double *speed){ if(*motor_state==2){ //如果是静止状态,之前的计数,不论是什么样子都为零 *count=0; //计数为零 TIM_SetCounter(TIMx,0); //定时器清零 } else //如果不是静止状态 { *count=TIM_GetCounter(TIMx);//记录两个脉冲之间的定时器的计数值 TIM_SetCounter(TIMx,0); //清零定时计数器,开始新的计数 } if(*count==0){ //如果定时计数器计数为零,则速度为零 *speed=0; } else if(*count>10) //防止重复进入中断,因为电机的速度不会达到,16077.1mm/s = 16.0071m/s { //0.5/311.0=0.00160771704 每两个脉冲之间轮子走的真实距离,这里根据大家自己的情况轮子情况而定 //0.001607717/(1/100000.0)=160.771704 两个脉冲之间只计量到一个数的速度是160.771704m/s,这里的100000是因为定时器的计数频率是100k,也就是每记一个数用时1/100000s。 //计数器个数越多,速度越小,个数超过10000认为静止,10000可以修改,也就是计数器的溢出值 *speed=1000*160.771704/(double)*count; *count=0; //速度计算完毕,计数缓存清零,等待下次计数 } //获取三路霍尔的组合状态的一种容易理解的方法 *hall_state_now=(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_10)<<2)|(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_11)<<1)|(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_12)); if(*count>10||*count==0) { switch(*hall_state_last) { // 反转 正转 不变 case 1:if(*hall_state_now!=3){ *motor_state=1;}else if(*hall_state_now==1){ }else{ *motor_state=0;};break; case 3:if(*hall_state_now!=2){ *motor_state=1;}else if(*hall_state_now==3){ }else{ *motor_state=0;};break; case 2:if(*hall_state_now!=6){ *motor_state=1;}else if(*hall_state_now==2){ }else{ *motor_state=0;};break; case 6:if(*hall_state_now!=4){ *motor_state=1;}else if(*hall_state_now==6){ }else{ *motor_state=0;};break; case 4:if(*hall_state_now!=5){ *motor_state=1;}else if(*hall_state_now==4){ }else{ *motor_state=0;};break; case 5:if(*hall_state_now!=1){ *motor_state=1;}else if(*hall_state_now==5){ }else{ *motor_state=0;};break; default:break; } } if(*motor_state==1) //根据电机正反,给速度符号 *speed=-*speed; *hall_state_last=*hall_state_now;//记录上一次霍尔组合状态,用于下一次判断电机正反 }
左轮三相霍尔中断服务函数:
//Lvoid EXTI15_10_IRQHandler(void){ if(EXTI_GetITStatus(EXTI_Line10)==SET){ EXTI_ClearITPendingBit(EXTI_Line10); //清除LINE10线路挂起位 } if(EXTI_GetITStatus(EXTI_Line11)==SET){ EXTI_ClearITPendingBit(EXTI_Line11); //清除LINE11线路挂起位 } if(EXTI_GetITStatus(EXTI_Line12)==SET){ EXTI_ClearITPendingBit(EXTI_Line12); //清除LINE12线路挂起位 } left_hall_deal(TIM4,&left_motor_state,&left_hall_state_last,&left_hall_state_now,&left_count,&left_speed);}
定时器中断服务函数:
void TIM4_IRQHandler(void){ if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET) //溢出中断 { TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除中断标志位 left_motor_state=2;//电机静止,还需要根据具体情况,测量电机最低速是多少 left_count=0; left_speed=0; }}
主函数:
//=================================头文件===================================#include "sys.h"#include "delay.h"#include "exti.h"#include "tim4.h"/*===================================================================程序功能:直流无刷有感电机的速度测量测试程序编写:公众号:小白学移动机器人其他 :如果对代码有任何疑问,可以私信小编,一定会回复的。=====================================================================------------------关注公众号,获得更多有趣的分享---------------------===================================================================*/int main(void){ GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁用JTAG 启用 SWD NVIC_PriorityGroupConfig(2); //=====设置中断分组 delay_init(); //=====延时函数初始化 Hall_EXIT_Config(GPIOE,GPIO_Pin_10); //LA 霍尔外部中断检测用 Hall_EXIT_Config(GPIOE,GPIO_Pin_11); //LB Hall_EXIT_Config(GPIOE,GPIO_Pin_12); //LC TIM4Init(10000-1,720-1); //=====测速用 while(1) { }}//===================================END======================================
3.STM32无刷电机速度PID闭环控制
关于无刷电机的速度闭环控制和有刷直流除了速度测量之外没有区别,可以参照我之前的文章,下方链接,点击访问。
4.参考:
5.公众号:
如果你感觉,我的文章比较适合你,关注我,点个赞,给你不一样的惊喜。

发表评论
最新留言
关于作者
