单片机BLDC PID控制实验
单片机 :STM32F407
开发板:DMF407电机开发板
平台:keil V5.31
HSE 为8MHZ
HSI为16MHZ
第一步:当只有一对级时,转子旋转一圈,霍尔输出一个完整脉冲(其中高电平与低电
平持续时间均为180°电角度);
第二步:计算其中高电平的持续时间,即:t = C / Ft(其中t为180°电角度所代表的时
间,Ft是霍尔脉冲的频率,C为计数次数)
第三步:所以旋转一圈,需要的总时间为T = 2*C/Ft;
第四步:所得出的结果单位为:s/圈 ,倒数即为:圈/s,需将其单位转化为RPM即:Ft/
(2*C)*60。
第五步:当转子为2对级时,霍尔输出的高低电平时间均为360°电角度,所以速度公式
为:Ft/(4*C)*60。
主函数:
int main(void) { uint8_t debug_cmd = 0; /* 存放上位机指令 */ float current_lpf[4] = {0.0f}; /* 存放三相电流以及母线电流 */ uint8_t key,t; char buf[32]; float current[3] = {0.0f}; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */ delay_init(168); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init(); /* 初始化LED */ key_init(); /* 初始化按键 */ lcd_init(); /* 初始化LCD */ bldc_init(168000/18-1,0); bldc_ctrl(MOTOR_1,CCW,0); /* 初始无刷电机接口1速度 */ pid_init(); g_point_color = WHITE; g_back_color = BLACK; lcd_show_string(10,10,240,24,24,"BLDC Motor Test",g_point_color); lcd_show_string(10,40,200,16,16,"KEY0:Step++",g_point_color); lcd_show_string(10,60,200,16,16,"KEY1:Step--",g_point_color); lcd_show_string(10,80,200,16,16,"KEY2:Stop",g_point_color); adc_nch_dma_init(); #if DEBUG_ENABLE /* 开启调试*/ debug_init(); /* PID调试初始化*/ debug_send_motorcode(BLDC_MOTOR ); /* 直流无刷电机 */ debug_send_motorstate(IDLE_STATE); /* 电机空闲 */ /* 初始化同步数据(选择第x组PIDX,目标速度地址,P,I,D参数)到上位机 */ debug_send_initdata(TYPE_PID1,(float*)(&g_speed_pid.SetPoint),S_KP,S_KI,S_KD);/* 速度环PID参数(PID1)*/ #endif while (1) { t++; if(t % 20 == 0) { sprintf(buf,"PWM_Duty:%.1f%%",(float)((g_bldc_motor1.pwm_duty/MAX_PWM_DUTY)*100));/* 显示控制PWM占空比 */ lcd_show_string(10,100,200,16,16,buf,g_point_color); sprintf(buf,"SetSpeed:%4d ",(int16_t)*user_setpoint); /* 显示设置速度 */ lcd_show_string(10,120,200,16,16,buf,g_point_color); sprintf(buf,"M1 Speed:%4d ",(int16_t)g_bldc_motor1.speed); lcd_show_string(10,140,200,16,16,buf,g_point_color); /* 显示测量速度 */ sprintf(buf,"M1 pos:%4d",g_bldc_motor1.pos); lcd_show_string(10,160,200,16,16,buf,g_point_color); /* 显示位置变化 */ sprintf(buf,"Power:%.3fV ",g_adc_value[0]*ADC2VBUS); lcd_show_string(10,180,200,16,16,buf,g_point_color); /* 显示电源电压 */ sprintf(buf,"Temp:%.1fC ",get_temp(g_adc_value[1])); lcd_show_string(10,200,200,16,16,buf,g_point_color); /* 显示温度 */ LED0_TOGGLE(); /* LED0(红灯) 翻转 */ current[0] = adc_amp_un[0] * ADC2CURT; /* U */ current[1] = adc_amp_un[1] * ADC2CURT; /* V */ current[2] = adc_amp_un[2] * ADC2CURT; /* W */ /* 一阶数字滤波 滤波系数0.1 用于显示 */ FirstOrderRC_LPF(current_lpf[0],current[0],0.1f); FirstOrderRC_LPF(current_lpf[1],current[1],0.1f); FirstOrderRC_LPF(current_lpf[2],current[2],0.1f); FirstOrderRC_LPF(current_lpf[3],adc_amp_bus,0.1f); if(g_bldc_motor1.run_flag == STOP) /* 停机的电流显示 */ { current_lpf[0] = 0; current_lpf[1] = 0; current_lpf[2] = 0; } sprintf(buf,"Amp U:%.3fmA ",(float)current_lpf[0]); lcd_show_string(10,230,200,16,16,buf,g_point_color); sprintf(buf,"Amp V:%.3fmA ",(float)current_lpf[1]); lcd_show_string(10,250,200,16,16,buf,g_point_color); sprintf(buf,"Amp W:%.3fmA ",(float)current_lpf[2]); lcd_show_string(10,270,200,16,16,buf,g_point_color); sprintf(buf,"Amp Bus:%.3fmA ",(float)adc_amp_bus); lcd_show_string(10,290,200,16,16,buf,g_point_color); } key = key_scan(0); if(key == KEY0_PRES) /* 按下KEY1速度目标值++ */ { g_bldc_motor1.run_flag = RUN; /* 开启运行 */ start_motor1(); /* 开启运行 */ if(*user_setpoint == 0 && g_bldc_motor1.dir == CCW) { pid_init(); /* 换向时刻,重新初始化PID,防止速度突变 */ g_bldc_motor1.dir = CW; } *user_setpoint += 400; /* 顺时针旋转下递增 */ if(*user_setpoint >= 4000) *user_setpoint = 4000; if(*user_setpoint == 0) { g_bldc_motor1.run_flag = STOP; stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; } } else if(key == KEY1_PRES) /* 按下KEY1速度目标值-- */ { g_bldc_motor1.run_flag = RUN; /* 开启运行 */ start_motor1(); /* 开启运行 */ if(*user_setpoint == 0 && g_bldc_motor1.dir == CW) { pid_init(); g_bldc_motor1.dir = CCW; } *user_setpoint -= 400; /* 逆时针旋转下递增 */ if(*user_setpoint <= -4000) *user_setpoint = -4000; if(*user_setpoint == 0) { g_bldc_motor1.run_flag = STOP; stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; } } else if(key == KEY2_PRES) /* 按下KEY2关闭电机 */ { bldc_speed_stop(); /* 清除电机状态并关闭电机 */ } #if DEBUG_ENABLE /* Debug发送部分 */ /* 主要显示参数 */ debug_send_valtage(g_adc_value[0] * ADC2VBUS); /* 发送电压 */ debug_send_speed(g_bldc_motor1.speed); /* 发送速度 */ debug_send_temp(50,get_temp(g_adc_value[1])); /* 发送电机温度、驱动板温度 */ debug_send_current((float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000)); /* 发送电流 */ /* 电流波形和速度波形 */ debug_send_wave_data(1,(int16_t)g_bldc_motor1.speed); /* 选择通道1 发送实际速度 */ debug_send_wave_data(2,(int16_t)*user_setpoint); /* 选择通道2 发送实际速度 */ debug_send_wave_data(3,current_lpf[0]); /* 选择通道3 发送实际电流U */ debug_send_wave_data(4,current_lpf[1]); /* 选择通道4 发送实际电流V */ debug_send_wave_data(5,current_lpf[2]); /* 选择通道5 发送实际电流W */ debug_send_wave_data(6,current_lpf[3]); /* 选择通道6 发送实际母线电流 */ /* Debug接收部分 */ debug_receive_pid(TYPE_PID1,(float*)&g_speed_pid.Proportion,/* 查询接收PID助手的PID1参数 */ (float*)&g_speed_pid.Integral,(float*)&g_speed_pid.Derivative); debug_cmd = debug_receive_ctrl_code(); /* 读取命令 */ if(debug_cmd == HALT_CODE) /* 停机 */ { pid_init(); /* 重新初始化PID,防止积分过大失控 */ g_bldc_motor1.run_flag = STOP; /* 标记停机 */ stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; } else if(debug_cmd == RUN_CODE) /* 运行 */ { g_bldc_motor1.dir = CW; g_bldc_motor1.run_flag = RUN; /* 运行标记 */ *user_setpoint = 400; /* 自动设置目标 */ start_motor1(); /* 启动电机 */ debug_send_motorstate(RUN_STATE); /* 电机运行 */ } else if (debug_cmd == BREAKED) /* 刹车(电机停止 点击电机运行才可解除) */ { *user_setpoint = 0; /* 减速直至0 */ debug_send_motorstate(BREAKED_STATE); /* 电机刹车 */ } #endif delay_ms(10); } } /** * @brief 清除电机状态并关闭电机 * @param 无 * @retval 无 */ void bldc_speed_stop(void) { pid_init(); /* 重新初始化PID,防止积分过大失控 */ g_bldc_motor1.run_flag = STOP; /* 标记停机 */ stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; }PID:
void pid_init(void) { g_speed_pid.SetPoint = 0; /* 设定目标值 */ g_speed_pid.ActualValue = 0.0; /* 期望值输出 */ g_speed_pid.SumError = 0.0; /* 积分值 */ g_speed_pid.Error = 0.0; /* Error[1] */ g_speed_pid.LastError = 0.0; /* Error[-1] */ g_speed_pid.PrevError = 0.0; /* Error[-2] */ g_speed_pid.Proportion = S_KP; /* 比例常数 Proportional Const */ g_speed_pid.Integral = S_KI; /* 积分常数 Integral Const */ g_speed_pid.Derivative = S_KD; /* 微分常数 Derivative Const */ g_speed_pid.IngMax = 20; g_speed_pid.IngMin = -20; g_speed_pid.OutMax = 150; /* 输出限制 */ g_speed_pid.OutMin = -150; } /** * @brief 闭环PID控制算法设计 * @note 通过宏 INCR_LOCT_SELECT 选择使用位置式算法/增量式算法 * @param *PID:PID结构体句柄所对应的目标值 * @param Feedback_value : 实际值 * @retval 目标控制量 */ int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value) { PID->Error = (float)(PID->SetPoint - Feedback_value); /* 速度档位偏差*/ #if INCR_LOCT_SELECT PID->ActualValue += (PID->Proportion * (PID->Error - PID->LastError)) /* E[k]项*/ + (PID->Integral * PID->Error) /* E[k-1]项*/ + (PID->Derivative * (PID->Error - 2 * PID->LastError + PID->PrevError)); /*E[k-2]项*/ PID->PrevError = PID->LastError; /* 存储误差,用于下次计算*/ PID->LastError = PID->Error; #else PID->SumError += PID->Error; PID->ActualValue = (PID->Proportion * PID->Error) /* E[k]项*/ + (PID->Integral * PID->SumError) /* E[k-1]项*/ + (PID->Derivative * (PID->Error - PID->LastError)); /* E[k-2]项*/ PID->LastError = PID->Error; #endif return ((int32_t)(PID->ActualValue)); /* 返回实际控制数值*/ }定时器:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { uint8_t i; uint8_t bldc_dir=0; static uint8_t times_count=0; /* 定时器时间记录 */ int16_t temp_speed=0; /* 临时速度存储 */ if(htim->Instance == ATIM_TIMX_PWM) /* 55us */ { /******************************* 六步换向 *******************************/ if(g_bldc_motor1.run_flag == RUN) { if(g_bldc_motor1.dir == CW) { g_bldc_motor1.step_sta = hallsensor_get_state(MOTOR_1); } else { g_bldc_motor1.step_sta = 7 - hallsensor_get_state(MOTOR_1); } if((g_bldc_motor1.step_sta <= 6)&&(g_bldc_motor1.step_sta >= 1)) { pfunclist_m1[g_bldc_motor1.step_sta-1](); } else /* 编码器错误、接触不良、断开等情况 */ { stop_motor1(); g_bldc_motor1.run_flag = STOP; } /******************************* 速度计算 *******************************/ g_bldc_motor1.count_j++; /* 计算速度专用计数值 */ g_bldc_motor1.hall_sta_edge = uemf_edge(g_bldc_motor1.hall_single_sta);/* 检测单个霍尔信号的变化 */ if(g_bldc_motor1.hall_sta_edge == 0) /* 统计单个霍尔信号的高电平时间,当只有一对级的时候,旋转一圈为一个完整脉冲。一高一低相加即旋转一圈所花的时间*/ { /*计算速度*/ if(g_bldc_motor1.dir == CW) temp_speed = (SPEED_COEFF/g_bldc_motor1.count_j); else temp_speed = -(SPEED_COEFF/g_bldc_motor1.count_j); FirstOrderRC_LPF(g_bldc_motor1.speed,temp_speed,0.2379f); /* 一阶滤波 */ g_bldc_motor1.no_single = 0; g_bldc_motor1.count_j = 0; } if(g_bldc_motor1.hall_sta_edge == 1) /* 当采集到下降沿时数据清0 */ { g_bldc_motor1.no_single = 0; g_bldc_motor1.count_j = 0; } if(g_bldc_motor1.hall_sta_edge == 2) /* 霍尔值一直不变代表未换向 */ { g_bldc_motor1.no_single++; /* 不换相时间累计 超时则判定速度为0 */ if(g_bldc_motor1.no_single > 15000) { g_bldc_motor1.no_single = 0; g_bldc_motor1.speed = 0; /* 超时换向 判定为停止 速度为0 */ } } /******************************* 位置记录 *******************************/ if(g_bldc_motor1.step_last != g_bldc_motor1.step_sta) { bldc_dir = check_hall_dir(&g_bldc_motor1); if(bldc_dir == CCW) { g_bldc_motor1.pos -= 1; } else if(bldc_dir == CW) { g_bldc_motor1.pos += 1; } g_bldc_motor1.step_last = g_bldc_motor1.step_sta; } /******************************* PID控制 *******************************/ temp_pwm1 = increment_pid_ctrl(&g_speed_pid,g_bldc_motor1.speed); /* PID控制算法,输出期望值 */ FirstOrderRC_LPF(motor_pwm_s,temp_pwm1,0.085); /* 一阶滤波 */ if(motor_pwm_s < 0) /* 判断正负值 */ { g_bldc_motor1.pwm_duty = -motor_pwm_s; } else { g_bldc_motor1.pwm_duty = motor_pwm_s; } /******************************* 三相电流计算 *******************************/ for(i=0; i<3; i++) { adc_val_m1[i] = g_adc_val[i+2]; adc_amp[i] = adc_val_m1[i] - adc_amp_offset[i][ADC_AMP_OFFSET_TIMES]; /* 运动状态ADC值 - 停机状态ADC值 = 实际作用ADC值 */ if(adc_amp[i]>=0) /* 去除反电动势引起的负电流数据 */ adc_amp_un[i] = adc_amp[i]; } /* 运算母线电流(母线电流为任意两个有开关动作的相电流之和)*/ if(g_bldc_motor1.step_sta == 0x05) { adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[1])*ADC2CURT;/* UV */ } else if(g_bldc_motor1.step_sta== 0x01) { adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[2])*ADC2CURT;/* UW */ } else if(g_bldc_motor1.step_sta== 0x03) { adc_amp_bus= (adc_amp_un[1]+ adc_amp_un[2])*ADC2CURT;/* VW */ } else if(g_bldc_motor1.step_sta== 0x02) { adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[1])*ADC2CURT;/* UV */ } else if(g_bldc_motor1.step_sta == 0x06) { adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[2])*ADC2CURT;/* WU */ } else if(g_bldc_motor1.step_sta == 0x04) { adc_amp_bus= (adc_amp_un[2]+ adc_amp_un[1])*ADC2CURT;/* WV */ } } } else if(htim->Instance == TIM6) { /******************************* 采集电机停机状态下的偏置电压 *******************************/ times_count++; if(g_bldc_motor1.run_flag == STOP) { uint8_t i; uint32_t avg[3] = {0,0,0}; adc_amp_offset[0][adc_amp_offset_p] = g_adc_val[2]; /* 获取电机停机状态下的三相电流 */ adc_amp_offset[1][adc_amp_offset_p] = g_adc_val[3]; adc_amp_offset[2][adc_amp_offset_p] = g_adc_val[4]; adc_amp_offset_p ++; NUM_CLEAR(adc_amp_offset_p,ADC_AMP_OFFSET_TIMES); for(i=0; i<ADC_AMP_OFFSET_TIMES; i++) { avg[0] += adc_amp_offset[0][i]; /* 各相数值累加 */ avg[1] += adc_amp_offset[1][i]; avg[2] += adc_amp_offset[2][i]; } for(i=0; i<3; i++) { avg[i] /= ADC_AMP_OFFSET_TIMES; /* 取平均 */ adc_amp_offset[i][ADC_AMP_OFFSET_TIMES] = avg[i]; /* 赋值 */ } } } }测试结果:
速度环+电流环控制
主函数
int main(void) { uint8_t debug_cmd = 0; /* 存放上位机指令 */ float current_lpf[4] = {0.0f}; /* 存放三相电流以及母线电流 */ uint8_t key,t; char buf[32]; float current[3] = {0.0f}; int16_t speed_diplay = 0; float user_setpoint_temp = 0.0; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */ delay_init(168); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init(); /* 初始化LED */ key_init(); /* 初始化按键 */ lcd_init(); /* 初始化LCD */ bldc_init(168000/18-1,0); /* 18KHz */ bldc_ctrl(MOTOR_1,CCW,0); /* 初始无刷电机接口1速度 */ pid_init(); g_point_color = WHITE; g_back_color = BLACK; lcd_show_string(10,10,200,16,16,"BLDC Motor Test",g_point_color); lcd_show_string(10,30,200,16,16,"KEY0:Step++",g_point_color); lcd_show_string(10,50,200,16,16,"KEY1:Step--",g_point_color); lcd_show_string(10,70,200,16,16,"KEY2:Stop",g_point_color); adc_nch_dma_init(); #if DEBUG_ENABLE /* 开启调试 */ debug_init(); /* PID调试初始化 */ debug_send_motorcode(BLDC_MOTOR); /* 直流电机 */ debug_send_motorstate(IDLE_STATE); /* 电机空闲 */ /* 初始化同步数据(选择第x组PIDX,目标速度地址,P,I,D参数)到上位机 */ debug_send_initdata(TYPE_PID1,(float*)(&g_speed_pid.SetPoint),C_KP,C_KI,C_KD); /* 电流环PID参数(PID1)*/ debug_send_initdata(TYPE_PID2,(float*)(&g_speed_pid.SetPoint),S_KP,S_KI,S_KD); /* 速度环PID参数(PID2)*/ #endif while (1) { t++; if(t % 20 == 0) { sprintf(buf,"PWM_Duty:%.1f%% ",(float)((g_bldc_motor1.pwm_duty/MAX_PWM_DUTY)*100)); /* 显示控制PWM占空比 */ lcd_show_string(10,110,200,16,16,buf,g_point_color); user_setpoint_temp = (*user_setpoint); speed_diplay = g_bldc_motor1.speed; sprintf(buf,"SetSpeed:%4d ",(int16_t)user_setpoint_temp); /* 显示设置速度 */ lcd_show_string(10,110,200,16,16,buf,g_point_color); sprintf(buf,"M1 speed:%4d ",speed_diplay); /* 显示转速 */ lcd_show_string(10,130,200,16,16,buf,g_point_color); sprintf(buf,"M1 pos:%4d",g_bldc_motor1.pos); /* 显示位置变化 */ lcd_show_string(10,150,200,16,16,buf,g_point_color); sprintf(buf,"PWM_Duty:%.1f%% ",(float)((g_bldc_motor1.pwm_duty/(float)MAX_PWM_DUTY)*100)); /* 显示控制PWM占空比 */ lcd_show_string(10,170,200,16,16,buf,g_point_color); sprintf(buf,"Power:%.3fV ",g_adc_value[0]*ADC2VBUS); lcd_show_string(10,190,200,16,16,buf,g_point_color); sprintf(buf,"Temp:%.1fC ",get_temp(g_adc_value[1])); lcd_show_string(10,210,200,16,16,buf,g_point_color); LED0_TOGGLE(); /* LED0(红灯) 翻转 */ /* 三相电流计算 */ current[0] = adc_amp_un[0]* ADC2CURT;/*U*/ current[1] = adc_amp_un[1]* ADC2CURT;/*V*/ current[2] = adc_amp_un[2]* ADC2CURT;/*W*/ /*一阶数字滤波 滤波系数0.1 用于显示*/ FirstOrderRC_LPF(current_lpf[0],current[0],0.1f); FirstOrderRC_LPF(current_lpf[1],current[1],0.1f); FirstOrderRC_LPF(current_lpf[2],current[2],0.1f); FirstOrderRC_LPF(current_lpf[3],adc_amp_bus,0.1f); if(g_bldc_motor1.run_flag == STOP) /* 停机的电流显示 */ { current_lpf[0]=0; current_lpf[1]=0; current_lpf[2]=0; current_lpf[3]=0; } sprintf(buf,"Amp U:%.3fmA ",(float)current_lpf[0]); lcd_show_string(10,230,200,16,16,buf,g_point_color); sprintf(buf,"Amp V:%.3fmA ",(float)current_lpf[1]); lcd_show_string(10,250,200,16,16,buf,g_point_color); sprintf(buf,"Amp W:%.3fmA ",(float)current_lpf[2]); lcd_show_string(10,270,200,16,16,buf,g_point_color); sprintf(buf,"Amp Bus:%.3fmA ",(float)adc_amp_bus); lcd_show_string(10,290,200,16,16,buf,g_point_color); } key = key_scan(0); if(key == KEY0_PRES) /* 按下KEY0目标速度值++ */ { g_bldc_motor1.run_flag = RUN; /* 开启运行 */ start_motor1(); /* 开启运行 */ if(g_bldc_motor1.dir == CCW && *user_setpoint == 0) /* 切换方向条件*/ { g_bldc_motor1.dir = CW; } *user_setpoint += 400; /* 逆时针旋转下递增 */ if(*user_setpoint >= 3000) /* 最高不超过3000PRM */ *user_setpoint = 3000; if(*user_setpoint == 0) { pid_init(); /* 初始化PID */ g_bldc_motor1.run_flag = STOP; /* 标记停机 */ stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; } debug_data_temp = *user_setpoint; } else if(key == KEY1_PRES) /* 按下KEY1目标速度值-- */ { g_bldc_motor1.run_flag = RUN; /* 开启运行 */ start_motor1(); /* 运行电机 */ /* 切换方向条件 */ if(g_bldc_motor1.dir == CW && *user_setpoint == 0) { g_bldc_motor1.dir = CCW; } *user_setpoint -= 400; /* 逆时针旋转下递增 */ if(*user_setpoint <= -3000) /* 最高不超过300PRM */ *user_setpoint = -3000; if(*user_setpoint == 0) { pid_init(); /* 初始化PID */ g_bldc_motor1.run_flag = STOP; /* 标记停机 */ stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; } debug_data_temp = *user_setpoint; } else if(key == KEY2_PRES) /* 按下KEY2关闭电机 */ { bldc_speed_stop(); } #if DEBUG_ENABLE /* Debug发送部分 */ /* 主要显示参数 */ debug_send_valtage(g_adc_value[0] * ADC2VBUS); /* 发送电压*/ debug_send_speed(g_bldc_motor1.speed); /* 发送速度*/ debug_send_temp(50,get_temp(g_adc_value[1])); /* 发送电机温度、驱动板温度*/ debug_send_current((float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000));/*发送电流*/ /* 电流波形和速度波形 */ debug_send_wave_data(1,(int16_t)g_bldc_motor1.speed); /* 选择通道1 发送实际速度 */ debug_send_wave_data(2,(int16_t)*user_setpoint); /* 选择通道2 发送目标速度 */ debug_send_wave_data(3,current_lpf[0]); /* 选择通道3 发送实际电流U */ debug_send_wave_data(4,current_lpf[1]); /* 选择通道4 发送实际电流V */ debug_send_wave_data(5,current_lpf[2]); /* 选择通道5 发送实际电流W */ debug_send_wave_data(7,current_lpf[3]); /* 选择通道7 发送实际电流 */ debug_send_wave_data(8,g_current_pid.SetPoint); /* 选择通道8 发送目标电流 */ /* Debug接收部分 */ debug_receive_pid(TYPE_PID1,(float*)&g_current_pid.Proportion, /* 查询接收PID助手的PID1参数*/ (float*)&g_current_pid.Integral,(float*)&g_current_pid.Derivative); debug_receive_pid(TYPE_PID2,(float*)&g_speed_pid.Proportion, /* 查询接收PID助手的PID2参数*/ (float*)&g_speed_pid.Integral,(float*)&g_speed_pid.Derivative); debug_cmd = debug_receive_ctrl_code(); /* 读取命令 */ if(debug_cmd == HALT_CODE) /* 停机 */ { pid_init(); g_bldc_motor1.run_flag = STOP; /* 标记停机 */ stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; } else if(debug_cmd == RUN_CODE) /* 运行 */ { g_bldc_motor1.run_flag = RUN; /* 运行标记 */ *user_setpoint = 400; /* 自动设置目标 */ debug_data_temp = *user_setpoint; start_motor1(); /* 启动电机 */ debug_send_motorstate(RUN_STATE); /* 电机运行 */ } else if (debug_cmd == BREAKED) /* 刹车(电机停止 点击电机运行才可解除) */ { *user_setpoint = 0; /* 减速直至0 */ debug_send_motorstate(BREAKED_STATE); /* 电机刹车 */ } #endif delay_ms(10); } } /** * @brief 关闭电机并清除状态 * @param 无 * @retval 无 */ void bldc_speed_stop(void) { pid_init(); g_bldc_motor1.run_flag = STOP; /* 标记停机 */ stop_motor1(); /* 停机 */ g_bldc_motor1.speed = 0; motor_pwm_s = 0; g_bldc_motor1.pwm_duty = 0; }PID
void pid_init(void) { /* 设定电流目标1500mA(空载最小电流400mA左右)较高的转速对应的电流较大,力矩较大,可适应较高的转速调节*/ /* 【注意】如设置的转速对应的电流超过了电流设定值,将导致PID转至电流环调节,转速将无法继续提升 */ g_current_pid.SetPoint = 1500.0; g_current_pid.ActualValue = 0.0; /* 设定目标Desired Value */ g_current_pid.LastError = 0.0; /* Error[1] */ g_current_pid.LastError = 0.0; /* Error[-1] */ g_current_pid.PrevError = 0.0; /* Error[-2] */ g_current_pid.Proportion = C_KP; /* 比例常数 Proportional Const */ g_current_pid.Integral = C_KI; /* 积分常数 Integral Const */ g_current_pid.Derivative = C_KD; /* 微分常数 Derivative Const */ g_current_pid.IngMax = 9000; g_current_pid.IngMin = 600; g_current_pid.OutMin = 600; g_current_pid.OutMax = 9000; g_speed_pid.SetPoint = 0; /* 设定目标Desired Value */ g_speed_pid.ActualValue = 0.0; /* 设定目标Desired Value */ g_speed_pid.Ui = 0.0; g_speed_pid.Up = 0.0; g_speed_pid.Ud = 0.0; g_speed_pid.Error = 0.0; /* Error[1] */ g_speed_pid.LastError = 0.0; /* Error[-1] */ g_speed_pid.PrevError = 0.0; /* Error[-2] */ g_speed_pid.Proportion = S_KP; /* 比例常数 Proportional Const */ g_speed_pid.Integral = S_KI; /* 积分常数 Integral Const */ g_speed_pid.Derivative = S_KD; /* 微分常数 Derivative Const */ g_speed_pid.IngMax = 9000; g_speed_pid.IngMin = -9000; g_speed_pid.OutMax = 9000; /* 输出限制 */ g_speed_pid.OutMin = -9000; } /** * @brief 位置式PID算法 * @param *PID:PID结构体句柄所对应的目标值 * @param Feedback_value : 实际值 * @retval 目标控制量 */ int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value) { PID->Error = (float)(PID->SetPoint - Feedback_value); /* 目标值与实际值的偏差值 */ PID->Up = PID->Proportion * PID->Error; PID->Ui += (PID->Error * PID->Integral); LIMIT_OUT(PID->Ui,PID->IngMax,PID->IngMin); /* 积分限制 */ PID->Ud = PID->Derivative * (PID->Error - PID->LastError); PID->ActualValue = PID->Up + PID->Ui + PID->Ud; LIMIT_OUT(PID->ActualValue,PID->OutMax,PID->OutMin); /* 输出限制 */ PID->LastError = PID->Error; /* 存储上次误差,以便下次计算使用 */ return ((int32_t)(PID->ActualValue)); /* 返回实际控制数值 */ }void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { uint8_t i; uint8_t bldc_dir=0; static uint8_t times_count=0; /* 定时器时间记录 */ int16_t temp_speed=0; /* 临时速度存储 */ if(htim->Instance == ATIM_TIMX_PWM) /* 55us */ { if(g_bldc_motor1.run_flag == RUN) { /******************************* 三相电流计算 *******************************/ for(i=0; i<3; i++) { adc_val_m1[i] = g_adc_val[i+2]; /* UVW三相ADC通道 */ adc_amp[i] = adc_val_m1[i] - adc_amp_offset[i][ADC_AMP_OFFSET_TIMES]; if(adc_amp[i] >= 0) /* 去除反电动势引起的负电流数据 */ adc_amp_un[i] = adc_amp[i]; } /* 运算母线电流(母线电流为任意两个有开关动作的相电流之和)*/ if(g_bldc_motor1.step_sta == 0x05) { adc_amp_bus = (adc_amp_un[0] + adc_amp_un[1])*ADC2CURT; /* UV */ } else if(g_bldc_motor1.step_sta == 0x01) { adc_amp_bus = (adc_amp_un[0] + adc_amp_un[2])*ADC2CURT; /* UW */ } else if(g_bldc_motor1.step_sta == 0x03) { adc_amp_bus = (adc_amp_un[1]+ adc_amp_un[2])*ADC2CURT; /* VW */ } else if(g_bldc_motor1.step_sta == 0x02) { adc_amp_bus = (adc_amp_un[0]+ adc_amp_un[1])*ADC2CURT; /* UV */ } else if(g_bldc_motor1.step_sta == 0x06) { adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[2])*ADC2CURT; /* WU */ } else if(g_bldc_motor1.step_sta == 0x04) { adc_amp_bus= (adc_amp_un[2]+ adc_amp_un[1])*ADC2CURT; /* WV */ } /******************************* 六步换向 *******************************/ if(g_bldc_motor1.dir == CW) { g_bldc_motor1.step_sta = hallsensor_get_state(MOTOR_1); } else { g_bldc_motor1.step_sta = 7 - hallsensor_get_state(MOTOR_1); } if((g_bldc_motor1.step_sta <= 6)&&(g_bldc_motor1.step_sta >= 1)) { pfunclist_m1[g_bldc_motor1.step_sta-1](); } else /* 编码器错误、接触不良、断开等情况 */ { stop_motor1(); g_bldc_motor1.run_flag = STOP; } /******************************* 速度计算 *******************************/ g_bldc_motor1.count_j++; /* 计算速度专用计数值 */ g_bldc_motor1.hall_sta_edge = uemf_edge(g_bldc_motor1.hall_single_sta); if(g_bldc_motor1.hall_sta_edge == 0) /* 统计单个霍尔信号的高电平时间 */ { /* 计算速度 */ if(g_bldc_motor1.dir == CW) temp_speed = (SPEED_COEFF/g_bldc_motor1.count_j); else temp_speed = -(SPEED_COEFF/g_bldc_motor1.count_j); FirstOrderRC_LPF(g_bldc_motor1.speed,temp_speed,0.2379); g_bldc_motor1.no_single = 0; g_bldc_motor1.count_j = 0; } if(g_bldc_motor1.hall_sta_edge == 1) { g_bldc_motor1.no_single = 0; g_bldc_motor1.count_j = 0; } if(g_bldc_motor1.hall_sta_edge == 2) /* 霍尔值一直不变代表未换向 */ { g_bldc_motor1.no_single++; /* 不换相时间累计 超时则判定速度为0 */ if(g_bldc_motor1.no_single > 15000) { g_bldc_motor1.no_single = 0; g_bldc_motor1.speed = 0; /* 超时换向 判定为停止 速度为0 */ } } /******************************* 位置记录以及堵转标记 *******************************/ if(g_bldc_motor1.step_last != g_bldc_motor1.step_sta) { g_bldc_motor1.hall_keep_t = 0; bldc_dir = check_hall_dir(&g_bldc_motor1); if(bldc_dir == CCW) { g_bldc_motor1.pos -= 1; } else if(bldc_dir == CW) { g_bldc_motor1.pos += 1; } g_bldc_motor1.step_last = g_bldc_motor1.step_sta; } else if(g_bldc_motor1.run_flag == RUN) /* 运行且霍尔保持时 */ { g_bldc_motor1.hall_keep_t++; /* 换向一次所需计数值(时间) 单位1/18k */ if(g_bldc_motor1.hall_keep_t > 15000) /* 堵转 */ { g_bldc_motor1.hall_keep_t = 0; #if LOCK_TAC stop_motor1(); g_bldc_motor1.run_flag = STOP;; /* 标记停机 */ g_bldc_motor1.pwm_duty = 0; #endif g_bldc_motor1.locked_rotor = 1; /* 标记堵转 */ } } /******************************* PID控制 *******************************/ if(g_bldc_motor1.run_flag == RUN) /* 进入PID闭环控制 */ { pid_c_count++; pid_s_count++; if(pid_s_count > 2) { /* 开启上位机调试 在PID执行之前对调节数值进行预先判断 */ #if DEBUG_ENABLE debug_set_point_range(3000,-3000,6000); /* 控制目标调节范围(3000~-3000)并且最大步进值不超过6000 RPM */ debug_data_temp = *user_setpoint; if(*user_setpoint < 0) /* 上位机指令欲切换旋转方向 */ { /* 为简化控制逻辑 只以电机状态作为判依据(同步)*/ if(g_bldc_motor1.speed == 0) { g_bldc_motor1.dir = CCW; } else if(g_bldc_motor1.dir == CW && g_bldc_motor1.speed != 0)/* 当前状态不能立刻切换 */ { *user_setpoint = 0; } } else if(*user_setpoint > 0) { if(g_bldc_motor1.speed == 0) { g_bldc_motor1.dir = CW; } else if(g_bldc_motor1.dir == CCW && g_bldc_motor1.speed != 0)/* 当前状态不能立刻切换 */ { *user_setpoint = 0; } } #endif /******************************* PID计算 *******************************/ temp_pwm1 = increment_pid_ctrl(&g_speed_pid,g_bldc_motor1.speed); FirstOrderRC_LPF(motor_pwm_s,temp_pwm1,0.085); if(motor_pwm_s < 0) { motor_pwm_sl = -motor_pwm_s; } else { motor_pwm_sl = motor_pwm_s; } *user_setpoint = debug_data_temp; /* 重新保持上位机指令要求 */ pid_s_count = 0; } if(pid_c_count > 1) /* 电流环 */ { /* 换向尖峰电流大于设定的电流值将导致PID调节转至电流环调节 速度环无法起作用,转速无法调节 */ if(adc_amp_bus > (g_current_pid.SetPoint - 20)) { cf_count++; /* 滤除换向尖峰电流的影响 */ if(cf_count > 4) { cf_count = 0; temp_pwm2 = increment_pid_ctrl(&g_current_pid,adc_amp_bus); FirstOrderRC_LPF(motor_pwm_c,temp_pwm2,0.085);/* 一阶数字滤波 滤波系数0.08 */ } } else { cf_count = 0; temp_pwm2 = increment_pid_ctrl(&g_current_pid,adc_amp_bus); FirstOrderRC_LPF(motor_pwm_c,temp_pwm2,0.085); } pid_c_count = 0; } /* 电流环输出值大于速度环输出则使用速度环调节 */ if(motor_pwm_c > motor_pwm_sl) { g_bldc_motor1.pwm_duty = motor_pwm_sl; if(motor_pwm_s < 0) /* 正反转积分控制 */ g_current_pid.Ui = -g_speed_pid.Ui; else g_current_pid.Ui = g_speed_pid.Ui; } else /* 速度环输出值大于电流环输出则使用电流环调节 */ { g_bldc_motor1.pwm_duty = motor_pwm_c; if(motor_pwm_s < 0) g_speed_pid.Ui = -g_current_pid.Ui; else g_speed_pid.Ui = g_current_pid.Ui; } } } } if(htim->Instance == TIM6) { /******************************* 采集电机停机状态下的偏置电压 *******************************/ times_count++; if(g_bldc_motor1.run_flag == STOP) { uint8_t i; uint32_t avg[3] = {0,0,0}; adc_amp_offset[0][adc_amp_offset_p] = g_adc_val[2]; /* 得到还未开始运动时三相的基准电压 */ adc_amp_offset[1][adc_amp_offset_p] = g_adc_val[3]; adc_amp_offset[2][adc_amp_offset_p] = g_adc_val[4]; adc_amp_offset_p++; NUM_CLEAR(adc_amp_offset_p,ADC_AMP_OFFSET_TIMES); /* 最大采集ADC_AMP_OFFSET_TIMES次,超过即从0开始继续采集 */ for(i = 0; i < ADC_AMP_OFFSET_TIMES; i++) /* 将采集的每个通道值累加 */ { avg[0] += adc_amp_offset[0][i]; avg[1] += adc_amp_offset[1][i]; avg[2] += adc_amp_offset[2][i]; } for(i = 0; i < 3; i++) /* 取平均即软件滤波 */ { avg[i] /= ADC_AMP_OFFSET_TIMES; adc_amp_offset[i][ADC_AMP_OFFSET_TIMES] = avg[i]; /* 得到还未开始运动时的基准电压 */ } } /******************************* 定时判断电机是否发生堵塞 *******************************/ if(times_count == SMAPLSE_PID_SPEED) { #if (LOCK_TAC == 2) if(g_bldc_motor1.locked_rotor == 1) /* 堵转处理,当到达一定速度后可进入闭环控制 */ { clc++; if(clc > 50) /* 延迟2s后重新启动 */ { #if DEBUG_ENABLE /*开启调试*/ debug_send_motorstate(RUN_STATE); /* 电机运行*/ #endif clc = 0; pid_init(); stop_motor1(); g_speed_pid.SetPoint = 400.0; /* 400PRM */ g_bldc_motor1.pwm_duty = 500; /* 加速启动速度 */ g_bldc_motor1.run_flag = RUN; /* 开启运行 */ start_motor1(); /* 运行电机 */ g_bldc_motor1.locked_rotor = 0; } } #endif times_count = 0; } } }测试结果: