当前位置: 首页 > news >正文

【嵌入式电机控制#24】BLDC:霍尔测速(高难度,重理解)

        前面我们实现了霍尔传感器检测转子位置、改变导通策略的换相方法,但是无刷电机自身存在着难以直接消除的非线性。也就是说,我们无法保证给定占空比后的速度是准确的,这个时候我们就必须依靠测速来实现速度控制

        此部分开始,将会进入电控算法中代码整体可读性最差、逻辑最复杂的部分,这不是代码本身造成的,而是无刷控制逻辑本身需求就很高。

        后面做FOC使用了SDK库,工作量将会减少很多。

        一、霍尔计数原理

        与有刷电机的Encoder类似,无刷的HALL接口也是通过不断采样数字信号来获得计数值的。

这里的采样频率,就是计数器CCR工作的频率。区别在于,我们做无刷时,习惯性把传感器异或信号频率直接进行估算,这会导致一系列问题。

        

                霍尔信号频率与电机转速理论表达式如下所示。       

                 

                 问题在于,虽然以上方法在数学角度讲能够测出转速,但是经过实际测试,我们发现由于无刷采用了六步换相的驱动方式,每相间的时间差不同,所以用以上方式测出的转速存在较大误差。

                在直流无刷电机工程中,我们采用求和平均的测速方法。 

        具体做法是,利用预设定时器中断,调用多次输入捕获中断中的sum和CNT值,触发fHALL和spd的计算,进而算出实时转速。

         需要注意的是,控制理论中严格规定,控制系统的采样必须以时间为基准。无论是在有刷中的霍尔编码器、电压放大器采样,还是有刷中的霍尔传感器,在反馈过程中,都采用了时间窗口作为最终反馈数据的测量标准。

         时间窗口,也就是我们常说的动态窗口的一种。在嵌入式系统中,固定时间长内采样次数会因为各种非线性而飘忽不定,比如霍尔传感器的输入信号,每次采样在时间域上不一定是等长的。只有计算测量变量一个隐式的时间加权平均和,才能更精确的测量数据并减少其对控制性能影响。

        

二、定时器参数的确定(重要)

        (1)霍尔定时器计数周期(fTIM)确定

                a.  从奈奎斯特采样定律角度考虑

                        如果霍尔输出信号存在最小脉宽,则采样周期不得大于最小脉宽度的1/2

                b.  从定时器频率角度考虑

                        在不损失性能的情况下,定时器频率尽量要远大于输入脉冲频率(>10倍)

               

                我们的霍尔定时器周期大概在18 - 20ms,远大于霍尔脉宽频率

        (2)SYSTiCK中断周期

                SYSTICK中断周期尽量为霍尔计数周期的数倍,但不得损失采样速率

                我们正是通过定时中断限制一个时间窗口,让SUM和CNT进行加权平均运算

         (3)中断优先级设定

                霍尔中断 > 定时器中断

三、源码解析

       1.  输入捕获函数(霍尔样本采集)


const uint8_t HallDirCcw [7] = {0, 3, 6, 2, 5, 1, 4};  
//霍尔序列表,保存着上一次的霍尔相位值//这里使用霍尔序列表和霍尔方向的原因是,在无刷电机中,霍尔方向和电机控制方向不一定是一个值
//当我们在计算速度方向(正负)时,不能参考电机控制方向,而是霍尔的电角度方向void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{int32_t RT_hallPhase = 0; //此次霍尔相位寄存器RT_hallPhase = HALL_GetPhase();     // 获取相位//进行换向BLDCMotor_PhaseCtrl(RT_hallPhase);
//	 HAL_TIM_GenerateEvent(&htim1, TIM_EVENTSOURCE_COM); // Èí¼þÉú³ÉCOMʼþ
//  __HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_COM);//进行CCR累加RT_hallcomp += __HAL_TIM_GET_COMPARE(&htim2, TIM_CHANNEL_1 );RT_hallcnt++;//统计CNTif(HallDirCcw[RT_hallPhase] == LS_hallPhase) // ÐòÁÐÓë±íÖеÄÒ»ÖÂ{//如果上次状态转移符合霍尔相位表,则标记为逆时针RT_hallDir = MOTOR_DIR_CCW; }else/若不符合,则是顺时针RT_hallDir = MOTOR_DIR_CW;//进行旧相位值迭代LS_hallPhase = RT_hallPhase; // ¼Ç¼ÕâÒ»¸öµÄ»ô¶ûÖµ
}

        2. 定时器中断处理(加权均值计算)

float Speed_hz = 0;
void HAL_SYSTICK_Callback()
{if(timeTick != 0)timeTick--;//提前定义了ticktime = 0,开始计时else {uint32_t tmpCC = 0;//初始化加权结果if(RT_hallcnt == 0) //为了避免分母是0而出错,我们应该做判断。{//原则上讲,分母为0,则计算结果必然是0,因为你什么采集信号都没有Speed_hz = 0;}else {//进行加权结果计算tmpCC = RT_hallcomp / RT_hallcnt; // tmpCC:Á½´Î²¶»ñÖ®¼äµÄ²¶»ñÖµ,Speed_hz = (float)HALL_F/(float)(tmpCC); //估算霍尔频率}RT_hallcomp = 0;RT_hallcnt  = 0;//时间窗口参量清零if(RT_hallDir == MOTOR_DIR_CW)//依据霍尔方向进行速度符号处理Speed_hz = Speed_hz > 0 ? Speed_hz : (-Speed_hz);elseSpeed_hz = Speed_hz < 0 ? Speed_hz : (-Speed_hz);//      printf("%.3f Hz, %.2f RPS, %.2fRPM\n", Speed_hz, Speed_hz/PPR, (Speed_hz/PPR)*60);//建议用vofa输出最终速度,printf则最好用DMA发出去Vofa_data((Speed_hz/PPR)*60,VOFA_CHANNEL0);//计时器重新初始化isTimeUp = 0;timeTick = TIMECNT;}
}

         3. 执行函数

        

void BLDCMotor_PhaseCtrl(int32_t Phase){if(MOTOR_DIR_CW == Motor_Dir) Phase = 0x07 ^ Phase;// ½«µÍÈýλÒì»ò 111b ^ 010b -> 101belse {Phase = 0x07 ^ Phase;// ½«µÍÈýλÒì»ò 111b ^ 010b -> 101b}//根据控制方向进行输出控制,其他与之前相同switch(Phase){case 5: //B+  A-{/*  Channe3 configuration */ HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);/*  Channe2 configuration  */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2,4200 * PWM_set);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);  /*  Channe1 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 4201);HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);}break;case 4:// C+ A-{/*  Channe2 configuration */ HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);/*  Channe3 configuration  */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3,4200 * PWM_set);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);  /*  Channe1 configuration  */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,4201);HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);}break;case 6://C+ B-{/*  Channe1 configuration  */ HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);/*  Channe3 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3,4200 * PWM_set);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);  /*  Channe2 configuration  */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2,4201);HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);}break;case 2: // A+ B-{/*  Channe3 configuration */HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);/*  Channe1 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,4200 * PWM_set);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);  /*  Channe2 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2,4201);HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);}break;case 3:// A+ C-{/*  Channe2 configuration */HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);/*  Channe1 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,4200 * PWM_set);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);  /*  Channe3 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3,4201);    HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);}break;case 1: // B+ C-{/*  Channe1 configuration */ HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);/*  Channe2 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 4200 * PWM_set);HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);  /*  Channe3 configuration */__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, 4201);HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);}}
//		      HAL_TIM_GenerateEvent(&htim1, TIM_EVENTSOURCE_COM);}

http://www.lryc.cn/news/609889.html

相关文章:

  • 聊聊IT行业初创团队质量管理前期准备
  • 十二、请求响应-请求:数组参数和集合参数
  • 编码器-解码器架构:从原理到实践
  • 压缩与归档命令
  • Linux 逻辑卷管理
  • Javascript面试题及详细答案150道之(046-060)
  • Redis之Hash和List类型常用命令
  • Dubbo 3.x源码(32)—Dubbo Provider处理服务调用请求源码
  • 《算法导论》第 1 章 - 算法在计算中的作用
  • Java开发时出现的问题---语言特性与基础机制陷阱
  • 从HTTP到WebSocket:打造极速实时通讯体验
  • 安全扫描:目标主机支持RSA密钥交换问题
  • 国产化低代码平台如何筑牢企业数字化安全底座
  • 消防器材检测数据集介绍-9,600 张图片 智慧安防系统 建筑施工安全监管 AI 消防巡检机器人 自动审核系统 公共场所安全监测
  • Solidity全局变量与安全实践指南
  • [论文阅读] 人工智能 + 教学 | 从代码到职业:用机器学习预测竞赛程序员的就业潜力
  • 安全扫描:目标使用过期的TLS1.0 版协议问题
  • 【乐企板式文件】不动产销售类发票已支持
  • MySQL三大日志详解(binlog、undo log、redo log)
  • 赋能未来:数字孪生驱动能源系统智能化升级
  • 【项目实践】在系统接入天气api,根据当前天气提醒,做好plan
  • Linux(centos)安全狗
  • 【芯片设计专用执行单元:PWM如何重塑能源与智能控制】
  • sqli-labs靶场less29~less35
  • 2025.08.04 移除元素
  • 【测试工程思考】测试自动化基础能力建设
  • 使用mybatis生成器生成实体类mapper和查询参数文件,实现简单增删改查。使用log4j输出日志到控制台。使用配置文件注册Bean,配置视图解析器
  • 每天学一个Linux命令(38):vi/vim
  • Excel商业智能分析报表 【销售管理分析仪】
  • 免费MCP: JSON 转 Excel MCP