完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
概述 小车外形: 功能简介 利用摄像头识别前车尾部的AprilTag,得到前车位置,传回stm32主控板处理,使两车在行驶时保持恒定距离,实现自动跟车。 1.openMV4摄像头 1.1 Apriltag识别与串口传输 AprilTag是一个视觉基准库,在AR,机器人,相机校准领域广泛使用。通过特定的标志(与二维码相似,但是降低了复杂度以满足实时性要求),可以快速地检测标志,并计算相对位置。 Apriltag示例: 通过识别Apriltag,可以得到x,y,z三个方向的距离以及偏移角度。这里只需要三维的距离即可,通过串口传回stm32. import sensor, image, time, math,pyb from pyb import UART sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger... sensor.skip_frames(time = 2000) sensor.set_auto_gain(False) # must turn this off to prevent image washout... sensor.set_auto_whitebal(False) # must turn this off to prevent image washout... clock = time.clock() uart = UART(3, 115200)#串口波特率 f_x = (2.8 / 3.984) * 160 # find_apriltags defaults to this if not set f_y = (2.8 / 2.952) * 120 # find_apriltags defaults to this if not set c_x = 160 * 0.5 # find_apriltags defaults to this if not set (the image.w * 0.5) c_y = 120 * 0.5 # find_apriltags defaults to this if not set (the image.h * 0.5) def degrees(radians): return (180 * radians) / math.pi while(True): clock.tick() img = sensor.snapshot() for tag in img.find_apriltags(fx=f_x, fy=f_y, cx=c_x, cy=c_y): # defaults to TAG36H11 img.draw_rectangle(tag.rect(), color = (255, 0, 0)) img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0)) print_args = (tag.x_translation(), tag.y_translation(), tag.z_translation()) #degrees(tag.x_rotation()), degrees(tag.y_rotation()), degrees(tag.z_rotation())) # Translation units are unknown. Rotation units are in degrees. # print("Tx %f, Ty %f, Tz %f" % print_args) uart.write("A%.2f,B%.2f,C%.2f," % print_args+'rn')#设置特定格式,以便于stm32分割取得数据 #pyb.delay(500) # print(clock.fps()) 2. STM32主控板(型号为F407) 2.1 时钟与中断配置 附上stm32时钟示意图: 定时器示意图: 定时器分配: 所有时钟初始化的函数:(每个函数的详细内容在后面) TIM8_PWM_Init(400-1,20-1); //用于控制电机,168M/20=8.4Mhz的计数频率,重装载值400,所以PWM频率为 8.4M/400=21Khz. TIM3_PWM_Init(200-1,8400-1);//用于控制舵机,50HZ TIM2_Int_Init(400-1,20-1);//定时中断,21KHZ TIM7_Int_Init(500-1,8400-1);//用于编码器计数,20HZ,50ms中断一次 uart_init(115200); //初始化串口1波特率为115200 Encoder_Init_TIM4();//编码器接口初始化 2.2 串口收发与数据处理 串口中断:USART1,USART2 串口初始化函数(以USART1为例): void uart_init(u32 bound){ //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟 //串口1对应引脚复用映射 GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1 //USART1端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10 //USART1 初始化设置 USART_InitStructure.USART_BaudRate = bound;//波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_Cmd(USART1, ENABLE); //使能串口1 USART_ClearFlag(USART1, USART_FLAG_TC); #if EN_USART1_RX USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、 #endif } 串口中断处理函数: void USART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; #ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了. OSIntEnter(); #endif if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) { Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据 if((USART_RX_STA&0x8000)==0)//接收未完成 { if(USART_RX_STA&0x4000)//接收到了0x0d { if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始 else USART_RX_STA|=0x8000; //接收完成了 } else //还没收到0X0D { if(Res==0x0d)USART_RX_STA|=0x4000; else { USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; USART_RX_STA++; if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收 } } } } #ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了. OSIntExit(); #endif } #endif 字符串接收与处理(从openMV接收到的数据): /*涉及到的全局变量 float data[3];//x,y,z方向的距离,浮点数形式 unsigned char data_string[3][7];//x,y,z方向的距离,字符串形式 */ if(USART_RX_STA&0x8000) { //清空字符串 for(i=0;i<2;i++) { for(j=0;j<6;j++) { data_string[j]=' '; } } len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度 for(t=0,j=0;t j=0; if(USART_RX_BUF[t]=='A') { t++;//去掉首字母 while(USART_RX_BUF[t]!=',') { data_string[0][j]=USART_RX_BUF[t]; t++; j++; } } if(USART_RX_BUF[t]=='B') { t++;//去掉首字母 while(USART_RX_BUF[t]!=',') { data_string[1][j]=USART_RX_BUF[t]; t++; j++; } } if(USART_RX_BUF[t]=='C') { t++;//去掉首字母 while(USART_RX_BUF[t]!=',') { data_string[2][j]=USART_RX_BUF[t]; t++; j++; } } } //把字符串转化为浮点数 data[0]=myatof(data_string[0])/100.0;//x data[1]=myatof(data_string[1])/100.0;//y data[2]=myatof(data_string[2])*(-1)/100.0;//z,输入是负数,转换为正 //USART2发送数据 // Usart_SendString( RS232_USART, (uint8_t *)USART_RX_BUF ); //LCD更新显示 //显示xyz // CLEAR(10); // Gui_DrawFont_GBK16(30,0,BLACK,WHITE,clear); // Gui_DrawFont_GBK16(30,0,BLACK,WHITE,data_string[0]); // Gui_DrawFont_GBK16(30,20,BLACK,WHITE,clear); // Gui_DrawFont_GBK16(30,20,BLACK,WHITE,data_string[1]); // Gui_DrawFont_GBK16(30,40,BLACK,WHITE,clear); // Gui_DrawFont_GBK16(30,40,BLACK,WHITE,data_string[2]); USART_RX_STA=0;//清除标志位 } } 字符串转化为两位小数浮点数(用于后续PID控制): int myatof(const char *str)//此函数仅适用于两位小数的浮点数,返回的是乘100后的int值,因float返回有错误 { int flag = 1;//表示正数 int res =0; u8 i=1; //小数点后两位 while(*str != ' |