STM32 HAL庫快速實戰總集《完整項目重構》--基於黑龍江科技大學機電工業機器人實訓


系列目錄

點擊查看

前言

在前面章節,我們完成了所有子項目的構建與測試。但是隨着子項目的增加,代碼趨向混亂,不利於閱讀與修改。這里將對整個項目進行重構,提供一個最終使用版本。

項目重構

控制功能重構

在之前的項目中,獲取命令使用的是while一直等待,帶來的后果就是在超聲波循跡等需要while循環的地方,無法直接結束(在這里加入獲取命令會一直等待,十分麻煩)。

添加串口中斷

這里使用串口接收中斷代碼進行重寫控制函數。
首先打開usart.c,定義一個命令緩沖符,用於存放接收到的命令

/* USER CODE BEGIN 0 */
uint8_t blue_cmd=NULL;
/* USER CODE END 0 */

然后加入中斷回調函數

/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{	
  if(huart->Instance==USART3)//如果觸發USART3中斷
  { 
    HAL_UART_Receive_IT(&huart3,&blue_cmd,1);//打開中斷,等待下一次中斷的到來
  }
}
/* USER CODE END 1 */

通過該函數,可以自動接收到串口3發送的字符,並存入blue_cmd中。
同時在main.c初始化的時候開啟串口中斷

 /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart3,&blue_cmd,1);//打開串口中斷

然后在uasrt.h中加入

/* USER CODE BEGIN Private defines */
extern uint8_t blue_cmd;
/* USER CODE END Private defines */

這樣就能在別的.c文件中使用這個變量。

重寫get_cmd()

打開motor.c,將原來的get_cmd()注釋掉,然后添加以下代碼

char get_cmd(void)
{
  uint8_t ch=NULL;//定義一個臨時變量
	if(blue_cmd!=NULL)//判斷串口有沒有接收到字符
	{
	usart_send_str(&huart3,&blue_cmd);//返回發送的字符,方便查看
	ch=blue_cmd;//將blue_cmd存入ch中
	blue_cmd=NULL;//清空緩沖區,這樣做是避免發送兩個命令之間,重復對前一次命令進行判斷,會造成誤識別。
	return ch;//返回接收到的字符
	}
	return 0;//如果沒有接收到字符,返回0
}

電機控制重構

將電機控制放在main.c中與后面的項目太格格不入了。
將mian.c中的電機控制部分剪切掉,打開motor.c,添加motor_task函數。
注意,為了方便電機與機械臂的控制,這里停止命令修改為Z。

點擊查看代碼
void motor_task(){
	char input=NULL;
	usart_send_str(&huart3,(unsigned char *)"Motor Control Begin!\r\n");
	while(input!='Q'){
		input=get_cmd();
		if(input){
		switch(input){
				case 'B':
				usart_send_str(&huart3,(unsigned char *)"car go\r\n");
				motor_set(1000,1000);
				break;
				case 'C':
				usart_send_str(&huart3,(unsigned char *)"car back\r\n");
				motor_set(-1000,-1000);
				break;
				case 'D':
				usart_send_str(&huart3,(unsigned char *)"car left\r\n");
				motor_set(1000,0);
				break;
				case 'E':
				usart_send_str(&huart3,(unsigned char *)"car right\r\n");
				motor_set(0,1000);
				break;
				case 'Z':
				usart_send_str(&huart3,(unsigned char *)"car stop\r\n");
				motor_set(0,0);
				break;
			}
		}
	}usart_send_str(&huart3,(unsigned char *)"Motor Control End!\r\n");
}

然后在motor.h中添加函數聲明

void motor_task(void);

然后在main.c中添加函數入口

case '1':motor_task();break;

舵機控制重構

之前的舵機控制為了方便調節角度,每發一個命令才動一次。對於手動控制,這樣是非常慢的。這里復制一份get_rad重構為手動控制。

點擊查看代碼
/**
 * @brief  :手動控制舵機0到舵機5
 * @param  :None
 * @retval None
 **/
void arm_task(void)
{
	char menu=NULL;
	char menu_before=NULL;
	int rad[6]={0,0,0,0,0,0};
	while(menu!='Q'){
		menu=get_cmd();
		if(menu){menu_before=menu;}//如果有命令傳來,備份命令到menu_before
		if(!menu){menu=menu_before;}//如果沒有命令傳來,還原上一個命令
		switch(menu)
		{
			case 'A':rad[0]++;break;
			case 'B':rad[0]--;break;
			case 'C':rad[1]++;break;
			case 'D':rad[1]--;break;
			case 'E':rad[2]++;break;
			case 'F':rad[2]--;break;
			case 'G':rad[3]++;break;
			case 'H':rad[3]--;break;
			case 'I':rad[4]++;break;
			case 'J':rad[4]--;break;
			case 'K':rad[5]++;break;
			case 'L':rad[5]--;break;
			case 'Z':break;//空過
		}
		HAL_Delay(50);//兩次修改間隔,調節速度
		arm_set(rad);
		}
	printf_rad(rad);//退出循環后打印當前角度
return;
}

在robot-arm.h中聲明該函數void arm_task(void);

然后在main.c中添加函數入口

case '5':arm_task();break;

還有個問題就是舵機運動過快,且沒法調整時間。
因此還需要重構arm_set()
之前的邏輯是,直接改變占空比,這樣幅度很大。
如果一度一度的增加占空比,就可以減小幅度。並且通過在中間穿插延時函數,就可以調節速度。
首先聲明一個機械臂狀態變量。
在robot-arm.c頂部添加int arm_state[6]={0,0,0,0,0,0};
然后在robot-arm.h中聲明該變量extern int arm_state[6];
重命名arm_set()為rad2compare()

rad2compare()
/**
 * @brief  :控制舵機0到舵機5,左上轉為正,右下轉為負,DJ0-5 +-135°,DJ6+-90°
 * @param  :rad[6],分別儲存每個舵機轉動角度
 * @retval None
 **/
void rad2compare(const int rad[6])
{
	int compare[6]={
		1500+rad[0]*arm_toggle[0]*1000/135,
		1500+rad[1]*arm_toggle[1]*1000/135,
		1500+rad[2]*arm_toggle[2]*1000/135,
		1500+rad[3]*arm_toggle[3]*1000/135,
		1500+rad[4]*arm_toggle[4]*1000/135,
		1500+rad[5]*arm_toggle[5]*1000/90,
		};

__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,compare[0]);
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_3,compare[1]);
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_4,compare[2]);
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_1,compare[3]);
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_2,compare[4]);
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,compare[5]);
		return;
}

然后重構set_rad()

set_rad()
void arm_set(const int rad[6])
{	int set_status=1;//定義標志符,判斷是否需要繼續循環
	int diff[6]={
		rad[0]-arm_state[0],
		rad[1]-arm_state[1],
		rad[2]-arm_state[2],
		rad[3]-arm_state[3],
		rad[4]-arm_state[4],
		rad[5]-arm_state[5],
		};//定義一個數組,計算需要轉到的角度於當前角度的差值
	while(set_status){
		if (0 > diff[0]){diff[0]++;arm_state[0]--;}//如果差值小於0,說明當前位置需要正向運動,當前狀態值--
		else if (diff[0] > 0)diff[0]--,arm_state[0]++;//如果差值大於0,說明當前位置需要正向運動,當前狀態值++
		if (0 > diff[1])diff[1]++,arm_state[1]--;
		else if(diff[1] > 0)diff[1]--,arm_state[1]++;
		if(0 > diff[2])diff[2]++,arm_state[2]--;
		else if(diff[2] > 0)diff[2]--,arm_state[2]++;
		if(0 > diff[3])diff[3]++,arm_state[3]--;
		else if(diff[3] > 0)diff[3]--,arm_state[3]++;
		if(0 > diff[4])diff[4]++,arm_state[4]--;
		else if(diff[4] > 0)diff[4]--,arm_state[4]++;
		if(0 > diff[5])diff[5]++,arm_state[5]--;
		else if(diff[5] > 0)diff[5]--,arm_state[5]++;
		rad2compare(arm_state);
		if((diff[0]==0)&&(diff[1]==0)&&(diff[2]==0)&&(diff[3]==0)&&(diff[4]==0)&&(diff[5]==0))set_status=0;//如果差值為0后,跳出循環
		HAL_Delay(10);//舵機調節間隔,越長速度越慢
	}
		return;
}

入口函數重構

順勢把主函數里的switch重構下,修改下細節,使其不至於太難看。

點擊查看代碼
    /* USER CODE BEGIN 3 */
	input=get_cmd();
	if(input){
	switch(input){
		case '1':
			usart_send_str(&huart3,(unsigned char *)"Motor Control Begin!\r\n");
			motor_task();
			motor_set(0,0);
			usart_send_str(&huart3,(unsigned char *)"Motor Control End!\r\n");break;
		case '2':
			usart_send_str(&huart3,(unsigned char *)"Hcsr Task Begin!\r\n");
			while(get_cmd()!='Q'){hcsr_task();}
			motor_set(0,0);
			usart_send_str(&huart3,(unsigned char *)"Hcsr Task End!\r\n");break;
		case '3':
			usart_send_str(&huart3,(unsigned char *)"Color Task Begin!\r\n");
			while(get_cmd()!='Q'){color_task();}
			usart_send_str(&huart3,(unsigned char *)"Color Task End!\r\n");break;
		case '4':
			usart_send_str(&huart3,(unsigned char *)"XunJi Task Begin!\r\n");
			while((xunji_task())&&(get_cmd()!='Q'));
			usart_send_str(&huart3,(unsigned char *)"XunJi Task End!\r\n");break;
		case '5':
			usart_send_str(&huart3,(unsigned char *)"Arm Control Begin!\r\n");
			arm_task();
			usart_send_str(&huart3,(unsigned char *)"Arm Control End!\r\n");break;
		default:usart_send_str(&huart3,(unsigned char *)"Error Cmd\r\n");break;
	}
	}
	sound_task();
  }
  /* USER CODE END 3

現在每個子項目都能正確的進入和退出了。

顏色識別重構

目前識別到顏色后,沒有對應的動作。按照實際思路,識別到不同顏色的物體,應該通過機械臂搬運到不同的地方。
首先在color_task()中添加相應任務程序,假設識別到紅色,搬運到左邊,識別到綠色,搬運到右邊,識別到藍色,搬運到后邊。

color_task()
/**
 * @brief  :顏色傳感器對應任務
 * @param  :無
 * @retval 無
**/
void color_task(){
	static uint32_t systick_ms_yanse = 0;
	int millis=HAL_GetTick();//獲取系統時間
	uint8_t cmd_return[128];
	char color_value;
	if (millis - systick_ms_yanse > 20) {
		systick_ms_yanse = HAL_GetTick();
		color_value = get_adc_color_middle();//獲取a0的ad值,計算出距離
		if(color_value){
			if(color_value=='R'){left_task();}
			else if(color_value=='G'){right_task();}
			else if(color_value=='B'){back_task();}
			sprintf((char *)cmd_return, "Color = [%c]\r\n", color_value);
			usart_send_str(&huart3,cmd_return);
			color_value=NULL;
		}else
		{usart_send_str(&huart3,(uint8_t *)"no wood block \r\n");
		}
	}
}
然后在robot-arm.h中聲明對應的任務
void left_task(void);
void right_task(void);
void back_task(void);

別忘了在sensor.c頂部引入robot-arm.h文件。
然后再robot-arm.c中定義相應函數,加入對應動作組

動作組代碼參考
首先調節好各種狀態的角度,注意一定要自己調節,不同機器角度不同可能撞壞
/* 關節初始狀態*/
const int arm_begin[6]={0,85,-135,-54,0,0};
/* 夾住物體*/
const int arm_clamp[6]={0,-35,-70,-59,0,0};
/* 運輸物體*/
const int arm_doing[6]={0,-42,-37,0,0,0};

void left_task(){
	arm_set(arm_doing);//切換到運輸模式
	HAL_Delay(1000);
	arm_set(arm_clamp);//准備夾取
	
	HAL_Delay(1000);
	int arm_clamped[6];
	memcpy(arm_clamped,arm_clamp,sizeof(arm_clamp));
	arm_clamped[5]+=64;
	arm_set(arm_clamped);//夾取
	
	HAL_Delay(1000);
	memcpy(arm_clamped,arm_doing,sizeof(arm_clamp));
	arm_clamped[5]+=64;//切回到運輸模式
	arm_set(arm_clamped);
	
	HAL_Delay(1000);
	arm_clamped[0]+=90;//左轉
	arm_set(arm_clamped);
	
	HAL_Delay(1000);
	arm_clamped[1]=arm_clamp[1];
	arm_clamped[2]=arm_clamp[2];
	arm_clamped[3]=arm_clamp[3];
	arm_clamped[4]=arm_clamp[4];
	arm_set(arm_clamped);//准備放下
	
	HAL_Delay(1000);
	arm_clamped[5]=0;
	arm_set(arm_clamped);//松開爪子
	
	HAL_Delay(1000);
	memcpy(arm_clamped,arm_doing,sizeof(arm_clamp));
	arm_clamped[0]+=90;//切回到運輸模式
	HAL_Delay(1000);
	arm_set(arm_clamped);
}

void right_task(){
	arm_set(arm_doing);//切換到運輸模式
	HAL_Delay(1000);
	arm_set(arm_clamp);//准備夾取
	
	HAL_Delay(1000);
	int arm_clamped[6];
	memcpy(arm_clamped,arm_clamp,sizeof(arm_clamp));
	arm_clamped[5]+=64;
	arm_set(arm_clamped);//夾取
	
	HAL_Delay(1000);
	memcpy(arm_clamped,arm_doing,sizeof(arm_clamp));
	arm_clamped[5]+=64;//切回到運輸模式
	arm_set(arm_clamped);
	
	HAL_Delay(1000);
	arm_clamped[0]-=90;//右轉
	arm_set(arm_clamped);
	
	HAL_Delay(1000);
	arm_clamped[1]=arm_clamp[1];
	arm_clamped[2]=arm_clamp[2];
	arm_clamped[3]=arm_clamp[3];
	arm_clamped[4]=arm_clamp[4];
	arm_set(arm_clamped);//准備放下
	
	HAL_Delay(1000);
	arm_clamped[5]=0;
	arm_set(arm_clamped);//松開爪子
	
	HAL_Delay(1000);
	memcpy(arm_clamped,arm_doing,sizeof(arm_clamp));
	arm_clamped[0]-=90;//切回到運輸模式
	HAL_Delay(1000);
	arm_set(arm_clamped);
	HAL_Delay(1000);
	arm_set(arm_begin);//切回待機模式
}
void back_task(){

	arm_set(arm_begin);//切回待機模式
		arm_set(arm_doing);//切換到運輸模式
	HAL_Delay(1000);
	arm_set(arm_clamp);//准備夾取
	
	HAL_Delay(1000);
	int arm_clamped[6];
	memcpy(arm_clamped,arm_clamp,sizeof(arm_clamp));
	arm_clamped[5]+=64;
	arm_set(arm_clamped);//夾取
	
	HAL_Delay(1000);
	memcpy(arm_clamped,arm_doing,sizeof(arm_clamp));
	arm_clamped[5]+=64;//切回到運輸模式
	arm_set(arm_clamped);
	
	HAL_Delay(1000);
	arm_clamped[1]=-arm_clamped[1];
	arm_clamped[2]=-arm_clamped[2];
	arm_clamped[3]=-arm_clamped[3];
	arm_clamped[4]=-arm_clamped[4];
	arm_set(arm_clamped);//鏡像到后面
	
	HAL_Delay(1000);
	arm_clamped[5]=0;
	arm_set(arm_clamped);//松開爪子
	
	HAL_Delay(1000);
	arm_set(arm_doing);
	
	HAL_Delay(1000);
	arm_set(arm_begin);//切回待機模式
}

工程源碼

國內用戶請使用gitee克隆或是使用代理訪問Github
https://github.com/USTHzhanglu/stm32-hal/tree/main/robot-full-version


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM