【STM32】使用HAL库进行电机速度环PID控制,代码+调参

这篇具有很好参考价值的文章主要介绍了【STM32】使用HAL库进行电机速度环PID控制,代码+调参。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

主控:STM32F103C8T6

1. 电机测速

在进行速度控制之前,我们首先需要进行速度采样,这里参见这篇博文

2. 电机驱动

​ 这里不细说电机驱动模块的选型和使用,而是说一个常见的误区。我们驱动电机要使用两路PWM,一般是一路给PWM信号,一路是纯低电平。但这其实是不好的,正确的做法是一路给PWM,另一路给纯高电平。此时PWM的占空比越低,电机的速度越快。

​ 如果大家使用的是类似于A4950或者DRV8870这样的电机驱动芯片,它们的数据手册中都会有这样的描述

​ 这是DRV8870的,明确说明了PWM加高电平是最佳控制方式。

【STM32】使用HAL库进行电机速度环PID控制,代码+调参

这是A4950的,用曲线图的方式说明了PWM加高电平时电流会更加稳定。【STM32】使用HAL库进行电机速度环PID控制,代码+调参

​ 此外,如果使用PWM加高电平的控制方式,在设置速度为0时,两路信号都会输出纯高电平,此时会触发芯片的刹车模式,使得电机快速停止转动,避免我们制作的小车出现刹不住车的情况。

3. 速度环实现

​ PID的原理就不赘述了,我们直接看代码。

​ 现在我们已经在定时器中断中完成了电机的速度采样,得到了电机的速度,接下来我们需要进行PID计算,并输出相应占空比的PWM给电机。

​ 但是在此之前,我们需要编写PID的计算函数和进行相关初始化,下面是代码

PID.h部分

#ifndef _PID_H_
#define _PID_H_

#include "stm32f1xx.h"
#include "encoder.h"
#include <stdio.h>
#include "control.h"

//PID三个参数的值
#define KP_speed 2
#define KI_speed 0
#define KD_speed 0

typedef struct _PID//PID参数结构体
{
    float kp,ki,kd;
    float err,lastErr;
    float integral,maxIntegral; //积分值
    float output,maxOutput;
}PID;

void PID_Init(void);
float Speed_PID_Realize(PID* pid,float target,float feedback);//一次PID计算

PID.c部分

#include "pid.h"
PID pid_speed;

/**********************************
 * 功能:PID结构体参数初始化
 * 输入:无
 * 返回:无
 * *******************************/
void PID_Init(void)//PID参数初始化
{
    pid_speed.err = 0;
    pid_speed.integral = 0;
    pid_speed.maxIntegral = 1000; 
    pid_speed.maxOutput = __HAL_TIM_GetAutoreload(&PWM_TIM);
    pid_speed.lastErr = 0;
    pid_speed.output = 0;
    pid_speed.kp = KP_speed;
    pid_speed.ki = KI_speed;
    pid_speed.kd = KD_speed;
}

/****************************************
 * 作用:速度环PID计算
 * 参数:PID参数结构体地址;目标值;反馈值
 * 返回值:无
 * ****************************************/
float Speed_PID_Realize(PID* pid,float target,float feedback)//一次PID计算
{
    pid->err = target - feedback;
    if(pid->err < 0.3 && pid->err > -0.3) pid->err = 0;//pid死区
    pid->integral += pid->err;
    
    if(pid->ki * pid->integral < -pid->maxIntegral) pid->integral = -pid->maxIntegral / pid->ki;//积分限幅
    else if(pid->ki * pid->integral > pid->maxIntegral) pid->integral = pid->maxIntegral / pid->ki;

    if(target == 0) pid->integral = 0; // 刹车时清空i


    pid->output = (pid->kp * pid->err) + (pid->ki * pid->integral) + (pid->kd * (pid->err - pid->lastErr));//全量式PID

    //输出限幅
    if(target >= 0)//正转时
    {
        if(pid->output < 0) pid->output = 0;
        else if(pid->output > pid->maxOutput) pid->output = pid->maxOutput;
    }
    else if(target < 0)//反转时
    {
        if(pid->output < -pid->maxOutput) pid->output = -pid->maxOutput;
        else if(pid->output > 0) pid->output = 0;
    }

    pid->lastErr = pid->err;
    if(target == 0) pid->output = 0; // 刹车时直接输出0
    return pid->output;
}

​ 这里的速度环代码在一般的PID上加了点东西。首先是PID死区,即err值很小时认为err=0,让速度发生很小的抖动时,PID输出不会变化,避免大幅度震荡的产生;其次是当目标值为0的时候让积分部分和输出同时等于0,使刹车更加迅速。

​ PID_Init()函数需要放在main.c的循环之前,Speed_PID_Realize()函数需要放在定时器中断的电机测速部分后面。

​ 所以现在的定时器中断函数如下

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器回调函数,用于计算速度和PID计算
{	
    if(htim->Instance==GAP_TIM.Instance)//间隔定时器中断,是时候计算速度了
    {
        /**********************************电机测速************************************/
        motor1.direct = __HAL_TIM_IS_TIM_COUNTING_DOWN(&ENCODER_TIM);//如果向上计数(正转),返回值为0,否则返回值为1
        motor1.totalCount = COUNTERNUM_1 + motor1.overflowNum * RELOADVALUE_1;//一个周期内的总计数值等于目前计数值加上溢出的计数值
        
        if(motor1.lastCount - motor1.totalCount > 19000) // 在计数值溢出时进行防溢出处理
        {
            motor1.overflowNum++;
            motor1.totalCount = COUNTERNUM_1 + motor1.overflowNum * RELOADVALUE_1;//一个周期内的总计数值等于目前计数值加上溢出的计数值
        }
        else if(motor1.totalCount - motor1.lastCount > 19000) // 在计数值溢出时进行防溢出处理
        {
            motor1.overflowNum--;
            motor1.totalCount = COUNTERNUM_1 + motor1.overflowNum * RELOADVALUE_1;//一个周期内的总计数值等于目前计数值加上溢出的计数值
        }
        
        motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 3000;//算得每秒多少转,除以4是因为4倍频
        motor1.speed = Speed_Low_Filter(motor1.speed,speed_Record);
        motor1.lastCount = motor1.totalCount; //记录这一次的计数值
        
        /***************************PID速度环**********************************/
        motor_Out = Speed_PID_Realize(&pid_speed,Target_Speed,motor1.speed);
        //Target_Speed是目标速度,自行定义就好
        if(motor_Out >= 0)
    	{
        __HAL_TIM_SetCompare(&MOTOR1_TIM, MOTOR1_CHANNEL_FORWARD, 1000);
        __HAL_TIM_SetCompare(&MOTOR1_TIM, MOTOR1_CHANNEL_BACKWARD, 1000-motor_Out);
    	}
    	else
    	{
        __HAL_TIM_SetCompare(&MOTOR1_TIM, MOTOR1_CHANNEL_BACKWARD, 1000);
        __HAL_TIM_SetCompare(&MOTOR1_TIM, MOTOR1_CHANNEL_FORWARD, 1000+motor_Out);
    	}
        /**********************************************************************/
    }
}

如果觉得定时器中断函数看起来很乱,可以将测速和PID分别封装成函数,定时器中断负责调用函数即可。

现在我们就已经能实现电机的速度环控制了,剩下的就是进行PID调参了。

4. 速度环调参

这里需要说明一下,我给电机测速加上了平均滤波,PID参数在滤波和不滤波的情况下会有比较大的区别

电机速度环比较好调,我的调参经验是这样:

  1. 先让I=D=0,使P从很小值开始增加,直到电机的速度达到目标速度的一半左右。
  2. 一点点增大I,使得电机的速度能够很快达到目标值,哪怕有点震荡、超调也没事。
  3. 增大D,使超调和震荡逐步减小,还剩有一点点超调就行了。保留一点点超调是为了使达到稳定所需的时间比较短。

为了更好地进行PID调参,我们最好使用能画曲线的串口上位机,这里推荐VOFA+,使用VOFA+进行PID调参可以看这里

速度环调得好的效果应该是这样的:

【STM32】使用HAL库进行电机速度环PID控制,代码+调参

上图中,绿线是电机的实际速度,红线是电机的目标速度。我们主要检查以下几点:

  1. 从静止到正最大转速
  2. 从正最大转速到反最大转速
  3. 从正最大转速或反最大转速到静止

如果电机到正反最大转速所需时间较短,有一点点超调,没有震荡;到静止时所需时间很短,且没有震荡,那么就说明速度环调好了。

下图展示了速度环调好后电机速度稳定和停下来所需的时间

【STM32】使用HAL库进行电机速度环PID控制,代码+调参

电机速度稳定需要60ms左右,而停下来只需要20ms左右

当然不同电机不同环境下情况会不一样,我这里只是一个参考。文章来源地址https://www.toymoban.com/news/detail-419236.html

到了这里,关于【STM32】使用HAL库进行电机速度环PID控制,代码+调参的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32】使用HAL库进行电机测速,原理、代码、滤波

    参考资料: https://blog.csdn.net/lzzzzzzm/article/details/119416134 野火STM32电机开发教程 常见的编码器有两种,分别为霍尔编码器和GMR编码器。 1.1 霍尔编码器 ​ 霍尔编码器圆盘上分布有磁极,当圆盘随电机主轴转动时,会输出两路相位差90°的方波,用这两路方波可测出电机的转速和

    2024年01月24日
    浏览(47)
  • STM32 HAL库PID控制电机 第二章 TB6612FNG芯片驱动GB37-520电机

    1 电路图 2 TB6612简介 TB6612是双驱动,可同时驱动两个电机 STBY:接单片机的IO口清零电机全部停止,置1通过AIN1 AIN2,BIN1,BIN2 来控制正反转 VM:建议接10V以内电源( 瞬间上电12V可能会有尖峰电压击穿器件 ) VCC:接5V电源 GND:接电源负极 PWMA:接单片机的PWM口 ,控制转速 PWM

    2023年04月22日
    浏览(95)
  • stm32(HAL)库编码器电机pid代码及利用VOFA+对Pid波形显示调参

    PID控制是一种经典的反馈控制算法,它通过不断地调整输出来使系统的实际值与设定值尽量接近,并保持在设定值附近。PID控制器由三个部分组成:比例§、积分(I)和微分(D)。 比例作用(P):比例作用通过测量实际值与设定值之间的偏差,乘以一个比例系数来产生输出。输出

    2024年02月13日
    浏览(62)
  • 使用Simulink代码生成工具基于STM32开发板对永磁同步电机进行开环控制

    代码链接:【免费】使用Simulink代码生成工具对永磁同步电机进行开环控制资源-CSDN文库 本文介绍使用Simulink代码生成功能在STM32开发板平台上运行永磁同步电机。 硬件基础: Nucleo-G431RB开发板 X-NUCLEO-IHM07M1驱动扩展板 57BLDC-24V-210W时代超群直流电机 软件基础: MATLAB 2022b 安装

    2023年04月14日
    浏览(63)
  • 基于正点原子电机实验的pid调试助手代码解析(速度环控制)

    通过前两节文章,我已经了解了基本的pid算法,现在在完成了电机编码测速,pid控制电机转速的前提,我们来解析一下下位机是如何pid调试助手进行数据传递的. 首先用c#写一个PID调试助手,然后拟定好传递数据的通信协议,然后下位机配置好串口,下位机使用串口发送指令给

    2024年02月13日
    浏览(98)
  • PID模块化__以stm32直流电机速度为例

    本篇使用到的基于这个STM32CubeMX 直流电机PID速度控制、HAL库、cubemx、PID、速度控制、增量式 由于上次使用的pid没有模块化,当多出使用pid的时候就会很麻烦 所以这次使用的模块化的 在main.c或者其他位置创建pid的变量 注意一定要在pid计算之前初始化 all_moto_pid_init ,不然会导

    2024年02月14日
    浏览(35)
  • 【STM32】使用HAL库对ULN2003控制28BYJ-48步进电机

    步进电机是将电脉冲信号转变为角位移或线位移,通过控制施加在电机线圈上的电脉冲顺序、频率和数量,可以控制步进电机的转向、速度和旋转角度。 配合以直线运动执行机构(螺纹丝杆)或齿轮箱装置,更可以实现更加复杂、精密的线性运动控制要求。 在非超载的情况下,

    2024年02月16日
    浏览(54)
  • STM32机器人控制开发教程No.4 使用串口通信控制电机(基于HAL库)

    在机器人控制中,单片机(Arduino/STM32)与上位机(Raspberry Pi/NVIDIA Jetson nano)之间的通信经常采用串口通信的方式,那应该如何使用STM32的串口通信以及根据自己定义的协议来完成数据的接收与发送呢?在本篇文章中将给你演示如何通过自定协议来完成对电机的控制以及获取编码

    2023年04月25日
    浏览(57)
  • [FOC-Simulink]使用Simulink代码生成工具基于STM32开发板对永磁同步电机进行开环控制

    代码链接:【免费】使用Simulink代码生成工具对永磁同步电机进行开环控制资源-CSDN文库 本文介绍使用Simulink代码生成功能在STM32开发板平台上运行永磁同步电机。 硬件基础: Nucleo-G431RB开发板 X-NUCLEO-IHM07M1驱动扩展板 57BLDC-24V-210W时代超群直流电机 软件基础: MATLAB 2022b 安装

    2024年02月13日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包