您的位置:首页 > 游戏 > 游戏 > 南宁个人做网站的_怎么查看网站收录_网站优化外包公司_推广公司有哪些公司

南宁个人做网站的_怎么查看网站收录_网站优化外包公司_推广公司有哪些公司

2024/11/15 17:17:37 来源:https://blog.csdn.net/SeniorMao007/article/details/143575781  浏览:    关键词:南宁个人做网站的_怎么查看网站收录_网站优化外包公司_推广公司有哪些公司
南宁个人做网站的_怎么查看网站收录_网站优化外包公司_推广公司有哪些公司

通用定时器 PWM 输出实验

一、什么是开环系统?

在没有引入PID时,我们控制速度的系统是一个开环系统,控制电机的速度一般是通过控制PWM的占空比来控速。

我们需要目标速度,就一步一步的去尝试,速度慢了,就加大占空比,快了,就减小占空比,但是在实际尝试的过程中,我发现,很难人为控制占空比得到一个很精确的速度。

这个开环系统就是,设置占空比–>得到电机转速–>根据电机转速来判断占空比是否应该减小OR增大–>调整占空比

二、什么是PID?

PID:Proportional(比例)、Integral(积分)、Differential(微分)的缩写。

用一句话来说,就是对输入偏差进行积分微分计算,用运算的叠加结果去控制执行机构

听起来很简单吧,一句话就讲完了。

啊😥,要是这么简单就好了。来看看下面的图,就是一个基本的PID控制框图

图片

这就是形成了一个闭环系统,r(t)是输入量,u(t)是输出量

贴一篇链接:

一文读懂PID控制算法(抛弃公式,从原理上真正理解PID控制)

大家可以康康看,好好理解

KP,KI,KD三个参数的作用

KP增加时,响应速度变快,当Ki增加时,能最终趋于目标值,KD增加时,可以减小震荡。

P:比例控制系统快速响应,快速接近于目标值,但是存在静态误差,输出到达不了目标值,会有误差。

I:积分控制系统的准确性,消除累积的误差,输出到达目标值

D:微分控制系统的稳定性,具有超前的控制作用,防止输出超过目标

大家理解这三个参数的作用,这样在调参的时候就会很快,是加大还是减小参数。

三、PID算法的离散化

1、什么是位置式PID?

这方面我就不大写阔论了,好多大佬都比我讲的好,我在这里贴出几个链接,大家可以参考

增量式pid+位置式PID(电机位置闭环控制)

位置式 PID 控制算法和增量式 PID 控制算法

这个大佬的教程也很不错,我也学习了很久,顿悟了很多

而且这个大佬的教程是一系列的,大家可以有选择的学习

PID-电机速度控制-B

电机控制进阶——PID速度控制–CSDN

如果大家不理解位置式PID公式的话,可以首先了解如何使用,如何调参,如何使用PID进行闭环控制,这里以速度为例

大家可以看我下面位置式PID的实现,我直接给出了源代码,大家可以参考

位置式是离散型的PID,大家记住比例Kp,积分Ki,微分Kd这三个参数的作用,再就是位置式的代码化实现

2、位置式PID实现

pid.h

#ifndef __PID_H#define __PID_H   #include "sys.h"
typedef struct  {     float target_val;                 //目标值     float err;                         //偏差值     float err_last;                    //上一个偏差值
     float Kp,Ki,Kd;                    //比例、积分、微分系数
     float integral;                    //积分值   float output_val;          //输出值}PID;
extern PID pid;

‍pid.h​​​​​​​

#include "pid.h"#include "encoder.h"
//位置式    有误差,速度比较慢
PID pid;void PID_param_init(void){    /* 初始化参数 */    pid.err=0.0;    pid.err_last=0.0;    pid.integral=0.0;    //积分项
    pid.Kp=15.0;  //最优      pid.Ki=1.0;    //0.05  0.1     p  15.0  i 0.1   kd 2.5    pid.Kd=1.5;    //调节成功      p  25.0  i 0.03  kd 0.025      
  //kd 4.0 最大     15 1.0 1.5
  pid.target_val = 26;  //目标值  pid.output_val=0.0;    //输出值}//位置式pid   传入实际值即可float PID_realize(float actual_val){  /*计算目标值与实际值的误差*/  pid.err = pid.target_val - actual_val;  //目标值和实际值的误差
  /*积分项*/  pid.integral += pid.err;        //误差累积
  /*PID算法实现*/  pid.output_val = pid.Kp * pid.err +              pid.Ki * pid.integral +              pid.Kd * (pid.err - pid.err_last);  //位置式
  /*误差传递*/  pid.err_last = pid.err;
  /*返回当前实际值*/  return pid.output_val;}

还有一种位置

3、什么是增量式PID?

关于这个增量式PID大家可以参考下面的链接,好好学习,大佬还是讲的很明白的

电机速度环和位置环PID调参教程–B站

【STM32F4系列】【HAL库】电机控制(转速和角度)(PID实战1)_32 hal库将pid坐标转换化为角度_Hz1213825的博客-CSDN博客

4、增量式PID实现

一般使用增量式PI就可以控制住速度了,所以我们采用增量式PI控制速度,闭环速度环,大家可以参考下面的代码,传入目标值和当前值,输出PI运算后的输出

进行PI调参,进而控制电机速度​​​​​​​

// 速度环pi控制  使用增量式/**************************************************************************函数功能:增量PI控制器入口参数:编码器测量值,目标速度返回  值:电机PWM根据增量式离散PID公式out+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]e(k)代表本次偏差e(k-1)代表上一次的偏差  以此类推out代表增量输出在我们的速度控制闭环系统里面,只使用PI控制pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)**************************************************************************/int Incremental_PI(int Encoder, int Target){    float Kp = 10.0, Ki = 1;    static int error, out, err_last;             // 误差  输出  上一次误差    error = Encoder - Target;                    // 求出速度偏差,由测量值减去目标值。    out += Kp * (error - err_last) + Ki * error; // 使用增量 PI 控制器求出电机 PWM。    err_last = error;                            // 保存上一次偏差    return out;                                  // 增量输出}

四、采用VOFA+调试PID

参考我的这一篇文章:如何使用VOFA+?一款好用的上位机软件(VOFA+的三种数据传输协议)——以PID调参为例

大家可以选择firewater协议格式或者justfloat协议格式,这两种协议都可以显示数据的波形,可以一边调参,一边查看曲线,再修改参数,直至达到目标值为止。

firewater协议格式

我做出个示例
使用firewater协议格式,可以是任何类型的数据,但是以逗号隔开,最后必须以\n结尾,这样在上位机中就可以显示出波形了

下方分别代表当前速度,目标速度,输出

printf("%f,%f,%f\n",current,target,out);  //脉冲,目标值,out
justfloat协议格式

如果这种协议不懂得话

大家可以直接使用我编写的库,简单好用

vofa.c​​​

/*要点提示:1. float和unsigned long具有相同的数据结构长度2. union据类型里的数据存放在相同的物理空间*/typedef union{    float fdata;    unsigned long ldata;} FloatLongType;/*将浮点数f转化为4个字节数据存放在byte[4]中*/void Float_to_Byte(float f,unsigned char byte[]){    FloatLongType fl;    fl.fdata=f;    byte[0]=(unsigned char)fl.ldata;    byte[1]=(unsigned char)(fl.ldata>>8);    byte[2]=(unsigned char)(fl.ldata>>16);    byte[3]=(unsigned char)(fl.ldata>>24);}
void JustFloat_Test(void)  //justfloat 数据协议测试{    float a=1,b=2;  //发送的数据 两个通道
  u8 byte[4]={0};    //float转化为4个字节数据  u8 tail[4]={0x00, 0x00, 0x80, 0x7f};  //帧尾
  //向上位机发送两个通道数据  Float_to_Byte(a,byte);  //u1_printf("%f\r\n",a);  u1_SendArray(byte,4);  //1转化为4字节数据 就是  0x00 0x00 0x80 0x3F
  Float_to_Byte(b,byte);  u1_SendArray(byte,4);  //2转换为4字节数据 就是  0x00 0x00 0x00 0x40 
  //发送帧尾  u1_SendArray(tail,4);  //帧尾为 0x00 0x00 0x80 0x7f
}//向vofa发送数据  三个数据  三个通道  可视化显示  帧尾void vofa_sendData(float a,float b,float c){    //float a=1,b=2;  //发送的数据 两个通道
    u8 byte[4]= {0};    //float转化为4个字节数据    u8 tail[4]= {0x00, 0x00, 0x80, 0x7f};  //帧尾
    //向上位机发送两个通道数据    Float_to_Byte(a,byte);    //u1_printf("%f\r\n",a);    u1_SendArray(byte,4);  //1转化为4字节数据 就是  0x00 0x00 0x80 0x3F
    Float_to_Byte(b,byte);    u1_SendArray(byte,4);  //2转换为4字节数据 就是  0x00 0x00 0x00 0x40
    Float_to_Byte(c,byte);    u1_SendArray(byte,4);
    //发送帧尾    u1_SendArray(tail,4);  //帧尾为 0x00 0x00 0x80 0x7f}

使用

void vofa_sendData(float a,float b,float c)    //a,b,c代表三个通道波形

五、目前

我要实现的是速度环和位置闭环,我现在首先目标是单环控制,首先速度环,然后位置环

速度环采用增量式PI控制,位置环采用位置式PID控制

速度环没问题(在这里无论是位置式还是增量式都实现了进行速度控制,这里建议增量式PI)

但是目前位置式PID控制位置环出现了点问题,当我调参的时候,无论目标速度调成多大,电机的转速总是会趋近于最大转速,目前这个问题还未解决,暂定

大家可以参考我的代码,采用VOFA+上位机显示调参波形,根据波形进行调参,希望可以帮助到大家。

我只贴出主要控制代码​​​​​​​

/* * @Author: _oufen * @Date: 2023-03-31 18:23:31 * @LastEditTime: 2023-04-01 19:34:31 * @Description: */#include "timer4.h"#include "led.h"#include "encoder.h"#include "motor.h"#include "vofa.h"#include "usart.h"
// int encoder_speed;     // 实际速度  近似脉冲// int target_speed = 30; // 目标速度  每10ms 30个脉冲// int Moto1;             // 轮子输出值
int encoder_position;int target_position = 13;int Moto1;
void Timer4_Init(u16 psc, u16 arr) // Timer4_Init(7200-1,1000-1);{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;    NVIC_InitTypeDef NVIC_InitStructure;    // TIM_OCInitTypeDef  TIM_OCInitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    TIM_DeInit(TIM4); // 定时器4恢复默认设置
    //  MY_GPIO_Init(GPIOB,GPIO_Pin_6,GPIO_Mode_AF_PP);    //  MY_GPIO_Init(GPIOB,GPIO_Pin_7,GPIO_Mode_AF_PP);    //  MY_GPIO_Init(GPIOB,GPIO_Pin_8,GPIO_Mode_AF_PP);    //  MY_GPIO_Init(GPIOB,GPIO_Pin_9,GPIO_Mode_AF_PP);
    TIM_TimeBaseStructure.TIM_Period = arr;    TIM_TimeBaseStructure.TIM_Prescaler = psc;    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_Init(&NVIC_InitStructure);
    TIM_ClearFlag(TIM4, TIM_FLAG_Update);    TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // 清除更新中断请求位            TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM4, ENABLE);}
// 向vofa发送数据  三个数据  三个通道  可视化显示  帧尾void vofa_sendData(float a, float b, float c){    u8 byte[4] = {0};                      // float转化为4个字节数据    u8 tail[4] = {0x00, 0x00, 0x80, 0x7f}; // 帧尾
    // 向上位机发送两个通道数据    Float_to_Byte(a, byte);    // u1_printf("%f\r\n",a);    u1_SendArray(byte, 4); // 1转化为4字节数据 就是  0x00 0x00 0x80 0x3F
    Float_to_Byte(b, byte);    u1_SendArray(byte, 4); // 2转换为4字节数据 就是  0x00 0x00 0x00 0x40
    Float_to_Byte(c, byte);    u1_SendArray(byte, 4);
    // 发送帧尾    u1_SendArray(tail, 4); // 帧尾为 0x00 0x00 0x80 0x7f}
// 速度环pi控制  使用增量式/**************************************************************************函数功能:增量PI控制器入口参数:编码器测量值,目标速度返回  值:电机PWM根据增量式离散PID公式out+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]e(k)代表本次偏差e(k-1)代表上一次的偏差  以此类推out代表增量输出在我们的速度控制闭环系统里面,只使用PI控制pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)**************************************************************************/int Incremental_PI(int Encoder, int Target){    float Kp = 10.0, Ki = 1;    static int error, out, err_last;             // 误差  输出  上一次误差    error = Encoder - Target;                    // 求出速度偏差,由测量值减去目标值。    out += Kp * (error - err_last) + Ki * error; // 使用增量 PI 控制器求出电机 PWM。    err_last = error;                            // 保存上一次偏差    return out;                                  // 增量输出}
/**************************************************************************函数功能:位置式PID控制器入口参数:编码器测量位置信息,目标位置返回  值:电机PWM根据位置式离散PID公式out=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]e(k)代表本次偏差e(k-1)代表上一次的偏差∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,,k;out代表输出**************************************************************************/int Position_PID(int Encoder, int Target){    float Position_KP = 15, Position_KI = 0.1, Position_KD = 0.1;                                       // pid    static float error, out, Integral_error, error_last;                                           // 误差 输出 积分 上一次误差    error = Encoder - Target;                                                                      // 求出速度偏差,由测量值减去目标值。    Integral_error += error;                                                                       // 求出偏差的积分    out = Position_KP * error + Position_KI * Integral_error + Position_KD * (error - error_last); // 位置式PID控制器    error_last = error;                                                                            // 保存上一次偏差    return out;                                                                                    // 增量输出}int myabs(int a){    int temp;    if (a < 0)        temp = -a;    else        temp = a;    return temp;}
void Set_pwm(int pwm){    if (pwm > 0)        AIN1 = 0, AIN2 = 1;    else        AIN1 = 1, AIN2 = 0;
    PWMA = myabs(pwm);  //PWMA --> TIM1->CCR1}void Xianfu_Pwm(void){    int Amplitude = 99;     if (Moto1 < -Amplitude)        Moto1 = -Amplitude;    if (Moto1 > Amplitude)        Moto1 = Amplitude;}
// 定时器定时调用/*void AutoReloadCallback(){    encoder_speed += Read_Encoder(2);                    // 读取真实速度                                                               //printf("Encoder = %d\r\n", encoder_speed);    Moto1 = Incremental_PI(encoder_speed, target_speed); // PID计算    Xianfu_Pwm();                                        // 对输出进行限幅    // printf("Moto1 = %d\r\n", PWMA);    Set_pwm(Moto1);
    vofa_sendData(encoder_speed, target_speed, PWMA); // 向上位机发送数据}*/
void AutoReloadCallback(){    encoder_position = Read_Encoder(2); // 读取真实速度    //printf("Encoder = %d\r\n", encoder_position);    Moto1 = Position_PID(encoder_position, target_position); // PID计算    Xianfu_Pwm();                                            // 对输出进行限幅    // printf("Moto1 = %d\r\n", PWMA);    Set_pwm(Moto1);    vofa_sendData(encoder_position, target_position, PWMA); // 向上位机发送数据}
void TIM4_IRQHandler(void) // 10ms{    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)    {        AutoReloadCallback(); // 定时调用        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);        LED1 = !LED1;    }}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com