直流无刷减速电机PID控制

最近做了直流无刷减速电机的控制的项目,针对项目中遇到的问题做下总结。
#PID_Control

PID 代码(速度环 位置环 串级

STM32F407VET6

STM32CubeMX

更新记录

V1.0.0 2022/8/5

  1. 完善了RTOS程序模板,包括RTT功能.
  2. 完成了位置PID 速度环控制.
  3. 增加了上位机串口通信协议,可用上位机调节PID参数.

V1.0.1 2022/8/8

  1. 实现了通过M法测速 将单位时间内编码器的脉冲数转换为速度,
    速度分两个 a、电机转子的转速 b、减速器输出轴转速

b = a / REDUCTION_RATIO(减速比).

/* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数 */
Shaft_Speed = ((float)(pitch_cfg.cnt)) / (((float)(ENCODER_TOTAL_RESOLUTION)) * 0.01f);

2、需要理清楚几个概念:

1、每转脉冲数 PPR 圆周分辨率,Pulse Per Revolution 或 PPR (分辨率)
2、每转计数 CPR CPR = PPR * 4
3、RPM 每分钟转速

例如,某旋转编码器的分辨率为100PPR,表示每旋转一圈,该编码器可输出100个脉冲信号;
同理,某旋转编码器的分辨率为256PPR,则表示每旋转一圈的脉冲数为256个.

3、设置PID的目标值 为转轴转速 来实现调速,减速器输出轴转速太小了,上位机不支持.


V1.0.2 2022/8/11

  1. 增加了对电机位置的控制,通过控制脉冲数控制电机位置
    /* 电机转一圈的脉冲数*/
    #define PULSE_SUM 124264=12672

V1.0.3 2022/8/16

  1. 增加了串级PID,位置环和速度环
    /* 电机转一圈的脉冲数*/
    #define PULSE_SUM 12672 // htim3.Init.Prescaler = 0,理论上最多可转4圈
    #define PULSE_SUM_FOUR 3168 // htim3.Init.Prescaler = 4-1 ,相当于之前1us采集一次编码器脉冲,现在4us采集一次,总采集数为65535 一圈脉冲数为 3168,理论上最多可转20圈,而且速度按最大值进行旋转,所以位置PID速度不可控。

/* Private define ------------------------------------------------------------*/
/* 编码器接口倍频数 */
#define ENCODER_MODE                           TIM_ENCODERMODE_TI12
#define PITCH_TICK2DEG                     0.028869198  //TT电机
#define PITCH_MOTOR_MAX_OUTPUT             400          //扭矩
#define YAW_MOTOR_MAX_OUTPUT               400
#define DOWN_LIMIT_OFFSET                  -200
#define ENCODER_TIM_PERIOD                 65535   /*计数器最大值*/
/* 编码器物理分辨率 */
#define ENCODER_RESOLUTION                  12         //PPR
/* 减速电机减速比 */
#define REDUCTION_RATIO                     264
/* 电机转一圈的脉冲数*/
#define PULSE_SUM                           12672   // htim3.Init.Prescaler = 0,理论上最多可转4圈
#define PULSE_SUM_FOUR                      3168    // htim3.Init.Prescaler = 4-1 ,相当于之前1us采集一次编码器脉冲,现在4us采集一次,
                                                    // 总采集数为65535  一圈脉冲数为 3168,理论上最多可转20圈,而且速度按最大值进行旋转,所以位置PID速度不可控。
/* 经过倍频之后的总分辨率 */
#if (ENCODER_MODE == TIM_ENCODERMODE_TI12)
  #define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 4)  /* 4倍频后的总分辨率 CPR*/
#else
  #define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 2)  /* 2倍频后的总分辨率 */
#endif
/***
* @brief  bsp_motor_get_position
* @param  None
* @retval None
*/
int16_t bsp_motor_get_position(motor_id_e id)
{
  int32_t pos = 0;
  if(id == YAW_MOTOR)
  {
    pos = -(int16_t)__HAL_TIM_GET_COUNTER(&htim4);
    //pos = (int16_t)__HAL_TIM_GET_COUNTER(&htim3);
  }
  else if(id == PITCH_MOTOR)
  {
    //pos = (int32_t)__HAL_TIM_GET_COUNTER(&htim2);
    pos = (int16_t)__HAL_TIM_GET_COUNTER(&htim1);
  }
  return pos;
}

一、速度环PID

/***
* @brief  pitch_test
* @param  None
* @retval None
*/
/* 电机转轴转速 */
__IO float Shaft_Speed = 0.0f;

void vPitch_Task(void const * argument)
{
  TickType_t xLastWakeTime = xTaskGetTickCount();
	//set_p_i_d(15.0, 2.0, 0.0);
	//set_pid_target(30.0);
	static int count = 0;
  int test = 1;
	int pid_out = 0;
	
  pitch_cfg.pwm = 0;
  pitch_cfg.run2up = 1;

  while(test)
  {
    vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(10));
 
    if(pitch_cfg.run2up)
    {
      bsp_motor_set_direction(PITCH_MOTOR, HD2UP);//向上运动
			//bsp_motor_set_speed(PITCH_MOTOR, &(pitch_cfg.pwm));
    }
    else
    {
      bsp_motor_set_direction(PITCH_MOTOR, HD2DOWN);//向上运动
    }
    
    pitch_cfg.cnt = bsp_motor_get_position(PITCH_MOTOR);
		DBG_Printf("%s: Encoder:%d \r\n", __FUNCTION__, pitch_cfg.cnt);
		/* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数  */
    Shaft_Speed = ((float)(pitch_cfg.cnt)) / (((float)(ENCODER_TOTAL_RESOLUTION)) * 0.01f);
		
		DBG_Printf("Speed of motor shaft:%f r/s \r\n", Shaft_Speed);  //电机转子的转速
		//DBG_Printf("SPeed of motor output shaft:%f r/s \r\n", Shaft_Speed / REDUCTION_RATIO);  //减速器输出轴的转速
		
		uint32_t speed_temp = 0;
		speed_temp = (uint32_t)Shaft_Speed;
#if (PID_ASSISTANT_EN)
	count++;
	if (count==8)
	{
		count = 0;
		set_computer_value(SEND_FACT_CMD, CURVES_CH1, &speed_temp , 1); // 给通道 1 发送实际值
	}

#endif
	
		pid_out = (int)PID_realize(speed_temp);
		
		DBG_Printf("%s: PID_OUT:%d \r\n", __FUNCTION__, pid_out);
		pitch_cfg.pwm = pwm_val_protect(pid_out);
	  DBG_Printf("%s: PWM:%d \r\n", __FUNCTION__, pitch_cfg.pwm);
	  DBG_Printf("\r\n");
	  bsp_motor_set_speed(PITCH_MOTOR, &(pitch_cfg.pwm));
  }
}

二、位置环PID

/***
* @brief
*   pitch_test
* @param  None
* @retval None
*/
/* 电机转轴转速 */
__IO float Shaft_Speed = 0.0f;
extern __IO int16_t EncoderOverflowCnt;

void vPitch_Task(void const * argument)
{
  TickType_t xLastWakeTime = xTaskGetTickCount();
	 __IO int32_t encoderNow = 0;    /*当前时刻总计数值*/
	
	//set_p_i_d(15.0, 2.0, 0.0);
	//set_pid_target(30.0);
	static int count = 0;
  int test = 1;
	int pid_out = 0;
	
  pitch_cfg.pwm = 0;
  pitch_cfg.run2up = 1;

  while(test)
  {
    vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(10));
 
		
    if(pitch_cfg.run2up)
    {
      bsp_motor_set_direction(PITCH_MOTOR, HD2UP);//向上运动
			//bsp_motor_set_speed(PITCH_MOTOR, &(pitch_cfg.pwm));
    }
    else
    {
      bsp_motor_set_direction(PITCH_MOTOR, HD2DOWN);//向上运动
    }
		DBG_Printf("\r\n");
    pitch_cfg.cnt = bsp_motor_read_encoder(PITCH_MOTOR);
		DBG_Printf("%s: Encoder:%d \r\n", __FUNCTION__,pitch_cfg.cnt);
		DBG_Printf("%s:flowCnt:%d \r\n", __FUNCTION__,EncoderOverflowCnt);
		/*【1】读取编码器的值*/
		encoderNow = pitch_cfg.cnt + EncoderOverflowCnt * ENCODER_TIM_PERIOD;/*获取当前的累计值*/
   
		DBG_Printf("%s: encoder_Now:%d \r\n", __FUNCTION__, encoderNow);
		/*【2】PID运算,得到PWM控制值*/
		pid_out = (int)PID_realize(encoderNow);
		DBG_Printf("%s: pid_out:%d \r\n", __FUNCTION__, pid_out);
		pitch_cfg.pwm = pwm_val_protect(pid_out);
		
	
		/*【3】PWM控制电机*/
		DBG_Printf("%s: pwm:%d \r\n", __FUNCTION__, pitch_cfg.pwm);
	  uint32_t value_tt = 0;
	if (pid.target_val > 0)
    {
		  bsp_motor_set_direction(PITCH_MOTOR, HD2UP);//向上运动
			bsp_motor_set_speed(PITCH_MOTOR, &(pitch_cfg.pwm));/*传入编码器的[总计数值],实现电机【位置】控制*/
		}	
    else if (pid.target_val < 0)	
    {
		  bsp_motor_set_direction(PITCH_MOTOR, HD2DOWN);//向下运动
			bsp_motor_set_speed(PITCH_MOTOR, &(pitch_cfg.pwm));/*传入编码器的[总计数值],实现电机【位置】控制*/
		}	
    else
    {
		  bsp_motor_set_speed(PITCH_MOTOR, &value_tt);
		}			
	  
		/*【4】数据上传到上位机显示*/
#if (PID_ASSISTANT_EN)
	count++;
	if (count==8)
	{
		count = 0;
		int Temp = encoderNow;
		set_computer_value(SEND_FACT_CMD, CURVES_CH1, &Temp , 1); // 给通道 1 发送实际值
	}

#endif
  }
}

三、串级PID

/***
* @brief  pitch_test
* @param  None
* @retval None
*/
/* 电机转轴转速 */

extern __IO int16_t EncoderOverflowCnt;

void vPitch_Task(void const * argument)
{
  TickType_t xLastWakeTime = xTaskGetTickCount();
	int encoderNow = 0;    /*当前时刻总计数值*/
	static uint32_t location_timer = 0;    // 位置环周期
	static __IO int encoderLast = 0;   /*上一时刻总计数值*/
	int encoderDelta = 0; /*当前时刻与上一时刻编码器的变化量*/
	static int count = 0;
  int32_t actual_speed = 0.0f;
  int test = 1;
	uint32_t value_tt = 0;
	int actual_speed_int = 0;
	
  pitch_cfg.pwm = 0;
 
  while(test)
  {
    vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(10));
 
		DBG_Printf("\r\n");
    pitch_cfg.cnt = bsp_motor_read_encoder(PITCH_MOTOR);
		DBG_Printf("%s: Encoder:%d \r\n", __FUNCTION__,pitch_cfg.cnt);
		DBG_Printf("%s:flowCnt:%d \r\n", __FUNCTION__,EncoderOverflowCnt);
		
		/*【1】读取编码器的值*/
		encoderNow = pitch_cfg.cnt + EncoderOverflowCnt * ENCODER_TIM_PERIOD;/*获取当前的累计值*/
		
    /* 计算速度环的传入值 */
		encoderDelta = encoderNow - encoderLast; /*得到变化值  速度环的传入值*/
	  encoderLast = encoderNow;/*更新上次的累计值*/
		
		DBG_Printf("%s: encoderNow:%d \r\n", __FUNCTION__, encoderNow);
		DBG_Printf("%s: encoderDelta:%d \r\n", __FUNCTION__, encoderDelta);
		/*【2】位置PID运算,得到速度目标值*/
		if ((location_timer++ % 2) == 0)
		{
		  float control_val = 0;   /*当前控制值*/
			/*位置PID计算*/
		  control_val = location_pid_realize(&pid_location, encoderNow);  
		  DBG_Printf("%s: speed_current:%.2f \r\n", __FUNCTION__, control_val);
      /*目标速度值限制*/
		  speed_val_protect(&control_val);

		  /*设定速度PID的目标值*/
		  set_pid_target(&pid_speed, control_val); 
			DBG_Printf("%s: pid_speed_target:%.2f \r\n", __FUNCTION__, control_val);
			
			#if defined(PID_ASSISTANT_EN)
			if ((location_timer % 16) == 8)
			{
				int temp_speed = 0;
				temp_speed = (int)control_val;
				set_computer_value(SEND_TARGET_CMD, CURVES_CH2, &temp_speed, 1);     // 给通道2发送速度目标值
			}
			#endif
		}
		
		/*【3】速度PID运算,得到PWM控制值*/
		actual_speed = (int32_t)((encoderDelta) / (ENCODER_TOTAL_RESOLUTION*0.01f));
		DBG_Printf("%s: actual_speed:%d \r\n", __FUNCTION__, actual_speed);
		
		actual_speed_int = actual_speed;
		pitch_cfg.pwm =  pwm_val_protect((int)speed_pid_realize(&pid_speed, actual_speed));

		DBG_Printf("%s: actual_pwm:%d \r\n", __FUNCTION__, pitch_cfg.pwm);
		if (pid_speed.target_val > 0)
    {
		  bsp_motor_set_direction(PITCH_MOTOR, HD2UP);//向上运动
			bsp_motor_set_speed(PITCH_MOTOR, &(pitch_cfg.pwm));/*传入编码器的[总计数值],实现电机【位置】控制*/
		}	
    else if (pid_speed.target_val < 0)	
    {
		  bsp_motor_set_direction(PITCH_MOTOR, HD2DOWN);//向下运动
			bsp_motor_set_speed(PITCH_MOTOR, &(pitch_cfg.pwm));/*传入编码器的[总计数值],实现电机【位置】控制*/
		}	
    else
    {
		  bsp_motor_set_speed(PITCH_MOTOR, &value_tt);
		}			
	  
		/*【4】数据上传到上位机显示*/
#if (PID_ASSISTANT_EN)
	count++;
	if(count%12 == 5)
	{
		set_computer_value(SEND_FACT_CMD, CURVES_CH1, &encoderNow, 1);   /*给通道1发送实际的电机【位置】值*/
	}
	else if(count%12 == 10)
	{
		set_computer_value(SEND_FACT_CMD, CURVES_CH2, &actual_speed_int, 1); /*给通道2发送实际的电机【速度】值*/
	}

#endif
  }
}
  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
本文档的主要内容详细介绍的是直流无电机的工作原理的详细资料。主要内容包括了:直流无电机的优越性,直流无电机控制结构 ,直流无电机控制原理,P.I.D 控制简介,电机驱动器的保护措施   直流电机具有响应快速、较大的起动转矩、从零转速至额定转速具备可提供额定转矩的性能,但直流电机的优点也正是它的缺点,因为直流电机要产生额定负载下恒定转矩的性能,则电枢磁场与转子磁场须恒维持 90°,这就要藉由碳及整流子。碳及整流子在电机转动时会产生火花、碳粉因此除了会造成组件损坏之外,使用场合也受到限制。交流电机没有碳及整流子,免维护、坚固、应用广,但特性上若要达到相当于直流电机的性能须用复杂控制技术才能达到。现今半导体发展迅速功率组件切换频率加快许多,提升驱动电机的性能。微处理机速度亦越来越快,可实现将交流电机控制置于一旋转的两轴直交坐标系统中,适当控制交流电机在两轴电流分量,达到类似直流电机控制并有与直流电机相当的性能。   此外已有很多微处理机将控制电机必需的功能做在芯片中,而且体积越来越小;像模拟/数字转换器(Analog-to-digital converter,ADC)、脉冲宽度调制(pulse wide modulator,PWM)…等。直流无电机即是以电子方式控制交流电机换相,得到类似直流电机特性又没有直流电机机构上缺失的一种应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值