stm32项目平衡车详解(stm32F407)下
stm32項目平衡車詳解(stm32F407)下
本文章學習借鑒于創客學院團隊,以表感謝。教學視頻
B站學習地址
HC-SRO4 超聲波測距避障功能開發
TSL1401 CCD攝像頭實現小車巡線功能
小車通信功能分析及ESP32模塊介紹
oled 顯示置入屏幕顯示小車狀態
文章目錄
- stm32項目平衡車詳解(stm32F407)下
- 前言
- 一、HC-SRO4 超聲波測距避障功能開發
- HC-SRO4超聲波測距模塊?
- 超聲波測距避障功能開發
- 避障模式開發
- 二、TSL1401 CCD攝像頭實現小車巡線功能
- TSL1401 CCD攝像頭?
- 巡線小車功能開發(編寫程序)
- TSL1401 示例代碼如下:
- 項目工程開始
- 三、遙控小車
- 藍牙控制
- esp32 項目工程的使用
- esp32 使用例程模板
- 通訊協議的制定
- 通訊協議定制庫函數文件(范例)
- oled 顯示置入屏幕顯示小車狀態
- 開始配置工程
- 四、stm32項目平衡車項目下載源碼
前言
前面我們已經實現了平衡小車的直立環,平衡環,轉向環,下面我們將實現小車平衡小車蔽障與巡線功能的開發。如下如所示:
一、HC-SRO4 超聲波測距避障功能開發
HC-SRO4超聲波測距模塊?
HC- SR04是一種超聲波接近傳感器,可以告訴您物體是否在它前面,并且還提供傳感器和物體之間的距離。這些傳感能力使其特別適用于需要知道遠離物體或障礙物的機器人,例如墻壁或不應撞擊的家具。
點擊查看,HC-SR04超聲波傳感器原理
超聲波測距避障功能開發
cube 搭建工程項目
下面給中斷使能
啟動定時器TIM7 去計時
避障模式開發
避障模式原理就是通過超聲波加測距離,通過轉向來躲避障礙物。所以我們會用轉向環來控制小車轉向。
//啟動超聲波,檢測距離
//啟動超聲波檢測函數void HC_SRC04_Start(void) {HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0, GPIO_PIN_SET); //設置高電平啟動delay_us(20); HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0, GPIO_PIN_RESET); //設置高低平啟動 }調用定時器中毒阿女函數進行計數,利用聲音傳播速度和接收時間測算距離
/*** @brief This function handles TIM7 global interrupt.*/ void TIM7_IRQHandler(void) {/* USER CODE BEGIN TIM7_IRQn 0 *//* USER CODE END TIM7_IRQn 0 */HAL_TIM_IRQHandler(&htim7);/* USER CODE BEGIN TIM7_IRQn 1 *//* USER CODE END TIM7_IRQn 1 */ }/* USER CODE BEGIN 1 */ uint8_t ECHO_IRQ_FLAG = 0;uint8_t time_cnt = 0; //參數二,溢出次數 static uint8_t last_distence =0;void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {int time = 0;if(GPIO_Pin == GPIO_PIN_1){if(ECHO_IRQ_FLAG == 0) //上升沿中斷{ECHO_IRQ_FLAG=1; HAL_TIM_Base_Start_IT(&htim7); //開啟定時器,開始計時}else{ECHO_IRQ_FLAG = 0; //下降沿中斷HAL_TIM_Base_Stop_IT(&htim7);//關閉定時器,計時結束time = time_cnt*1000 + TIM7->CNT / 2; //統計聲(單次)音返回時間Distence = 340*100* time /1000000; //微妙轉化為秒if(Distence == 0) Distence = last_distence;else last_distence = Distence;}}}配合pid 控制函數,使用超聲波測距的距離,進行避障,小車達到避障距離就開始轉彎避障,(避障距離建議超過小車直徑即可)
int Balance_Pwm,Vertical_Pwm,Trun_Pwm; int Motor1,Motor2; int Encoder_left,Encoder_right; float Movement = 0; //目標速度 int CCD; //目標角度int FS_MODE; //0 遙控模式1,避障模式 2巡線模式 int Distence; //檢測小車與障礙物的距離//PID控制任務 void Car_Task_100HZ(void) {//啟動超聲波檢測模式HC_SRC04_Start();//1、確定直立環的PWMBalance_Pwm = Vertical_Ring_PD(OutMpu.pitch,OutMpu.gyro_x); //*形參:(float Angle):x軸的角度/(float Gyro):x軸的角速度//2、確定速度環的PWMEncoder_left = Read_Encoder(1); //左輪Encoder_right = -Read_Encoder(2); //右輪Vertical_Pwm = Vertical_speed_PI(Encoder_left,Encoder_right,OutMpu.pitch,Movement);//3、確定方向環的PWMif(FS_MODE == 0) //遙控模式Trun_Pwm = Vertical_turn_PD(CCD,OutMpu.yaw);else if(FS_MODE == 1) //避障模式{if(Distence < 20) //直線距離在20cm 直接轉向 20度Trun_Pwm = Vertical_turn_PD(20,OutMpu.yaw); //直接轉向 20度elseTrun_Pwm = 0; //直行}else if(FS_MODE == 2) //巡線模式{}//4、確定最終電機的左右pwm Motor1 = Balance_Pwm+Vertical_Pwm+Trun_Pwm;Motor2 = Balance_Pwm+Vertical_Pwm-Trun_Pwm;PWM_Limiting((int) Motor1,(int) Motor2);//4、設置電機Set_PWM(Motor1,Motor2); //*形參;(int motor1):電機1對應的PWM值/(int motor2):電機2對應的PWM值}}二、TSL1401 CCD攝像頭實現小車巡線功能
巡線就是小車按照軌跡進行一定的運動,我們通過設計頭模塊進項軌跡實施采集分析調整車輛運動方向。
TSL1401 CCD攝像頭?
參考案例 STM32版CCD線性攝像頭尋線尋跡小車
CCD采集到外部像素,感應光線的強度通過AD 輸出
不同的光線強度的灰度值是不一樣的,通過信號傳輸給控制器,控制器根據灰度值來確定我們的條件。
上圖所從左到右,由亮變暗在變亮,中間黑色區域是最暗大的,我們使用線性 就是橫排采集一條線,判斷內容。
上圖看到SI由采集的到最后AD輸出的電壓值。
巡線小車功能開發(編寫程序)
電路圖如下:
CCD查看使用教程
TSL1401 示例代碼如下:
/********************************************************************************* File Name : main.c* Description : Main program body******************************************************************************** This notice applies to any and all portions of this file* that are not between comment pairs USER CODE BEGIN and* USER CODE END. Other portions of this file, whether * inserted by the user or by software development tools* are owned by their respective copyright owners.** COPYRIGHT(c) 2018 STMicroelectronics** Redistribution and use in source and binary forms, with or without modification,* are permitted provided that the following conditions are met:* 1. Redistributions of source code must retain the above copyright notice,* this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright notice,* this list of conditions and the following disclaimer in the documentation* and/or other materials provided with the distribution.* 3. Neither the name of STMicroelectronics nor the names of its contributors* may be used to endorse or promote products derived from this software* without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.********************************************************************************//* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f0xx_hal.h" #include "adc.h" #include "usart.h" #include "gpio.h"/* USER CODE BEGIN Includes */ #include "stdio.h" #include <math.h>#define CCD_MIDDLE 0 #define CCD_THRESHOLD 1 #define CCD_LEFT 2 #define CCD_RIGHT 3#define TSL_CLK_H HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET) #define TSL_CLK_L HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_RESET)#define TSL_SI_H HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET) #define TSL_SI_L HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET) #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF); return ch; } /* USER CODE END Includes *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void);/* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*//* USER CODE END PFP *//* USER CODE BEGIN 0 */ uint16_t ADV[128]={0}; uint8_t i;struct tCCD {uint16_t middle; //中間位置值uint16_t threshold; //像素ad閾值uint16_t left; //左跳變的位置uint16_t right; //右跳變的位置 };struct tCCD CCD; /************************************************************************************************** *函數名:abs() *功能:將數進行絕對值處理 *形參:number 需要進行絕對值處理的數 *返回值:經過處理后絕對值 **************************************************************************************************/ int abs (int number) {return( number>=0 ? number : -number ); } /************************************************************************************************** *函數名:Dly_us() *功能:延時函數,用來調整CCD曝光 *形參:無 *返回值:無 ***************************************************************************************************/ void Dly_us(void) {int ii; for(ii=0;ii<220;ii++); } /************************************************************************************************** *函數名:Get_Adc() *功能:得到CCD數據 *形參:無 *返回值:讀取到的電壓值 ***************************************************************************************************/ uint16_t Get_Adc(void) {HAL_ADC_Start(&hadc);HAL_ADC_PollForConversion(&hadc, 50);if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc), HAL_ADC_STATE_REG_EOC))return HAL_ADC_GetValue(&hadc);elsewhile(1); } /*************************************************************************************************** *函數名:RD_TSL() *功能:讀取CCD原始數據 *形參:無 *返回值:無 ****************************************************************************************************/void RD_TSL(void) {uint8_t i=0,tslp=0;TSL_CLK_H;TSL_SI_L;Dly_us();TSL_SI_H;TSL_CLK_L;Dly_us();TSL_CLK_H;TSL_SI_L;Dly_us();for(i=0;i<128;i++){TSL_CLK_L;Dly_us();ADV[tslp]=(Get_Adc())>>4;++tslp;TSL_CLK_H;Dly_us();} }/*************************************************************************************************************************函數名:Find_Middle_CCD()*功能:讀取CCD中值*形參:無*返回值:CCD中值位置*************************************************************************************************************************/uint8_t Find_CCD_DATA(void){static uint8_t i,j;//static uint8_t Last_Middle_CCD;uint8_t Middle_CCD_Value;static uint16_t value1_max,value1_min;value1_max=ADV[0];//讀取最大值for(i=5;i<123;i++){if(value1_max<=ADV[i])value1_max=ADV[i];}value1_min=ADV[0]; //得到最小值for(i=5;i<123;i++){if(value1_min>=ADV[i])value1_min=ADV[i];} //計算閾值CCD.threshold=(value1_max+value1_min)/2;//計算左跳變值for(i = 5;i<118; i++){if(ADV[i]>CCD.threshold&&ADV[i+1]>CCD.threshold&&ADV[i+2]>CCD.threshold&&ADV[i+3]<CCD.threshold&&ADV[i+4]<CCD.threshold&&ADV[i+5]<CCD.threshold){ CCD.left=i;break; }}//計算右跳變值for(j = 118;j>5; j--){if(ADV[j]<CCD.threshold&&ADV[j+1]<CCD.threshold&&ADV[j+2]<CCD.threshold&&ADV[j+3]>CCD.threshold&&ADV[j+4]>CCD.threshold&&ADV[j+5]>CCD.threshold){ CCD.right=j;break; }}//計算中值CCD.middle =(CCD.right+CCD.left)/2; // if(abs(Middle_CCD_Value-Last_Middle_CCD)>70){ // Middle_CCD_Value=Last_Middle_CCD; // Last_Middle_CCD=Middle_CCD_Value; // }return Middle_CCD_Value;}/***************************************************************************************************************************/void ANO_Send_Data(void) {HAL_UART_Transmit(&huart1,(uint8_t *)&CCD,sizeof(CCD),0xFFFF); }/* USER CODE END 0 */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_ADC_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *///每5ms發送一幀數據HAL_Delay(5);//讀取CCD原始數據,讀取128個像素,RD_TSL();//對CCD數據進行處理(求閾值、中值、左右跳變值)Find_CCD_DATA();//通過串口將封裝好的CCD數據向外發送ANO_Send_Data();/* USER CODE BEGIN 3 */}/* USER CODE END 3 */}/** System Clock Configuration */ void SystemClock_Config(void) {RCC_OscInitTypeDef RCC_OscInitStruct;RCC_ClkInitTypeDef RCC_ClkInitStruct;RCC_PeriphCLKInitTypeDef PeriphClkInit;/**Initializes the CPU, AHB and APB busses clocks */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;RCC_OscInitStruct.HSICalibrationValue = 16;RCC_OscInitStruct.HSI14CalibrationValue = 16;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**Initializes the CPU, AHB and APB busses clocks */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**Configure the Systick interrupt time */HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);/**Configure the Systick */HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* SysTick_IRQn interrupt configuration */HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); }/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @param None* @retval None*/ void _Error_Handler(char * file, int line) {/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */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/*** @}*/ /*** @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/項目工程開始
工程CCD工程 ,將工程下載到051 開發板進行數據采集發送
F407,串口3 使能,開啟中斷
開啟DMA 接受數據
導出工程即可打開工程文件
定義ccd
main 函數使用
//啟動CCD數據接受
HAL_UART_Receive_DMA(&huart3, (uint8_t)&CCD, sizeof(CCD));*
設置中斷檢測初始化128像素后再次重新接受
下面可以打印了
通過CCD 獲取攝像頭捕捉的中間值得差值在64以內就可以讓小車通過轉彎來重新定義方向和位置
Trun_Pwm = Vertical_turn_PD(CCD.middle,OutMpu.yaw); //直接轉向 20度
struct tCCD
{
uint16_t middle; //中間位置值
uint16_t threshold; //像素ad閾值
uint16_t left; //左跳變的位置
uint16_t right; //右跳變的位置
};
三、遙控小車
藍牙控制
esp32 項目工程的使用
使用過esp8266 的就了解過esp32 是 8266 升級款。
前期有幾期是關于Node mcu esp8266 物聯網模塊的
工作原理,小車F407 通過esp32 聯網,和手機上位機軟件進行數據交互請求,esp32 通過串口6 發送小車指令,來控制小車,前后,轉向。
打開工程,使能串口6,開啟中斷
使用keil5 打開工程,配置串口接受esp32數據
FreeRTOS 任務里面配置數據的接收,其中使用到了隊列管理xQueueCreate ,引入范例文件esp32 到工程,
esp32 使用例程模板
方法函數的介紹
ESP32_Init(); esp32 的初始化
ESP32_Send_Data() esp32 數據的發送
ESP32_Cmd_Rcv()命令的接受
ESP32_Data_Rcv()數據的接受
ESPSend_Cmd() 命令的發送
uart_data_send() 串口的發送
esp32.c
#include "esp32.h" #include "usart.h" #include <stdarg.h> #include <string.h> #include "connect.h" #include "FreeRTOS.h" #include "task.h" #include "cmsis_os.h"uint8_t NET_MODE = 0; //0、藍牙模式 1、wifi模式 默認藍牙 uint8_t WIFI_CONNECT_FLAG = 0; //WIFI連接標志位 uint8_t BLE_CONNECT_FLAG = 0; //BLE連接標志位tEsp32_RcvBuf Esp32Rcv; //ESP數據接收緩沖區tTimeDelay ESP32_TimeDelay;extern QueueHandle_t Message_Queue;#define AT_CWSAP_STRING "AT+CWSAP=\"FarsightESP32\",\"123456789\",5,3\r\n" #define AT_BLEADVDATA_STRING "AT+BLEADVDATA=\"0201060B09466172736967687420030302A0\"\r\n"volatile tATCmd ATCmds[20]= {//*CmdSend, *CmdRcv, TimeOut, CmdStatus, {NULL,NULL,0,NO_RCV},{"AT\r\n", "OK", 5000, NO_RCV, }, //檢測AT指令 {"AT+CIPAPMAC?\r\n", "CIPAPMAC", 2000, NO_RCV, }, //獲取MAC地址 {AT_CWSAP_STRING, "CWSAP" , 2000, NO_RCV, }, //建立MAC相關的AP名稱 {"AT+CWMODE=3\r\n", "OK" , 2000, NO_RCV, }, //設置WIFI模式AP+Station{"AT+CIPMUX=1\r\n", "OK" , 2000, NO_RCV, }, //設置多連接{"AT+CIPSERVER=1\r\n", "OK" , 2000, NO_RCV, }, //初始化TCP服務器 默認IP(192.168.4.1)默認端口號(333){"AT+CIPSTO=0\r\n", "OK" , 2000, NO_RCV, }, //設置TCP連接時間{"AT+CIPSEND=0\r\n", "OK" , 500, NO_RCV, }, //TCP發送數據{"AT+RST\r\n", "ready" , 1000, NO_RCV, }, //重啟AT指令:{"AT+BLEINIT=2\r\n", "OK" , 1000, NO_RCV, }, //初始化為 BLE server:{"AT+BLEADDR?\r\n", "BLEADDR" , 2000, NO_RCV, }, //查詢自身的 BLE 地址{AT_BLEADVDATA_STRING, "OK" , 2000, NO_RCV, }, //配置廣播數據包{"AT+BLEGATTSSRVCRE\r\n", "OK", 1000, NO_RCV, }, //創建服務:{"AT+BLEGATTSSRVSTART\r\n", "OK" , 3000, NO_RCV, }, //開啟服務 {"AT+BLEADVSTART\r\n", "OK" , 1000, NO_RCV, }, //開始廣播{"AT+BLEGATTSNTFY\r\n" , ">" , 500, NO_RCV, }, //服務器發送數據{"CMDSTR_NOUSE", "OK" , 2000, NO_RCV, }, };void uart_data_send(uint8_t *fmt, uint16_t len) {taskENTER_CRITICAL(); HAL_UART_Transmit(&huart6, (uint8_t *)fmt, len,100);taskEXIT_CRITICAL(); }tCmdStatus ESPSend_Cmd(tATCmdNum ATCmdNum) { uint8_t len;//清空接收緩存以及接收狀態ATCmds[ATCmdNum].CmdStatus = NO_RCV;//發送命令len = strlen(ATCmds[ATCmdNum].CmdSend);uart_data_send((uint8_t *)ATCmds[ATCmdNum].CmdSend, len);HAL_UART_Transmit(&huart1,(uint8_t *)ATCmds[ATCmdNum].CmdSend, len,100);//打開超時定時器SetTime(&ESP32_TimeDelay, ATCmds[ATCmdNum].TimeOut);while(ATCmds[ATCmdNum].CmdStatus != RCV_SUCCESS){ESP32_Cmd_Rcv(ATCmdNum);if(ATCmds[ATCmdNum].CmdStatus == RCV_TIMEOUT)return RCV_TIMEOUT;}return RCV_SUCCESS; }/*發送數據函數*/ tCmdStatus ESP32_Send_Data(uint8_t *SendBuf,uint8_t len) {uint8_t buf[30] = {0};tATCmdNum ATCmdNum;if(! (BLE_CONNECT_FLAG || WIFI_CONNECT_FLAG)) //未連接狀態不能發送數據{DBG("未連接設備\n");return NO_CONNECT;} if(NET_MODE == BLE_MODE) //藍牙模式{sprintf((char *)buf,"AT+BLEGATTSNTFY=%d,%d,%d,%d\r\n",0,1,2,len);ATCmdNum = AT_BLEGATTSNTFY; }else //WIFI模式{sprintf((char *)buf,"AT+CIPSEND=%d,%d\r\n",0,len);ATCmdNum = AT_CIPSEND;}uart_data_send(buf,strlen((char *)buf)); //發送命令//打開超時定時器ATCmds[ATCmdNum].CmdStatus = NO_RCV; //清接收狀態SetTime(&ESP32_TimeDelay, ATCmds[ATCmdNum].TimeOut);while(ATCmds[ATCmdNum].CmdStatus != RCV_SUCCESS){ ESP32_Cmd_Rcv(ATCmdNum);if(ATCmds[ATCmdNum].CmdStatus == RCV_TIMEOUT)return RCV_TIMEOUT;}uart_data_send( SendBuf,len); //發送數據DBG("send data ok\n");return RCV_SUCCESS;}void ESP32_Cmd_Rcv(tATCmdNum ATCmdNum) {memset(&Esp32Rcv,0,sizeof(Esp32Rcv));if(xQueueReceive(Message_Queue, &Esp32Rcv,0 )){DBG("%s", Esp32Rcv.RcvBuf);//接收處理命令if(strstr((const char*)Esp32Rcv.RcvBuf,ATCmds[ATCmdNum].CmdRcv) != NULL){ATCmds[ATCmdNum].CmdStatus = RCV_SUCCESS; } //打開接收指示燈//SetLedRun(LED_RX);}else{if(CompareTime(&ESP32_TimeDelay)){ATCmds[ATCmdNum].CmdStatus = RCV_TIMEOUT;}} }void ESP32_Data_Rcv(void) {memset(&Esp32Rcv,0,sizeof(Esp32Rcv));if(xQueueReceive(Message_Queue, &Esp32Rcv,0 )){//接收處理數據(保護客戶端發來的數據,還有其他調試數據) DBG("%s", Esp32Rcv.RcvBuf);if(NET_MODE == BLE_MODE) //藍牙模式{if(strstr((char *)(Esp32Rcv.RcvBuf),"WRITE") != NULL ) //收到客戶端數據{DBG("收到上位機數據\n");BLE_CONNECT_FLAG = 1; //對方打開讀寫特征值時,置連接標志//提取處理數據;EP32_RcvData_Extract(Esp32Rcv.RcvBuf,Esp32Rcv.DataLen); return ; }if(strstr((char *)(Esp32Rcv.RcvBuf),"BLEDISCONN") != NULL) //客戶端斷開連接{DBG("藍牙斷開連接,重新廣播\n"); BLE_CONNECT_FLAG = 0; //清除連接標志位//重新廣播ESPSend_Cmd(AT_BLEADVDATA);ESPSend_Cmd(AT_BLEADVSTART);}}else //WIFI模式{if((!WIFI_CONNECT_FLAG) && (strstr((char *)(Esp32Rcv.RcvBuf),"CONNECT")!=NULL )) //收到客戶端數據{DBG("WIFI已連接\n"); WIFI_CONNECT_FLAG = 1; //置連接標志位}if(strstr((char *)(Esp32Rcv.RcvBuf),"+IPD") != NULL ) //收到客戶端數據{DBG("WIFI收到上位機數據\n"); //提取并處理數據;EP32_RcvData_Extract(Esp32Rcv.RcvBuf,Esp32Rcv.DataLen); return ; } if(strstr((char *)(Esp32Rcv.RcvBuf),"CLOSED") != NULL) //客戶端斷開連接{DBG("WIFI斷開連接\n"); WIFI_CONNECT_FLAG = 0; //清除連接標志位} }} }void ESP32_Init(void) {tATCmdNum i = AT_IDIE;if(NET_MODE == BLE_MODE) //藍牙模式初始化{for(i = AT_BLEINIT; i<=AT_BLEADVSTART ; i++){if( ESPSend_Cmd(i) != RCV_SUCCESS){ DBG("PES32 Init failed\n");return ;}}}else //WIFI模式初始化{for(i = AT; i<=AT_CIPSTO ; i++){ if( ESPSend_Cmd(i) != RCV_SUCCESS){DBG("PES32 Init failed\n");return ;}}DBG("PES32 Init Success\n");} }esp32.h
#ifndef __ESP32_H #define __ESP32_H#include <stdint.h> #include <stdio.h> #include "delay.h"extern uint8_t Esp32_RcvBuf [255]; extern uint8_t Esp32_RBuffLen ; //#define DEBUG #ifdef DEBUG #define DBG(x...) printf(x) #else #define DBG(x...) #endif #define BLE_MODE 0 #define WIFI_MODE 1//AT命令序列號 typedef enum {AT_IDIE = 0,AT,AT_CIPAPMAC,AT_CWSAP,AT_CWMODE,AT_CIPMUX,AT_CIPSERVER,AT_CIPSTO,AT_CIPSEND,AT_RST,AT_BLEINIT,AT_BLEADDR,AT_BLEADVDATA,AT_BLEGATTSSRVCRE,AT_BLEGATTSSRVSTART,AT_BLEADVSTART,AT_BLEGATTSNTFY,CMDSTR_NOUSE,}tATCmdNum;//命令返回結果的狀態 typedef enum{NO_RCV = 0,RCV_SUCCESS,RCV_TIMEOUT,NO_CONNECT, }tCmdStatus;typedef struct{char *CmdSend; //發送的命令char *CmdRcv; //正確返回包含的字符串uint16_t TimeOut; //超時的時間tCmdStatus CmdStatus; //命令返回的狀態 }tATCmd; typedef struct {uint8_t DataLen;uint8_t RcvBuf [255]; }tEsp32_RcvBuf;extern tTimeDelay ESP32_TimeDelay; extern uint8_t Esp32_RcvBuf [255]; extern uint8_t ESP32_RCV_FLAG; extern uint8_t NET_MODE ; extern uint8_t WIFI_CONNECT_FLAG ; //WIFI連接標志位 extern uint8_t BLE_CONNECT_FLAG ; //BLE連接標志位void ESP32_Cmd_Rcv(tATCmdNum ATCmdNum); tCmdStatus ESP32_Send_Data(uint8_t *SendBuf,uint8_t len); void ESP32_Init(void); void ESP32_Data_Rcv(void);#endif /*__ESP32_H*/通訊協議的制定
通訊協議定制庫函數文件(范例)
connect.h
#ifndef __CONNECT_H #define __CONNECT_H#include <stdint.h>//功能宏定義 #define MOVEMENT 0X01 //小車動作 #define READINFO 0X02 //讀取PID、模式、版本,恢復默認參數 #define SETMODE 0X03 //設置小車工作模式 #define WRITEPID1 0X10 //設置PID1 #define WRITEPID2 0X11 //設置PID2//小車動作宏定義 #define CAR_STOP 0X00 //停止 #define CAR_FORWARD 0X01 //前進 #define CAR_BACK 0X02 //后退 #define CAR_TURN_LEFT 0X03 //左轉 #define CAR_TURN_RIGHT 0X04 //右轉//小車工作模式定義 #define REMOTE_MODE 0X01 //遙控模式 #define LINE_TRACK_MODE 0X02 //巡線模式 #define AVOID_MODE 0X03 //蔽障模式//讀取小車信息定義 #define READ_ALL_ARG 0X00 //讀取所有的數據 #define READ_PID 0X01 //讀取PID數據 #define READ_WORK_MODE 0X02 //讀取當前工作模式 #define READ_VERINFO 0XA0 //讀取版本信息 #define RESTORE_DEFAULT_ARG 0XA1 //恢復默認參數//定義版本相關信息 #define Hardware_Type 10 #define Hardware_VER 10 #define Software_VER 10 #define Protocol_VER 10//定義小車上報數據的功能類型 #define CAR_VER 0x00 //版本信息 #define CAR_POSE 0x01 //姿態 #define CAR_SENSER 0x02 //傳感器原始數據 #define CAR_RCDATA 0x03 //小車接收到的遙控數據 #define CAR_POWER 0x04 //小車電量 #define CAR_MOTO 0x05 //電機轉速 #define CAR_SENSER2 0x06 //超聲波距離 #define CAR_MOD 0X07 //小車模式 #define CAR_PID_1 0x10 //PID1數據 #define CAR_PID_2 0x11 //PID2的數據 #define CAR_PID_3 0X12 //PID3的數據 #define CAR_CCD_SEN 0XF1 //CCD的數據 #define CAR_User_Waveform 0xA1 typedef struct {u8 send_check;u8 send_version;u8 send_status;u8 send_senser;u8 send_senser2;u8 send_pid1;u8 send_pid2;u8 send_pid3;u8 send_pid4;u8 send_pid5;u8 send_pid6;u8 send_rcdata;u8 send_offset;u8 send_motopwm;u8 send_power;u8 send_user;u8 send_speed;u8 send_location;}dt_flag_t;u8 EP32_RcvData_Extract(const uint8_t *Buff,int len); void Connect_Send_data(u8 CMD_Data);#endifconnect.c
#include "inv_mpu_user.h" #include "connect.h" #include "string.h" #include "stdio.h" #include "esp32.h" #include "car_task.h" #include "contrl.h"/************************************************************************************************************** *函數名:package_report_data() *功能:添加幀頭功能字及校驗位 *形參:(u8 fun):功能字/(u8*data):要發送的數據包/(u8 len):長度 *返回值:無 **************************************************************************************************************/ static void package_report_data(u8 fun,u8*data,u8 len) {static u8 send_buf[40]={0}; //添加static,給棧減小一點壓力u16 check_sum=0;u8 i;//封裝協議頭if(len>28)return; send_buf[0]=0XAA; send_buf[1]=0XAA; send_buf[2]=fun; send_buf[3]=len; //封裝數據for(i=0;i<len;i++)send_buf[4+i]=data[i];//計算校驗值for(i=0;i<len+4;i++) check_sum+=send_buf[i]; send_buf[len+4]=((check_sum)&0xFF);//發送數據ESP32_Send_Data(send_buf,len+5); }/********************************************************************************************************* *函數名:ANO_VER() *功能:發送版本信息 *形參:HardwareType,HardwareVER,SoftwareVER,ProtocolVER *返回值:無 *********************************************************************************************************/ static void ANO_VER(u8 HardwareType,u16 HardwareVER,u16 SoftwareVER,u16 ProtocolVER) {static u8 tbuf[7]; tbuf[0]=(HardwareType)&0XFF;tbuf[1]=((HardwareVER)>>8)&0XFF;tbuf[2]=(HardwareVER)&0XFF;tbuf[3]=((SoftwareVER)>>8)&0XFF;tbuf[4]=(SoftwareVER)&0XFF;tbuf[5]=((ProtocolVER)>>8)&0XFF;tbuf[6]=(ProtocolVER)&0XFF;package_report_data(CAR_VER,tbuf,7); }/************************************************************************************************************* *函數名:ANO_MOD() *功能:發送當前模式 *形參:data模式 *返回值:無 *************************************************************************************************************/ static void ANO_MOD(u8 data) {u8 tbuf[1];tbuf[0]=(data)&0XFF; package_report_data(CAR_POSE,tbuf,1); } /********************************************************************************************************** *函數名:ANO_CAR_POSE() *功能:發送姿態 *形參:angle(x俯仰,y橫滾,z偏航) *返回值:無 ***********************************************************************************************************/ static void ANO_CAR_POSE(void) {static u8 tbuf[6]; int32_t anglex,angley,anglez;anglex=((short)(OutMpu.pitch))*100;angley=((short)(OutMpu.roll))*100;anglez=((short)(OutMpu.yaw))*100;tbuf[0]=((anglex)>>8)&0XFF; tbuf[1]=(anglex)&0XFF;tbuf[2]=((angley)>>8)&0XFF;tbuf[3]=(angley)&0XFF;tbuf[4]=((anglez)>>8)&0XFF;tbuf[5]=(anglez)&0XFF;package_report_data(CAR_POSE,tbuf,6); }/************************************************************************************************************** *函數名:ANO_SENSER() *功能:發送傳感器原數據 *形參:acc:陀螺儀,gyro:加速度計,mag:電子羅盤 *返回值:無 **************************************************************************************************************/ static void ANO_SENSER(void) {static u8 tbuf[18]; u8 accx,accy,accz,gyrox,gyroy,gyroz,magx,magy,magz;accx=(u8)((OutMpu.acc_x)*100);accy=(u8)((OutMpu.acc_y)*100);accz=(u8)((OutMpu.acc_z)*100);gyrox=(u8)((OutMpu.gyro_x)*100);gyroy=(u8)((OutMpu.gyro_x)*100);gyroz=(u8)((OutMpu.gyro_x)*100);// magx=(u8)((mag->x)*100); // magy=(u8)((mag->y)*100); // magz=(u8)((mag->z)*100);tbuf[0]=((accx)>>8)&0XFF; tbuf[1]=(accx)&0XFF;tbuf[2]=((accy)>>8)&0XFF;tbuf[3]=(accy)&0XFF;tbuf[4]=((accz)>>8)&0XFF;tbuf[5]=(accz)&0XFF;tbuf[6]=((gyrox)>>8)&0XFF; tbuf[7]=(gyrox)&0XFF;tbuf[8]=((gyroy)>>8)&0XFF;tbuf[9]=(gyroy)&0XFF;tbuf[10]=((gyroz)>>8)&0XFF;tbuf[11]=(gyroz)&0XFF;tbuf[12]=((magx)>>8)&0XFF; tbuf[13]=(magx)&0XFF;tbuf[14]=((magy)>>8)&0XFF;tbuf[15]=(magy)&0XFF;tbuf[16]=((magz)>>8)&0XFF;tbuf[17]=(magz)&0XFF;package_report_data(CAR_SENSER,tbuf,18); } /************************************************************************************************************* *函數名:ANO_PID() *功能:發送PID數據 *形參:PID1,PID2,PID3的參數 *返回值:無 **************************************************************************************************************/ static void ANO_PID(u8 Function) {static u8 tbuf[18];int16_t PID1_P,PID1_I,PID1_D,PID2_P,PID2_I,PID2_D,PID3_P,PID3_I,PID3_D;PID1_P=(u16)((PID.Balance_Kp)*100);PID1_I=(u16)((PID.Balance_Ki)*100);PID1_D=(u16)((PID.Balance_Kd)*100);PID2_P=(u16)((PID.Velocity_Kp)*100);PID2_I=(u16)((PID.Velocity_Ki)*100);PID2_D=(u16)((PID.Velocity_Kd)*100);PID3_P=(u16)((PID.Turn_Kp)*100);PID3_I=(u16)((PID.Turn_Ki)*100);PID3_D=(u16)((PID.Turn_Kd)*100);tbuf[0]=((PID1_P)>>8)&0XFF; tbuf[1]=(PID1_P)&0XFF;tbuf[2]=((PID1_I)>>8)&0XFF;tbuf[3]=(PID1_I)&0XFF;tbuf[4]=((PID1_D)>>8)&0XFF;tbuf[5]=(PID1_D)&0XFF;tbuf[6]=((PID2_P)>>8)&0XFF; tbuf[7]=(PID2_P)&0XFF;tbuf[8]=((PID2_I)>>8)&0XFF;tbuf[9]=(PID2_I)&0XFF;tbuf[10]=((PID2_D)>>8)&0XFF;tbuf[11]=(PID2_D)&0XFF;tbuf[12]=((PID3_P)>>8)&0XFF; tbuf[13]=(PID3_P)&0XFF;tbuf[14]=((PID3_I)>>8)&0XFF;tbuf[15]=(PID3_I)&0XFF;tbuf[16]=((PID3_D)>>8)&0XFF;tbuf[17]=(PID3_D)&0XFF;if(Function==1)package_report_data(CAR_PID_1,tbuf,18);if(Function==2)package_report_data(CAR_PID_2,tbuf,18);if(Function==3)package_report_data(CAR_PID_1,tbuf,18); }/************************************************************************************************************** *函數名:ANO_CCD_SEN() *功能:發送CCD數據 *形參:ccd:CCD數據包括(閾值,中值,左跳變,右跳變) *返回值:無 ***************************************************************************************************************/ void ANO_CCD_SEN(void) {static u8 tbuf[8];u16 CCD_MIDDLE,CCD_THRESHOLD,CCD_LEFT,CCD_RIGHT;CCD_MIDDLE=CCD.middle;CCD_THRESHOLD=CCD.threshold;CCD_LEFT=CCD.left;CCD_RIGHT=CCD.right;tbuf[0]=((CCD_MIDDLE)>>8)&0XFF;tbuf[1]=(CCD_MIDDLE)&0XFF;tbuf[2]=((CCD_THRESHOLD)>>8)&0XFF;tbuf[3]=(CCD_THRESHOLD)&0XFF;tbuf[4]=((CCD_LEFT)>>8)&0XFF;tbuf[5]=(CCD_LEFT)&0XFF;tbuf[6]=((CCD_RIGHT)>>8)&0XFF;tbuf[7]=(CCD_RIGHT)&0XFF;package_report_data(CAR_CCD_SEN,tbuf,8); }/************************************************************************************************************* *函數名:ANO_POWER() *功能:發送電量 *形參:data:電量(0%,25%,50%,75%,100%) *返回值: **************************************************************************************************************/ void ANO_POWER(u16 data) {static u8 tbuf[2];tbuf[0]=((data)>>8)&0XFF; tbuf[1]=(data)&0XFF;package_report_data(CAR_POWER,tbuf,2); } /************************************************************************************************************* *函數名:ANO_MOTO() *功能:發送電機轉速 *形參:PWM_MOTO:電機轉速 *返回值:無 **************************************************************************************************************/ void ANO_MOTO(float PWM_MOTO) {u16 PWM_Percentage;static u8 tbuf[2];PWM_Percentage=fabs(PWM_MOTO)*1.24;tbuf[0]=((PWM_Percentage)>>8)&0XFF; tbuf[1]=(PWM_Percentage)&0XFF;package_report_data(CAR_MOTO,tbuf,2); } /************************************************************************************************************** *函數名:Connect_Send_data() *功能:發送數據給上位機 *形參: *返回值:無 **************************************************************************************************************/ void Connect_Send_data(u8 CMD_Data) {switch(CMD_Data){case READ_ALL_ARG:// DBG("\r發送數據到上位機\n");// ANO_VER(Hardware_Type,Hardware_VER,Software_VER,Protocol_VER); // ANO_MOD(FS_MODE);ANO_CAR_POSE();ANO_SENSER(); // ANO_RCDATA(F_CMD,Movement); // ANO_POWER(power);ANO_MOTO(Encoder_right); // ANO_SENSER2(Distance);ANO_CCD_SEN();break;case READ_PID:DBG("\r發送PID請求\n");ANO_PID(1);break;case READ_WORK_MODE:DBG("\r發送當前模式\n");ANO_MOD(FS_MODE);break;case READ_VERINFO:DBG("\r發送版本信息\n");ANO_VER(Hardware_Type,Hardware_VER,Software_VER,Protocol_VER);break;default:break;}}/************************************************************************************************************** *函數名:Host_Data_Receive_Anl() *功能:上位機數據包解析 *形參:(u8 *data_buf):緩存區中的接收到的數據/(u8 num):數據包長度 *返回值:無 **************************************************************************************************************/ void Host_Data_Receive_Anl(u8 *data_buf,u8 num) {u8 sum = 0,i;u8 function = *(data_buf+2),cmd = *(data_buf+4);//從幀頭道數據位的計算校驗;DBG("收到有效數據:");for(i=0;i<(num-1);i++){DBG("%x ",*(data_buf+i));sum += *(data_buf+i);}DBG("\r\n");校驗不過直接返回if(sum != *(data_buf+num-1) ) return; //判斷幀頭if(!(*(data_buf)==0xAA && *(data_buf+1)==0xAF)) return; switch(function){case MOVEMENT: DBG("設置小車動作\n");switch(cmd){case CAR_STOP:DBG("小車停止\n");if(FS_MODE==0){Contrl_Turn=64;Movement=0;}break;case CAR_FORWARD:DBG("小車前進\n");if(FS_MODE==0){Contrl_Turn=64;Movement=50;}break;case CAR_BACK:DBG("小車后退\n");if(FS_MODE==0){Contrl_Turn = 64;Movement = -50;}break;case CAR_TURN_LEFT:DBG("小車左轉\n");if(FS_MODE==0){Contrl_Turn=30;Movement=0;}break;case CAR_TURN_RIGHT:DBG("小車右轉\n");if(FS_MODE==0){Contrl_Turn=98;Movement=0;}break;default:break;}break;case READINFO: DBG("讀取小車基本信息\n");switch(cmd){case READ_PID:DBG("讀取PID數據\n");break;case READ_WORK_MODE:DBG("讀取當前工作模式\n");break;case READ_VERINFO:DBG("讀取版本信息\n");break;case RESTORE_DEFAULT_ARG:DBG("恢復默認參數\n");PID.Balance_Kp=200;PID.Balance_Kd=1;PID.Velocity_Kp=-60;PID.Velocity_Ki=-0.3;PID.Turn_Kp = 18;PID.Turn_Kd = 0.18;break; default:break;} break;case SETMODE: DBG("設置小車工作模式\n");switch(cmd){case REMOTE_MODE:DBG("遙控模式\n");FS_MODE = 0;break;case AVOID_MODE:DBG("避障模式\n");FS_MODE = 1;break;case LINE_TRACK_MODE:DBG("巡線模式\n");FS_MODE = 2;break;default:break;} break;case WRITEPID1: //上位機發送9個的16位數,*100后發送過來的,DBG("設置小車PID1\n");PID.Balance_Kp = 0.01*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );PID.Balance_Ki = 0.01*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );PID.Balance_Kd = 0.01*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );PID.Velocity_Kp = 0.01*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );PID.Velocity_Ki = 0.01*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );PID.Velocity_Kd = 0.01*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );PID.Turn_Kp = 0.01*( (vs16)(*(data_buf+16)<<8)|*(data_buf+17) );PID.Turn_Ki = 0.01*( (vs16)(*(data_buf+18)<<8)|*(data_buf+19) );PID.Turn_Kd = 0.01*( (vs16)(*(data_buf+20)<<8)|*(data_buf+21) );break;case WRITEPID2: DBG("設置小車PID2\n");break;default:break;}}/********************************************************************************************************** *函數名:EP32_RcvData_Extract *功能:提取ESP32中有效數據,并且將其拷貝到緩存區中 *形參:中斷接收的數據 *返回值:成功返回0,失敗返回1 注:數據格式命令 幀頭 幀頭 功能 長度 數據 校驗 例: 前進 0xaa 0xaf 0x01 0x01 0x01 計算 ***********************************************************************************************************/u8 EP32_RcvData_Extract(const uint8_t *Buff,int len) { static u8 RxBuffer[50];static int data_len = 0,data_cnt = 0;static u8 state = 0;u8 i, data;for(i=0;i<len ;i++){data = Buff[i];//判斷幀頭 if(state==0&&data==0xAA){state=1;RxBuffer[0]=data;}else if(state==1&&data==0xAF){state=2;RxBuffer[1]=data;}//功能字截止到0XF1else if(state==2&&data<0XF1){state=3;RxBuffer[2]=data;}//數據長度小于50else if(state==3&&data<50){state = 4;RxBuffer[3]=data;data_len = data;data_cnt = 0;}//將數據復制到RxBuffer中else if(state==4&&data_len>0){data_len--;RxBuffer[4+data_cnt++]=data;if(data_len==0)state = 5;}//計算校驗值else if(state==5){state = 0;RxBuffer[4+data_cnt]=data;Host_Data_Receive_Anl(RxBuffer,data_cnt+5);memset(RxBuffer,0,50);return 0;}}return 1; }oled 顯示置入屏幕顯示小車狀態
096 oled 已經不陌生了,都已經使用很多次了。iic 數據傳輸。
2. 按鍵原理圖
蜂鳴器 電氣原理圖
電池adc接口 電氣原理圖
開始配置工程
cube工程配置端口
輸出數據到oled 屏幕
控制板需要接收按鍵按下電平,所以設置輸入模式
oled 管腳設置輸出模式
蜂鳴器一樣設置輸出模式
ok 導出工程打開keil5 開始編輯程序
四、stm32項目平衡車項目下載源碼
本文章學習借鑒于創客學院團隊,以表感謝。教學視頻
資源是免費下載的,點擊下載即可
上一節,stm32項目平衡車詳解(stm32F407)上
總結
以上是生活随笔為你收集整理的stm32项目平衡车详解(stm32F407)下的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【图像分割】基于脉冲耦合神经网络实现图像
- 下一篇: 【LeGO-LOAM论文阅读(二)--特