赞
踩
野火电子《电机应用开发实战指南-基于STM32》章节步进电机速度环控制实现学习总结
官方文档:https://github.com/Embedfire-motor/ebf_motor_tutorial_stm32f407/blob/master/improve_part/step_motor_speed_loop_control.rst
使用STM32的定时器输出比较翻转模式TIM_OCMode_Toggle
,可以实现输出一个频率可调的PWM波。该模式下生成的PWM频率为:
P
W
M
频率
=
定时器频率
时钟预分频系数
×
定时器
P
W
M
通道输出比较值
×
2
PWM频率=\frac{定时器频率}{时钟预分频系数\times定时器PWM通道输出比较值\times2}
PWM频率=时钟预分频系数×定时器PWM通道输出比较值×2定时器频率
野火官方的例程中提供的步进PWM脉冲输出初始化配置如下,该输出模式下定时器自动重装载值一定要设置为最大值,即65535;
void TIM_PWMOUTPUT_Config(void) { TIM_OC_InitTypeDef TIM_OCInitStructure; /*使能定时器*/ MOTOR_PUL_CLK_ENABLE(); TIM_StepperHandle.Instance = MOTOR_PUL_TIM; /* 累计 TIM_Period个后产生一个更新或者中断*/ ///当定时器从0计数到TIM_PERIOD-1,即为TIM_PERIOD次,为一个定时周期 TIM_StepperHandle.Init.Period = TIM_PERIOD-1; //自动重装载值 TIM_PERIOD固定为65536 //定时器时钟源TIMxCLK = 2 * PCLK1 // PCLK1 = HCLK / 2 // => TIMxCLK=HCLK/2=SystemCoreClock/2*2=72MHz // 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=100KHz TIM_StepperHandle.Init.Prescaler = TIM_PRESCALER-1; //分频系数72/3=24M TIM_PRESCALER=3 /*计数方式*/ TIM_StepperHandle.Init.CounterMode = TIM_COUNTERMODE_UP; /*采样时钟分频*/ TIM_StepperHandle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; TIM_StepperHandle.Init.RepetitionCounter = 0 ; /*初始化定时器*/ HAL_TIM_OC_Init(&TIM_StepperHandle); /*PWM模式配置--这里配置为输出比较模式*/ TIM_OCInitStructure.OCMode = TIM_OCMODE_TOGGLE; /*比较输出的计数值*/ TIM_OCInitStructure.Pulse = OC_Pulse_num; /*当定时器计数值小于CCR1_Val时为高电平*/ TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH; /*设置互补通道输出的极性*/ TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_LOW; /*快速模式设置*/ TIM_OCInitStructure.OCFastMode = TIM_OCFAST_DISABLE; /*空闲电平*/ TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_RESET; /*互补通道设置*/ TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET; HAL_TIM_OC_ConfigChannel(&TIM_StepperHandle, &TIM_OCInitStructure, MOTOR_PUL_CHANNEL_x); /* 确定定时器 */ HAL_TIM_Base_Start(&TIM_StepperHandle); /* 启动比较输出并使能中断 */ HAL_TIM_OC_Start_IT(&TIM_StepperHandle,MOTOR_PUL_CHANNEL_x); /*使能比较通道*/ TIM_CCxChannelCmd(MOTOR_PUL_TIM,MOTOR_PUL_CHANNEL_x,TIM_CCx_ENABLE); }
程序通过增量式PID算法实现了步进电机转速的闭环控制,PID传入的参数为单次采样周期(20ms)内编码器的脉冲计数值,PID经过计算后输出的值为期望设定的编码器单次采样脉冲计数值,可以通过运算最终将值转化为步进电机脉冲PWM输出通道比较值,进行调整步进脉冲输出频率达成转速闭环控制的目的。
期望设定的编码器单次采样脉冲值(脉冲频率)>>步进单次采样内的输出脉冲数量>>步进脉冲输出频率>>转速控制
TIM_STEP_FREQ
步进电机定时器的时钟频率 由定时器时钟频率和预分频系数决定
PULSE_RATIO
脉冲系数,步进电机旋转360度机械角度所需脉冲数量与编码器实际输出脉冲数量的比值;
eg.步进电机步距角1.8度 细分器32细分 则步进旋转一圈需要360/1.8*32=6400个脉冲;编码器分辨率为一圈4000个脉冲则脉冲比为6400/4000=1.6
SAMPLING_PERIOD
为编码器采样频率(50Hz),同时也是PID函数的控制周期
OC_Pulse_num
步进电机PWM脉冲输出通道定时器的值
由
步进脉冲输出频率
=
定时器时钟频率
时钟分频系数
×
步进脉冲输出通道比较值
×
2
步进脉冲输出频率= \frac{定时器时钟频率}{时钟分频系数\times 步进脉冲输出通道比较值\times2}
步进脉冲输出频率=时钟分频系数×步进脉冲输出通道比较值×2定时器时钟频率
可得
步进脉冲输出通道比较值
=
定时器时钟频率
步进电机脉冲输出频率
×
时钟分频系数
×
2
步进脉冲输出通道比较值=\frac{定时器时钟频率}{步进电机脉冲输出频率\times时钟分频系数\times2}
步进脉冲输出通道比较值=步进电机脉冲输出频率×时钟分频系数×2定时器时钟频率
步进脉冲输出频率可以通过编码器的脉冲周期转换获得
编码器脉冲比
=
步进电机单圈脉冲数
编码器单圈脉冲数
编码器脉冲比=\frac{步进电机单圈脉冲数}{编码器单圈脉冲数}
编码器脉冲比=编码器单圈脉冲数步进电机单圈脉冲数
编码器脉冲频率
=
编码器单次采样脉冲数
采样周期
=
编码器单次采样脉冲数
×
采样频率
编码器脉冲频率=\frac{编码器单次采样脉冲数}{采样周期}=编码器单次采样脉冲数\times采样频率
编码器脉冲频率=采样周期编码器单次采样脉冲数=编码器单次采样脉冲数×采样频率
步进电机脉冲输出频率
=
编码器脉冲比
×
编码器脉冲频率
步进电机脉冲输出频率=编码器脉冲比\times编码器脉冲频率
步进电机脉冲输出频率=编码器脉冲比×编码器脉冲频率
最终可以得到
步进脉冲输出通道比较值
=
定时器时钟频率
编码器脉冲比
×
单次采样脉冲数
×
采样频率
×
时钟分频系数
×
2
步进脉冲输出通道比较值=\frac{定时器时钟频率}{编码器脉冲比\times单次采样脉冲数\times采样频率\times时钟分频系数\times2}
步进脉冲输出通道比较值=编码器脉冲比×单次采样脉冲数×采样频率×时钟分频系数×2定时器时钟频率
也就是闭环控制程序第32行的内容OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / (timer_delay * PULSE_RATIO * SAMPLING_PERIOD))) >> 1;
野火官方提供的闭环控制算法(增量式PID)如下:
/** * @brief 步进电机速度闭环控制 * @retval 无 * @note 基本定时器中断内调用 */ void Stepper_Speed_Ctrl(void) { /* 编码器相关变量 */ static __IO int32_t last_count = 0; __IO int32_t capture_count = 0; __IO int32_t capture_per_unit = 0; /* 经过pid计算后的期望值 */ static __IO float cont_val = 0.0f; __IO float timer_delay = 0.0f; /* 当电机运动时才启动pid计算 */ if((sys_status.MSD_ENA == 1) && (sys_status.stepper_running == 1)) { /* 计算单个采样时间内的编码器脉冲数 */ capture_count =__HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (encoder_overflow_count * ENCODER_TIM_PERIOD); capture_per_unit = capture_count - last_count; last_count = capture_count; /* 单位时间内的编码器脉冲数作为实际值传入pid控制器 */ cont_val += PID_realize((float)capture_per_unit);// 进行 PID 计算 /* 判断速度方向 */ cont_val > 0 ? (MOTOR_DIR(CW)) : (MOTOR_DIR(CCW)); /* 计算得出的期望值取绝对值 */ timer_delay = fabsf(cont_val); /* 计算比较计数器的值 */ OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / (timer_delay * PULSE_RATIO * SAMPLING_PERIOD))) >> 1; #if PID_ASSISTANT_EN int Temp = capture_per_unit; // 上位机需要整数参数,转换一下 set_computer_value(SEED_FACT_CMD, CURVES_CH1, &Temp, 1); // 给通道 1 发送实际值 #else printf("实际值:%d,目标值:%.0f\r\n", capture_per_unit, pid.target_val);// 打印实际值和目标值 #endif } else { /*停机状态所有参数清零*/ // OC_Pulse_num = 0xFFFF; last_count = 0; cont_val = 0; pid.actual_val = 0; pid.err = 0; pid.err_last = 0; pid.err_next = 0; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。