Summary of E-Sports School Games----one-dimensional cricket system [code open source]

 2022/4/21

The overall mechanical structure was built. Finally, due to financial problems, I chose to use the shelf placed by the wind force last year to build the camera [openmv]. Looking at the national competition questions of that year, I chose ov7670, but after discussion, we think that the recognition of openmv is better. ,,The ball below uses a water pipe with an outer diameter of 3.2cm, and then divides the water pipe into two, and then places the ball. The steering gear is MG996R steering gear, and I originally wanted to use MG995R [because before The seniors used this when they made one], but found that the 996R responds faster. Through the adjustment of the duty cycle of the steering gear, the duty cycle is passed into the PID algorithm to complete the tilting and balance of the tube. The board uses the stm32mini development board. This time, you don’t need to consider the size of the board, just choose the one with more IO ports.

2022/4/22

I started to write code more formally in the early morning, and I still had a headache how to adjust and how to call this PID, because it is indeed rarely used at ordinary times, and then I thought of a simple method, first debug the duty cycle of the steering gear, and know what the shaft is. The time is the level, the highest and the lowest. Record the three duty ratios separately to prepare for the subsequent PID call. After adjustment, we will place the shaft of the steering gear horizontally to install the mechanical structure [this will save us a lot later. Less things], it means that our initial device is horizontal, so it is enough to directly add or subtract the calculated PID duty cycle to the above, and the debugging is much simpler.

Xplot running code:

运行函数
//send_wave();
//getdatas();
//get_cmd();


函数定义
//void send_wave(void)
//{
//	//定义通道名帧头帧尾
//	u8 frameNameHead[] = "AABBCC";
//	u8 frameNameEnd[] = "CCBBAA";
//	
//	//定义数据帧头帧尾
//	u8 frameDataHead[] = "DDEEFF";
//	u8 frameDataEnd[] = "FFEEDD";
//	
//	//定义通道名
//	u8 name[] = {"x_now,I,D,PWM_X,flag,aim,MOtor_X_PWM"};
//	
//	//赋值数据
//	float channels[7];
//	channels[0] = x_now;
//	channels[1] = kp;
//	channels[2] = kd;
//	channels[3] = data[0];
//	channels[4] = flag;
//	channels[5] = X_aim;
//	channels[6] = Motor_X_PWM;
//	//通过串口1,向上位机发送数据
//	usart_senddatas(USART1,frameNameHead,sizeof(frameNameHead)-1);
//	usart_senddatas(USART1,name,sizeof(name)-1);
//	usart_senddatas(USART1,frameNameEnd,sizeof(frameNameEnd)-1);
//	
//	usart_senddatas(USART1,frameDataHead,sizeof(frameDataHead)-1);
//	usart_senddatas(USART1,(u8*)channels,sizeof(channels));
//	usart_senddatas(USART1,frameDataEnd,sizeof(frameDataEnd)-1);
//	
//}

//void getdatas(void)
//{
//	data[0] = kp*X.err[0]+flag*ki*X.e_I+kd*(X.err[0]-X.err[1]);
//}

//void get_cmd(void)
//{
//	char u_buff[10];
//	float u_d1,u_d2,u_d3;
//	if(usart_get_data(u_buff,&u_d1,&u_d2,&u_d3))
//	{
//		if(strcmp(u_buff,"PID") == 0) //比较命令控制字符是否为PID
//		{
//			kp = u_d1;
//		  ki = u_d2;
//			kd = u_d3;
//		}
//	}
//	memset(u_buff,0,sizeof(u_buff));
//}




usart.c

//char usart_readbuff[30] = {0}; //串口接受缓存数组
//u8 usart_readok = 0; //一帧数据处理标志
//void USART1_IRQHandler(void)                	//串口1中断服务程序
//{
//	u8 temp;
//	static u8 count = 0;  // 接收数组控制变量
//	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //判断是否为接收中断
//	{
//		temp = USART_ReceiveData(USART1);	//读取接收到的数据,并清除中断标志
//		if(temp == '#' && usart_readok == 0) 
//		{
//			usart_readbuff[count] = '#';
//			usart_readok = 1;
//			count = 0;
//		}
//		else if(usart_readok==0)
//		{
//			usart_readbuff[count] = temp; //保存接收到的数据到接收缓存数组
//			count++; //数组下标切换
//			if(count >= 30) // 防止数据越界
//				count = 0;
//		}
//	}
//}


// 

//u8 usart_get_data(char *cmd,float *d1,float *d2,float *d3)
//{
//	u8 flag = 0;
//	if(usart_readok == 1)
//	{
//		if(sscanf(usart_readbuff,"%3s=%f,%f,%f#",
//											cmd,d1,d2,d3)==4)
//		{
//			flag = 1;
//		}
//		//清除接收完成标志
//		memset(usart_readbuff,0,sizeof(usart_readbuff));
//		usart_readok = 0;
//	}
//	return flag;
//}


//void usart_senddatas(USART_TypeDef* USARTx,u8* addr,int size)
//{
//	while(size--) //判断数据发送完没有
//	{
//		while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET);//等待上一个byte的数据发送结束。
//		USART_SendData(USARTx,*addr);//调用STM32标准库函数发送数据
//		addr++; //地址偏移
//	}
//}

Two o'clock in the morning: A girl in our group and I are still using a soft armor yplot for the first time to debug the PID parameters on the computer. Now that I think about it, I still feel painful. Finally, I adjusted it to about 12 noon, which is not bad. Satisfied, let’s leave it for now. When the tuning was about the same, we felt that there was nothing to do, so we were a little slack, because after looking at these questions, we felt that as long as the PID was tuned out, there would be no difficulty [ Sow trouble for later! ! ! !

Because there are eight questions, I originally used the touch screen to read the information sent by the serial port to select the function, but I accidentally shorted the power supply, and it burned in an instant. Hey, I changed to OLED later [Multi-level menu , on-board button control], set two-level menu, because there are only eight functions, and found that there is still a button at that time, I thought about using it for leveling [to keep the system level], so there are 2 functions.

//按键控制发送信息
void Menu_key_set(void)
{
    key_state = KEY_Scan(0);
    if (key_state == 1)
    {
			  OLED_Clear();
        func_index = table[func_index].next;
    }
		if(key_state == 2)
		{
		    TIM_SetCompare1(TIM3,14.5);
		}
    if(key_state == 3)
		{
			  OLED_Clear();
				func_index = table[func_index].enter;
		}
    current_operation_index = table[func_index].current_operation;
    (*current_operation_index)();
}

//多级菜单
void meun1_func(void)
{
    OLED_ShowString(0, 0, "   1.I    ", 16);
    OLED_ShowString(0, 2, "   2.II", 16);
}

void meun3_func(void)
{
    OLED_ShowString(0, 0, "   1.1    ", 16);
    OLED_ShowString(0, 2, "   2.2", 16);
    OLED_ShowString(0, 4, "   3.3", 16);
    OLED_ShowString(0, 6, "   4.4", 16);
}

void meun2_func(void)
{
    OLED_ShowString(0, 0, "   1.21    ", 16);
    OLED_ShowString(0, 2, "   2.22", 16);
    OLED_ShowString(0, 4, "   3.23", 16);
    OLED_ShowString(0, 6, "   4.24", 16);
}

Functions 1, 2, and 3 all move from 1 to a certain point, because we recorded the pixel position of each point in detail with openmv at the beginning, which is equivalent to passing the X_aim of the target position into the pid algorithm That's it, then openmv will read the current coordinates of the ball in real time and then send it back to the MCU with the help of serial port 3, and send this X_now back to the PID to calculate the duty cycle.

The latter 4 and 5 are three points compared to the previous ones, and need to be stable for 2 seconds, so you need to add the timer function, and then start using TIM1, but I found that timing cannot be performed, and I still don’t know why , Then it’s time to arrive at TIM2, it will be fine all at once, it’s really metaphysical, the timer time is set to 0.5s, and a static static variable is added to the timer interrupt, so that you need to pass the flag once and it’s up to you. When the time is up, as soon as the value of the corresponding flag changes, the timing is over

//定时器2中断服务程序
void TIM2_IRQHandler(void)
{
    //判断TIM2更新中断是否发生
    if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
{
        //代码应用区
		i++;
		t++;
		if(i%20 == 0)
		{
			timeflag = 1;
		}
		if(t%20 == 0)
		{
			lanyaflag= 1;  
    }
		
} 

 At that time, it was how to start the timing of the timer. I still thought about it for some time. Later, I set a jinruflag flag to meet the requirements and initialize it once. Then, according to the value of timeflag, if it is 1, the time is up, and then go to the next step. one point.

Why is my time so long? Hey, the main pid adjustment is not very good, so I want to give the ball more time to stabilize, because our test found that when the time is too short, it can indeed reach the target point, but it does not Stabilize, the result is that there is an initial velocity when going to the next point, so that the next point will be more unstable, a vicious circle, so I think about lengthening the time, giving it a little more time, let him in a Point to stabilize.

else if(a==5)
	{
		if(timeflag == 0)
		{
			X_aim = 83;
			task();
			if(jinruflag==0)
			{
				TIM2_Init(5000-1,7200-1);         //0.5s中断一次   
				delay_ms(20);
				jinruflag = 1;
			}
		}	
		if(timeflag == 1)
		{
			 X_aim = 145;
			 task();
		}
	}

Question 6 uses the independent selection of four points, let the ball balance, see that the previous three fixed points become four random points, just use Bluetooth input, we choose the Bluetooth app upper computer software , what is sent is four characters. After receiving it, put it directly in the received array and call it directly. Indeed, there is no problem with the idea, but there is a problem with Bluetooth reception. We can send the string on the mobile app side , but the serial port of the single-chip microcomputer does not accept characters. Changing several serial ports is of no avail. It took several hours, and finally suddenly used a bluetooth code that was adjusted before the project, and finally found the root of the problem- --->

This is the interrupt code of the serial port. You will find that there are 0X0D and 0X0A judgments. By the way, this is the presence of an extra line of newline characters that we input in the app software, because this interrupt is judged by judging 0X0D and 0X0A The reception is completed. If you don't add this to the interrupt, you don't need to change the line.

void USART2_IRQHandler(void)                    //串口1中断服务程序
    {
    u8 Res;
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        {
        Res =USART_ReceiveData(USART2);    //读取接收到的数据
        if((USART2_RX_STA&0x8000)==0)//接收未完成
            {
            if(USART2_RX_STA&0x4000)//接收到了0x0d
                {
                if(Res!=0x0a)USART2_RX_STA=0;//接收错误,重新开始
                else USART2_RX_STA|=0x8000;    //接收完成了 
                }
            else //还没收到0X0D
                {    
                if(Res==0x0d)USART2_RX_STA|=0x4000;
                else
                    {
                    USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res;
                    USART2_RX_STA++;
                    if(USART2_RX_STA>(USART_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收      
                    }         
                }
            }   
        						
     } 

} 

Function 7: Run back and forth four times at 2 and 4, and finally go to 5, because there are only a few hours left at that time, and our hardware circuit has not been connected, so I am a little anxious, and I have not had a good rest. Chaos, I thought of a timing plan at the time, using static static variables to calculate, whether there are 8 times to 2, 4 times, to determine whether to go back and forth four times, it is easy to think about, just pass the existing coordinates and openmv The real-time coordinates are subtracted to get the position difference, which is passed to the PID, but the PID parameters are not adjusted very well. The ball starts from 2, and then goes to 2 before it reaches 4, so the error is getting bigger and bigger, so that it reaches 2 In the end, it flew out at 5. At that time, it was impossible to adjust the PID, but we found that the two points 2 and 4 we chose were hovering between 1 and 5, so we simply put the judgment range directly to Shrinking is similar to giving the ball a prediction. The original goal of the ball is 4, and it will start to adjust greatly when it reaches 4 or soon to 4. Now we make it 3.5, and the distance difference from 2 becomes smaller It will judge ahead of time, and the effect is really good after the change, only one time it failed to meet the standard [much better than the one time before], and this question was also like this later.

else if(a == 7)
	{
		if(x_now<57&&x_now>46)
  {
   while(1)
   {
    X_aim = 93;                      //原来 4的X_aim是 113
    task();
    if(x_now<114&&x_now>106)
     break;
   }
   count++;
  }
  if(x_now<114&&x_now>106)
  {
   while(1)
   {
    X_aim = 70;                         //原来的2的X_aim = 53
     task();
    if(x_now<57&&x_now>50)
     break;
   } 
   count++;
  }
  if(count >= 8)
  {
   while(1)
   {
    X_aim = 145;
     task();
   }
  }
	}

The last one is the robustness test. This is very similar to the previous 1, 2, and 3. If the PID is adjusted well, it will be very fast. Ours is a bit slow. This question is within the limit range limited by the number of seconds .

Later, when time did not permit, I did 8 questions.

Summary review:

1. PID tuning is very important. If there is any good recommended software, can you recommend it to me? ?

2. The step-down and voltage regulation of the hardware circuit need to be paid special attention to

The specific and detailed functions of some modules in 3.32 are still not clear.

4. Our teamwork is still not very good, the strength of the team is very important

The code is open source: MMMMMs426/-: One-dimensional cricket code (github.com)

Guess you like

Origin blog.csdn.net/weixin_63032791/article/details/130354381