228|1

258

帖子

0

TA的资源

纯净的硅(初级)

[航芯高性能MCU系列:ACM32F403开发板]6.光电编码器实现步进电机闭环系统(位置环) [复制链接]

本帖最后由 xld0932 于 2025-4-16 11:56 编辑

1.前言

    通过学习正点原子实现步进电机进行加减速控制,实现对滑台的运动控制,使用光电编码器实现步进电机闭环系统(位置环)

 

2.步进电机闭环系统组成(《摘录于DMF407电机控制专题教程》)

    同直流有刷电机的闭环系统类似,步进电机的闭环系统的组成由: 步进电机+编码器构成,编码器反馈步进电机的实际旋转位置给控制器,这样就可以控制器知晓是否有按要求到达指定目标位置,当有偏差就可及时纠偏了。接着来看总结的一些闭环系统与开环系统之间的优缺点:

14.png

 

3.步进电机闭环系统原理(《摘录于DMF407电机控制专题教程》)

15.png

    上图为步进电机闭环系统的整体控制流程,其中编码器作为反馈通道, 反馈步进电机的实际位置, 系统将实际位置与目标位置进行比较, 计算差值,再把偏差值代入到 PID 控制器中,之后控制器输出期望值,最后作用于步进电机使其旋转至指定位置停止!这就是位置闭环控制。

 

4.步进电机位置闭环控制实现(增量式/位置式)

4.1.步进电机初始化

void TIM_OC1PreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload)
{
    uint16_t tmpccmr1 = 0;

    tmpccmr1  = TIMx->CCMR1;

    tmpccmr1 &= (uint16_t) ~((uint16_t)0x0008);

    tmpccmr1 |= TIM_OCPreload;

    TIMx->CCMR1 = tmpccmr1;
}

void stepper_init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    System_Module_Enable(EN_GPIOCD);

    /* Direction */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.Pin       = GPIO_PIN_9;
    GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull      = GPIO_PULLUP;
    GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);

    /* Limit Switch */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.Pin       = GPIO_PIN_10 | GPIO_PIN_11;
    GPIO_InitStruct.Mode      = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull      = GPIO_PULLUP;
    GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    TIM_Base_InitTypeDef TIM_Base_InitStruct;
    TIM_OC_InitTypeDef   TIM_OC_InitStruct;
    uint32_t             TIM_Clock;

    System_Module_Enable(EN_GPIOCD);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.Pin       = GPIO_PIN_8;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Alternate = GPIO_FUNCTION_2;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    System_Module_Enable(EN_TIM1);

    if (System_Get_SystemClock() == System_Get_APBClock())
    {
        TIM_Clock = System_Get_APBClock();
    }
    else
    {
        TIM_Clock = System_Get_APBClock() * 2;
    }

    TIM_Base_InitStruct.Prescaler         = psc;
    TIM_Base_InitStruct.Period            = arr;
    TIM_Base_InitStruct.RepetitionCounter = 0;
    TIM_Base_InitStruct.CounterMode       = TIM_COUNTERMODE_UP;
    TIM_Base_InitStruct.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
    TIM_TimeBase_Init(TIM1, &TIM_Base_InitStruct);

    TIM_OC_InitStruct.OCMode       = OUTPUT_MODE_MATCH_TOGGLE;
    TIM_OC_InitStruct.Pulse        = 0;
    TIM_OC_InitStruct.OCPolarity   = OUTPUT_POL_ACTIVE_HIGH;
    TIM_OC_InitStruct.OCNPolarity  = OUTPUT_POL_ACTIVE_HIGH;
    TIM_OC_InitStruct.OCFastMode   = OUTPUT_FAST_MODE_DISABLE;
    TIM_OC_InitStruct.OCIdleState  = OUTPUT_IDLE_STATE_0;
    TIM_OC_InitStruct.OCNIdleState = OUTPUT_IDLE_STATE_0;
    TIM_OC1Init(TIM1, &TIM_OC_InitStruct);

    TIM_OC1PreloadConfig(TIM1, 0);

    TIM_Cmd(TIM1, ENABLE);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);
}

 

4.2.编码器初始化

void bsp_EncoderInit(void)
{
    GPIO_InitTypeDef     GPIO_InitStruct;
    TIM_Base_InitTypeDef TIM_Base_InitStruct;
    TIM_IC_InitTypeDef   TIM_IC_InitStruct;

    System_Module_Enable(EN_GPIOAB);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.Pin       = GPIO_PIN_0 | GPIO_PIN_1;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Alternate = GPIO_FUNCTION_3;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    System_Module_Enable(EN_TIM2);

    TIM_Base_InitStruct.Prescaler         = 0;
    TIM_Base_InitStruct.Period            = 0xFFFFFFFF;
    TIM_Base_InitStruct.RepetitionCounter = 0;
    TIM_Base_InitStruct.CounterMode       = TIM_COUNTERMODE_UP;
    TIM_Base_InitStruct.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
    TIM_TimeBase_Init(TIM2, &TIM_Base_InitStruct);

    TIM_SelectSlaveMode(TIM2, TIM_SLAVE_MODE_ENC1);
    TIM_TI1FP1_ConfigInputStage(TIM2, TIM_SLAVE_CAPTURE_ACTIVE_FALLING, TIM_TI1_FILTER_LVL(6));

    TIM_IC_InitStruct.ICPolarity  = TIM_SLAVE_CAPTURE_ACTIVE_FALLING;
    TIM_IC_InitStruct.ICSelection = TIM_ICSELECTION_DIRECTTI;
    TIM_IC_InitStruct.ICPrescaler = TIM_IC1_PRESCALER_1;
    TIM_IC_InitStruct.TIFilter    = TIM_TI1_FILTER_LVL(6);
    TIM_ICInit(TIM2, &TIM_IC_InitStruct, TIM_CHANNEL_1);

    TIM2->CNT = 0;

    TIM_Cmd(TIM2, ENABLE);
}

 

4.3.步进电机及编码器参数定义

#ifndef __STEPPER_H
#define __STEPPER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "fxx_std.h"

enum STEPPER_DIRECTION
{
    CCW = 0,                           /* 逆时针 */
    CW  = 1                            /* 顺时针 */
};

#define PULSE_REV       400                                 /* 每圈脉冲数 */
#define TIM_FREQ        180000000U                          /* 定时器主频 */
#define T1_FREQ         (TIM_FREQ/180)                      /* 频率ft值 */

#define FREQ_UINT       (T1_FREQ/(SECOND/SAMPLING_PERIOD))  /* 对定时器的频率做单位换算,避免数值太大溢出 */
#define ENCODER_SPR     (float)(600*4)                      /* 编码器单圈线数*4倍频(根据编码器线数) */
#define MPR             8                                   /* 步进电机旋转一圈,丝杠的距离;单位:mm/r  */
#define PPM             (ENCODER_SPR/MPR)                   /* 每mm内编码器的脉冲数;单位:Pules/mm */
#define MPP             ((float)(MPR)/ENCODER_SPR)          /* 编码器单步步进距离 */
#define FEEDBACK_CONST  (float)(PULSE_REV/ENCODER_SPR)      /* 编码器和步进电机驱动器的比值(表示每个编码器输出线数对应的步进电机脉冲数) */

void stepper_init(uint16_t arr, uint16_t psc);              /* 步进电机接口初始化 */
void stepper_motion_ctrl(uint8_t dir, uint16_t location_m); /* 步进电机位置运动控制函数 */

#ifdef __cplusplus
}
#endif

#endif

 

4.4.PID控制参数定义

#ifndef __PID_H
#define __PID_H

#ifdef __cplusplus
extern "C" {
#endif

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "fxx_std.h"

#define  INCR_LOCT_SELECT  0           /* 0:选择位置式  1:增量式控制 */

#if INCR_LOCT_SELECT
#define  L_KP              0.5f        /* P参数 */
#define  L_KI              0.05f       /* I参数 */
#define  L_KD              0.00f       /* D参数 */

#define SMAPLSE_PID_SPEED  20          /* 采样率 单位ms */
#else
#define  L_KP              0.5f        /* P参数 */
#define  L_KI              0.05f       /* I参数 */
#define  L_KD              0.00f       /* D参数 */

#define SMAPLSE_PID_SPEED  20          /* 采样率 单位ms */
#endif

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] */
    __IO float  IngMin;                /* 积分限制 */
    __IO float  IngMax;
    __IO float  OutMin;                /* 输出限制 */
    __IO float  OutMax;
} PID_TypeDef;

void pid_init(void);                                                /* PID初始化 */
int32_t increment_pid_ctrl(PID_TypeDef *PID, float Feedback_value); /* PID控制算法 */

#ifdef __cplusplus
}
#endif

#endif

 

4.5.PID控制

#include "pid.h"
#include "stepper.h"

PID_TypeDef  g_location_pid;           /* 位置PID参数结构体*/

void pid_init(void)
{
    /*位置环初始化*/
    g_location_pid.SetPoint    = (float)(50 * PPM); /* 设定目标Desired Value*/
    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*/
    g_location_pid.IngMax      = 20;
    g_location_pid.IngMin      = -20;
    g_location_pid.OutMax      = 150;               /* 输出限制 */
    g_location_pid.OutMin      = -150;
}

int32_t increment_pid_ctrl(PID_TypeDef *PID, float Feedback_value)
{
    PID->Error = (float)(PID->SetPoint - Feedback_value);                                         /* 偏差 */

#if  INCR_LOCT_SELECT
    PID->ActualValue += (PID->Proportion * (PID->Error - PID->LastError))                         /* E[k]项 */
                        + (PID->Integral * PID->Error)                                            /* E[k-1]项 */
                        + (PID->Derivative * (PID->Error - 2 * PID->LastError + PID->PrevError)); /* E[k-2]项 */
    PID->PrevError = PID->LastError;                                                              /* 存储误差,用于下次计算 */
    PID->LastError = PID->Error;
#else
    PID->SumError += PID->Error;

    if (PID->SumError > PID->IngMax)
    {
        PID->SumError = PID->IngMax;
    }
    else if (PID->SumError < PID->IngMin)
    {
        PID->SumError = PID->IngMin;
    }

    PID->ActualValue = (PID->Proportion * PID->Error)                       /* E[k]项 */
                       + (PID->Integral * PID->SumError)                    /* E[k-1]项 */
                       + (PID->Derivative * (PID->Error - PID->LastError)); /* E[k-2]项 */
    PID->LastError = PID->Error;
#endif

    if (PID->ActualValue > PID->OutMax)
    {
        PID->ActualValue = PID->OutMax;
    }
    else if (PID->ActualValue < PID->OutMin)
    {
        PID->ActualValue = PID->OutMin;
    }

    return ((int32_t)(PID->ActualValue)); /* 返回实际控制数值 */
}

 

4.6.步进电机运动控制

void stepper_motion_ctrl(uint8_t dir, uint16_t location_m) /* location_m如果这个值设置100,那就是20ms的采样频率内要输出100个脉冲信号 */
{
    float step_delay = 0.0;                                /* 步进延时 */

    if (location_m == 0)
    {
        TIM_CCxCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_Disable); /* 当期望值与目标值一致时,代表已到指定位置,停止输出 */
        g_step_angle = 0;                                 /* 清空数据 */
    }
    else
    {
        if (dir == CW)                 /* 设置旋转方向 */
        {
            GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);
        }
        else
        {
            GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_RESET);
        }

        /* 经过PID计算得到的结果是编码器的输出期望值,
           将其转换为步进电机所需的脉冲数( 编码器期望值 * (步进电机一圈所需脉冲数 / 编码器一圈计数值))*/
        step_delay = (float)(SMAPLSE_PID_SPEED / (location_m * FEEDBACK_CONST)); /* 单个脉冲的时间宽度,单位: ms */

        /* ms转成s step_delay = step_delay/1000 */
        /*
           T : 单个脉冲所需的时间
           c : 需要求解的比较值
           f : 定时器计数频率
           T  = c *(1/f)
           step_delay/1000 = c*1/1000000 (单位是s)
           c = step_delay*1000   求出整一个脉冲的计数值,需要除以2 因为一个完整的脉冲需要翻转2次
           所要的计数值:c/2 = Sstep_delay*1000/2 = step_delay*500
         */
        g_step_angle = (uint16_t)(step_delay * 500); /* 算出来的结果是周期,这里除以2,半周期翻转一次 */

        TIM_CCxCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_Enable);
    }
}

 

5.附件

Project.zip (1.31 MB, 下载次数: 0)

最新回复

光电编码器实现步进电机闭环系统(位置环这个测试案例,收藏   详情 回复 发表于 昨天 07:45
个人签名We are a team and we work as a team !

回复
举报

7098

帖子

0

TA的资源

五彩晶圆(高级)

光电编码器实现步进电机闭环系统(位置环这个测试案例,收藏


回复
您需要登录后才可以回帖 登录 | 注册

开源项目 更多>>
    随便看看
    查找数据手册?

    EEWorld Datasheet 技术支持

    相关文章 更多>>
    推荐帖子
    学习笔记分享】【MSP430学习笔记】IAR开发环境中的数据、函数定位方法

    IAR集成开发环境下,变量和函数的定位方法。 71081 qq:906411450 triton.zhang 本帖最后由 Triton.zhang 于 2011-9-2 ...

    用EK-LM3S811-ND制作的 万年历、温湿度计

    这是用TI研讨会送的EK-LM3S811-ND做的一个简单的时钟万年历、带有温湿度显示、时间设置、整点报时功能,显示使用了128X64的LCD, ...

    补贴:ADS1115程序+原理图+计算方法

    前几天发过一个帖子说ADS1115检测接近0V的电压就会出错,这几天比较忙,没怎么花时间来解决这个问题,现在问题仍然存在。 在直 ...

    晒一个完整的LM3S811完成的小综合项目,语句详解。

    一个LM3S811的EV板项目,涉及PWM、计数、字符液晶,超声测距,计时采用的是SYSTICK(比TIMER省事),附带时钟显示(简易的,不是 ...

    MSP430F6638单片机中断、时钟与低功耗

    本帖最后由 火辣西米秀 于 2020-6-13 20:56 编辑 各种中断向量 482788【注意】排序顺序为实验顺序,不代表优先级大小 中 ...

    TI MSP430单片机的微量计

    508541508542508543508544

    DM642硬中断延迟问题

    问题描述: 系统调试过程中发现硬件中断经常得不到及时响应,根据现象推断中断会被延迟达150us以上。 问题分析: 中断信号是 ...

    isp芯片在边缘计算中的应用

    isp芯片在边缘计算中的应用有哪些?

    【USB充电器DIY】买的快充模块到了

    又买了几种快充模块。 794476

    关闭
    站长推荐上一条 1/10 下一条

     
    EEWorld订阅号

     
    EEWorld服务号

     
    汽车开发圈

     
    机器人开发圈

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 13

    北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表