Openmv识别Apriltag码并与stm32进行串口通信

这篇具有很好参考价值的文章主要介绍了Openmv识别Apriltag码并与stm32进行串口通信。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        本文使用带有独立处理图像模块的摄像头Openmv进行Apriltag码的识别,并将Openmv与stm32进行串口通信,将Apriltag码的ID、中心位置相对于Openmv摄像头中心坐标的偏移量、以及Apriltag码相对于Openmv镜头的距离通过串口通信传输给stm32。

        接线图Openmv通过电脑USB口供电,Openmv接三根线,一根与stm32共地,一根将Openmv的P4与stm32的A10相连接,另一根将Openmv的P5管脚与stm32端的A9相连接(即两者的Rx和Tx交错链接,以实现串口通信):

stm32和openmv串口通信,stm32,嵌入式硬件,单片机        

stm32和openmv串口通信,stm32,嵌入式硬件,单片机

串口通信采取的方式:

        采取串口通信发送hex数据包的方式,通讯协议为两个帧头和一个帧尾。由于整型数据较为容易发送,可以通过将Openmv端的tag_id(int)直接发送、Apriltag码相对于Openmv镜头中心的横向偏移量x_translation(float)、以及距离distance(float)乘以1000或者10000进行发送。另外,发送负数也会导致错误,因此加入一个标志位,在Openmv端只发送正整数,通过标志位的值,在stm32端判断是否需要加上负号。

        可以通过time.sleep_ms()来设置摄像头的帧数,实际上每秒发送数据的速度达不到帧数。

        采取的串口通信为使用串口com3,波特率为9600,数据位8位,无校验位,停止位1。事实上返回的两个float都是相对距离,并不是实际距离,若要实际距离只需要如下:

# f_x 是x的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/3.984*656,这个值是用毫米为单位的焦距除以x方向的感光元件的长度,乘以x方向的感光元件的像素(OV7725)
# f_y 是y的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/2.952*488,这个值是用毫米为单位的焦距除以y方向的感光元件的长度,乘以y方向的感光元件的像素(OV7725)

stm32和openmv串口通信,stm32,嵌入式硬件,单片机

Openmv端python代码:

import sensor
import image
import time
import struct
from pyb import UART
# 使用openmv的串口3(com3)
# 波特率要跟需要通信的设备一样 Tx 和 Rx 引脚交错连接

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA) #QQVGA
sensor.skip_frames(30)
sensor.set_auto_gain(False)  # 关闭
sensor.set_auto_whitebal(False)  # 关闭
clock = time.clock()

uart = UART (3, 9600)   #初始化串口3,波特率为9600(注意:下位机stm32记得也配置成9600)
uart.init(9600, bits=8, parity=None, stop=1)  #设置波特率为9600,数据位8位,无校验位,停止位1位
#编码模式UTF-8

# 注意!与find_qrcodes不同,find_apriltags 不需要软件矫正畸变就可以工作。

# f_x 是x的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/3.984*656,这个值是用毫米为单位的焦距除以x方向的感光元件的长度,乘以x方向的感光元件的像素(OV7725)
# f_y 是y的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/2.952*488,这个值是用毫米为单位的焦距除以y方向的感光元件的长度,乘以y方向的感光元件的像素(OV7725)
# c_x 是图像的x中心位置   c_y 是图像的y中心位置

tag_families = 0
tag_families |= image.TAG36H11  # comment out to disable this family (default family)

f_x = (2.8 / 3.984) * 160 # 默认值
f_y = (2.8 / 2.952) * 120 # 默认值
c_x = 160 * 0.5 # 默认值(image.w * 0.5)
c_y = 120 * 0.5 # 默认值(image.h * 0.5)

#apriltag家族tag36h11
def family_name(tag):
    if tag.family() == image.TAG36H11:
        return "TAG36H11"

while(True):
    clock.tick()
    img = sensor.snapshot()
    for tag in img.find_apriltags(families=tag_families,fx=f_x, fy=f_y, cx=c_x, cy=c_y): # TAG36H11
        img.draw_rectangle(tag.rect(), color = (255, 0, 0))
        img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))
        print_args = (family_name(tag), tag.id(), tag.x_translation(), tag.y_translation(), tag.z_translation())
        print("Tag Family %s, Tag ID %d, Tx: %f, Ty %f, Tz %f" % print_args)
        #帧头 + 帧头 + id信息 + x坐标 + z距离 + 标志位 + 帧尾
        #使用struct将数据打包并发送,保证float型x坐标数据准确及其信息的完整性
        if tag.x_translation() >= 0:
            data = struct.pack("<bbiiibb",
                                0xAA,     #帧头
                                0xAE,     #帧头
                                tag.id(), #数据id
                                #x的坐标
                                int(10000*tag.x_translation()),#数据1
                                #z的坐标
                                -int(10000*tag.z_translation()),#数据2
                                0xBF,     #标志位表示大于零
                                0xAC)     #帧尾
        else:
            data = struct.pack("<bbiiibb",
                               0xAA,     #帧头
                               0xAE,     #帧头
                               tag.id(), #数据id
                               #x的坐标
                               -int(1000*tag.x_translation()),#数据1
                               #z的坐标
                               -int(10000*tag.z_translation()),#数据2
                               0xCF,     #标志位表示小于零
                               0xAC)     #帧尾
        uart.write(data)  #com3串口发送
        time.sleep_ms(50)
       # uart.write("Tag Family %s, Tag ID %d, Tx: %f, Ty %f, Tz %f" % print_args+"\r\n")#与windows通信
    print(clock.fps())

将python文件复制粘贴到Openmv端的main.py中,即可实现Openmv脱机自动执行Apriltag码识别

stm32端代码:

Serial.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t receive_data[13];
uint8_t Serial_RxFlag;					//定义接收数据包标志位
int tag_id;			//apriltag码的id 包括 0 ,1 ,2三个数据
//偏移距离数值越大与openmv镜头中心偏离越远
//相对距离数值越大距离openmv镜头越远
//x_translation的正负表示物体相对openmv镜头中心的右左
//x_translation<0,表示apriltag码相对于openmv镜头中心在左
//x_translation>0,表示apriltag码相对于openmv镜头中心在右
int x_translation;  //apriltag码偏离openmv镜头中心的相对位移 
int distance;		//apriltag码距离openmv镜头中心的相对距离
int Sign;			//标志位
void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure ;
	USART_InitStructure.USART_BaudRate= 9600;	//串口通信波特率为9600
	USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1,&USART_InitStructure);

	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd =	ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	USART_Cmd(USART1,ENABLE);
}
/**
  * 函    数:获取串口接收数据包标志位
  * 参    数:无
  * 返 回 值:串口接收数据包标志位,范围:0~1,接收到数据包后,标志位置1,读取后标志位自动清零
  */
uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)			//如果标志位为1
	{
		Serial_RxFlag = 0;
		return 1;					//则返回1,并自动清零标志位
	}
	return 0;						//如果标志位为0,则返回0
}

/**
  * 函    数:USART1中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量
	static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断
	{
		uint8_t RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量
		
		/*使用状态机的思路,依次处理数据包的不同部分*/
		
		/*当前状态为0,接收数据包包头*/
		if (RxState == 0)
		{
			if (RxData == 0xAA)			//如果数据确实是包头
			{
				RxState = 1;			//置下一个状态			//数据包的位置归零
			}
		}
		/*当前状态为1,接收数据包数据*/
		else if (RxState == 1)
		{	
			if (RxData == 0xAE)
			{
				RxState = 2;
				pRxPacket = 0;
			}
		}
		/*当前状态为2,接收数据包包尾*/
		else if (RxState == 2)
		{
			receive_data[pRxPacket] = RxData;	//将数据存入数据包数组的指定位置
			pRxPacket ++;				//数据包的位置自增
			if (pRxPacket >= 13)			//如果收够12个数据
			{
				RxState = 3;			//置下一个状态
			}
		}
		else if(RxState == 3)
		{
			if (RxData == 0xAC)			//如果数据确实是包尾部
			{
				RxState = 0;			//状态归0
				Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包	
			}
		}
		tag_id = receive_data[3] << 24 | receive_data[2] << 16 | receive_data[1] << 8 | receive_data[0];//位移运算,将hex16进制转换成int整型
		if(receive_data[12] == 0xBF)
		{
			x_translation = receive_data[7] << 24 | receive_data[6] << 16 | receive_data[5] << 8 | receive_data[4];
		}
		else if(receive_data[12] == 0xCF)
		{
			x_translation = -(receive_data[7] << 24 | receive_data[6] << 16 | receive_data[5] << 8 | receive_data[4]);
		}
		distance = receive_data[11] << 24 | receive_data[10] << 16 | receive_data[9] << 8 | receive_data[8];
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位
	}
}

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H
void Serial_Init(void);
uint8_t Serial_GetRxFlag(void);
void USART1_IRQHandler(void);
extern uint8_t receive_data[];
uint8_t Serial_GetRxFlag(void);
extern int tag_id;
extern int x_translation;
extern int distance;
#endif

main.c文章来源地址https://www.toymoban.com/news/detail-859318.html

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "OLED.h"
#include "Serial.h"

int main(void)
{
	OLED_Init();
	Serial_Init();
	
	OLED_ShowString(1,1,"tag_id:");
	OLED_ShowString(2,1,"x_trs:");
	OLED_ShowString(3,1,"Diace:");
	while(1)
	{
		OLED_ShowNum(1,10,tag_id,2);
		OLED_ShowSignedNum(2,9,x_translation,5);
		OLED_ShowNum(3,10,distance,5);
    }
}

到了这里,关于Openmv识别Apriltag码并与stm32进行串口通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【嵌入式】openmv与stm32的串口通信

    【嵌入式】openmv与stm32的串口通信

    参考:(文中部分图/文字/代码来自以下文章,部分内容由于时间久远已经找不到原作者,可联系注明或删除) PYTHON串口数据打包发送STM32接收数据解析 openmv中文文档 这里以openmv循迹代码为例 main.py 关于struct.pack: 函数原型:struct.pack(fmt, v1, v2, …) fmt是格式字符串 v1,v2是要转

    2024年02月14日
    浏览(12)
  • 关于openmv与stm32之间的串口通信实现

    关于openmv与stm32之间的串口通信实现

    已经在研一度过快3个月了,研究生学习跟本科学习还是有很大区别的,要善于自己找资料善于自己总结,因此我也决定从现在开始时不时的写写博客总结后面每段时间遇到的各种困难以及解决方法,为以后工作积累经验也方便以后查阅。 这是我第一篇博客,之前都是在做关

    2024年02月14日
    浏览(10)
  • stm32f103与openmv串口通信

    串口通信是指通过串行通信接口进行数据传输的一种通信方式。在串口通信中,数据被分成一个个的字节,按照一定的顺序依次发送和接收。串口通信通常使用UART(通用异步收发传输)协议进行数据传输。 串口通信在嵌入式系统中应用非常广泛,其主要用途包括: 调试和监

    2024年02月13日
    浏览(9)
  • 【STM32+OPENMV】矩形识别

    有关OPENMV最大色块追踪及与STM32通信内容,详情见【STM32+HAL】与OpenMV通信 1、芯片:STM32F103C8T6 2、CUBEMX配置软件 3、KEIL5 4、OPENMV 寻找黑色矩形,并将最大矩形的四个边缘坐标发送给STM32 1、寻找最大的矩形,并沿矩形边框绘制线条 2、完整通信+识别代码 【STM32+OPENMV】矩形识别资

    2024年03月15日
    浏览(9)
  • 【Qt上位机与STM32进行串口通信】-2-Qt串口开发

    【Qt上位机与STM32进行串口通信】-2-Qt串口开发

    系列文章目标:Qt上位机与STM32进行串口通信,控制多个LED的状态。 本篇文章的主要目标: 1、设计两个界面,串口连接界面、控制界面。 2、只有在串口连接成功才能打开控制界面。 3、打开控制界面时,串口保持连接。 4、自定义控件,提升开发效率。 以下是我入门Qt的视频

    2024年02月06日
    浏览(13)
  • stm32串口发送数据包进行解析,实现人机交互

    stm32串口发送数据包进行解析,实现人机交互

    串口收发解析数据包 学过stm32的同学都知道,利用串口与32进行通讯非常的方便,在正点原子的官方历程中我们就可以看到,在串口中断服务函数里面,对接受的数据用一个十六位的数据来判断是否接受完成(即是否在数据包的末尾接收到0x0D,0x0A,他们分别对应的是r n),

    2024年02月14日
    浏览(5)
  • 使用jetson nano的串口与stm32进行通信

    使用jetson nano的串口与stm32进行通信

    首先, jetson nano可以跑python, 那就简单了: 端口就是: GND, 8, 10, 分别接到我STM32F4最小系统板子的UART2: 板子为了方便观察, 就用uart2接收, 用uart1发送, 为啥不用同一个串口呢? 嗯…我乐意… STM32的代码就如上面的, 随便写写就好了. 把UART1的TX接到usb串口上, 然后在jetson上用python运行上

    2024年02月12日
    浏览(19)
  • 江科协STM32教程——STM32 使用串口助手进行LED灯的点亮熄灭灭控制全代码过程(USART串口通信的简单应用)

    江科协STM32教程——STM32 使用串口助手进行LED灯的点亮熄灭灭控制全代码过程(USART串口通信的简单应用)

            使用USART串口通信,通过上位机串口助手发送符串给STM32执行LED灯点亮熄灭操作的流程。         基本的配置如下所示,GPIO口配置的为PA9推挽输出,用于LED灯的操作。其余注意USART_ITConfig和USART_Cmd开启USART接收中断和使能USART运行。         接下来要进行发送字

    2024年03月21日
    浏览(9)
  • K210学习笔记(二) K210与STM32进行串口通信,K210收,STM32发

    K210学习笔记(二) K210与STM32进行串口通信,K210收,STM32发

    想用STM32通过串口给K210发数据,并在屏幕上显示,看了好几篇博客,终于搞通了,大家感兴趣也可以看看。 K210学习笔记(一) K210与STM32串口通信相关 接线,STM32的串口引脚比较固定,而K210就牛比了,任意映射,懒人福音。这里我找了两个没有被复用的IO,IO9和IO10 STM32 K210 GND

    2023年04月08日
    浏览(8)
  • STM32CubeMx学习与K210串口通信+识别橘色色块——点亮小灯

     K210模块的串口发送代码 识别色块 +数字处理 这里zz是4个数字的数,则需要分成千百位和低二位传输 zz是距离。也映射了距离的算法 整体K210代码 STM32串口接受端函数 判断帧头帧尾 如果数据正常 判断 数据是否合格 若合格则点灯 否则灭灯 不要忘记加入

    2024年02月14日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包