当前位置:   article > 正文

野火电子《电机应用开发实战指南-基于STM32》章节[步进电机速度环控制实现]学习总结_野火电机应用开发实战指南

野火电机应用开发实战指南

野火电子《电机应用开发实战指南-基于STM32》章节步进电机速度环控制实现学习总结

官方文档:https://github.com/Embedfire-motor/ebf_motor_tutorial_stm32f407/blob/master/improve_part/step_motor_speed_loop_control.rst

1. 使用定时器输出可变频率的PWM(占空比固定为50%)

使用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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
2. 步进电机闭环控制算法

程序通过增量式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;
   }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/963138
推荐阅读
相关标签
  

闽ICP备14008679号