搜索
bottom↓
回复: 0

《DMF407电机控制专题教程_V1.0》 第13章

[复制链接]

出0入234汤圆

发表于 2022-8-16 14:51:47 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-8-16 18:24 编辑

1)实验平台:正点原子DMF407电机开发板
2)平台购买地址: https://detail.tmall.com/item.htm?&id=677230699323
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/stm32dj/ATK-DMF407.html
4)对正点原子电机开发板感兴趣的同学可以加群讨论: 592929122 lQLPJxaFi2zaB4UWWrDAMgIsFEW2pwLb3abnwDMA_90_22.png
lQDPJxaFi2nfFizMjM0CbLCPlxn_FVheIQLb3aGrwFQA_620_140.jpg

lQLPJxaFi2nfFhLMkM0BXrDNvOUyeU_FPgLb3aGvQNIA_350_144.png


第13章 直流有刷电机三环控制实现


本章我们主要来学习直流有刷电机三环(电流环+速度环+位置环)PID控制的原理,并实现三环PID控制的实验。
本章分为如下几个小节:
13.1 三环PID控制原理
13.2 硬件设计
13.3 程序设计
13.4 下载验证


13.1 三环PID控制原理

三环PID控制就是将三个PID控制系统(例如:电流环、速度环以及位置环)串联起来,然后对前一个系统(外环)的输出进行偏差的计算,计算结果作为后一个系统(内环)的输入,具体的控制流程如图13.1.1所示:
image002.jpg
图13.1.1三环PID控制流程

图13.1.1中,电机位置是优先控制的对象,三环PID控制的流程如下:
① 首先设置目标位置,系统会计算出位置偏差,然后将偏差输入到位置环(最外环);
② 位置环的输出和实际速度进行偏差的计算,计算后的偏差输入到速度环(次外环);
③ 速度环的输出和实际电流进行偏差的计算,计算后的偏差输入到电流环(内环),电流环的输出用于控制PWM的占空比,进而控制电机的位置。
注意:多环PID控制系统的原理是相通的,大家掌握了三环PID控制的内容,其他的多环控制系统就可以迎刃而解了。
13.2 硬件设计
1. 例程功能
1、本实验以电机开发板的直流有刷电机驱动接口1为例,融合速度环、电流环以及位置环PID控制实验,加入三环PID控制算法,对电流、速度以及电机位置进行闭环控制。
2、当按键0按下,就增大目标位置值,电机正转到目标位置;当按键1按下,就减小目标位置值,电机反转到目标位置;当按键2按下,电机回到初始位置。
3、屏幕显示按键功能、占空比、目标位置以及实际位置。
4、串口1和上位机进行数据通信。
5、LED0闪烁指示程序运行。
2. 硬件资源
1)LED灯
LED0 – PE0
2)独立按键
KEY0 – PE2
KEY1 – PE3
KEY2 – PE4
3)定时器1、3、6
TIM1正常输出通道 PA8
    TIM1互补输出通道 PB13
        TIM3 编码器A相输入通道 PC6
TIM3 编码器B相输入通道 PC7
4)ADC
ADC1通道8  PB0(电流)
5)SD(刹车)信号输出 PF10
6)串口1
        USART1_TX  PB6(发送)
    USART1_RX  PB7(接收)
3. 原理图
image004.jpg
图13.2.1 直流有刷电机接口原理图

        图13.2.1就是我们DMF407电机开发板的直流有刷电机接口1原理图,本实验我们除了用到基础驱动所需的引脚,还需要用到PM1_ENCA(PC6)、PM1_ENCB(PC7)和PM1_AMPU(PB0)这3个引脚。PC6、PC7分别用于连接编码器的A、B两相,PB0用于检测电流采集电路对应的输出电压。
本实验的硬件接线部分和上一章节一致,这里不再赘述。
13.3 程序设计
本实验所用到的基础驱动、编码器测速以及电流采集的代码在前面实验都有介绍过了。我们在程序解析中只讲解三环PID控制相关的函数,下面介绍一下三环PID控制的配置步骤。
三环PID控制的配置步骤
1)配置相关定时器
配置基础驱动、编码器测速相关的定时器,实现基础驱动以及编码器测速功能。
2)配置相关ADC
        配置电流采集相关ADC,实现电流采集功能。
4)初始化串口1
初始化串口1,开启串口接收中断,串口1在PID控制中用于上位机通信。
注意:在PID控制的代码中,串口1仅用于PID数据上传,尽量不要输出其他信息,否则有可能影响PID数据。
5)定义PID参数结构体变量
为了方便管理PID相关的控制量,我们需要定义3个PID参数结构体变量,方法如下:
PID_TypeDef  g_location_pid;        /* 位置环PID参数结构体 */
PID_TypeDef  g_speed_pid;                /* 速度环PID参数结构体 */
PID_TypeDef  g_current_pid;                /* 电流环PID参数结构体 */
6)初始化PID参数
把三环PID控制系统的目标值、期望输出值、累计偏差等清零,然后分别配置各个环的PID系数。
7)初始化上位机调试
调用debug_init函数初始化所需内存,为上位机的调试做准备。
8)编写中断服务函数
在定时器6的更新中断回调函数里面进行三环PID计算,计算后的结果用于控制PWM的占空比。
13.3.1 程序流程图
image006.png
图13.3.1.1 三环PID控制程序流程图

13.3.2 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。首先我们看三环PID控制的相关源码,其包括两个文件:pid.c和pid.h。
首先看pid.h头文件的几个宏定义:
/* PID相关参数 */

#define  INCR_LOCT_SELECT  0       /* 0:位置式,1:增量式 */

#if INCR_LOCT_SELECT

/* 定义位置环PID参数相关宏 */
#define  L_KP      0.06f             /* P参数 */
#define  L_KI      0.00f             /* I参数 */
#define  L_KD      0.01f             /* D参数 */

/* 定义速度环PID参数相关宏 */
#define  S_KP      5.00f             /* P参数 */
#define  S_KI      0.30f             /* I参数 */
#define  S_KD      0.01f             /* D参数 */

/* 定义电流环PID参数相关宏 */
#define  C_KP      8.00f             /* P参数 */
#define  C_KI      4.00f             /* I参数 */
#define  C_KD      1.00f             /* D参数 */
#define  SMAPLSE_PID_SPEED  50     /* 采样周期 单位ms */

#else

/* 定义位置环PID参数相关宏 */
#define  L_KP      0.06f             /* P参数 */
#define  L_KI      0.00f             /* I参数 */
#define  L_KD      0.01f             /* D参数 */

/* 定义速度环PID参数相关宏 */
#define  S_KP      5.00f             /* P参数 */
#define  S_KI      0.30f             /* I参数 */
#define  S_KD      0.01f             /* D参数 */

/* 定义电流环PID参数相关宏 */
#define  C_KP      8.00f             /* P参数 */
#define  C_KI      4.00f             /* I参数 */
#define  C_KD      1.00f             /* D参数 */
#define  SMAPLSE_PID_SPEED  50     /* 采样周期 单位ms */

#endif

/*PID结构体*/
typedef struct
{
    __IO float  SetPoint;                   /* 目标值 */
    __IO float  ActualValue;                   /* 期望输出值 */
    __IO float  SumError;                   /* 误差累计 */
    __IO float  Proportion;                   /* 比例常数 P */
    __IO float  Integral;                   /* 积分常数 I */
    __IO float  Derivative;                   /* 微分常数 D */
    __IO float  Error;               /* Error[1] */
    __IO float  LastError;                   /* Error[-1] */
    __IO float  PrevError;                   /* Error[-2] */
} PID_TypeDef;

extern PID_TypeDef  g_location_pid;         /* 位置环PID参数结构体 */
extern PID_TypeDef  g_speed_pid;         /* 速度环PID参数结构体 */
extern PID_TypeDef  g_current_pid;         /* 电流环PID参数结构体 */
可以把上面的宏定义分成两部分,第一部分是PID计算方式以及PID系数的宏定义,我们可以通过改变INCR_LOCT_SELECT这个宏的值来选择相应的PID计算方式,第二部分则是PID参数相关的结构体,这个结构体用于管理PID控制所需要的控制量,本实验中我们定义了位置环、速度环以及电流环PID参数的结构体变量,它们分别是g_ location _pid、g_speed_pid、g_current_pid。
下面看pid.c的程序,我们先看PID初始化函数,其定义如下:
/**
* @brief       pid初始化
* @param       无
* @retval      无
*/
void pid_init(void)
{
    /* 初始化位置环PID参数 */
    g_location_pid.SetPoint = 0.0;                /* 目标值 */
    g_location_pid.ActualValue = 0.0;        /* 期望输出值 */
    g_location_pid.SumError = 0.0;                /* 积分值*/
    g_location_pid.Error = 0.0;                        /* Error[1]*/
    g_location_pid.LastError = 0.0;                /* Error[-1]*/
    g_location_pid.PrevError = 0.0;                /* Error[-2]*/
    g_location_pid.Proportion = L_KP;   /* 比例常数 Proportional Const */
    g_location_pid.Integral = L_KI;                /* 积分常数 Integral Const */
    g_location_pid.Derivative = L_KD;   /* 微分常数 Derivative Const */

    /* 初始化速度环PID参数 */
    g_speed_pid.SetPoint = 0.0;                        /* 目标值 */
    g_speed_pid.ActualValue = 0.0;                /* 期望输出值 */
    g_speed_pid.SumError = 0.0;                        /* 积分值 */
    g_speed_pid.Error = 0.0;                        /* Error[1] */
    g_speed_pid.LastError = 0.0;                /* Error[-1] */
    g_speed_pid.PrevError = 0.0;                /* Error[-2] */
    g_speed_pid.Proportion = S_KP;                /* 比例常数 Proportional Const */
    g_speed_pid.Integral = S_KI;                /* 积分常数 Integral Const */
    g_speed_pid.Derivative = S_KD;                /* 微分常数 Derivative Const */

    /* 初始化电流环PID参数 */
    g_current_pid.SetPoint = 0.0;                /* 目标值 */
    g_current_pid.ActualValue = 0.0;        /* 期望输出值 */
    g_current_pid.SumError = 0.0;                /* 积分值*/
    g_current_pid.Error = 0.0;                        /* Error[1]*/
    g_current_pid.LastError = 0.0;                /* Error[-1]*/
    g_current_pid.PrevError = 0.0;                /* Error[-2]*/
    g_current_pid.Proportion = C_KP;        /* 比例常数 Proportional Const */
    g_current_pid.Integral = C_KI;                /* 积分常数 Integral Const */
    g_current_pid.Derivative = C_KD;        /* 微分常数 Derivative Const */
}
        该函数主要是将三环PID控制系统的目标值、期望输出值、累计偏差等清零,然后配置PID系数。
        接下来我们看积分限幅函数,其定义如下:
/**
* @brief       积分限幅
* @param       *PID:PID结构体变量地址
* @param       max_limit:最大值
* @param       min_limit:最小值
* @retval      无
*/
void integral_limit( PID_TypeDef *PID , float max_limit, float min_limit )
{
    if (PID->SumError >= max_limit)                            /* 超过限幅 */
    {
        PID->SumError = max_limit;                              /* 限制积分 */
    }
    else if (PID->SumError <= min_limit)                      /* 超过限幅 */
    {
        PID->SumError = min_limit;
    }
}
        此函数可以对位置式PID的积分进行限幅,避免出现积分深度饱和的问题。大家在使用该函数之前,需要先调整好PID系数,再根据系统能达到的最大累计偏差来设置限幅。
最后要介绍的是定时器的更新中断回调函数,它在dcmotor_time.c中实现,具体的定义如下:
/**
* @brief       定时器更新中断回调函数
* @param        htim:定时器句柄指针
* @note        此函数会被定时器中断函数共同调用的
* @retval      无
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     static uint8_t val = 0;
   
        /* 定时器3相关程序 */
     if (htim->Instance == TIM3)
     {
           /* 判断CR1的DIR位 */
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   
        {
            g_timx_encode_count--;                /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                /* DIR位为0,也就是递增计数 */
        }
     }

        /* 定时器6相关程序 */
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();     /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);           /* 5ms计算一次速度 */
         
        if (val % SMAPLSE_PID_SPEED == 0)       /* 进行一次pid计算 */
        {
            if (g_run_flag)                         /* 判断电机是否启动了 */
            {
                          /* 获取当前编码器总计数值,用于位置闭环控制 */
                g_motor_data.location = (float)Encode_now;              
               
                integral_limit(&g_location_pid , 1000 ,-1000); /* 位置环积分限幅 */
                integral_limit(&g_speed_pid , 200 ,-200);       /* 速度环积分限幅 */
                integral_limit(&g_current_pid , 150 ,-150);     /* 电流环积分限幅 */
               
/* 设置闭环死区,避免小幅度位置抖动 */
                if((g_location_pid.Error <= 20)&&(g_location_pid.Error >= -20))                     
                {
                    g_location_pid.Error = 0;                 /* 偏差太小了,直接清零 */
                    g_location_pid.SumError = 0;         /* 清除积分 */
                }
               
                g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid,
g_motor_data.location);      /* 位置环PID控制(最外环) */
               
                if (g_motor_data.motor_pwm >= 120) /* 限制外环输出(目标速度) */
                {
                    g_motor_data.motor_pwm = 120;
                }
                else if (g_motor_data.motor_pwm <= -120)
                {
                    g_motor_data.motor_pwm = -120;
                }
                /* 设置目标速度,外环输出作为内环输入 */
                g_speed_pid.SetPoint = g_motor_data.motor_pwm;   
      
                g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid,
g_motor_data.speed);         /* 速度环PID控制(次外环) */
               
                if ( g_motor_data.motor_pwm > 0)   /* 判断速度环输出值是否为正数 */
                {
                    dcmotor_dir(0);                    /* 输出为正数,设置电机正转 */
                }
                else
                {
                                 /* 输出取反 */
                    g_motor_data.motor_pwm = -g_motor_data.motor_pwm;   
                    dcmotor_dir(1);                    /* 设置电机反转 */
                }
                    
                if (g_motor_data.motor_pwm >= 100) /* 限制外环输出(目标电流) */
                {
                    g_motor_data.motor_pwm = 100;
                }
                          /* 设置目标电流,外环输出作为内环输入 */
                g_current_pid.SetPoint = g_motor_data.motor_pwm;        

                g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid,
g_motor_data.current);                /* 电流环PID控制(内环) */
               
                if (g_motor_data.motor_pwm >= 8200)        /* 限制占空比 */
                {
                    g_motor_data.motor_pwm = 8200;
                }
                else if (g_motor_data.motor_pwm <= 0) /* 滤掉无效输出 */
                {
                    g_motor_data.motor_pwm = 0;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                          /* 选择通道1,发送实际位置(波形显示)*/
                debug_send_wave_data( 1 ,g_motor_data.location);  
                          /* 选择通道2,发送目标位置(波形显示)*/                                      
                debug_send_wave_data( 2 ,g_location_pid.SetPoint);                                      
#endif
                dcmotor_speed(g_motor_data.motor_pwm); /* 设置占空比 */
            }
            val = 0;
        }
        val ++;
     }
}
        该函数我们只介绍定时器6相关的程序,进入更新中断回调函数后,所执行的代码逻辑如下:
第一步,判断是不是定时器6的寄存器基地址,如果是则获取编码器的计数总值并存入变量Encode_now中,接着计算电机速度。
第二步,每隔50ms进行一次PID计算,在计算PID之前,需要判断g_run_flag是否为1,如果是则说明电机已经启动,可以开始PID计算。
        第三步,把变量Encode_now的值存入g_motor_data.location这个成员中,然后对三环的积分进行限幅并设置闭环死区。当系统偏差值进入死区范围之内,我们直接让偏差等于0,并清除累计偏差,此时PID系统不再参与控制,这样可以避免小幅度偏差带来的位置抖动。
第四步,进行位置环PID计算并限制其输出,然后将位置环的输出作为速度环的目标值;速度环PID计算完后,我们先判断其输出,如果输出是正数,则设置电机正转,如果输出是负数,则将输出取反,设置电机反转。因为电流环PID的目标值和输出都是正数,我们无法通过电流环的输出来确定电机的转向,所以这里需要通过速度环的输出来确定电机的转向,并保证电流环的目标值是正数。
        第五步,将速度环的输出作为电流环的目标值,然后进行电流环PID计算并限制其输出。
第六步,发送实际位置、目标位置的波形数据到上位机,最后设置PWM的占空比,进而控制电机的位置。
在main.c里面编写如下代码:
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;

    HAL_Init();                                                                /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);                /* 设置时钟,168Mhz */
    delay_init(168);                                                        /* 延时初始化 */
    usart_init(115200);                                                /* 串口1初始化,用于上位机调试 */
    led_init();                                                                /* 初始化LED */
    lcd_init();                                                                /* 初始化LCD */
    key_init();                                                                /* 初始化按键 */
    pid_init();                                                                /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);        /* 168Mhz的计数频率 */
    dcmotor_init();                                                        /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);        /* 编码器定时器初始化 */
    btim_timx_int_init(1000 - 1 , 84 - 1);  /* 基本定时器初始化,1ms计数周期 */
    adc_nch_dma_init();
   
#if DEBUG_ENABLE                                                         /* 开启调试 */
   
    debug_init();                                                        /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);                        /* 上传电机类型(直流有刷电机) */
    debug_send_motorstate(IDLE_STATE);                /* 上传电机状态(空闲) */
   
    /* 同步数据PID参数到上位机 ,无论同步哪一组数据,目标值地址只能是外环PID的 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint),
L_KP, L_KI, L_KD);  /* 位置环PID参数(PID1)*/
    debug_send_initdata(TYPE_PID2, (float *)(&g_location_pid.SetPoint),
S_KP, S_KI, S_KD);  /* 速度环PID参数(PID2)*/
    debug_send_initdata(TYPE_PID3, (float *)(&g_location_pid.SetPoint),
C_KP, C_KI, C_KD);  /* 电流环PID参数(PID3)*/

#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);

    while (1)
    {
        key = key_scan(0);                                                        /* 按键扫描 */
        if(key == KEY0_PRES)                                                        /* 当key0按下 */
        {
            g_run_flag = 1;                                                        /* 标记电机启动 */
            dcmotor_start();                                                        /* 开启电机 */
                   /* 正转一圈,电机旋转圈数 = 编码器总计数值 / 44 / 30 */
            g_location_pid.SetPoint += 1320;                        
            
            if (g_location_pid.SetPoint >= 6600)                /* 限制电机位置 */
            {
                g_location_pid.SetPoint = 6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);                /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY1_PRES)                                        /* 当key1按下 */
        {
            g_run_flag = 1;                                                        /* 标记电机启动 */
            dcmotor_start();                                                        /* 开启电机 */
            g_location_pid.SetPoint -= 1320;                        /* 反转一圈 */
            
            if (g_location_pid.SetPoint <= -6600)        /* 限制电机位置 */
            {
                g_location_pid.SetPoint = -6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);                /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY2_PRES)                                        /* 当key2按下 */
        {
            g_location_pid.SetPoint = 0;                                /* 恢复初始位置 */
        }
        
#if DEBUG_ENABLE
        
        /* 接收PID助手设置的位置环PID参数 */
        debug_receive_pid(TYPE_PID1,(float *)&g_location_pid.Proportion,
(float *)&g_location_pid.Integral,(float *)&g_location_pid.Derivative);

/* 接收PID助手设置的速度环PID参数 */
        debug_receive_pid(TYPE_PID2,(float *)&g_speed_pid.Proportion,
(float *)&g_speed_pid.Integral,(float *)&g_speed_pid.Derivative);

/* 接收PID助手设置的电流环PID参数 */
        debug_receive_pid(TYPE_PID3,(float *)&g_current_pid.Proportion,
(float *)&g_current_pid.Integral,(float *)&g_current_pid.Derivative);

        debug_set_point_range(6600, -6600, 6600);        /* 设置目标调节范围 */
        
        debug_cmd = debug_receive_ctrl_code();                /* 读取上位机指令 */

        if (debug_cmd == HALT_CODE)                                        /* 电机停机 */
        {
            g_location_pid.SetPoint = 0;                                /* 恢复初始位置 */
        }
        else if (debug_cmd == RUN_CODE)                                /* 电机运行 */
        {
            g_run_flag = 1;                                                        /* 标记电机启动 */
            dcmotor_start();                                                        /* 开启电机 */
            g_location_pid.SetPoint = 1320;                        /* 设置目标位置 */
            debug_send_motorstate(RUN_STATE);                /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            lcd_dis();
            LED0_TOGGLE();                                                                /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
            debug_send_speed(g_motor_data.speed);                /* 发送速度 */
            g_debug.encode_p = g_motor_data.location;        /* 传入编码器当前总计数值 */
            debug_upload_data(&g_debug, TYPE_HAL_ENC);        /* 发送编码器当前总计数值 */
#endif
        }
        delay_ms(10);
    }
}
main.c的代码逻辑如下:
第一步,初始化相关的外设,例如定时器、串口以及ADC等。
第二步,初始化PID参数、上位机调试,它们分别调用的是pid_init和debug_init函数。
第三步,同步电机的状态(空闲)、类型(直流有刷电机)、PID参数到上位机。需要注意的是,在多环控制中,只有最外环的目标值可以手动设置,因此,无论同步哪一组PID参数,目标值地址只能是最外环PID的(本实验最外环为位置环)。
第四步,在while循环里面检测按键是否按下,如果key0按下,则目标位置增加1320,也就是目标计数值增加1320,根据公式:电机旋转圈数 = 目标计数值变化量 / 44 / 30,电机将会正转一圈;如果key1按下,则目标位置减小1320,电机将会反转一圈,然后上传电机状态(运行);如果key2按下,电机将回到初始位置。
第五步,接收上位机下发的PID参数、设置目标位置的调节范围。
第六步,调用debug_receive_ctrl_code函数,接收上位机下发的命令。如果上位机的命令为HALT_CODE(停机),则让电机回到初始位置;如果上位机的命令是RUN_CODE,则开启电机,设置目标位置为1320,上传电机状态(运行)到上位机。
第七步,每隔200ms更新一次数据到屏幕,然后调用debug_upload_data函数发送编码器位置值(计数总值)到上位机。
注意:我们可以通过串口来打印系统各环的累计偏差,以此确定各个环的积分限幅范围,切记不能用串口1打印,否则可能会影响PID数据。
根据经验,在只有电流环或者速度环组成的系统中,限幅的范围需要比累计偏差的最大值稍大一点,否则系统可能存在静态误差;在纯位置环或者最外环是位置环的系统中,限幅的范围可以比累计偏差最大值要小,大家可以去实际感受一下。需要注意的是,不同的PID系数对累计偏差的影响可能很大,因此,我们在重新调整PID系数之后,需要查看积分限幅范围是否合理。
13.4 下载验证
下载代码后,可以看到LED0在闪烁,说明程序已经正常在跑了,LCD上显示按键功能、占空比、位置、速度以及电流信息,当我们按下KEY0,目标位置将增大,电机正转;按下KEY1,目标位置将减小,电机反转;按下KEY2,电机将回到初始位置。我们再打开PID调试助手,选择对应的串口端口,我这边是COM7,接着选择通道1和2,点击“开始”按钮,即可开始显示波形,如下图13.4.1所示:
image008.jpg
图13.4.1 三环PID控制波形

        图13.4.1中,橙线代表目标位置,红线代表实际位置,当我们按下KEY0,目标位置增大,橙线先发生变化,而红线(实际位置)会逐渐靠近橙线(目标位置);按下KEY1,目标速度将减小,曲线的变化同理;按下KEY2,电机将回到初始位置,目标位置将为0。
        注意:1、本实验需要使用USB数据线连接开发板的串口1到电脑,并启动电机之后才会有波形变化;2、如果发现波形不对,请检查电机的M+和M-接线;3、PID系数并不是通用的,如果PID曲线不理想,大家需要根据自己的实际系统去调节。 

阿莫论坛20周年了!感谢大家的支持与爱护!!

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-10-2 19:13

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表