电机应用开发-直流有刷电机位置环控制实现

目录

直流有刷电机位置环控制实现

硬件设计

直流电机位置环控制-位置式PID实现

编程要点

配置基本定时器6产生定时中断来执行PID运算

配置定时器1输出PWM控制电机

配置定时器3读取编码器的计数值

编写位置式PID算法

主体功能

直流电机位置环控制-增量式PID实现

编程要点

配置基本定时器6产生定时中断来执行PID运算

配置定时器1输出PWM控制电机

配置定时器3读取编码器的计数值

编写增量式PID算法

主体功能


直流有刷电机位置环控制实现

可以以开始为参考,记录正转多少圈或反转多少圈。如一圈脉冲为1920,刚开始目标值为0,输入目标值脉冲为1920后正转一圈,再输入目标值脉冲为1920*2后正转一圈,然后输入目标值脉冲为0即可反转两圈。 

硬件设计

可选:L298N电机驱动板、野火MOS搭建的驱动板。

直流电机位置环控制-位置式PID实现

编程要点

配置基本定时器产生定时中断来执行PID运算

配置定时器输出PWM控制电机

配置定时器读取编码器的计数值

编写位置式PID算法

编写速度控制函数

增加上位机曲线观察相关代码

编写按键控制代码

配置基本定时器6产生定时中断来执行PID运算
TIM_HandleTypeDef TIM_TimeBaseStructure;
 
/**
  * @brief  初始化基本定时器定时,默认50ms产生一次中断
  * @param  无
  * @retval 无
  */
void TIMx_Configuration(void)
{
    HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 1, 3);
    HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
	
	__TIM6_CLK_ENABLE();
	
	TIM_TimeBaseStructure.Instance = TIM6;
    TIM_TimeBaseStructure.Init.Period = 50 * 50 - 1;
    TIM_TimeBaseStructure.Init.Prescaler = 1680 - 1;
    TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
    TIM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&TIM_TimeBaseStructure);
 
    // 开启定时器更新中断
    HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);
	
    uint32_t temp = (__HAL_TIM_GET_AUTORELOAD(&TIM_TimeBaseStructure) + 1) / 50.0;  // 计算周期,单位ms
    set_computer_value(SEND_PERIOD_CMD, CURVES_CH1, &temp, 1);  					// 给通道 1 发送目标值
}
 
/**
  * @brief  定时器更新事件回调函数
  * @param  无
  * @retval 无
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim == (&TIM_TimeBaseStructure))
    {
        motor_pid_control();	// 每50ms执行一次PID运算
    }
}

配置定时器1输出PWM控制电机
TIM_HandleTypeDef  DCM_TimeBaseStructure;
 
/**
  * @brief  初始化控制通用定时器
  * @param  无
  * @retval 无
  */
void Motor_TIMx_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    TIM_OC_InitTypeDef  TIM_OCInitStructure;
 
	__HAL_RCC_GPIOA_CLK_ENABLE();
    __TIM1_CLK_ENABLE();
 
	// PA8--PWM_TIM_CH1
	GPIO_InitStruct.Pin 		= GPIO_PIN_8;
    GPIO_InitStruct.Mode 		= GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed 		= GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate 	= PWM_TIM_GPIO_AF;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// PA9--PWM_TIM_CH2
    GPIO_InitStruct.Pin 		= GPIO_PIN_9;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// TIM1 66.7us一次周期
    DCM_TimeBaseStructure.Instance = TIM1;
    DCM_TimeBaseStructure.Init.Period = 5600 - 1;
    DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
    DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
    DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
 
    /*PWM模式配置*/
    TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
    TIM_OCInitStructure.Pulse = 0;
    TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
    TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
    TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
}
 
/**
  * @brief  设置TIM通道的占空比
	* @param  channel		通道	(1,2,3,4)
	* @param  compare		占空比
	*	@note 	无
  * @retval 无
  */
void TIM1_SetPWM_pulse(uint32_t channel, int compare)
{
    switch (channel)
    {
        case TIM_CHANNEL_1:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_1, compare);
            break;
 
        case TIM_CHANNEL_2:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_2, compare);
            break;
 
        case TIM_CHANNEL_3:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_3, compare);
            break;
 
        case TIM_CHANNEL_4:
            __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_4, compare);
            break;
    }
}

配置定时器3读取编码器的计数值
TIM_HandleTypeDef  DCM_TimeBaseStructure;
 
/**
  * @brief  初始化控制通用定时器
  * @param  无
  * @retval 无
  */
void Motor_TIMx_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    TIM_OC_InitTypeDef  TIM_OCInitStructure;
 
	__HAL_RCC_GPIOA_CLK_ENABLE();
    __TIM1_CLK_ENABLE();
 
	// PA8--PWM_TIM_CH1
	GPIO_InitStruct.Pin 		= GPIO_PIN_8;
    GPIO_InitStruct.Mode 		= GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed 		= GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate 	= PWM_TIM_GPIO_AF;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// PA9--PWM_TIM_CH2
    GPIO_InitStruct.Pin 		= GPIO_PIN_9;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
	// TIM1 66.7us一次周期
    DCM_TimeBaseStructure.Instance = TIM1;
    DCM_TimeBaseStructure.Init.Period = 5600 - 1;
    DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
    DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
    DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
 
    /*PWM模式配置*/
    TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
    TIM_OCInitStructure.Pulse = 0;
    TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
    TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
    TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
 
    /*配置PWM通道*/
    HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
    /*开始输出PWM*/
    HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
}
 
/**
  * @brief  定时器更新事件回调函数
  * @param  无
  * @retval 无
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim == (&TIM_EncoderHandle))
    {
        /* 判断当前计数器计数方向 */
        if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle))
            /* 下溢 */
        {
            Encoder_Overflow_Count--;
        }
        else
            /* 上溢 */
        {
            Encoder_Overflow_Count++;
        }
    }
}

编写位置式PID算法
typedef struct
{
    float target_val;	// 目标值
    float actual_val;	// 实际值
    float err;       	// 定义偏差值
    float err_last;  	// 定义上一个偏差值
    float Kp,Ki,Kd;  	// 定义比例、积分、微分系数
    float integral;  	// 定义积分值
}_pid;					// 位置式PID
_pid pid;
 
/**
  * @brief  PID参数初始化
	*	@note 	无
  * @retval 无
  */
void PID_param_init(void)
{
    /* 初始化参数 */
    pid.target_val 	= 100.0;
    pid.actual_val 	= 0.0;
    pid.err 		= 0.0;
    pid.err_last 	= 0.0;
    pid.integral 	= 0.0;
    pid.Kp 			= 5.0;
    pid.Ki 			= 2.0;
    pid.Kd 			= 0.0;
	
    float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
    set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3);     // 给通道 1 发送 P I D 值
}
 
/**
  * @brief  设置目标值
  * @param  val		目标值
	*	@note 	无
  * @retval 无
  */
void set_pid_target(float temp_val)
{
    pid.target_val = temp_val;	// 设置当前的目标值
}
 
/**
  * @brief  获取目标值
  * @param  无
	*	@note 	无
  * @retval 目标值
  */
float get_pid_target(void)
{
    return pid.target_val;		// 设置当前的目标值
}
 
/**
  * @brief  设置比例、积分、微分系数
  * @param  p:比例系数 P
  * @param  i:积分系数 i
  * @param  d:微分系数 d
	*	@note 	无
  * @retval 无
  */
void set_p_i_d(float p, float i, float d)
{
    pid.Kp = p;    // 设置比例系数 P
    pid.Ki = i;    // 设置积分系数 I
    pid.Kd = d;    // 设置微分系数 D
}
 
/**
  * @brief  PID算法实现
  * @param  actual_val:实际值
  *	@note 	无
  * @retval 通过PID计算后的输出
  */
float PID_realize(float actual_val)
{
    /*计算目标值与实际值的误差*/
    pid.err = pid.target_val - actual_val;
	
    /*误差累积*/
    pid.integral += pid.err;
	
    /*PID算法实现*/
    pid.actual_val = pid.Kp * pid.err + pid.Ki * pid.integral + pid.Kd * (pid.err - pid.err_last);
	
    /*误差传递*/
    pid.err_last = pid.err;
	
    /*返回当前实际值*/
    return pid.actual_val;
}
 
/**
  * @brief  电机位置式 PID 控制实现(定时调用)
  * @param  无
  * @retval 无
  */
void motor_pid_control(void)
{
    if (is_motor_en == 1)     					// 电机在使能状态下才进行控制处理
    {
        float cont_val = 0;                     // 当前控制值
        int32_t Capture_Count = 0;              // 当前时刻总计数值
 
        /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD  */
        Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
 
		cont_val = PID_realize(Capture_Count);  // 进行 PID 计算

        if (cont_val > 0)    					// 判断电机方向
        {
            set_motor_direction(MOTOR_FWD);
        }
        else
        {
            cont_val = -cont_val;
            set_motor_direction(MOTOR_REV);
        }

        cont_val = (cont_val > 5500 * 0.48) ? 5500 * 0.48 : cont_val; 			// 速度上限处理
        set_motor_speed(cont_val);                                              // 设置 PWM 占空比
        set_computer_value(SEND_FACT_CMD, CURVES_CH1, &Capture_Count, 1);       // 给通道 1 发送实际值
    }
}

主体功能
#define CIRCLE_PULSES    (16 * 4 * 30)    // 编码器一圈可以捕获的脉冲,4倍物理分辨率 * 减速电机减速比

int main(void)
{
    int32_t target_location = CIRCLE_PULSES;

    初始化
	
    set_computer_value(SEND_STOP_CMD, CURVES_CH1, NULL, 0);    				// 同步上位机的停止按钮状态
    set_computer_value(SEND_TARGET_CMD, CURVES_CH1, &target_location, 1);   // 给通道 1 发送目标值

    while (1)
    {
        /* 接收数据处理 */
        receiving_process();

        /* 扫描KEY1 */
        if (Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON)
        {
            set_computer_value(SEND_START_CMD, CURVES_CH1, NULL, 0);        // 同步上位机的启动按钮状态
            set_pid_target(target_location);    							// 设置目标值
            set_motor_enable();                 							// 使能电机
        }

        /* 扫描KEY2 */
        if (Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON)
        {
            set_motor_disable();     										// 停止电机
            set_computer_value(SEND_STOP_CMD, CURVES_CH1, NULL, 0);         // 同步上位机的停止按钮状态
        }

        /* 扫描KEY3 */
        if (Key_Scan(KEY3_GPIO_PORT, KEY3_PIN) == KEY_ON)
        {
            /* 增加一圈 */
            target_location += CIRCLE_PULSES;
            set_pid_target(target_location);
            set_computer_value(SEND_TARGET_CMD, CURVES_CH1,  &target_location, 1);     // 给通道 1 发送目标值
        }

        /* 扫描KEY4 */
        if (Key_Scan(KEY4_GPIO_PORT, KEY4_PIN) == KEY_ON)
        {
            /* 减少一圈 */
            target_location -= CIRCLE_PULSES;
            set_pid_target(target_location);
            set_computer_value(SEND_TARGET_CMD, CURVES_CH1,  &target_location, 1);     // 给通道 1 发送目标值
        }
    }
}

直流电机位置环控制-增量式PID实现

编程要点

配置基本定时器产生定时中断来执行PID运算

配置定时器输出PWM控制电机

配置定时器读取编码器的计数值

编写增量式PID算法

编写速度控制函数

增加上位机曲线观察相关代码

编写按键控制代码

配置基本定时器6产生定时中断来执行PID运算

同上。

配置定时器1输出PWM控制电机

同上。

配置定时器3读取编码器的计数值

同上。

编写增量式PID算法
typedef struct
{
	float target_val;	// 目标值
	float actual_val;	// 实际值
	float err;       	// 定义当前偏差值
	float err_next;  	// 定义下一个偏差值
	float err_last;  	// 定义最后一个偏差值
	float Kp, Ki, Kd;	// 定义比例、积分、微分系数
}_pid;					// 速度环PID
_pid pid;
 
/**
  * @brief  PID参数初始化
	*	@note 	无
  * @retval 无
  */
void PID_param_init()
{
    /* 初始化参数 */
    pid.target_val 	= 100;
    pid.actual_val 	= 0.0;
    pid.err 		= 0.0;
    pid.err_last 	= 0.0;
    pid.err_next 	= 0.0;
    pid.Kp 			= 6;
    pid.Ki 			= 2.5;
    pid.Kd 			= 0;
 
    float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
    set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3);     // 给通道 1 发送 P I D 值
}
 
/**
  * @brief  设置目标值
  * @param  val		目标值
	*	@note 	无
  * @retval 无
  */
void set_pid_target(float temp_val)
{
    pid.target_val = temp_val;    // 设置当前的目标值
}
 
/**
  * @brief  获取目标值
  * @param  无
	*	@note 	无
  * @retval 目标值
  */
float get_pid_target(void)
{
    return pid.target_val;    // 设置当前的目标值
}
 
/**
  * @brief  设置比例、积分、微分系数
  * @param  p:比例系数 P
  * @param  i:积分系数 i
  * @param  d:微分系数 d
	*	@note 	无
  * @retval 无
  */
void set_p_i_d(float p, float i, float d)
{
    pid.Kp = p;    // 设置比例系数 P
    pid.Ki = i;    // 设置积分系数 I
    pid.Kd = d;    // 设置微分系数 D
}
 
/**
  * @brief  PID算法实现
  * @param  actual_val:实际值
	*	@note 	无
  * @retval 通过PID计算后的输出
  */
float PID_realize(float actual_val)
{
    /*计算目标值与实际值的误差*/
    pid.err = pid.target_val - actual_val;
	
    /*PID算法实现*/
    pid.actual_val += pid.Kp * (pid.err - pid.err_next) + pid.Ki * pid.err + pid.Kd * (pid.err - 2 * pid.err_next + pid.err_last);
    
	/*传递误差*/
    pid.err_last = pid.err_next;
    pid.err_next = pid.err;
	
    /*返回当前实际值*/
    return pid.actual_val;
}
 
/**
  * @brief  电机增量式 PID 控制实现(定时调用)
  * @param  无
  * @retval 无
  */
void motor_pid_control(void)
{
    if (is_motor_en == 1)     					// 电机在使能状态下才进行控制处理
    {
        float cont_val = 0;              		// 当前控制值
        int32_t Capture_Count = 0;  			// 当前时刻总计数值
 
        /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD  */
        Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
 
        cont_val = PID_realize(Capture_Count);  // 进行 PID 计算
 
        if (cont_val > 0)    					// 判断电机方向
        {
            set_motor_direction(MOTOR_FWD);
        }
        else
        {
            cont_val = -cont_val;
            set_motor_direction(MOTOR_REV);
        }
 
        cont_val = (cont_val > 5500 * 0.48) ? 5500 * 0.48: cont_val;     	// 速度上限处理
        set_motor_speed(cont_val);                                       	// 设置 PWM 占空比
        set_computer_value(SEND_FACT_CMD, CURVES_CH1, &Capture_Count, 1); 	// 给通道 1 发送实际值
    }
}

主体功能

同上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值