活动介绍

#include “encoder.h” #include “main.h” #include “usart.h” #include “stdio.h” #include “tim.h” #include “math.h” #include “hall.h” #include “mymath.h” #include “gpio.h” #include “arm_math.h” #include “adc.h” #include “foc.h” float Encoder_Theta_coefficient; //编码器转换成电角度的系数值 float Encoder_Theta_m_coefficient; //编码器转换成机械角度的系数值 float We_coefficient; //编码器转电角速度的系数值 float Wm_coefficient; //编码器转机械角速度的系数值 float t; float Current_BandWidth; //电流环带宽 float CurrentLoop_Kpd; //电流环D轴比例增益 float CurrentLoop_Kid ; //电流环D轴积分增益 float CurrentLoop_Kpq ; //电流环Q轴比例增益 float CurrentLoop_Kiq; //电流环Q轴积分增益 int Speed_BandWidth ; //速度环带宽 float Ba ; //有功阻尼系数 float SpeedLoop_Kp ; //速度环比例增益 float SpeedLoop_Ki ; //速度环积分增益 void Foc_Init() { float temp1,temp2; Motor1.Wm_Set=0; Motor1.Id_Set=0; //设定为20k(电流采样由定时器1的PWM比较触发,频率也为20k) Motor1_Time.PWM_Fre = 168000000.0f / (TIM1_ARR * TIM1_PSC) / 2; Motor1_Time.PWM_Ts = 1 / Motor1_Time.PWM_Fre; //电流环:速度环:位置环的速度比例有多种,有16:4:1, 2:1:1, 1:1:1,都可以 Motor1_Time.Current_Loop_TimeGain = 1.0f; Motor1_Time.Speed_Loop_TimeGain = 1.0f; Motor1_Time.Locate_Loop_TimeGain = 1.0f; //计算频率设定为40k Motor1_Time.Calculate_Fre = 84000000.0f / (TIM2_ARR * TIM2_PSC ); Motor1_Time.Calculate_Ts = 1 / Motor1_Time.Calculate_Fre; //电流环设定为20k Motor1_Time.Current_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Current_Loop_TimeGain; Motor1_Time.Current_Loop_Ts = 1 / Motor1_Time.Current_Loop_Fre; //速度环设定为10k Motor1_Time.Speed_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Speed_Loop_TimeGain; Motor1_Time.Speed_Loop_Ts = 1 / Motor1_Time.Speed_Loop_Fre; //位置设定为10k Motor1_Time.Locate_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Locate_Loop_TimeGain; Motor1_Time.Locate_Loop_Ts = 1 / Motor1_Time.Locate_Loop_Fre; //编码器计数值转换为角度的系数值 Encoder_Theta_coefficient = 360 * MOTOR1_Pn /(ENCODER_NUMBER * 4); Encoder_Theta_m_coefficient = 360 /(ENCODER_NUMBER * 4); //编码器计数值转换为角速度的系数值 We_coefficient = 2* PI /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; Wm_coefficient = 60.0f /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; //计算电流环的PI系数 temp1=MOTOR1_Ld/MOTOR1_R; temp2=MOTOR1_Lq/MOTOR1_R; t=(temp1<temp2)?temp1:temp2; Current_BandWidth = 2*PI/t; //算出来的带宽太大了,有九千多,感觉是电机的参数给的可能有问题?不知道,反正不用这个带宽 //小了响应速度不够快,大了电流波动大 Current_BandWidth =1500; CurrentLoop_Kpd = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kid = Current_BandWidth * MOTOR1_R; CurrentLoop_Kpq = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kiq = Current_BandWidth * MOTOR1_R; //计算速度环的带宽 Speed_BandWidth = 150; Ba = (Speed_BandWidth *MOTOR1_J - MOTOR1_B ) / (1.5f * MOTOR1_Pn * MOTOR1_flux); //这个值在速度环我看别人的仿真不知道为啥给扔了,在速度环的时候不要这个 SpeedLoop_Kp = ( Speed_BandWidth * MOTOR1_J ) / (1.5f * MOTOR1_Pn * MOTOR1_flux) ; SpeedLoop_Ki = Speed_BandWidth * SpeedLoop_Kp; } //整个闭环控制计算可以满足40k的处理频率 void Foc_Control(void) { static int Number1=0,Number2=0,Number3=0; Motor1.Encoder_Value=Get_Encoder_Count(); //根据编码器计算角度 Motor1.Theta=Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_coefficient,0.0f,360.0f); Motor1.Theta_m = Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_m_coefficient,0.0f,360.0f); if(++Number1 >= Motor1_Time.Locate_Loop_TimeGain) { //执行位置环 Locate_Loop(); Number1= 0; } if(++Number2 >= Motor1_Time.Speed_Loop_TimeGain) { //执行速度环 Speed_Loop(); Number2= 0; } if(++Number3 >= Motor1_Time.Current_Loop_TimeGain) { //执行电流环 Current_Loop(); Number3= 0; } //对电压进行反Park变换 Park_Inverse_Transform(); //执行SVPWM并设定电机占空比 FOC_SVPWM(); //执行高频方波注入算法 // HFI_Sensorless(); } //SVPWM void FOC_SVPWM(void) { uint8_t N,A,B,C; float Vref1,Vref2,Vref3,X,Y,Z,temp1,Tfirst,Tsecond,T0,Ta,Tb,Tc,Tcm1,Tcm2,Tcm3; //计算转子所在的山区 Vref1=Motor1.Ubeta; Vref2=(SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; Vref3=(-SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; A=Vref1>0 ? 1 :0 ; B=Vref2>0 ? 1 :0 ; C=Vref3>0 ? 1 :0 ; N=4*C+2*B+A; temp1=SQRT_3* SVPWM_TS/ UDC; X=temp1*Vref1; Y=-temp1*Vref3; Z=-temp1*Vref2; //矢量作用时间计算 switch(N) { case 1: Tfirst= Z; Tsecond= Y; Motor1.Sector= 2; break; case 2: Tfirst= Y; Tsecond= -X; Motor1.Sector= 6; break; case 3: Tfirst= -Z; Tsecond= X; Motor1.Sector= 1; break; case 4: Tfirst= -X; Tsecond= Z; Motor1.Sector= 4; break; case 5: Tfirst= X; Tsecond= -Y; Motor1.Sector= 3; break; case 6: Tfirst= -Y; Tsecond= -Z; Motor1.Sector= 5; break; default: Tfirst= 0; Tsecond= 0; Motor1.Sector= 0; break; } //超限判断 if(( Tfirst + Tsecond )> SVPWM_TS) { Tfirst=(Tfirst/(Tfirst+Tsecond))/ SVPWM_TS; Tsecond=(Tsecond/(Tfirst+Tsecond))/ SVPWM_TS; } T0= (SVPWM_TS- Tfirst- Tsecond)/2; Ta=T0/2; Tb=Ta+Tfirst/2; Tc=Tb+Tsecond/2; //每相桥臂切换时间计算 switch(N) { case 1: Tcm1=Tb; Tcm2=Ta; Tcm3=Tc; break; case 2: Tcm1=Ta; Tcm2=Tc; Tcm3=Tb; break; case 3: Tcm1=Ta; Tcm2=Tb; Tcm3=Tc; break; case 4: Tcm1=Tc; Tcm2=Tb; Tcm3=Ta; break; case 5: Tcm1=Tc; Tcm2=Ta; Tcm3=Tb; break; case 6: Tcm1=Tb; Tcm2=Tc; Tcm3=Ta; break; default: break; } Motor1.Tcm1=Tcm1; Motor1.Tcm2=Tcm2; Motor1.Tcm3=Tcm3; //设置定时器1的PWM占空比 __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,TIM1_ARR*(1-2*Tcm1/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,TIM1_ARR*(1-2*Tcm2/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,TIM1_ARR*(1-2*Tcm3/ SVPWM_TS)); } void ADC_Transform(void) { //顺便软件滤波一下 //按照无刷驱动板的原理图上的公式算的,0.02是电阻值,电流转成的电压 除以电阻就是电流 // Motor1.Ibeta=((HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1) * 3.3f /4095.0f) -1.24f)/8.0f/0.02f; 单位A Motor1.Ia =IRFilter1(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1)) * 0.005f -7.75f; Motor1.Ib =IRFilter2(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_2)) * 0.005f -7.75f; Motor1.Ic =IRFilter3(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_3)) * 0.005f -7.75f; } //位置环 void Locate_Loop(void) { //用不上暂时还没学,就没写 } //速度环 void Speed_Loop(void) { static float temp; static int Encoder_Last; float Encoder_W_temp; if(Encoder_Z_Flag==1) { Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last + Encoder_temp); Encoder_Z_Flag=0; } else Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last); //根据编码器计算角速度 速度范围波动会比较大,因为采样频率太高了,编码器的分辨率又不够高,所以这是很正常的。降低采样频率(即减小TIM2的时钟频率)可以减小波动 Motor1.We = Encoder_W_temp * We_coefficient; //单位 rad/s(电角速度) Motor1.Wm = Encoder_W_temp * Wm_coefficient; //单位 RPM(角速度) Encoder_Last=Motor1.Encoder_Value; Motor1.Iq_Set = SpeedLoop_Kp * (Motor1.Wm_Set - Motor1.Wm) + temp; temp = Value_SetMaxMin(temp +SpeedLoop_Ki * (Motor1.Wm_Set - Motor1.Wm) * Motor1_Time.Speed_Loop_Ts,-10.0f,10.0f); //给个限值防止他一直往上加,一启动电机速度飞飚,这个值是慢慢试的,大概速度到3000RPM时,这个值为10.多 } //电流环 void Current_Loop(void) { static float temp,temp2; //对三相电流进行采样,三相电流相加不为0,是因为硬件上有误差(野火客服说的,可恶!) 电流采样频率和电流频率一样都为20k ADC_Transform(); //对电流进行Clark变换,再Park变换 Motor1.Ialpha = Motor1.Ia; Motor1.Ibeta = (Motor1.Ia + 2*Motor1.Ib)/SQRT_3; Motor1.Id = Motor1.Ialpha* qfcosd(Motor1.Theta)+ Motor1.Ibeta* qfsind(Motor1.Theta); Motor1.Iq = -Motor1.Ialpha* qfsind(Motor1.Theta)+ Motor1.Ibeta* qfcosd(Motor1.Theta); //PI环节 //这个电流环的积分项我没整明白,加上积分的话,积分项会直接冲到最大,且一直为最大,然后就电机转动也没有更好,感觉就是反作用,先不整了,把它干掉,后面再研究 Motor1.Ud = CurrentLoop_Kpd *(Motor1.Id_Set - Motor1.Id) + temp - Motor1.We * MOTOR1_Lq * Motor1.Iq; // temp = Value_SetMaxMin( temp+ CurrentLoop_Kid * (Motor1.Id_Set - Motor1.Id) * Motor1.Calculate_Ts, -0.1f, 0.1f); Motor1.Uq = CurrentLoop_Kpq *(Motor1.Iq_Set - Motor1.Iq) + temp2 + Motor1.We * (MOTOR1_Ld * Motor1.Id + MOTOR1_flux); // temp2 = Value_SetMaxMin( temp2+ CurrentLoop_Kiq * (Motor1.Iq_Set - Motor1.Iq) * Motor1.Calculate_Ts, -0.1f, 0.1f); //合成电压最大值为SQRT_3 * UDC / 3 = 13.85 , Ud 最大值设为3 是为了高频方波注入算法, 这些限值都是试出来的,我也不会算理论值;这样限值转速最大能到2600RPM左右,够了 Motor1.Ud = Value_SetMaxMin(Motor1.Ud,-3.0f,3.0f); Motor1.Uq = Value_SetMaxMin(Motor1.Uq,-12.0f,12.0f); // Motor1.Ud = 0; // Motor1.Uq = 2; } 这是电机控制FOC的速度和电流环驱动代码 #include "main.h" #include "adc.h" #include "dma.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" #include "bsp_key.h" #include "hall.h" #include "arm_math.h" #include "arm_const_structs.h" #include "math.h" #include "encoder.h" #include "mymath.h" #include "foc.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ MOTOR Motor1; TIME Motor1_Time; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM5_Init(); MX_USART1_UART_Init(); MX_ADC1_Init(); MX_DMA_Init(); /* USER CODE BEGIN 2 */ My_Timer_Init(); Encoder_Init(); Foc_Init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //按键1按下,电机启动 if(Key_Scan(KEY1_GPIO_Port,KEY1_Pin)==KEY_ON) { //点亮LED_D11,串口输出电机启动,打开控制端口,设置初始速度(占空比)和初始方向,判断扇区并启动一次 LED_D11_ON; printf("Motor Running\r\n"); HAL_GPIO_WritePin(MOTOR_SD_GPIO_Port,MOTOR_SD_Pin,GPIO_PIN_SET); // Motor1.Direction=Motor_Clockwise; // Motor1.Speed=Motor_Speed_20; //初始执行一次霍尔换相 // HAL_TIM_PeriodElapsedCallback(&htim2); Motor1.Wm_Set=800; }else if(Key_Scan(KEY2_GPIO_Port,KEY2_Pin)==KEY_ON) //按键2按下,电机停止 { //关闭LED_D11,串口输出电机停止,关闭控制端口,设置初始速度(占空比)和初始方向 LED_D11_OFF; printf("Motor Stop\r\n"); HAL_GPIO_WritePin(MOTOR_SD_GPIO_Port,MOTOR_SD_Pin,GPIO_PIN_RESET); // Motor1.Direction=Motor_Stop; // Motor1.Speed=Motor_Speed_0; Motor1.Wm_Set=0; }else if(Key_Scan(KEY3_GPIO_Port,KEY3_Pin)==KEY_ON) //按键3按下,电机加速 { // if(Motor1.Speed<Motor_Speed_100) // Motor1.Speed++; Motor1.Wm_Set +=100; }else if(Key_Scan(KEY4_GPIO_Port,KEY4_Pin)==KEY_ON) //按键4按下,电机减速 { // if(Motor1.Speed>Motor_Speed_20) // Motor1.Speed--; Motor1.Wm_Set -=100; }else if(Key_Scan(KEY5_GPIO_Port,KEY5_Pin)==KEY_ON) //按键5按下,电机反向 { // if(Motor1.Direction==Motor_Clockwise) // Motor1.Direction=Motor_AntiClockwise; // else if(Motor1.Direction==Motor_AntiClockwise) // Motor1.Direction=Motor_Clockwise; } printf("%.2f,%.2f\n",Motor1.Wm,Motor1.Wm_Set); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 168; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ //定时器2更新中断(20k) void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim == &htim2) { //测试中断所需时间 HAL_GPIO_WritePin(GPIO_TEST1_GPIO_Port,GPIO_TEST1_Pin,GPIO_PIN_SET); Foc_Control(); HAL_GPIO_WritePin(GPIO_TEST1_GPIO_Port,GPIO_TEST1_Pin,GPIO_PIN_RESET); } } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 这是主函数,我现在需要你为代码添加位置环的控制部分,要求可以实现位置控制的功能

时间: 2025-07-20 21:21:35 浏览: 8
<think>我们将在现有代码的基础上添加位置环控制。根据之前的讨论,我们需要在FOC控制循环中集成位置环,并且位置环的执行频率由Motor1_Time.Locate_Loop_TimeGain控制(在Foc_Init中初始化为1,即与计算频率相同,40kHz)。但实际位置环不需要这么高的频率,我们可以调整Motor1_Time.Locate_Loop_TimeGain来降低位置环的执行频率。 位置环控制思路: 1. 添加目标位置变量(target_angle)和位置控制使能标志(position_enabled)到Motor1结构体中,或者作为全局变量。 2. 在位置环函数Locate_Loop()中,如果位置控制使能,则计算位置误差(注意角度是0-360°的循环,需要处理边界),然后通过位置控制器(例如比例控制器)计算出速度指令(Motor1.Wm_Set)。 3. 在速度环中,速度环的设定值Motor1.Wm_Set将由位置环的输出提供(当位置控制使能时)。 修改步骤: 步骤1:在foc.h中定义位置环相关的变量和函数 步骤2:在foc.c中实现位置环函数Locate_Loop() 步骤3:修改main.c,添加按键控制目标位置和位置控制使能 考虑到现有代码结构,我们将在Motor1结构体中添加位置环相关的变量。但是,由于Motor1结构体在foc.h中定义,我们可以在foc.h中修改Motor1结构体。 但是,由于现有代码中Motor1结构体没有位置环相关变量,我们需要扩展它。另外,为了避免对原有代码造成太大影响,我们也可以使用全局变量。这里为了保持代码的模块化,我们扩展Motor1结构体。 然而,查看现有代码,Motor1结构体在foc.h中并没有定义,而是在main.c中定义为全局变量Motor1。因此,我们需要在foc.h中声明Motor1为外部变量,并在foc.h中定义位置环控制所需的新变量(如目标位置、位置使能标志)作为Motor1结构体的成员。 但是,由于原代码中Motor1结构体类型定义在foc.c中(未在头文件中定义),这样其他文件无法访问其成员。因此,我们需要将Motor1结构体类型定义移到foc.h中,并使其成为公共结构体。 由于修改较大,我们采用另一种方法:在foc.c中添加全局变量,并在foc.h中通过extern声明,然后在main.c中通过函数接口设置这些变量。 为了最小化修改,我们选择在foc.c中添加两个全局变量: float target_angle = 0.0f; // 目标位置(度) uint8_t position_enabled = 0; // 位置控制使能标志 并在foc.h中声明: extern float target_angle; extern uint8_t position_enabled; 然后,在位置环函数Locate_Loop()中,使用这些变量。 步骤1:修改foc.h 添加以下声明: extern float target_angle; extern uint8_t position_enabled; 步骤2:在foc.c中定义这些全局变量,并实现Locate_Loop()函数 步骤3:修改main.c中的按键控制,通过按键设置目标位置和使能位置控制。 具体实现如下:</think>### 位置环控制集成方案 我将为您的FOC电机控制系统添加位置环控制功能,保持原有结构不变,仅添加必要的位置控制逻辑。以下是完整的修改方案: #### 1. 在`foc.h`中添加位置控制声明 ```c /* 添加位置控制声明 */ #ifdef __cplusplus extern "C" { #endif // 位置环控制函数声明 void Locate_Loop(void); // 位置控制变量声明 extern float target_angle; // 目标位置(度) extern uint8_t position_enabled; // 位置控制使能标志 #ifdef __cplusplus } #endif ``` #### 2. 在`foc.c`中添加位置控制实现 ```c /* 添加位置控制全局变量 */ float target_angle = 0.0f; // 目标位置(度) uint8_t position_enabled = 0; // 位置控制使能标志 /* 位置环实现 */ void Locate_Loop(void) { // 位置控制未使能时直接返回 if (!position_enabled) return; // 计算位置误差(处理360°边界) float error = target_angle - Motor1.Theta_m; // 处理360°圆周上的最短路径 if (error > 180.0f) { error -= 360.0f; } else if (error < -180.0f) { error += 360.0f; } // 比例控制输出作为速度指令 const float Kp_position = 0.5f; // 位置环比例增益(可调整) Motor1.Wm_Set = Kp_position * error; // 速度限幅(根据电机性能调整) const float max_speed = 1000.0f; // 最大速度限制 Motor1.Wm_Set = Value_SetMaxMin(Motor1.Wm_Set, -max_speed, max_speed); // 位置到达检测(误差小于1度视为到达) if (fabsf(error) < 1.0f) { Motor1.Wm_Set = 0; // 停止电机 // 可选:到达后自动禁用位置控制 // position_enabled = 0; } } ``` #### 3. 修改`main.c`添加位置控制按键功能 ```c /* USER CODE BEGIN PV */ // 位置控制临时变量 static float position_increment = 30.0f; // 位置调整步长 /* USER CODE END PV */ int main(void) { // ... 初始化代码保持不变 ... while (1) { // 原有按键功能保持不变... /* 新增位置控制按键功能 */ if(Key_Scan(KEY1_GPIO_Port, KEY1_Pin) == KEY_ON) { // 启动位置控制 position_enabled = 1; LED_D11_ON; printf("Position control ON\n"); HAL_Delay(200); } else if(Key_Scan(KEY2_GPIO_Port, KEY2_Pin) == KEY_ON) { // 停止位置控制 position_enabled = 0; Motor1.Wm_Set = 0; // 停止电机 LED_D11_OFF; printf("Position control OFF\n"); HAL_Delay(200); } else if(Key_Scan(KEY3_GPIO_Port, KEY3_Pin) == KEY_ON) { // 位置增加 target_angle += position_increment; if(target_angle >= 360.0f) target_angle -= 360.0f; printf("Target: %.1f°\n", target_angle); HAL_Delay(200); } else if(Key_Scan(KEY4_GPIO_Port, KEY4_Pin) == KEY_ON) { // 位置减少 target_angle -= position_increment; if(target_angle < 0.0f) target_angle += 360.0f; printf("Target: %.1f°\n", target_angle); HAL_Delay(200); } else if(Key_Scan(KEY5_GPIO_Port, KEY5_Pin) == KEY_ON) { // 位置归零 target_angle = 0.0f; printf("Target set to 0°\n"); HAL_Delay(200); } // 位置监控(每200ms输出一次) static uint32_t last_print = 0; if(HAL_GetTick() - last_print > 200) { printf("Target: %.1f° | Actual: %.1f° | Error: %.1f°\n", target_angle, Motor1.Theta_m, target_angle - Motor1.Theta_m); last_print = HAL_GetTick(); } HAL_Delay(10); } } ``` ### 位置控制原理说明 #### 1. 控制结构 ``` 位置环 → 速度环 → 电流环 → PWM输出 ↑ ↑ ↑ 目标位置 位置环输出 速度环输出 ``` #### 2. 关键参数说明 1. **位置环比例增益 (Kp_position)** ```c const float Kp_position = 0.5f; // 位置环比例增益 ``` - 控制位置响应速度 - 值越大响应越快,但可能导致超调 - 推荐范围:0.1~2.0(根据电机性能调整) 2. **位置调整步长** ```c static float position_increment = 30.0f; // 位置调整步长 ``` - 每次按键调整的角度值 - 可根据需要调整为5°、15°、45°等 3. **最大速度限制** ```c const float max_speed = 1000.0f; // 最大速度限制 ``` - 防止位置环输出过大速度指令 - 应根据电机最大转速设置 ### 位置环工作流程 1. **位置环调用** - 在`Foc_Control()`函数中周期性调用 - 调用频率由`Motor1_Time.Locate_Loop_TimeGain`控制 2. **位置误差计算** ```c float error = target_angle - Motor1.Theta_m; ``` - 计算目标位置与实际位置的差值 - 处理360°边界问题 3. **比例控制** ```c Motor1.Wm_Set = Kp_position * error; ``` - 输出作为速度环的设定值 - 简单高效的比例控制 4. **速度限幅** ```c Motor1.Wm_Set = Value_SetMaxMin(Motor1.Wm_Set, -max_speed, max_speed); ``` - 防止速度指令超出电机能力范围 5. **位置到达检测** ```c if (fabsf(error) < 1.0f) { Motor1.Wm_Set = 0; } ``` - 当误差小于1°时停止电机 - 可添加到达提示或自动禁用位置控制 ### 优化建议 #### 1. 添加S曲线加减速 ```c // 在位置环中添加S曲线速度规划 void Position_Planner(void) { static float current_position = 0.0f; const float max_accel = 2000.0f; // 最大加速度 (°/s²) // 计算S曲线速度规划 float position_error = target_angle - current_position; float direction = (position_error > 0) ? 1.0f : -1.0f; // 计算加速距离 float accelerate_distance = 0.5f * max_speed * max_speed / max_accel; // 速度规划 if (fabsf(position_error) > 2 * accelerate_distance) { // 梯形速度规划 if (fabsf(Motor1.Wm_Set) < max_speed) { Motor1.Wm_Set += direction * max_accel * Motor1_Time.Locate_Loop_Ts; } } else { // 减速阶段 float decelerate_speed = sqrtf(2 * max_accel * fabsf(position_error)); Motor1.Wm_Set = direction * fminf(fabsf(Motor1.Wm_Set), decelerate_speed); } // 更新当前位置 current_position += Motor1.Wm_Set * Motor1_Time.Locate_Loop_Ts; } ``` #### 2. 抗积分饱和处理 ```c // 在位置环中添加积分项(可选) static float position_integral = 0.0f; const float Ki_position = 0.01f; // 积分增益 // 积分项计算 if (fabsf(error) > 5.0f) { // 仅在大误差时积分 position_integral += Ki_position * error * Motor1_Time.Locate_Loop_Ts; position_integral = Value_SetMaxMin(position_integral, -max_speed/2, max_speed/2); } // 输出速度指令 Motor1.Wm_Set = Kp_position * error + position_integral; ``` #### 3. 位置环参数初始化 ```c // 在Foc_Init()中添加位置环参数初始化 void Foc_Init() { // ...原有代码... // 位置环参数初始化 const float position_BandWidth = 10.0f; // 位置环带宽(Hz) Motor1_Time.Locate_Loop_Kp = position_BandWidth * 2 * PI * MOTOR1_J / (1.5f * MOTOR1_Pn * MOTOR1_flux); Motor1_Time.Locate_Loop_Ki = position_BandWidth * Motor1_Time.Locate_Loop_Kp; } ``` ### 完整的位置控制流程图 ``` 按键操作 │ ▼ 设置目标位置 ────┐ │ │ ▼ ▼ 位置环控制 ──▶ 速度环控制 ──▶ 电流环控制 ──▶ PWM输出 ▲ ▲ ▲ │ │ │ 位置反馈 ──────┘ │ │ │ 编码器读数 ───────────────┘ ``` 此实现方案保持了原有的FOC控制结构,仅添加了必要的位置控制逻辑,通过按键可以方便地控制目标位置和启停位置控制。位置环采用简单的比例控制,输出作为速度环的设定值,实现电机的位置控制功能。
阅读全文

相关推荐

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" #include "IR.h" // 循迹模块头文件 #include "OLED.h" // OLED 显示模块 #include "tb6612.h" // 电机驱动模块 #include "Encoder.h" // 编码器模块 #include <string.h> #include <stdio.h> /* Private define ------------------------------------------------------------*/ #define OLED_REFRESH_MS 300 /* Private variables ---------------------------------------------------------*/ char speed_buffer1[20]; // 显示缓冲区 volatile uint8_t oled_flag = 0; // OLED刷新标志 /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* Private user code ---------------------------------------------------------*/ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim5) { static uint16_t oled_cnt = 0; static uint8_t track_cnt = 0; // 每10ms执行一次循迹控制 track_cnt++; if (track_cnt >= 10) { track_cnt = 0; track_line_execute(); // 执行循迹逻辑 } // 每OLED_REFRESH_MS ms刷新一次OLED oled_cnt++; if (oled_cnt >= OLED_REFRESH_MS) { oled_cnt = 0; oled_flag = 1; } } } /** * @brief The application entry point. * @retval int */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM3_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_TIM5_Init(); MX_USART3_UART_Init(); /* 初始化外设 */ OLED_Init(); OLED_Clear(); TB6612_Init(); // 初始化电机驱动 Encoder_Init(); // 初始化编码器(如果使用) track_line_init(); // 初始化循迹模块 OLED_ShowString(1, 1, "System Ready"); HAL_TIM_Base_Start_IT(&htim5); // 启动定时器5中断 HAL_Delay(2000); OLED_Clear(); while (1) { if (oled_flag) { oled_flag = 0; sprintf(speed_buffer1, "State:%d", get_track_status()); // 获取当前循迹状态 OLED_ShowString(1, 1, speed_buffer1); } } } 帮我生成完整的代码,包括hel库自带的

/** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** */ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* USER CODE BEGIN Includes */ #include "OLED.h" #include "tb6612.h" #include "Encoder.h" #include "IR.h" #include "pid.h" #include <stdio.h> /* USER CODE END Includes */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define OLED_REFRESH_MS 300 /* USER CODE END PD */ /* Private variables ---------------------------------------------------------*/ char speed_buffer[20]; volatile uint8_t oled_flag = 0; int left_speed = 0; int right_speed = 0; PID_Controller pid; static uint32_t last_time = 0; static float base_speed = 50.0; /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); void app_LineWalking_Init(void); void app_LineWalking(void); /* USER CODE BEGIN 0 */ void app_LineWalking_Init(void) { PID_Init(&pid, 1.0, 0.0, 0.0); // 设置初始 PID 参数 last_time = HAL_GetTick(); } void app_LineWalking(void) { static uint8_t sensors[4]; float dt; uint32_t current_time = HAL_GetTick(); dt = (current_time - last_time) / 1000.0f; if (dt < 0.02f) return; get_line_sensors(sensors); int error = calculate_error(sensors); float output = PID_Update(&pid, error, dt); int left = base_speed - output; int right = base_speed + output; left = (left < 0) ? 0 : ((left > 100) ? 100 : left); right = (right < 0) ? 0 : ((right > 100) ? 100 : right); TB6612_MotorA_Run(MOTOR_FORWARD, left); TB6612_MotorB_Run(MOTOR_FORWARD, right); last_time = current_time; } void Display_Speed_Info(void) { char buffer[32]; OLED_Clear(); sprintf(buffer, "L Speed: %d", left_speed); OLED_ShowString(1, 1, buffer); sprintf(buffer, "R Speed: %d", right_speed); OLED_ShowString(2, 1, buffer); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim5) { static uint16_t oled_cnt = 0; oled_cnt++; if (oled_cnt >= OLED_REFRESH_MS) { oled_cnt = 0; oled_flag = 1; } } if (htim == &htim3) { static int16_t cntL = 0, cntR = 0; GetAndReset(&cntL, &cntR); left_speed = cntL; right_speed = cntR; app_LineWalking(); } } /* USER CODE END 0 */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM3_Init(); // PID 控制定时器 MX_TIM5_Init(); // OLED 显示定时器 MX_USART3_UART_Init(); OLED_Init(); TB6612_Init(); Encoder_Init(); app_LineWalking_Init(); OLED_ShowString(1, 1, "System Ready"); HAL_Delay(2000); OLED_Clear(); HAL_TIM_Base_Start_IT(&htim3); // 启动 PID 控制定时器 HAL_TIM_Base_Start_IT(&htim5); // 启动 OLED 显示定时器 while (1) { if (oled_flag) { oled_flag = 0; Display_Speed_Info(); } } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } void Error_Handler(void) { __disable_irq(); while (1) { } } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { OLED_ShowString(1, 1, "Assert Fail"); } #endif /* USE_FULL_ASSERT */ 帮我修改这段代码,没有使用定时器3,只有定时器5,形成完整代码

根据上述.c文件,帮我修改以下的代码/* main.c */ /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" #include "IR.h" // 添加循迹模块头文件 /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "OLED.h" #include "tb6612.h" #include "Encoder.h" #include "IR.h" #include "string.h" #include <stdio.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define OLED_REFRESH_MS 300 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ char speed_buffer1[20]; // 添加缓冲区定义 volatile uint8_t oled_flag = 0; // 添加OLED刷新标志 /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /** * @brief 定时器5中断回调(1ms触发一次) */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim5) { static uint16_t oled_cnt = 0; static uint8_t track_cnt = 0; // 每10ms执行一次循迹控制 track_cnt++; if (track_cnt >= 10) { track_cnt = 0; track_line_execute(); } // OLED刷新 oled_cnt++; if (oled_cnt >= OLED_REFRESH_MS) { oled_cnt = 0; oled_flag = 1; } } } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM3_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_TIM5_Init(); MX_USART3_UART_Init(); /* USER CODE BEGIN 2 */ OLED_Init(); OLED_Clear(); TB6612_Init(); Encoder_Init(); track_line_init(); // 初始化循迹模块 OLED_ShowString(1, 1, "System Ready"); HAL_TIM_Base_Start_IT(&htim5); HAL_Delay(2000); OLED_Clear(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if (oled_flag) { oled_flag = 0; sprintf(speed_buffer1, "State:%d", get_track_status()); OLED_ShowString(1, 1, speed_buffer1); } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ OLED_ShowString(1, 1, "Assert Fail"); /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */

#include "encoder.h" #include "main.h" #include "usart.h" #include "stdio.h" #include "tim.h" #include "math.h" #include "hall.h" #include "mymath.h" #include "gpio.h" #include "arm_math.h" #include "adc.h" #include "foc.h" float Encoder_Theta_coefficient; //编码器转换成电角度的系数值 float Encoder_Theta_m_coefficient; //编码器转换成机械角度的系数值 float We_coefficient; //编码器转电角速度的系数值 float Wm_coefficient; //编码器转机械角速度的系数值 float t; float Current_BandWidth; //电流环带宽 float CurrentLoop_Kpd; //电流环D轴比例增益 float CurrentLoop_Kid ; //电流环D轴积分增益 float CurrentLoop_Kpq ; //电流环Q轴比例增益 float CurrentLoop_Kiq; //电流环Q轴积分增益 int Speed_BandWidth ; //速度环带宽 float Ba ; //有功阻尼系数 float SpeedLoop_Kp ; //速度环比例增益 float SpeedLoop_Ki ; //速度环积分增益 void Foc_Init() { float temp1,temp2; Motor1.Wm_Set=0; Motor1.Id_Set=0; //设定为20k(电流采样由定时器1的PWM比较触发,频率也为20k) Motor1_Time.PWM_Fre = 168000000.0f / (TIM1_ARR * TIM1_PSC) / 2; Motor1_Time.PWM_Ts = 1 / Motor1_Time.PWM_Fre; //电流环:速度环:位置环的速度比例有多种,有16:4:1, 2:1:1, 1:1:1,都可以 Motor1_Time.Current_Loop_TimeGain = 1.0f; Motor1_Time.Speed_Loop_TimeGain = 1.0f; Motor1_Time.Locate_Loop_TimeGain = 1.0f; //计算频率设定为40k Motor1_Time.Calculate_Fre = 84000000.0f / (TIM2_ARR * TIM2_PSC ); Motor1_Time.Calculate_Ts = 1 / Motor1_Time.Calculate_Fre; //电流环设定为20k Motor1_Time.Current_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Current_Loop_TimeGain; Motor1_Time.Current_Loop_Ts = 1 / Motor1_Time.Current_Loop_Fre; //速度环设定为10k Motor1_Time.Speed_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Speed_Loop_TimeGain; Motor1_Time.Speed_Loop_Ts = 1 / Motor1_Time.Speed_Loop_Fre; //位置设定为10k Motor1_Time.Locate_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Locate_Loop_TimeGain; Motor1_Time.Locate_Loop_Ts = 1 / Motor1_Time.Locate_Loop_Fre; //编码器计数值转换为角度的系数值 Encoder_Theta_coefficient = 360 * MOTOR1_Pn /(ENCODER_NUMBER * 4); Encoder_Theta_m_coefficient = 360 /(ENCODER_NUMBER * 4); //编码器计数值转换为角速度的系数值 We_coefficient = 2* PI /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; Wm_coefficient = 60.0f /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; //计算电流环的PI系数 temp1=MOTOR1_Ld/MOTOR1_R; temp2=MOTOR1_Lq/MOTOR1_R; t=(temp1<temp2)?temp1:temp2; Current_BandWidth = 2*PI/t; //算出来的带宽太大了,有九千多,感觉是电机的参数给的可能有问题?不知道,反正不用这个带宽 //小了响应速度不够快,大了电流波动大 Current_BandWidth =1500; CurrentLoop_Kpd = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kid = Current_BandWidth * MOTOR1_R; CurrentLoop_Kpq = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kiq = Current_BandWidth * MOTOR1_R; //计算速度环的带宽 Speed_BandWidth = 150; Ba = (Speed_BandWidth *MOTOR1_J - MOTOR1_B ) / (1.5f * MOTOR1_Pn * MOTOR1_flux); //这个值在速度环我看别人的仿真不知道为啥给扔了,在速度环的时候不要这个 SpeedLoop_Kp = ( Speed_BandWidth * MOTOR1_J ) / (1.5f * MOTOR1_Pn * MOTOR1_flux) ; SpeedLoop_Ki = Speed_BandWidth * SpeedLoop_Kp; } //整个闭环控制计算可以满足40k的处理频率 void Foc_Control(void) { static int Number1=0,Number2=0,Number3=0; Motor1.Encoder_Value=Get_Encoder_Count(); //根据编码器计算角度 Motor1.Theta=Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_coefficient,0.0f,360.0f); Motor1.Theta_m = Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_m_coefficient,0.0f,360.0f); if(++Number1 >= Motor1_Time.Locate_Loop_TimeGain) { //执行位置环 Locate_Loop(); Number1= 0; } if(++Number2 >= Motor1_Time.Speed_Loop_TimeGain) { //执行速度环 Speed_Loop(); Number2= 0; } if(++Number3 >= Motor1_Time.Current_Loop_TimeGain) { //执行电流环 Current_Loop(); Number3= 0; } //对电压进行反Park变换 Park_Inverse_Transform(); //执行SVPWM并设定电机占空比 FOC_SVPWM(); //执行高频方波注入算法 // HFI_Sensorless(); } //SVPWM void FOC_SVPWM(void) { uint8_t N,A,B,C; float Vref1,Vref2,Vref3,X,Y,Z,temp1,Tfirst,Tsecond,T0,Ta,Tb,Tc,Tcm1,Tcm2,Tcm3; //计算转子所在的山区 Vref1=Motor1.Ubeta; Vref2=(SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; Vref3=(-SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; A=Vref1>0 ? 1 :0 ; B=Vref2>0 ? 1 :0 ; C=Vref3>0 ? 1 :0 ; N=4*C+2*B+A; temp1=SQRT_3* SVPWM_TS/ UDC; X=temp1*Vref1; Y=-temp1*Vref3; Z=-temp1*Vref2; //矢量作用时间计算 switch(N) { case 1: Tfirst= Z; Tsecond= Y; Motor1.Sector= 2; break; case 2: Tfirst= Y; Tsecond= -X; Motor1.Sector= 6; break; case 3: Tfirst= -Z; Tsecond= X; Motor1.Sector= 1; break; case 4: Tfirst= -X; Tsecond= Z; Motor1.Sector= 4; break; case 5: Tfirst= X; Tsecond= -Y; Motor1.Sector= 3; break; case 6: Tfirst= -Y; Tsecond= -Z; Motor1.Sector= 5; break; default: Tfirst= 0; Tsecond= 0; Motor1.Sector= 0; break; } //超限判断 if(( Tfirst + Tsecond )> SVPWM_TS) { Tfirst=(Tfirst/(Tfirst+Tsecond))/ SVPWM_TS; Tsecond=(Tsecond/(Tfirst+Tsecond))/ SVPWM_TS; } T0= (SVPWM_TS- Tfirst- Tsecond)/2; Ta=T0/2; Tb=Ta+Tfirst/2; Tc=Tb+Tsecond/2; //每相桥臂切换时间计算 switch(N) { case 1: Tcm1=Tb; Tcm2=Ta; Tcm3=Tc; break; case 2: Tcm1=Ta; Tcm2=Tc; Tcm3=Tb; break; case 3: Tcm1=Ta; Tcm2=Tb; Tcm3=Tc; break; case 4: Tcm1=Tc; Tcm2=Tb; Tcm3=Ta; break; case 5: Tcm1=Tc; Tcm2=Ta; Tcm3=Tb; break; case 6: Tcm1=Tb; Tcm2=Tc; Tcm3=Ta; break; default: break; } Motor1.Tcm1=Tcm1; Motor1.Tcm2=Tcm2; Motor1.Tcm3=Tcm3; //设置定时器1的PWM占空比 __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,TIM1_ARR*(1-2*Tcm1/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,TIM1_ARR*(1-2*Tcm2/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,TIM1_ARR*(1-2*Tcm3/ SVPWM_TS)); } void ADC_Transform(void) { //顺便软件滤波一下 //按照无刷驱动板的原理图上的公式算的,0.02是电阻值,电流转成的电压 除以电阻就是电流 // Motor1.Ibeta=((HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1) * 3.3f /4095.0f) -1.24f)/8.0f/0.02f; 单位A Motor1.Ia =IRFilter1(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1)) * 0.005f -7.75f; Motor1.Ib =IRFilter2(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_2)) * 0.005f -7.75f; Motor1.Ic =IRFilter3(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_3)) * 0.005f -7.75f; } //位置环 void Locate_Loop(void) { //用不上暂时还没学,就没写 } //速度环 void Speed_Loop(void) { static float temp; static int Encoder_Last; float Encoder_W_temp; if(Encoder_Z_Flag==1) { Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last + Encoder_temp); Encoder_Z_Flag=0; } else Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last); //根据编码器计算角速度 速度范围波动会比较大,因为采样频率太高了,编码器的分辨率又不够高,所以这是很正常的。降低采样频率(即减小TIM2的时钟频率)可以减小波动 Motor1.We = Encoder_W_temp * We_coefficient; //单位 rad/s(电角速度) Motor1.Wm = Encoder_W_temp * Wm_coefficient; //单位 RPM(角速度) Encoder_Last=Motor1.Encoder_Value; Motor1.Iq_Set = SpeedLoop_Kp * (Motor1.Wm_Set - Motor1.Wm) + temp; temp = Value_SetMaxMin(temp +SpeedLoop_Ki * (Motor1.Wm_Set - Motor1.Wm) * Motor1_Time.Speed_Loop_Ts,-10.0f,10.0f); //给个限值防止他一直往上加,一启动电机速度飞飚,这个值是慢慢试的,大概速度到3000RPM时,这个值为10.多 } //电流环 void Current_Loop(void) { static float temp,temp2; //对三相电流进行采样,三相电流相加不为0,是因为硬件上有误差(野火客服说的,可恶!) 电流采样频率和电流频率一样都为20k ADC_Transform(); //对电流进行Clark变换,再Park变换 Motor1.Ialpha = Motor1.Ia; Motor1.Ibeta = (Motor1.Ia + 2*Motor1.Ib)/SQRT_3; Motor1.Id = Motor1.Ialpha* qfcosd(Motor1.Theta)+ Motor1.Ibeta* qfsind(Motor1.Theta); Motor1.Iq = -Motor1.Ialpha* qfsind(Motor1.Theta)+ Motor1.Ibeta* qfcosd(Motor1.Theta); //PI环节 //这个电流环的积分项我没整明白,加上积分的话,积分项会直接冲到最大,且一直为最大,然后就电机转动也没有更好,感觉就是反作用,先不整了,把它干掉,后面再研究 Motor1.Ud = CurrentLoop_Kpd *(Motor1.Id_Set - Motor1.Id) + temp - Motor1.We * MOTOR1_Lq * Motor1.Iq; // temp = Value_SetMaxMin( temp+ CurrentLoop_Kid * (Motor1.Id_Set - Motor1.Id) * Motor1.Calculate_Ts, -0.1f, 0.1f); Motor1.Uq = CurrentLoop_Kpq *(Motor1.Iq_Set - Motor1.Iq) + temp2 + Motor1.We * (MOTOR1_Ld * Motor1.Id + MOTOR1_flux); // temp2 = Value_SetMaxMin( temp2+ CurrentLoop_Kiq * (Motor1.Iq_Set - Motor1.Iq) * Motor1.Calculate_Ts, -0.1f, 0.1f); //合成电压最大值为SQRT_3 * UDC / 3 = 13.85 , Ud 最大值设为3 是为了高频方波注入算法, 这些限值都是试出来的,我也不会算理论值;这样限值转速最大能到2600RPM左右,够了 Motor1.Ud = Value_SetMaxMin(Motor1.Ud,-3.0f,3.0f); Motor1.Uq = Value_SetMaxMin(Motor1.Uq,-12.0f,12.0f); // Motor1.Ud = 0; // Motor1.Uq = 2; }上述为电机控制FOC的电流速度环控制代码,请为代码补充位置环控制,要求代码准确无误

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "i2c.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "motor.h" #include "encoder.h" #include "oled.h" #include "stdio.h" #include "string.h" #include "openmv.h" #include "MPU6050.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ uint8_t state = 0,ustate = 0,udata = 0x00,mvstate = 0,kzstate = 0,carstate = 1;//mvstate 1 识别跟踪A kzstate 0跟踪A,1即将走出 carstate 0stop1运行 uint8_t data[2] = {0};//openmv数据cx,cy int v[2] = {0,0},lastv[2] = {0,0}; float qs[2] = {0.0,0.0},dq[2] = {0.0,0.0};//圈数 float pitch = 0.0,yaw = 0.0,roll = 0.0; int yaw0 = 0,yaw_offse = 30,yaw_guai = 40,yaw1 = 0; uint8_t cnt1 = 0,cnt2 = 0,cnt_guai = 0; //char str1[50]={0},str2[50]={0},str3[50]={0}; char str4[200]={0},stop[4]="stop"; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART2) { if(ustate == 0 && udata == 0xff) { ustate++; }else if(ustate == 1) { data[0] = udata; ustate++; }else if(ustate == 2) { data[1] = udata; ustate++; }else if(ustate == 3 && udata == 0xff) { ustate++; }else { ustate=0; } if(ustate==4) { //接收完成 kzstate = kongzhi(data,lastv,v); lastv[0] = v[0]; lastv[1] = v[1]; ustate = 0; } if(kzstate==0) { HAL_UART_Receive_IT(&huart2,&udata,1); } } } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM3) { cnt1++; cnt2++; cnt1 = cnt1 % 2; cnt2 = cnt2 % 10; duqu(dq); qs[0]=qs[0]+dq[0]; qs[1]=qs[1]+dq[1]; if(cnt1 == 0) { MPU6050_DMP_Get_Date(&pitch,&roll,&yaw); } if(cnt2 == 0) { if(carstate==1) { sprintf(str4,"0000000000%0.2f,%0.2f,%d\r\n",qs[0],qs[1],(int)yaw); HAL_UART_Transmit_IT(&huart1,(uint8_t*)str4,100); str4[0]='\0'; qs[0]=qs[1]=0.0; } } } } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_USART1_UART_Init(); MX_TIM3_Init(); MX_I2C2_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ int ret = 1; while(ret!=0) { ret = MPU6050_DMP_init(); } motor_init(); encoderinit(); HAL_UART_Receive_IT(&huart2,&udata,1); MPU6050_DMP_Get_Date(&pitch,&roll,&yaw); yaw1 = yaw0 = yaw; HAL_TIM_Base_Start_IT(&htim3); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { //mvstate = openmvstate(); if(cnt_guai < 4) { track(&kzstate,cnt_guai); }else { //停止 tiaosu(0,0); carstate = 0; } if(kzstate!=0) { if((yaw > yaw1 + yaw_offse) || (yaw < yaw1 - yaw_offse)) { cnt_guai++; } } kzstate = 0; yaw1 = yaw; /* if((kzstate > 3)&&(yaw < yaw0 + yaw_offse)&&(yaw > yaw0 - yaw_offse)) { cnt_guai=4; } */ /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ 用我这个代码,串口给上位机发送路程、角度数据的时候总是丢包是什么原因

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2024 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "i2c.h" #include "tim.h" #include "usart.h" #include "gpio.h" #define EEPROM_ADDR 0x08080000 // 自定义存储地址(需避开程序区) /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "oled.h" #include "IIC.h" #include "inv_mpu.h" #include "inv_mpu_dmp_motion_driver.h" #include "mpu6050.h" #include "stdio.h" #include "sr04.h" #include "motor.h" #include "encoder.h" #include "pid.h" #include "run.h" #include "xunji.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ extern int Encoder_Left,Encoder_Right; extern float roll,yaw,jiaosuduz; uint8_t display_buf[20]; uint32_t sys_tick; extern float distance; float speed1,speed2; float distance1=(0.065*3.14)/11; float Tdistance; int t=0; float ABCD_location[10]; static uint8_t i; //uint8_t ABCD_location[20]; // 编译错误:ABCD_location 已被定义为 float 数组 // 同样编译错误 uint8_t rx_buf[1]; uint8_t rx_data; uint8_t flag; void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void Read(void); volatile uint32_t seconds=0; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ //void save_distance_to_eeprom(float distance) /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C1_Init(); MX_TIM3_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_USART1_UART_Init(); MX_USART3_UART_Init(); /* USER CODE BEGIN 2 */ OLED_Init(); OLED_Clear(); MPU_Init(); xunji_Init(); mpu_dmp_init(); //OLED_ShowString(0,00,"Init Sucess",16); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4); HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL); HAL_TIM_Encoder_Start(&htim4,TIM_CHANNEL_ALL); HAL_UART_Receive_IT(&huart1,rx_buf,1); // HAL_NVIC_SetPriority( USART1_IRQn , 1, 0); // HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); // HAL_NVIC_SetPriority( TIM1_CC_IRQn , 2, 0); // HAL_NVIC_SetPriority( TIM1_CC_IRQn , 2, 0); // HAL_NVIC_SetPriority( TIM1_CC_IRQn , 2, 0); // Load(2000,2000); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { Read(); //OLED_ShowString(0, 1, "speed1:", 12); // ��ʾ�ٶ��ַ��� //OLED_ShowString(0, 3, "speed2:", 12); // ��ʾ�ٶ��ַ��� // sprintf((char *)display_buf,"Encoder_L:%d ",-Encoder_Left); // OLED_ShowString(0,4,display_buf,16); // sprintf((char *)display_buf,"Encoder_R:%d ",-Encoder_Right); // OLED_ShowString(0,6,display_buf,16); // speed1=-(Encoder_Left/11)*60; // char speedStr[16]; // sprintf(speedStr, "%.2f", speed1); // ��������ת��Ϊ�ַ�����������λС�� // OLED_ShowString(0, 1, (char*)speedStr, 12); // ��ʾ�ٶ��ַ��� // speed2=-(Encoder_Right/11)*60; // char speedStr2[16]; // sprintf(speedStr2, "%.2f", speed2); // OLED_ShowString(0, 2, (char*)speedStr2, 12); // ��ʾ�ٶ��ַ��� // Tdistance+=(Encoder_Left+Encoder_Right)*distance1; // char display_buf[16]; // sprintf(display_buf, "Tdistance: %.4f m",-0.01*Tdistance ); // ������λС�� // OLED_ShowString(0, 3, display_buf, 12); //OLED_ShowString(0, 1, "speed1:", 12); // ÏÔʾËÙ¶È×Ö·û´® //OLED_ShowString(0, 3, "speed2:", 12); // ÏÔʾËÙ¶È×Ö·û´® seconds++; HAL_Delay(1000); char buffer[20]; sprintf(buffer, "Time: %02lu:%02lu", (seconds / 60) % 60, seconds % 60); OLED_ShowString(0, 0, buffer, 12); // 假设OLED_ShowString是用于显示字符串的函数 // if(out1() == 0&&out2() == 0&&out3() == 0&&out4() == 1&&out1() == 1&&out2() == 1&&out3() == 1&&out4() ==0&&out1() == 0&&out2() == 0&&out3() == 1&&out4() == 1) // { // t++; // seconds==0; // } // if(t==4) // { // car_stop(); // } // // OLED_ShowString(0, 4,"t=", 12); sprintf((char *)display_buf,"Encoder_L:%d ",Encoder_Left); // OLED_ShowString(0,4,display_buf,16); sprintf((char *)display_buf,"Encoder_R:%d ",Encoder_Right); // OLED_ShowString(0,6,display_buf,16); speed1=-(Encoder_Left/11)*60; char speedStr[16]; sprintf(speedStr, "%.2f", speed1); // OLED_ShowString(0, 1, (char*)speedStr, 12); speed2=-(Encoder_Right/11)*60; char speedStr2[16]; sprintf(speedStr2, "%.2f", speed2); // OLED_ShowString(0, 2, (char*)speedStr2, 12); Tdistance+=(Encoder_Left+Encoder_Right)*distance1; char display_buf[16]; sprintf(display_buf, "Tdistance: %.2f",0.1+0.01*Tdistance*0.5); OLED_ShowString(0, 3, display_buf, 12); int a=0; char ABCD_location[20]; float position = ABCD_location[i]; sprintf(ABCD_location, "Pos: %.2f", position); OLED_ShowString(0, a, ABCD_location, 12); // #define EEPROM_ADDR 0x08080000 // 自定义存储地址(需避开程序区) // 读取距离 // float Tdistance(void) // { // return *(float*)EEPROM_ADDR; //} if(out1() == 0&&out2() == 1&&out3() == 1&&out4())//||out1() == 0&&out2() == 0&&out3() == 1&&out4() { a++; ABCD_location[i++]=0.1+0.01*Tdistance*0.5; HAL_Delay(20); } // 保存距离 //{ // HAL_FLASH_Erase Sector; // HAL_FLASH_Erase(&Sector, EEPROM_ADDR); // 擦除扇区 // HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLOAT, EEPROM_ADDR, distance); // 写入浮点数 //} // // sprintf((char *)display_buf,"Encoder_L:%d ",-Encoder_Left); // OLED_ShowString(0,4,display_buf,16); // sprintf((char *)display_buf,"Encoder_R:%d ",-Encoder_Right); // OLED_ShowString(0,6,display_buf,16); // sprintf((char *)display_buf,"jiaosuduz:%.1f ",jiaosuduz); //俯仰角 // OLED_ShowString(4,4,display_buf,16); // sprintf((char *)display_buf,"roll:%.1f ",roll); //翻滚角 // OLED_ShowString(4,6,display_buf,16); // sprintf((char *)display_buf,"yaw:%.1f ",yaw); //偏航角 // OLED_ShowString(4,8,display_buf,16); //Load(-800,-800); // xunji(); // HAL_Delay(2500); // if(out1() == 0&&out2() == 0&&out3() == 1&&out4() == 1) // { // car_stop(); // HAL_Delay(100); // car_right_go1(); // HAL_Delay(1300); // car_stop(); // HAL_Delay(100); // } // // // else // { // xunji(); // } // // Load(1200,800); // HAL_Delay(1500); // car_stop(); // HAL_Delay(50); // car_right_go1(); // HAL_Delay(910); // car_stop(); // HAL_Delay(50); // Load(500,1300); // HAL_Delay(100); // Load(-1150,-1300); // Load(-1200,1200);//cuo右轮不能负 // car_go(); // HAL_Delay(10000); // car_right_go1(); // HAL_Delay(830); // car_stop(); // HAL_Delay(50); // // // car_go(); // HAL_Delay(1000); // Load(900,800); // HAL_Delay(4000); // car_right_go1(); // HAL_Delay(950); // car_stop(); // HAL_Delay(50); // void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) //{ //} // if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)) // { // // } //// // if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)) // { //// // } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart==&huart1) // 假设使用的是 huart1,根据实际串口句柄修改 { rx_data=rx_buf[0]; //rx_data=rx_data-'0'; HAL_UART_Receive_IT(huart, rx_buf, 1); // HAL_UART_Transmit_IT(&huart2, rx_buf, 2); // 在此处添加接收完成后的处理逻辑,例如重新启动接收中断 OLED_ShowNum(1,1,rx_data,4,16); // 重新启动接收中断,以便持续接收 } } /* USER CODE END 3 */ /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ void Read(void) { if(uwTick-sys_tick<10) return; sys_tick=uwTick; Encoder_Left=Read_Speed(&htim2); Encoder_Right=-Read_Speed(&htim4); } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ 怎么才能将我储存的数据在OLED上显示

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include <stdio.h> #include <string.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ #define UART_PRINTF(fmt, ...) do { \ char __buf[256]; \ sprintf(__buf, fmt, ##__VA_ARGS__); \ HAL_UART_Transmit(&huart2, (uint8_t*)__buf, strlen(__buf), HAL_MAX_DELAY); \ } while(0) /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ typedef struct { uint32_t LastValue; uint32_t NextValue; uint32_t Difference; }CountValue; /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ CountValue CountValue1; uint32_t now_CountValue=0; float TargetSpeed=30; float n=0; float PID_Speed=0; /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ float Incremental_KP=0.15; float Incremental_KI=0.2; float Incremental_KD=0; float myIncremental_PID(float Now,float Target) { static float Bias,LastBias,Last_LastBias,P_out,I_out,D_out,Total_out,Results; Bias=Target-Now; P_out=Incremental_KP*(Bias-LastBias); I_out=Incremental_KI*Bias; D_out=Incremental_KD*(Bias-2*LastBias+Last_LastBias); Total_out=P_out+I_out+D_out; Results+=Total_out; Last_LastBias=LastBias; LastBias=Bias; if(Results>80) Results=80; if(Results<30) Results=30; return Results; } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM2_Init(); MX_TIM3_Init(); MX_TIM4_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);//开启编码器模式 HAL_TIM_Base_Start_IT(&htim2); //HAL_TIM_Base_Start(&htim2); HAL_TIM_Base_Start_IT(&htim4);//开启定时中断 //HAL_TIM_Base_Start(&htim4); HAL_TIM_Base_Start_IT(&htim3); // HAL_TIM_Base_Start(&htim3); HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//开启pwm模式 while (1) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); PID_Speed=myIncremental_PID(n,120); __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,(int)PID_Speed); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //GET_NUM(); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ //float n=0;//转速,单位为:转/秒 int32_t encoder_counter = 0;//STM32编码器模式读取的总脉冲数 static int32_t encoder_last_value = 0; /** * @function: void GET_NUM(void) * @description: 使用STM32编码器模式,读取编码器产生的脉冲值 * @param {*} * @return {*} */ void GET_NUM(void) { // 获取当前32位计数值(处理溢出) uint32_t current_count = __HAL_TIM_GET_COUNTER(&htim2); // 计算增量(处理定时器溢出) if(current_count < encoder_last_value) { // 发生上溢(65535 → 0) encoder_counter = (65535 - encoder_last_value) + current_count; } else { encoder_counter = current_count - encoder_last_value; } encoder_last_value = current_count; // 更新上次值 } /** * @function:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) * @description: 定时器中断回调函数,0.1S中断一次并计算转速,编码器产生的脉冲数 * @param {TIM_HandleTypeDef *htim} * @return {*} */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim==&htim4) { GET_NUM();//得到所记录的脉冲数 n=(float)encoder_counter/2400/0.1;//转速为n,r/s UART_PRINTF("%d\n",encoder_counter); } } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ 我想要这串代码计算每分钟多少转

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" #include "motor.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ extern float Motor1Speed; extern float Motor2Speed; int Motor1Pwm; int Motor2Pwm; /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /** *@brief 重定向printf(重定向fputc), *@param *@return */ int fputc(int ch,FILE *stream) { HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF); //实现串口打印 return ch; } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //开启定时器1 通道1 PWM输出 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4); //开启定时器1 通道4 PWM输出 HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);//开启定时器2 HAL_TIM_Encoder_Start(&htim4,TIM_CHANNEL_ALL);//开启定时器4 HAL_TIM_Base_Start_IT(&htim2); //开启定时器2 中断 HAL_TIM_Base_Start_IT(&htim4); //开启定时器4 中断(防止计数器溢出) HAL_TIM_Base_Start_IT(&htim1); //开启定时器1 中断 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ // __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,0); // __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_4,90); // HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_RESET); // HAL_GPIO_WritePin(BIN1_GPIO_Port,BIN1_Pin,GPIO_PIN_RESET); if(Motor1Speed<2.9)Motor1Pwm++; if(Motor1Speed>3.1)Motor1Pwm--; if(Motor2Speed<2.9)Motor2Pwm++; if(Motor2Speed>3.1)Motor2Pwm--; Motor_Reset(Motor1Pwm,Motor2Pwm); printf("Motor1Speed:%.2f Motor1Pwm:%d\r\n",Motor1Speed,Motor1Pwm); printf("Motor2Speed:%.2f Motor2Pwm:%d\r\n",Motor2Speed,Motor2Pwm); HAL_Delay(100); // __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,10); // __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_4,10); // HAL_GPIO_WritePin(BIN1_GPIO_Port,BIN1_Pin,GPIO_PIN_SET); // HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_SET); // uint8_t c_Data[]="串口输出测试:vcc\r\n"; // HAL_UART_Transmit(&huart1,c_Data,sizeof(c_Data),0xFFFF); // HAL_Delay(1000); // printf("printf:vcc\r\n"); // HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin); // HAL_Delay(500); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ 这段代码烧录进小车后,小车两个轮子先一起转动,然后一个轮子停止。另一个轮子加速转动,然后以一个很快的转速持续转动,这是什么原因

#include "main.h" #include "i2c.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "math.h" #include "oled.h" #include "stdio.h" #include <string.h> #define KEY1 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3) #define KEY0 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4) #define PPR 13 #define ENCODER_MULTIPLY 4 #define GEAR_RATIO 20 // 计算每圈对应的计数 #define COUNTS_PER_REV (PPR * ENCODER_MULTIPLY * GEAR_RATIO) /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* 速度环PID参数 */ float InnerTarget=0, InnerActual=0,Innerout=0;//目标值,实际值,输出值ֵ float InnerKp=1.5; float InnerKi=0.18; float InnerKd=0.1;//比例项积分项微分项的权重 float InnerError0=0,InnerError1=0,InnerErrorInt=0,InnerIntout=0;//本次误差,上次误差,误差积分 /* 位置环PID参数 */ float PositionTarget = 0, PositionActual = 0, Positionout = 0;//目标值,实际值,输出值ֵ float PositionKp = 0.4; float PositionKi = 0.0; float PositionKd = 0.03;//比例项积分项微分项的权重 float PositionError0 = 0, PositionError1 = 0, PositionErrorInt = 0, PositionIntOut = 0;//本次误差,上次误差,误差积分 extern unsigned char Hzk[][32]; uint32_t ccr_value=0; uint32_t arr_value; unsigned int x=0; int motor_direction; uint8_t rx_data; unsigned char* word0=(uint8_t *)"**<qianchen>**"; unsigned char* time=(uint8_t *)"*>2025-07-06<*"; unsigned char* word1=(uint8_t *)"Count:"; unsigned char* word2=(uint8_t *)"PWM:"; unsigned char* word3=(uint8_t *)"Speed:"; unsigned char* word4=(uint8_t *)"Target:"; unsigned char* word5=(uint8_t *)"Actual:"; unsigned char* word6=(uint8_t *)"Out:"; unsigned char* word7=(uint8_t *)"P_T:"; unsigned char* word8=(uint8_t *)"P_A:"; unsigned char* word9=(uint8_t *)"P_O:"; unsigned char* word10=(uint8_t *)"I_T:"; unsigned char* word11=(uint8_t *)"I_A:"; unsigned char* word12=(uint8_t *)"I_O:"; uint16_t count; float speed=0.0; uint8_t str_buff[64]; void delay(unsigned int x) { while(x--); } void Scan_Keys() { if(KEY0==GPIO_PIN_RESET) { delay(1000); if(KEY0==GPIO_PIN_RESET) { while(KEY0==GPIO_PIN_RESET); PositionTarget+=300; } } if(KEY1==GPIO_PIN_RESET) { delay(1000); if(KEY1==GPIO_PIN_RESET) { while(KEY1==GPIO_PIN_RESET); PositionTarget-=300; } } } void OLED_display_info() { OLED_Clear(); OLED_ShowString(8,0,word0,sizeof(word0)); OLED_ShowString(5,1,time,sizeof(time)); // OLED_ShowString(8,2,word1,sizeof(word1)); // OLED_ShowString(8,3,word2,sizeof(word2)); // OLED_ShowString(8,4,word3,sizeof(word3)); // OLED_ShowString(8,5,word4,sizeof(word4)); // OLED_ShowString(8,6,word5,sizeof(word5)); // OLED_ShowString(8,7,word6,sizeof(word6)); OLED_ShowString(8,2,word7,sizeof(word7)); OLED_ShowString(8,3,word8,sizeof(word8)); OLED_ShowString(8,4,word9,sizeof(word9)); OLED_ShowString(8,5,word10,sizeof(word10)); OLED_ShowString(8,6,word11,sizeof(word11)); OLED_ShowString(8,7,word12,sizeof(word12)); // OLED_ShowCHinese(0,3,0); // OLED_ShowCHinese(18,3,1); // OLED_ShowCHinese(36,3,2); // OLED_ShowCHinese(54,3,3); // OLED_ShowCHinese(0,6,4); // OLED_ShowCHinese(18,6,5); // OLED_ShowCHinese(36,6,6); // OLED_ShowCHinese(54,6,7); } void OLED_display_dat() { // sprintf((char*)str_buff,"%5d",count); // OLED_ShowString(64,2,str_buff,sizeof(str_buff)); // // arr_value = __HAL_TIM_GET_AUTORELOAD(&htim2); // 获取ARR值 // float duty_cycle = ((float)ccr_value / arr_value) * 100.0f; // 计算占空比 // // sprintf((char*)str_buff,"%+.0f",duty_cycle); // OLED_ShowString(64,3,str_buff,sizeof(str_buff)); // // sprintf((char*)str_buff,"%+.0f",speed); // OLED_ShowString(64,4,str_buff,sizeof(str_buff)); sprintf((char*)str_buff,"%+.0f",PositionTarget); OLED_ShowString(64,2,str_buff,sizeof(str_buff)); sprintf((char*)str_buff,"%+.0f",PositionActual); OLED_ShowString(64,3,str_buff,sizeof(str_buff)); sprintf((char*)str_buff,"%+.0f",Positionout); OLED_ShowString(64,4,str_buff,sizeof(str_buff)); sprintf((char*)str_buff,"%+.0f",InnerTarget); OLED_ShowString(64,5,str_buff,sizeof(str_buff)); sprintf((char*)str_buff,"%+.0f",InnerActual); OLED_ShowString(64,6,str_buff,sizeof(str_buff)); sprintf((char*)str_buff,"%+.0f",Innerout); OLED_ShowString(64,7,str_buff,sizeof(str_buff)); } uint16_t last_count = 0; uint16_t last_time_ms = 0; //void calculate_speed() //{ // count = __HAL_TIM_GET_COUNTER(&htim3); // uint16_t current_time_ms = HAL_GetTick(); // int16_t diff_count = count - last_count; // float delta_time_sec = (current_time_ms - last_time_ms) / 1000.0f; // // if (delta_time_sec > 0) // { // float rpm = ((float)diff_count / (float)COUNTS_PER_REV) * 60.0f / delta_time_sec; // // speed=rpm; // // InnerActual=speed/1220.0f*100; // // InnerError1 = InnerError0; // InnerError0 = InnerTarget - InnerActual; //// ErrorInt += Error0; // InnerIntOut += InnerKi * InnerError0; // // /*积分项输出限幅*/ // if(InnerIntOut>1000)InnerIntOut=1000; // if(InnerIntOut<0)InnerIntOut=0; // // Innerout = InnerKp * InnerError0 + InnerIntOut + InnerKd * (InnerError0 - InnerError1); // // if(Innerout > 1000)Innerout = 1000; // if(Innerout < 0)Innerout = 0; // // __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Innerout); // } // last_count = count; // last_time_ms = current_time_ms; //} void set_motor_direction_and_pwm(float pwm_value) { // 限制PWM范围 pwm_value = fmaxf(fminf(pwm_value, 1000.0f), 0); // 设置方向引脚(假设DIR_PIN控制方向) if (motor_direction > 0) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // 正转 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 反转 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); } // 设置PWM占空比 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pwm_value); } void calculate_Position() { count = __HAL_TIM_GET_COUNTER(&htim3); uint16_t current_time_ms = HAL_GetTick(); int16_t diff_count = count - last_count; float delta_time_sec = (current_time_ms - last_time_ms) / 1000.0f; if (delta_time_sec > 0) { // float rpm = ((float)diff_count / (float)COUNTS_PER_REV) * 60.0f / delta_time_sec; // // speed=rpm; // 更新位置实际值(编码器脉冲累计) PositionActual += diff_count; // 位置环控制:计算误差和方向 PositionError0 = PositionTarget - PositionActual; motor_direction = (PositionError0 >= 0) ? 1 : -1; // 目标>实际时正转,否则反转 // 位置环PID输出(取绝对值作为速度目标) PositionErrorInt += PositionError0; PositionIntOut = PositionKp * PositionError0 + PositionKi * PositionErrorInt; // 限制积分输出 PositionIntOut = fmaxf(fminf(PositionIntOut, 1000.0f), -1000.0f); // 速度环目标值(绝对值) InnerTarget = fabs(PositionIntOut); // 速度环目标值(绝对值) InnerTarget = fabs(PositionIntOut); // 速度环控制(InnerActual为编码器脉冲差值) InnerError0 = InnerTarget - fabs(diff_count); InnerIntout += InnerKi * InnerError0; InnerIntout = fmaxf(fminf(InnerIntout, 1000.0f), 0); // 速度环输出非负 Innerout = InnerKp * InnerError0 + InnerIntout; Innerout = fmaxf(fminf(Innerout, 1000.0f), 0); // 设置电机方向和PWM set_motor_direction_and_pwm(Innerout); // //速度环实际值 = 本周期编码器脉冲差值(或累计值) // InnerActual = PositionActual; // // //位置环控制 // PositionError1 = PositionError0; // PositionError0 = PositionTarget - PositionActual; // PositionErrorInt += PositionError0; // // /*积分项输出限幅*/ // if(PositionIntOut>1000)PositionIntOut=1000; // if(PositionIntOut<0)PositionIntOut=0; // Positionout = PositionKp * PositionError0 + PositionKi * PositionErrorInt + PositionKd * (PositionError0 - PositionError1); // // 确定方向和速度目标 // motor_direction = (Positionout >= 0) ? 1 : -1; // if(motor_direction==1) // { // HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1, GPIO_PIN_SET); // HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2, GPIO_PIN_RESET); // } // else if(motor_direction==-1) // { // HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1, GPIO_PIN_RESET); // HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2, GPIO_PIN_SET); // } // InnerTarget = fabs(Positionout); // InnerTarget = Positionout; // InnerError1 = InnerError0; // InnerError0 = InnerTarget - InnerActual; //// ErrorInt += Error0; // InnerIntout += InnerKi * InnerError0; // // /*积分项输出限幅*/ // if(InnerIntout>1000)InnerIntout=1000; // if(InnerIntout<0)InnerIntout=0; // // Innerout = InnerKp * InnerError0 + InnerIntout + InnerKd * (InnerError0 - InnerError1); // // if(Innerout > 1000)Innerout = 1000; // if(Innerout < 0)Innerout = 0; // // __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Innerout); } last_count = count; last_time_ms = current_time_ms; } //void dianjispeed() //{ // switch(x) // { // case 0: PositionTarget = 0; break; // case 1: PositionTarget = 300; break; // case 2: PositionTarget = 500; break; // case 3: PositionTarget = 800; break; // } //// __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,ccr_value); //} //void Send_PID_Data(float target, float actual, float output) //{ // char buffer[64]; // sprintf(buffer, "T:%.2f,A:%.2f,O:%.2f\r\n", target, actual, output); // HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), HAL_MAX_DELAY); //} /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM2_Init(); // MX_TIM3_Init(); // MX_I2C1_Init(); // MX_TIM4_Init(); // MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ // OLED_Init(); // OLED_display_info(); // // 启动编码器 // HAL_TIM_Encoder_Start(&htim3,TIM_CHANNEL_ALL); // // 启动串口接收中断 // HAL_UART_Receive_IT(&huart1, &rx_data, 1); // 启动定时器并启用中断 HAL_TIM_Base_Start_IT(&htim4); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1, GPIO_PIN_SET); // HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2, GPIO_PIN_RESET); HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1); // PositionActual = 0; // 初始化位置实际值 while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ Scan_Keys(); // Send_PID_Data(PositionTarget, PositionActual, Positionout); // dianjispeed(); // __HAL_TIM_SET_COUNTER(&htim3,0); // HAL_Delay(500); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim4) { calculate_Position(); // 计算速度 OLED_display_dat(); // 更新 OLED 显示 } } 以上是用位置环速度环双环PID算法控制MG310编码器电机的代码,如果改为控制步进电机转动指定角度该如何修改以上代码?

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2022 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /** ****************************************************************************** * 作者:好家伙VCC * 联系方式:[email protected] * 软件版本:V3.3.0 * 使用硬件版本:V3.3.0 * ****************************************************************************** * 更新日志: * * * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "adc.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "oled.h" #include "stdio.h" #include "motor.h" #include "niming.h" #include "pid.h" #include "cJSON.h" #include <string.h> #include "HC_SR04.h" #include "mpu6050.h" #include "inv_mpu.h" #include "inv_mpu_dmp_motion_driver.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ extern float Motor1Speed ;//声明电机1速度 extern float Motor2Speed ;//声明电机2速度 extern tPid pidMotor1Speed;//声明电机1PID速度控制结构体类型变量 extern tPid pidMotor2Speed; extern tPid pidFollow; //定距离跟随PID extern tPid pidMPU6050YawMovement; //利用6050偏航角 进行姿态控制的PID extern uint8_t Usart1_ReadBuf[255]; //串口1 缓冲数组 float p,i,d,a,b;//使用JSON时候使用的变量 uint8_t OledString[50];//OLED显示使用的字符串数组 extern float Mileage;//里程数 extern tPid pidHW_Tracking;//红外循迹的PID uint8_t g_ucaHW_Read[4] = {0};//保存红外对管电平的数组 int8_t g_cThisState = 0;//这次状态 int8_t g_cLastState = 0; //上次状态 float g_fHW_PID_Out;//红外对管PID计算输出速度 float g_fHW_PID_Out1;//电机1的最后循迹PID控制速度 float g_fHW_PID_Out2;//电机2的最后循迹PID控制速度 uint8_t g_ucUsart3ReceiveData; //保存串口三接收的数据 uint8_t Usart3String[50];//串口三输出字符串使用的字符串数组 float g_fHC_SR04_Read;//超声波传感器读取障碍物数据 float g_fFollow_PID_Out;//定距离跟随PID计算输出速度 float pitch,roll,yaw; //俯仰角 横滚角 航向角 float g_fMPU6050YawMovePidOut = 0.00f; //姿态PID运算输出 float g_fMPU6050YawMovePidOut1 = 0.00f; //第一个电机控制输出 float g_fMPU6050YawMovePidOut2 = 0.00f; //第一个电机控制输出 uint8_t g_ucMode = 0; //小车运动模式标志位 0:显示功能、1:PID循迹模式、2:手机遥控普通运动模式、3.超声波避障模式、4:PID跟随模式、5:遥控角度闭环 /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_ADC2_Init(); MX_USART3_UART_Init(); /* USER CODE BEGIN 2 */ OLED_Init(); //初始化OLED OLED_Clear() ; HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//开启定时器1 通道1 PWM输出 HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);//开启定时器1 通道4 PWM输出 HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);//开启定时器2 HAL_TIM_Encoder_Start(&htim4,TIM_CHANNEL_ALL);//开启定时器4 HAL_TIM_Base_Start_IT(&htim2); //开启定时器2 中断 HAL_TIM_Base_Start_IT(&htim4); //开启定时器4 中断 HAL_TIM_Base_Start_IT(&htim1); //开启定时器1 中断 __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE); //开启串口1接收中断 PID_init();//PID参数初始化 HAL_UART_Receive_IT(&huart3,&g_ucUsart3ReceiveData,1); //串口三接收数据 HAL_Delay(500);//延时0.5秒 6050上电稳定后初始化 MPU_Init(); //初始化MPU6050 while(MPU_Init()!=0);//初始化MPU6050模块的MPU 注意初始化阶段不要移动小车 while(mpu_dmp_init()!=0);//mpu6050,dmp初始化 // cJSON *cJsonData ,*cJsonVlaue; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ sprintf((char *)OledString," g_ucMode:%d",g_ucMode);//显示g_ucMode 当前模式 OLED_ShowString(0,6,OledString,12); //显示在OLED上 sprintf((char *)Usart3String," g_ucMode:%d",g_ucMode);//蓝牙APP显示 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 if(g_ucMode == 0) { //0LED显示功能 sprintf((char*)OledString, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度 OLED_ShowString(0,0,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数, sprintf((char*)OledString, "Mileage:%.2f", Mileage);//显示里程 OLED_ShowString(0,1,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数, sprintf((char*)OledString, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压 OLED_ShowString(0,2,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数, sprintf((char *)OledString,"HC_SR04:%.2fcm\r\n",HC_SR04_Read());//显示超声波数据 OLED_ShowString(0,3,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数, sprintf((char *)OledString,"p:%.2f r:%.2f \r\n",pitch,roll);//显示6050数据 俯仰角 横滚角 OLED_ShowString(0,4,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数, sprintf((char *)OledString,"y:%.2f \r\n",yaw);//显示6050数据 航向角 OLED_ShowString(0,5,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数, //蓝牙APP显示 sprintf((char*)Usart3String, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 //阻塞方式发送可以保证数据发送完毕,中断发送不一定可以保证数据已经发送完毕才启动下一次发送 sprintf((char*)Usart3String, "Mileage:%.2f", Mileage);//显示里程 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 sprintf((char*)Usart3String, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 sprintf((char *)Usart3String,"HC_SR04:%.2fcm\r\n",HC_SR04_Read());//显示超声波数据 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 sprintf((char *)Usart3String,"p:%.2f r:%.2f \r\n",pitch,roll);//显示6050数据 俯仰角 横滚角 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 sprintf((char *)Usart3String,"y:%.2f \r\n",yaw);//显示6050数据 航向角 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 //获得6050数据 while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //这个可以解决经常读不出数据的问题 //在显示模式电机停转 设置小车速度为0 motorPidSetSpeed(0,0); } if(g_ucMode == 1) { ///**** 红外PID循迹功能******************/ g_ucaHW_Read[0] = READ_HW_OUT_1;//读取红外对管状态、这样相比于写在if里面更高效 g_ucaHW_Read[1] = READ_HW_OUT_2; g_ucaHW_Read[2] = READ_HW_OUT_3; g_ucaHW_Read[3] = READ_HW_OUT_4; if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 ) { // printf("应该前进\r\n");//注释掉更加高效,减少无必要程序执行 g_cThisState = 0;//前进 } else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )//使用else if更加合理高效 { // printf("应该右转\r\n"); g_cThisState = -1;//应该右转 } else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 ) { // printf("快速右转\r\n"); g_cThisState = -2;//快速右转 } else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0) { // printf("快速右转\r\n"); g_cThisState = -3;//快速右转 } else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 0 ) { // printf("应该左转\r\n"); g_cThisState = 1;//应该左转 } else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 1 ) { // printf("快速左转\r\n"); g_cThisState = 2;//快速左转 } else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 1) { // printf("快速左转\r\n"); g_cThisState = 3;//快速左转 } g_fHW_PID_Out = PID_realize(&pidHW_Tracking,g_cThisState);//PID计算输出目标速度 这个速度,会和基础速度加减 g_fHW_PID_Out1 = 3 + g_fHW_PID_Out;//电机1速度=基础速度+循迹PID输出速度 g_fHW_PID_Out2 = 3 - g_fHW_PID_Out;//电机1速度=基础速度-循迹PID输出速度 if(g_fHW_PID_Out1 >5) g_fHW_PID_Out1 =5;//进行限幅 限幅速度在0-5之间 if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0; if(g_fHW_PID_Out2 >5) g_fHW_PID_Out2 =5;//进行限幅 限幅速度在0-5之间 if(g_fHW_PID_Out2 <0) g_fHW_PID_Out2 =0; if(g_cThisState != g_cLastState)//如何这次状态不等于上次状态、就进行改变目标速度和控制电机、在定时器中依旧定时控制电机 { motorPidSetSpeed(g_fHW_PID_Out1,g_fHW_PID_Out2);//通过计算的速度控制电机 } g_cLastState = g_cThisState;//保存上次红外对管状态 } if(g_ucMode == 2) { //***************遥控模式***********************// //遥控模式的控制在串口三的中断里面 } if(g_ucMode == 3) { //******超声波避障模式*********************// ////避障逻辑 if(HC_SR04_Read() > 25)//前方无障碍物 { motorPidSetSpeed(1,1);//前运动 HAL_Delay(100); } else{ //前方有障碍物 motorPidSetSpeed(-1,1);//右边运动 原地 HAL_Delay(500); if(HC_SR04_Read() > 25)//右边无障碍物 { motorPidSetSpeed(1,1);//前运动 HAL_Delay(100); } else{//右边有障碍物 motorPidSetSpeed(1,-1);//左边运动 原地 HAL_Delay(1000); if(HC_SR04_Read() >25)//左边无障碍物 { motorPidSetSpeed(1,1);//前运动 HAL_Delay(100); } else{ motorPidSetSpeed(-1,-1);//后运动 HAL_Delay(1000); motorPidSetSpeed(-1,1);//右边运动 HAL_Delay(50); } } } } if(g_ucMode == 4) { //**********PID跟随功能***********// g_fHC_SR04_Read=HC_SR04_Read();//读取前方障碍物距离 if(g_fHC_SR04_Read < 60){ //如果前60cm 有东西就启动跟随 g_fFollow_PID_Out = PID_realize(&pidFollow,g_fHC_SR04_Read);//PID计算输出目标速度 这个速度,会和基础速度加减 if(g_fFollow_PID_Out > 6) g_fFollow_PID_Out = 6;//对输出速度限幅 if(g_fFollow_PID_Out < -6) g_fFollow_PID_Out = -6; motorPidSetSpeed(g_fFollow_PID_Out,g_fFollow_PID_Out);//速度作用与电机上 } else motorPidSetSpeed(0,0);//如果前面60cm 没有东西就停止 HAL_Delay(10);//读取超声波传感器不能过快 } if(g_ucMode == 5) { //*************MPU6050航向角 PID转向控制*****************// sprintf((char *)Usart3String,"pitch:%.2f roll:%.2f yaw:%.2f\r\n",pitch,roll,yaw);//显示6050数据 俯仰角 横滚角 航向角 HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),0xFFFF);//通过串口三输出字符 strlen:计算字符串大小 //mpu_dmp_get_data(&pitch,&roll,&yaw);//返回值:0,DMP成功解出欧拉角 while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //这个可以解决经常读不出数据的问题 g_fMPU6050YawMovePidOut = PID_realize(&pidMPU6050YawMovement,yaw);//PID计算输出目标速度 这个速度,会和基础速度加减 g_fMPU6050YawMovePidOut1 = 1.5 + g_fMPU6050YawMovePidOut;//基础速度加减PID输出速度 g_fMPU6050YawMovePidOut2 = 1.5 - g_fMPU6050YawMovePidOut; if(g_fMPU6050YawMovePidOut1 >3.5) g_fMPU6050YawMovePidOut1 =3.5;//进行限幅 if(g_fMPU6050YawMovePidOut1 <0) g_fMPU6050YawMovePidOut1 =0; if(g_fMPU6050YawMovePidOut2 >3.5) g_fMPU6050YawMovePidOut2 =3.5;//进行限幅 if(g_fMPU6050YawMovePidOut2 <0) g_fMPU6050YawMovePidOut2 =0; motorPidSetSpeed(g_fMPU6050YawMovePidOut1,g_fMPU6050YawMovePidOut2);//将最后计算的目标速度 通过motorPidSetSpeed控制电机 } ///**** 红外PID循迹功能******************/ // g_ucaHW_Read[0] = READ_HW_OUT_1;//读取红外对管状态、这样相比于写在if里面更高效 // g_ucaHW_Read[1] = READ_HW_OUT_2; // g_ucaHW_Read[2] = READ_HW_OUT_3; // g_ucaHW_Read[3] = READ_HW_OUT_4; // if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 ) // { //// printf("应该前进\r\n");//注释掉更加高效,减少无必要程序执行 // g_cThisState = 0;//前进 // } // else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )//使用else if更加合理高效 // { //// printf("应该右转\r\n"); // g_cThisState = -1;//应该右转 // } // else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 ) // { //// printf("快速右转\r\n"); // g_cThisState = -2;//快速右转 // } // else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0) // { //// printf("快速右转\r\n"); // g_cThisState = -3;//快速右转 // } // else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 0 ) // { //// printf("应该左转\r\n"); // g_cThisState = 1;//应该左转 // } // else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 1 ) // { //// printf("快速左转\r\n"); // g_cThisState = 2;//快速左转 // } // else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 1) // { //// printf("快速左转\r\n"); // g_cThisState = 3;//快速左转 // } // g_fHW_PID_Out = PID_realize(&pidHW_Tracking,g_cThisState);//PID计算输出目标速度 这个速度,会和基础速度加减 // g_fHW_PID_Out1 = 3 + g_fHW_PID_Out;//电机1速度=基础速度+循迹PID输出速度 // g_fHW_PID_Out2 = 3 - g_fHW_PID_Out;//电机1速度=基础速度-循迹PID输出速度 // if(g_fHW_PID_Out1 >5) g_fHW_PID_Out1 =5;//进行限幅 限幅速度在0-5之间 // if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0; // if(g_fHW_PID_Out2 >5) g_fHW_PID_Out2 =5; // if(g_fHW_PID_Out2 <0) g_fHW_PID_Out2 =0; // if(g_cThisState != g_cLastState)//如何这次状态不等于上次状态、就进行改变目标速度和控制电机、在定时器中依旧定时控制电机 // { // motorPidSetSpeed(g_fHW_PID_Out1,g_fHW_PID_Out2);//通过计算的速度控制电机 // } // // g_cLastState = g_cThisState;//保存上次红外对管状态 ////通过串口三(蓝牙)输出信息 ////***************串口三输出****************************// // sprintf((char *)Usart3String,"V1:%.2fV2:%.2f\r\n",Motor1Speed,Motor2Speed);//显示两个电机转速 单位:转/秒 // HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 // // sprintf((char *)Usart3String,"Mileage%.2f\r\n",Mileage);//计算小车里程 单位cm // HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 // // sprintf((char *)Usart3String,"U:%.2fV\r\n",adcGetBatteryVoltage());//显示电池电压 // HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小 // // sprintf((char *)Usart3String,"HC_SR04:%.2fcm\r\n",HC_SR04_Read());//显示超声波数据 // HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),0xFFFF);//通过串口三输出字符 strlen:计算字符串大小 // // sprintf((char *)Usart3String,"pitch:%.2f roll:%.2f yaw:%.2f\r\n",pitch,roll,yaw);//显示6050数据 俯仰角 横滚角 航向角 // HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),0xFFFF);//通过串口三输出字符 strlen:计算字符串大小 // // //mpu_dmp_get_data(&pitch,&roll,&yaw);//返回值:0,DMP成功解出欧拉角 // while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //这个可以解决经常读不出数据的问题 // // // HAL_Delay(5);//注意调用不可以过于频繁HC_SR04_Read() ////*************MPU6050航向角 PID转向控制*****************// // sprintf((char *)Usart3String,"pitch:%.2f roll:%.2f yaw:%.2f\r\n",pitch,roll,yaw);//显示6050数据 俯仰角 横滚角 航向角 // HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),0xFFFF);//通过串口三输出字符 strlen:计算字符串大小 // // //mpu_dmp_get_data(&pitch,&roll,&yaw);//返回值:0,DMP成功解出欧拉角 // while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //这个可以解决经常读不出数据的问题 // // // g_fMPU6050YawMovePidOut = PID_realize(&pidMPU6050YawMovement,yaw);//PID计算输出目标速度 这个速度,会和基础速度加减 // g_fMPU6050YawMovePidOut1 = 1.5 + g_fMPU6050YawMovePidOut;//基础速度加减PID输出速度 // g_fMPU6050YawMovePidOut2 = 1.5 - g_fMPU6050YawMovePidOut; // if(g_fMPU6050YawMovePidOut1 >3.5) g_fMPU6050YawMovePidOut1 =3.5;//进行限幅 // if(g_fMPU6050YawMovePidOut1 <0) g_fMPU6050YawMovePidOut1 =0; // if(g_fMPU6050YawMovePidOut2 >3.5) g_fMPU6050YawMovePidOut2 =3.5; // if(g_fMPU6050YawMovePidOut2 <0) g_fMPU6050YawMovePidOut2 =0; // motorPidSetSpeed(g_fMPU6050YawMovePidOut1,g_fMPU6050YawMovePidOut2); ////**************避障功能********************// ////避障逻辑 // if(HC_SR04_Read() > 25)//前方无障碍物 // { // motorPidSetSpeed(1,1);//前运动 // HAL_Delay(100); // } // else{ //前方有障碍物 // motorPidSetSpeed(-1,1);//右边运动 原地 // HAL_Delay(500); // if(HC_SR04_Read() > 25)//右边无障碍物 // { // motorPidSetSpeed(1,1);//前运动 // HAL_Delay(100); // } // else{//右边有障碍物 // motorPidSetSpeed(1,-1);//左边运动 原地 // HAL_Delay(1000); // if(HC_SR04_Read() >25)//左边无障碍物 // { // motorPidSetSpeed(1,1);//前运动 // HAL_Delay(100); // } // else{ // motorPidSetSpeed(-1,-1);//后运动 // HAL_Delay(1000); // motorPidSetSpeed(-1,1);//右边运动 // HAL_Delay(50); // } // } // } ////*************无PID跟随功能************// // if(HC_SR04_Read() > 25) // { // motorPidSetSpeed(1,1);//前运动 // HAL_Delay(100); // } // if(HC_SR04_Read() < 20) // { // motorPidSetSpeed(-1,-1);//后运动 // HAL_Delay(100); // } ////**********PID跟随功能***********// // g_fHC_SR04_Read=HC_SR04_Read();//读取前方障碍物距离 // if(g_fHC_SR04_Read < 60){ //如果前60cm 有东西就启动跟随 // g_fFollow_PID_Out = PID_realize(&pidFollow,g_fHC_SR04_Read);//PID计算输出目标速度 这个速度,会和基础速度加减 // if(g_fFollow_PID_Out > 6) g_fFollow_PID_Out = 6;//对输出速度限幅 // if(g_fFollow_PID_Out < -6) g_fFollow_PID_Out = -6; // motorPidSetSpeed(g_fFollow_PID_Out,g_fFollow_PID_Out);//速度作用与电机上 // } // else motorPidSetSpeed(0,0);//如果前面60cm 没有东西就停止 // HAL_Delay(10);//读取超声波传感器不能过快 // ANO_DT_Send_F2(Motor1Speed*100, 3.0*100,Motor2Speed*100,3.0*100);//向匿名上位机通过F2帧发送4个int16类型的数据 曲线显示 // if(Usart_WaitReasFinish() == 0)//是否接收完毕 // { // cJsonData = cJSON_Parse((const char *)Usart1_ReadBuf); // if(cJSON_GetObjectItem(cJsonData,"p") !=NULL) // { // cJsonVlaue = cJSON_GetObjectItem(cJsonData,"p"); // p = cJsonVlaue->valuedouble; // pidMotor1Speed.Kp = p; // } // if(cJSON_GetObjectItem(cJsonData,"i") !=NULL) // { // cJsonVlaue = cJSON_GetObjectItem(cJsonData,"i"); // i = cJsonVlaue->valuedouble; // pidMotor1Speed.Ki = i; // } // if(cJSON_GetObjectItem(cJsonData,"d") !=NULL) // { // cJsonVlaue = cJSON_GetObjectItem(cJsonData,"d"); // d = cJsonVlaue->valuedouble; // pidMotor1Speed.Kd = d; // } // if(cJSON_GetObjectItem(cJsonData,"a") !=NULL) // { // // cJsonVlaue = cJSON_GetObjectItem(cJsonData,"a"); // a = cJsonVlaue->valuedouble; // pidMotor1Speed.target_val =a; // } // if(cJSON_GetObjectItem(cJsonData,"b") !=NULL) // { // // cJsonVlaue = cJSON_GetObjectItem(cJsonData,"b"); // b = cJsonVlaue->valuedouble; // pidMotor2Speed.target_val =b; // } // if(cJsonData != NULL){ // cJSON_Delete(cJsonData);//释放空间、但是不能删除cJsonVlaue不然会 出现异常错误 // } // memset(Usart1_ReadBuf,0,255);//清空接收buf,注意这里不能使用strlen // } // printf("P:%.3f I:%.3f D:%.3f A:%.3f\r\n",p,i,d,a); // __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 10); // __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, 10); // HAL_GPIO_WritePin(BIN1_GPIO_Port,BIN1_Pin,GPIO_PIN_SET); // HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_SET); // uint8_t c_Data[] = "串口输出测试:好家伙VCC\r\n"; // HAL_UART_Transmit(&huart1,c_Data,sizeof(c_Data),0xFFFF); // HAL_Delay(1000); // printf("printf:好家伙VCC测试\r\n"); // HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin); // HAL_Delay(500); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ #include "motor.h" #include "tim.h" #include "pid.h" #define MAX_SPEED_UP 3 extern float Motor1Speed ; extern float Motor2Speed ; extern tPid pidMotor1Speed; extern tPid pidMotor2Speed; float motorSpeedUpCut = 0.5;//加减速速度变量 void Motor_Set(int Motor1,int Motor2) { //取反更适合控制习惯,大于0小车向前运动 Motor1 =-Motor1; Motor2 =-Motor2; //1.先根据正负设置方向GPIO 高低电平 if(Motor1 <0) BIN1_SET; else BIN1_RESET; if(Motor2 <0) AIN1_SET; else AIN1_RESET; //2.然后设置占空比 if(Motor1 <0) { if(Motor1 <-99) Motor1 =-99; __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, (100+Motor1)); } else { if(Motor1 >99) Motor1 = 99; __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,Motor1); } if(Motor2<0) { if(Motor2 <-99) Motor2=-99; __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, (100+Motor2)); } else { if(Motor2 >99) Motor2 =99; __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, Motor2); } } /******************* * @brief 通过赋值PID目标速度控制小车运动 * @param Motor1SetSpeed:电机1目标速度 Motor2SetSpeed:电机2目标速度 * @return 无 * motorPidSetSpeed(1,2);//右边运动 motorPidSetSpeed(2,1);//左边运动 motorPidSetSpeed(1,1);//前运动 motorPidSetSpeed(-1,-1);//后运动 motorPidSetSpeed(-1,1); //向右原地旋转 motorPidSetSpeed(1,-1); //向左原地旋转 *******************/ void motorPidSetSpeed(float Motor1SetSpeed,float Motor2SetSpeed) { //设置Pid目标转速 pidMotor1Speed.target_val=Motor1SetSpeed; pidMotor2Speed.target_val=Motor2SetSpeed; //PID计算控制电机 Motor_Set(PID_realize(&pidMotor1Speed,Motor1Speed),PID_realize(&pidMotor2Speed,Motor2Speed)); } //向前加速函数 void motorPidSpeedUp(void) { if(motorSpeedUpCut <= MAX_SPEED_UP) motorSpeedUpCut +=0.5;//如果没有超过最大值就增加0.5 motorPidSetSpeed(motorSpeedUpCut,motorSpeedUpCut);//设置到电机 } //向前减速函数 void motorPidSpeedCut(void) { if(motorSpeedUpCut >=0.5)motorSpeedUpCut-=0.5;//判断是否速度太小 motorPidSetSpeed(motorSpeedUpCut,motorSpeedUpCut);//设置到电机 } #include "pid.h" //定义一个结构体类型变量 tPid pidMotor1Speed;//电机1速度PID闭环参数 tPid pidMotor2Speed;//电机2速度PID闭环参数 tPid pidHW_Tracking;//红外循迹的PID结构体类型变量 tPid pidFollow; //定距离跟随PID结构体类型变量 tPid pidMPU6050YawMovement; //利用6050偏航角 进行姿态控制的PID //给结构体类型变量赋初值 void PID_init() { pidMotor1Speed.actual_val=0.0; pidMotor1Speed.target_val=0.00; pidMotor1Speed.err=0.0; pidMotor1Speed.err_last=0.0; pidMotor1Speed.err_sum=0.0; pidMotor1Speed.Kp=15; pidMotor1Speed.Ki=5; pidMotor1Speed.Kd=0; pidMotor2Speed.actual_val=0.0; pidMotor2Speed.target_val=0.00; pidMotor2Speed.err=0.0; pidMotor2Speed.err_last=0.0; pidMotor2Speed.err_sum=0.0; pidMotor2Speed.Kp=15; pidMotor2Speed.Ki=5; pidMotor2Speed.Kd=0; pidHW_Tracking.actual_val=0.0; pidHW_Tracking.target_val=0.00;//红外循迹PID 的目标值为0 pidHW_Tracking.err=0.0; pidHW_Tracking.err_last=0.0; pidHW_Tracking.err_sum=0.0; pidHW_Tracking.Kp=-1.50; pidHW_Tracking.Ki=0; pidHW_Tracking.Kd=0.80; pidFollow.actual_val=0.0; pidFollow.target_val=22.50;//定距离跟随 目标距离22.5cm pidFollow.err=0.0; pidFollow.err_last=0.0; pidFollow.err_sum=0.0; pidFollow.Kp=-0.5;//定距离跟随的Kp大小通过估算PID输入输出数据,确定大概大小,然后在调试 pidFollow.Ki=-0.001;//Ki小一些 pidFollow.Kd=0; pidMPU6050YawMovement.actual_val=0.0; pidMPU6050YawMovement.target_val=0.00;//设定姿态目标值 pidMPU6050YawMovement.err=0.0; pidMPU6050YawMovement.err_last=0.0; pidMPU6050YawMovement.err_sum=0.0; pidMPU6050YawMovement.Kp=0.02;//6050航向角PID运动控制 pidMPU6050YawMovement.Ki=0; pidMPU6050YawMovement.Kd=0.1; } //比例p调节控制函数 float P_realize(tPid * pid,float actual_val) { pid->actual_val = actual_val;//传递真实值 pid->err = pid->target_val - pid->actual_val;//当前误差=目标值-真实值 //比例控制调节 输出=Kp*当前误差 pid->actual_val = pid->Kp*pid->err; return pid->actual_val; } //比例P 积分I 控制函数 float PI_realize(tPid * pid,float actual_val) { pid->actual_val = actual_val;//传递真实值 pid->err = pid->target_val - pid->actual_val;//当前误差=目标值-真实值 pid->err_sum += pid->err;//误差累计值 = 当前误差累计和 //使用PI控制 输出=Kp*当前误差+Ki*误差累计值 pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum; return pid->actual_val; } // PID控制函数 float PID_realize(tPid * pid,float actual_val) { pid->actual_val = actual_val;//传递真实值 pid->err = pid->target_val - pid->actual_val;////当前误差=目标值-真实值 pid->err_sum += pid->err;//误差累计值 = 当前误差累计和 //使用PID控制 输出 = Kp*当前误差 + Ki*误差累计值 + Kd*(当前误差-上次误差) pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum + pid->Kd*(pid->err - pid->err_last); //保存上次误差: 这次误差赋值给上次误差 pid->err_last = pid->err; return pid->actual_val; } 提取所有有关于红外循迹的代码部分,并将代码分别分装在pid,sensor,motor(c文件和h文件)

大家在看

recommend-type

《极品家丁(七改版)》(珍藏七改加料无雷精校全本)(1).zip

《极品家丁(七改版)》(珍藏七改加料无雷精校全本)(1).zip
recommend-type

密码::unlocked::sparkles::locked:创新,方便,安全的加密应用程序

隐身者 创新,方便,安全的加密应用程序。 加密无限位。 只记得一点。 Crypter是一款跨平台的加密应用程序,它使加密和解密变得很方便,同时仍然保持强大的安全性。 它解决了当今大多数安全系统中最弱的链接之一-弱密码。 它简化了安全密码的生成和管理,并且只需要记住一个位-MasterPass。 是一个加密应用程序,可以解密和加密包括文件和文件夹在内的任意数据。 该版本已发布,并针对macOS(OSX),Linux(适用于所有通过发行的发行版)和Windows(32和64位)进行了全面测试。 所有核心模块(提供核心功能的模块)都经过了全面测试。 会将MasterPass保存在操作系统的钥匙串中,因此您不必在每次打开应用程序时都输入它。 为了帮助加快开发速度,请发送PR剩下的内容做 如果您有任何建议,请打开一个问题,并通过PR进行改进! 还要签出 ( )一个分散的端到端加密消息传递应用程序。 链接到此自述文件: : 内容 安装 适用于所有主要平台的所有预构建二进制文件都可以在。 Crypter也适用于macOS的 。 因此,要安装它,只需在终端中运行以下命令:
recommend-type

HkAndroidSDK.zip

助于Android开发视频监控功能,根据ip地址可以远程操控,控制向左,向右,向下,向上以及转动摄像头,也可以放大和缩小
recommend-type

matlab的欧拉方法代码-BEM_flow_simulation:计算流体力学:使用边界元方法模拟障碍物周围/附近的流动

matlab的欧拉方法代码BEM_flow_simulation MATLAB上的计算流体力学: 目的是使用边界元素方法模拟任何障碍物附近或周围的任何形式的流动 使用BEM绕圆柱障碍物和接近均匀战争的潜在流动 非粘性势流的假设适用于导航斯托克斯方程(Euler方程),使用边界元方法,该代码模拟了在均匀垂直壁附近的尺寸稳定的圆柱障碍物周围的流动。 该系统不受其他方向的限制。 该代码是流体力学硕士1实习的主题,并且作为大型项目的第一块砖,该项目用于模拟复杂非均匀障碍物周围的粘性流动,因此可以自由继续。 类“ pot_flow_class”模拟垂直于垂直壁(两个障碍物之间的距离为H)附近圆柱2D障碍物(无量纲半径r = 1)附近的该势流。 流速为U = 1(无量纲)。 使用边界元素方法的第二层。 这样的流动的精确解决方案的代码允许验证无垂直壁模拟。
recommend-type

基于YOLO网络的行驶车辆目标检测matlab仿真+操作视频

1.领域:matlab,YOLO网络的行驶车辆目标检测算法 2.内容:基于YOLO网络的行驶车辆目标检测matlab仿真+操作视频 3.用处:用于YOLO网络的行驶车辆目标检测算法编程学习 4.指向人群:本硕博等教研学习使用 5.运行注意事项: 使用matlab2021a或者更高版本测试,运行里面的Runme_.m文件,不要直接运行子函数文件。运行时注意matlab左侧的当前文件夹窗口必须是当前工程所在路径。 具体可观看提供的操作录像视频跟着操作。

最新推荐

recommend-type

C#类库封装:简化SDK调用实现多功能集成,构建地磅无人值守系统

内容概要:本文介绍了利用C#类库封装多个硬件设备的SDK接口,实现一系列复杂功能的一键式调用。具体功能包括身份证信息读取、人证识别、车牌识别(支持臻识和海康摄像头)、LED显示屏文字输出、称重数据读取、二维码扫描以及语音播报。所有功能均被封装为简单的API,极大降低了开发者的工作量和技术门槛。文中详细展示了各个功能的具体实现方式及其应用场景,如身份证读取、人证核验、车牌识别等,并最终将这些功能整合到一起,形成了一套完整的地磅称重无人值守系统解决方案。 适合人群:具有一定C#编程经验的技术人员,尤其是需要快速集成多种硬件设备SDK的应用开发者。 使用场景及目标:适用于需要高效集成多种硬件设备SDK的项目,特别是那些涉及身份验证、车辆管理、物流仓储等领域的企业级应用。通过使用这些封装好的API,可以大大缩短开发周期,降低维护成本,提高系统的稳定性和易用性。 其他说明:虽然封装后的API极大地简化了开发流程,但对于一些特殊的业务需求,仍然可能需要深入研究底层SDK。此外,在实际部署过程中,还需考虑网络环境、硬件兼容性等因素的影响。
recommend-type

基于STM32F1的BLDC无刷直流电机与PMSM永磁同步电机源码解析:传感器与无传感器驱动详解

基于STM32F1的BLDC无刷直流电机和PMSM永磁同步电机的驱动实现方法,涵盖了有传感器和无传感两种驱动方式。对于BLDC电机,有传感器部分采用霍尔传感器进行六步换相,无传感部分则利用反电动势过零点检测实现换相。对于PMSM电机,有传感器部分包括霍尔传感器和编码器的方式,无传感部分则采用了滑模观测器进行矢量控制(FOC)。文中不仅提供了详细的代码片段,还分享了许多调试经验和技巧。 适合人群:具有一定嵌入式系统和电机控制基础知识的研发人员和技术爱好者。 使用场景及目标:适用于需要深入了解和实现BLDC和PMSM电机驱动的开发者,帮助他们掌握不同传感器条件下的电机控制技术和优化方法。 其他说明:文章强调了实际调试过程中可能遇到的问题及其解决方案,如霍尔传感器的中断触发换相、反电动势过零点检测的采样时机、滑模观测器的参数调整以及编码器的ABZ解码等。
recommend-type

基于Java的跨平台图像处理软件ImageJ:多功能图像编辑与分析工具

内容概要:本文介绍了基于Java的图像处理软件ImageJ,详细阐述了它的跨平台特性、多线程处理能力及其丰富的图像处理功能。ImageJ由美国国立卫生研究院开发,能够在多种操作系统上运行,包括Windows、Mac OS、Linux等。它支持多种图像格式,如TIFF、PNG、GIF、JPEG、BMP、DICOM、FITS等,并提供图像栈功能,允许多个图像在同一窗口中进行并行处理。此外,ImageJ还提供了诸如缩放、旋转、扭曲、平滑处理等基本操作,以及区域和像素统计、间距、角度计算等高级功能。这些特性使ImageJ成为科研、医学、生物等多个领域的理想选择。 适合人群:需要进行图像处理的专业人士,如科研人员、医生、生物学家,以及对图像处理感兴趣的普通用户。 使用场景及目标:适用于需要高效处理大量图像数据的场合,特别是在科研、医学、生物学等领域。用户可以通过ImageJ进行图像的编辑、分析、处理和保存,提高工作效率。 其他说明:ImageJ不仅功能强大,而且操作简单,用户无需安装额外的运行环境即可直接使用。其基于Java的开发方式确保了不同操作系统之间的兼容性和一致性。
recommend-type

MATLAB语音识别系统:基于GUI的数字0-9识别及深度学习模型应用 · GUI v1.2

内容概要:本文介绍了一款基于MATLAB的语音识别系统,主要功能是识别数字0到9。该系统采用图形用户界面(GUI),方便用户操作,并配有详尽的代码注释和开发报告。文中详细描述了系统的各个组成部分,包括音频采集、信号处理、特征提取、模型训练和预测等关键环节。此外,还讨论了MATLAB在此项目中的优势及其面临的挑战,如提高识别率和处理背景噪音等问题。最后,通过对各模块的工作原理和技术细节的总结,为未来的研究和发展提供了宝贵的参考资料。 适合人群:对语音识别技术和MATLAB感兴趣的初学者、学生或研究人员。 使用场景及目标:适用于希望深入了解语音识别技术原理的人群,特别是希望通过实际案例掌握MATLAB编程技巧的学习者。目标是在实践中学习如何构建简单的语音识别应用程序。 其他说明:该程序需要MATLAB 2019b及以上版本才能正常运行,建议使用者确保软件环境符合要求。
recommend-type

c语言通讯录管理系统源码.zip

C语言项目源码
recommend-type

Teleport Pro教程:轻松复制网站内容

标题中提到的“复制别人网站的软件”指向的是一种能够下载整个网站或者网站的特定部分,然后在本地或者另一个服务器上重建该网站的技术或工具。这类软件通常被称作网站克隆工具或者网站镜像工具。 描述中提到了一个具体的教程网址,并提到了“天天给力信誉店”,这可能意味着有相关的教程或资源可以在这个网店中获取。但是这里并没有提供实际的教程内容,仅给出了网店的链接。需要注意的是,根据互联网法律法规,复制他人网站内容并用于自己的商业目的可能构成侵权,因此在此类工具的使用中需要谨慎,并确保遵守相关法律法规。 标签“复制 别人 网站 软件”明确指出了这个工具的主要功能,即复制他人网站的软件。 文件名称列表中列出了“Teleport Pro”,这是一款具体的网站下载工具。Teleport Pro是由Tennyson Maxwell公司开发的网站镜像工具,允许用户下载一个网站的本地副本,包括HTML页面、图片和其他资源文件。用户可以通过指定开始的URL,并设置各种选项来决定下载网站的哪些部分。该工具能够帮助开发者、设计师或内容分析人员在没有互联网连接的情况下对网站进行离线浏览和分析。 从知识点的角度来看,Teleport Pro作为一个网站克隆工具,具备以下功能和知识点: 1. 网站下载:Teleport Pro可以下载整个网站或特定网页。用户可以设定下载的深度,例如仅下载首页及其链接的页面,或者下载所有可访问的页面。 2. 断点续传:如果在下载过程中发生中断,Teleport Pro可以从中断的地方继续下载,无需重新开始。 3. 过滤器设置:用户可以根据特定的规则过滤下载内容,如排除某些文件类型或域名。 4. 网站结构分析:Teleport Pro可以分析网站的链接结构,并允许用户查看网站的结构图。 5. 自定义下载:用户可以自定义下载任务,例如仅下载图片、视频或其他特定类型的文件。 6. 多任务处理:Teleport Pro支持多线程下载,用户可以同时启动多个下载任务来提高效率。 7. 编辑和管理下载内容:Teleport Pro具备编辑网站镜像的能力,并可以查看、修改下载的文件。 8. 离线浏览:下载的网站可以在离线状态下浏览,这对于需要测试网站在不同环境下的表现的情况十分有用。 9. 备份功能:Teleport Pro可以用来备份网站,确保重要数据的安全。 在实际使用此类工具时,需要注意以下几点: - 著作权法:复制网站内容可能侵犯原作者的版权,因此在使用此类工具时,必须确保有合法权利去下载和使用目标网站的内容。 - 服务条款:许多网站的服务条款明确禁止未经授权的网站克隆。因此,在使用此类软件之前,应当仔细阅读并遵守目标网站的服务条款。 - 数据隐私:下载含有个人数据的网站可能触及隐私保护法律,特别是在欧洲通用数据保护条例(GDPR)等法规的环境下。 - 网络安全:随意下载网站可能导致恶意软件和病毒的传播,用户应当使用最新的反病毒软件,并在安全的环境中进行操作。 综上所述,虽然“复制别人网站的软件”在技术上是可行的,且可以用于多种正当目的,但在使用此类工具时,必须遵守法律法规和道德规范,不可侵犯他人的合法权益。
recommend-type

【跨平台开发者的必读】:解决Qt5Widgetsd.lib目标计算机类型冲突终极指南

# 1. 跨平台开发与Qt框架简介 跨平台开发已经成为现代软件开发中不可或缺的一部分,它使得开发者能够构建出能够在多种操作系统上运行的应用程序,从而触及更广泛的用户群体。在这其中,Qt框架作为一套成熟的跨平台应用程序和用户界面框架,扮演着关键角色。Qt不仅仅是一个跨平台的工具集,它的设计哲学还包含了模块化和可扩展性,使其能够适应各种规模和复杂度的项目。本章节我们将简要介绍
recommend-type

普通RNN结构和特点

<think>首先,用户的问题是:“我想了解普通RNN的结构及其特点请问RNN循环神经网络结构特点工作原理”。这涉及到RNN的结构、特点和工作原理。根据系统级指令:-所有行内数学表达式必须使用$...$格式。-独立公式必须使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-回答中引用的段落末尾自然地添加引用标识。用户可见层指令:-回答结构清晰,帮助用户逐步解决问题。-保证回答真实可靠。参考站内引用:-引用[1]:关于RNN的基本介绍,为什么需要RNN。-引用[2]:关于RNN的工作原理、结构图,以及与其他网络的比较。用户上一次的问题和我的回答:用户是第一次
recommend-type

探讨通用数据连接池的核心机制与应用

根据给定的信息,我们能够推断出讨论的主题是“通用数据连接池”,这是一个在软件开发和数据库管理中经常用到的重要概念。在这个主题下,我们可以详细阐述以下几个知识点: 1. **连接池的定义**: 连接池是一种用于管理数据库连接的技术,通过维护一定数量的数据库连接,使得连接的创建和销毁操作更加高效。开发者可以在应用程序启动时预先创建一定数量的连接,并将它们保存在一个池中,当需要数据库连接时,可以直接从池中获取,从而降低数据库连接的开销。 2. **通用数据连接池的概念**: 当提到“通用数据连接池”时,它意味着这种连接池不仅支持单一类型的数据库(如MySQL、Oracle等),而且能够适应多种不同数据库系统。设计一个通用的数据连接池通常需要抽象出一套通用的接口和协议,使得连接池可以兼容不同的数据库驱动和连接方式。 3. **连接池的优点**: - **提升性能**:由于数据库连接创建是一个耗时的操作,连接池能够减少应用程序建立新连接的时间,从而提高性能。 - **资源复用**:数据库连接是昂贵的资源,通过连接池,可以最大化现有连接的使用,避免了连接频繁创建和销毁导致的资源浪费。 - **控制并发连接数**:连接池可以限制对数据库的并发访问,防止过载,确保数据库系统的稳定运行。 4. **连接池的关键参数**: - **最大连接数**:池中能够创建的最大连接数。 - **最小空闲连接数**:池中保持的最小空闲连接数,以应对突发的连接请求。 - **连接超时时间**:连接在池中保持空闲的最大时间。 - **事务处理**:连接池需要能够管理不同事务的上下文,保证事务的正确执行。 5. **实现通用数据连接池的挑战**: 实现一个通用的连接池需要考虑到不同数据库的连接协议和操作差异。例如,不同的数据库可能有不同的SQL方言、认证机制、连接属性设置等。因此,通用连接池需要能够提供足够的灵活性,允许用户配置特定数据库的参数。 6. **数据连接池的应用场景**: - **Web应用**:在Web应用中,为了处理大量的用户请求,数据库连接池可以保证数据库连接的快速复用。 - **批处理应用**:在需要大量读写数据库的批处理作业中,连接池有助于提高整体作业的效率。 - **微服务架构**:在微服务架构中,每个服务可能都需要与数据库进行交互,通用连接池能够帮助简化服务的数据库连接管理。 7. **常见的通用数据连接池技术**: - **Apache DBCP**:Apache的一个Java数据库连接池库。 - **C3P0**:一个提供数据库连接池和控制工具的开源Java框架。 - **HikariCP**:目前性能最好的开源Java数据库连接池之一。 - **BoneCP**:一个高性能的开源Java数据库连接池。 - **Druid**:阿里巴巴开源的一个数据库连接池,提供了对性能监控的高级特性。 8. **连接池的管理与监控**: 为了保证连接池的稳定运行,开发者需要对连接池的状态进行监控,并对其进行适当的管理。监控指标可能包括当前活动的连接数、空闲的连接数、等待获取连接的请求队列长度等。一些连接池提供了监控工具或与监控系统集成的能力。 9. **连接池的配置和优化**: 连接池的性能与连接池的配置密切相关。需要根据实际的应用负载和数据库性能来调整连接池的参数。例如,在高并发的场景下,可能需要增加连接池中连接的数量。另外,适当的线程池策略也可以帮助连接池更好地服务于多线程环境。 10. **连接池的应用案例**: 一个典型的案例是电商平台在大型促销活动期间,用户访问量激增,此时通用数据连接池能够保证数据库操作的快速响应,减少因数据库连接问题导致的系统瓶颈。 总结来说,通用数据连接池是现代软件架构中的重要组件,它通过提供高效的数据库连接管理,增强了软件系统的性能和稳定性。了解和掌握连接池的原理及实践,对于任何涉及数据库交互的应用开发都至关重要。在实现和应用连接池时,需要关注其设计的通用性、配置的合理性以及管理的有效性,确保在不同的应用场景下都能发挥出最大的效能。
recommend-type

【LabVIEW网络通讯终极指南】:7个技巧提升UDP性能和安全性

# 摘要 本文系统介绍了LabVIEW在网络通讯中的应用,尤其是针对UDP协议的研究与优化。首先,阐述了UDP的原理、特点及其在LabVIEW中的基础应用。随后,本文深入探讨了通过调整数据包大小、实现并发通信及优化缓冲区管理等技巧来优化UDP性能的LabVIEW方法。接着,文章聚焦于提升UDP通信安全性,介绍了加密技术和认证授权机制在LabVIEW中的实现,以及防御网络攻击的策略。最后,通过具体案例展示了LabVIEW在实时数据采集和远程控制系统中的高级应用,并展望了LabVIEW与UDP通讯技术的未来发展趋势及新兴技术的影响。 # 关键字 LabVIEW;UDP网络通讯;性能优化;安全性;