最近做了直流无刷减速电机的控制的项目,针对项目中遇到的问题做下总结。
#PID_Control
PID 代码(速度环 位置环 串级)
STM32F407VET6
STM32CubeMX
V1.0.0 2022/8/5
V1.0.1 2022/8/8
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
V1.0.3 2022/8/16
/* 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;
}
/***
* @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));
}
}
/***
* @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
}
}
/***
* @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
}
}