K210云台追踪装置
1、摄像头配置
sensor.set_pixformat(sensor.RGB565)#设置为彩色
sensor.set_framesize(sensor.QVGA)#设置分辨率,即图像大小
sensor.skip_frames(time = 100)
#sensor.set_auto_gain(False)
#sensor.set_auto_whitebal(False)
sensor.set_vflip(True)#垂直方向翻转
进行图像的垂直方向翻转:数据处理需求:在一些图像处理或者计算机视觉算法中,研究或者模型的训练可能需要统一图像的方向,以避免方向变化带来的影响。通过统一垂直方向的翻转,可以简化算法或模型的设计。
2、颜色预处理
颜色预处理:
颜色空间转换:通常将图像从RGB颜色空间转换为HSV(色相、饱和度、亮度)颜色空间。HSV颜色空间更适合进行颜色分析,因为它将颜色信息与亮度信息分开,使得颜色的描述更为直观和方便。
颜色过滤:在HSV空间中,可以设定一个颜色的范围(如蓝色范围、红色范围等),通过设定上下限来选择所需颜色范围内的像素,从而过滤掉非目标颜色的像素。
寻找最大色块:
在预处理过的图像中,可能会有多个符合颜色范围的区域(色块)。为了确定目标颜色的位置,可以使用以下技术:
- 二值化:将符合颜色范围的像素转换为二值图像,即只有目标颜色的区域是白色(或者其他亮度值),其他区域是黑色。
- 连通区域分析:找出二值图像中的连通区域(即连续的白色区域),计算每个区域的面积或像素数量。
- 选择最大色块:通常选择面积最大的连通区域作为目标颜色的位置。这种做法可以有效排除噪声或其他不相关区域的干扰,确保追踪的准确性和稳定性。
2、1 寻找最大色块函数
#寻找最大色块
def find_max(blobs):
max_size=0
max_blob=0
for blob in blobs:
if blob.pixels() > max_size:
max_blob=blob
max_size = blob.pixels()
return max_blob
这段代码是用来从一个包含多个色块(或者说连通区域)的列表 blobs 中找到面积(或像素数量)最大的色块,并返回该色块对象 max_blob。
max_size = 0:初始化一个变量 max_size,用来记录找到的最大色块的像素数量(或面积),初始值设为0。
max_blob = 0:初始化一个变量 max_blob,用来记录面积最大的色块对象,默认初始值为0。在实际使用中,可能会根据具体的数据结构或需求初始化为 None 或者其他适当的值。
for blob in blobs::遍历列表 blobs 中的每个色块对象 blob。
if blob.pixels() > max_size::检查当前遍历到的色块 blob 的像素数量(或面积)是否大于 max_size。
max_blob = blob 和 max_size = blob.pixels():如果当前 blob 的像素数量大于 max_size,则更新 max_blob 为当前的 blob 对象,并更新 max_size 为 blob 的像素数量。
最后,函数返回 max_blob,即面积最大的色块对象。
这段代码假设 blobs 是一个包含多个色块对象的列表,每个色块对象具有一个 pixels() 方法来获取其像素数量(或面积)。它通过遍历整个列表并比较每个色块的大小,来找到列表中面积最大的色块,并返回该色块对象。
过滤一些颜色小区域,可以在IDE右侧帧缓冲区框中选要识别的物体。框取红色物块后下面会显示其LAB的最大值和最小值,将thresholds列表修改即可
~LAB色彩空间(LAB color space)是一种用于描述颜色的色彩模型。它不同于常见的RGB(红绿蓝)和CMYK(青、洋红、黄、黑)色彩空间,而是基于人眼的感知方式和颜色感知的模型。~
LAB色彩空间有三个通道:
L(亮度)通道:表示颜色的亮度或明暗程度。在L通道中,黑色是0,白色是100。
- A通道:表示颜色从绿色到红色的范围。负值表示绿色,正值表示红色。B通道:表示颜色从蓝色到黄色的范围。负值表示蓝色,正值表示黄色
- LAB色彩空间的一个显著特点是,它可以描述人眼视觉系统的亮度和颜色感知方式。因此,它在许多领域中被广泛用于颜色相关的应用,如图像处理、计算机视觉、印刷、摄影等。特别是在计算机视觉中,LAB色彩空间常用于颜色识别、颜色分割、颜色校正等任务,因为它可以有效地分离亮度信息和颜色信息,从而提供更加灵活和准确的颜色处理能力~
2、2 串口发送函数
from machine import UART
from fpioa_manager import fm # GPIO重定向函数
fm.register(18, fm.fpioa.UART1_TX, force=True)
uart_A = UART(UART.UART1,9600, 8, 0, 1, timeout=1000, read_buf_len=4096)
#串口打包发送函数
def sending_data(x,y):
FH = bytearray([0x2C,0x12,x,y,0x5B])#拼字节
uart_A.write(FH);
sending_data(cx,cy)#发送cx,cy变量
注册一个GPIO口配置为UART1的TX,波特率为9600,将要发送的数据打包为字符串的形式进行发送,数据包除要发送的变量外,还有帧头(0x2C、0x12)和帧尾(0x5B)
2、3 寻找色块函数
img = sensor.snapshot()#拍摄一张照片,img为一个image对象,获取快照
blobs=img.find_blobs([thresholds[0]], pixels_threshold=100, area_threshold=100, merge=True, margin=10)
max_b=find_max(blobs)#寻找最大色块
cx=0;cy=0;
#用for循环把所有的色块找一遍。
if blobs:
max_b = find_max(blobs)
cx=max_b[5]
cy=max_b[6]
cw=max_b[2]
ch=max_b[3]
img.draw_rectangle(max_b.rect(),color=(0,0,0)) # rect
img.draw_cross(max_b[5], max_b[6]) # cx, cy
img = sensor.snapshot(): 这行代码通过sensor对象获取当前图像的快照,并将其赋值给变量img,img是一个图像对象,可以进行后续处理。
blobs = img.find_blobs([thresholds[0]], pixels_threshold=100, area_threshold=100, merge=True, margin=10): 这行代码使用find_blobs方法在图像img中查找符合指定颜色阈值的色块。参数说明:
[thresholds[0]]: 是一个阈值列表,表示要查找的颜色阈值。thresholds[0]应该是预先定义的阈值。
pixels_threshold=100: 指定了色块的像素阈值,即最小像素数,低于这个值的色块将被忽略。
area_threshold=100: 指定了色块的面积阈值,即最小面积,小于这个值的色块也将被忽略。
merge=True: 表示是否合并重叠的色块。
margin=10: 表示在色块边界周围增加的边距。
max_b = find_max(blobs): 这行代码调用find_max函数(假设已经定义,不在提供的代码中),并将blobs作为参数传递给它,返回找到的最大色块(或者是具有最大面积的色块)。cx = max_b[5]: 将最大色块的中心点的 x 坐标存储在变量 cx 中。
cy = max_b[6]: 将最大色块的中心点的 y 坐标存储在变量 cy 中。
cw = max_b[2]: 将最大色块的宽度存储在变量 cw 中。
ch = max_b[3]: 将最大色块的高度存储在变量 ch 中。
img.draw_rectangle(max_b.rect(), color=(0, 0, 0)): 这行代码在图像 img 上绘制一个矩形,矩形的位置和大小由 max_b.rect() 提供,颜色为黑色 (0, 0, 0)。这个矩形通常用来标记识别出的色块区域。
img.draw_cross(max_b[5], max_b[6]): 这行代码在图像 img 上绘制一个交叉十字,位置由 max_b[5] 和 max_b[6] 给出,即最大色块的中心点坐标 (cx, cy)。这个交叉十字通常用来标记色块的中心位置。
2、4 stm32串口函数
2、4、1 初始化配置:
int Openmv_X; /*OPENMV X Öá·´À¡×ø±ê*/
int Openmv_Y; /*OPENMV X Öá·´À¡×ø±ê*/
void Uart3_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 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(USART3, &USART_InitStructure);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART3, ENABLE);
}
2、4、2 中断函数配置:
void USART3_IRQHandler(void)
{
u8 com_data; // 定义接收到的数据变量
u8 i;
static u8 RxCounter1 = 0; // 接收计数器,用于记录接收到的字节数
static u16 RxBuffer1[5] = {0}; // 接收缓冲区,存储接收到的数据
static u8 RxState = 0; // 接收状态机的状态变量
if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) // 检查是否接收数据寄存器非空中断已发生
{
flag = 1; // 设置一个标志位(假设flag是一个全局变量)
USART_ClearITPendingBit(USART3, USART_IT_RXNE); // 清除中断挂起标志位
com_data = USART_ReceiveData(USART3); // 从USART3读取接收到的数据
// 状态机处理接收到的数据
if (RxState == 0 && com_data == 0x2C) // 开始接收序列,期望接收0x2C
{
RxState = 1; // 进入状态1
RxBuffer1[RxCounter1++] = com_data; // 将接收到的数据存入缓冲区
}
else if (RxState == 1 && com_data == 0x12) // 在0x2C之后,期望接收0x12
{
RxState = 2; // 进入状态2
RxBuffer1[RxCounter1++] = com_data; // 将接收到的数据存入缓冲区
}
else if (RxState == 2) // 接收数据状态
{
RxBuffer1[RxCounter1++] = com_data; // 将接收到的数据存入缓冲区
// 检查终止条件(已接收5字节且最后一个字节是0x5B)
if (RxCounter1 == 5 && com_data == 0x5B)
{
// 处理接收到的数据
Openmv_X = RxBuffer1[RxCounter1 - 3]; // 假设第三个字节是Openmv_X
Openmv_Y = RxBuffer1[RxCounter1 - 2]; // 假设第四个字节是Openmv_Y
// 重置状态和缓冲区
RxCounter1 = 0;
RxState = 0;
}
else if (RxCounter1 > 5) // 如果接收到超过5字节且没有终止条件
{
RxState = 0; // 重置状态
RxCounter1 = 0; // 重置计数器
for (i = 0; i < 5; i++)
{
RxBuffer1[i] = 0x00; // 清空缓冲区
}
}
}
else // 无效状态,重置所有状态和缓冲区
{
RxState = 0; // 重置状态
RxCounter1 = 0; // 重置计数器
for (i = 0; i < 5; i++)
{
RxBuffer1[i] = 0x00; // 清空缓冲区
}
}
}
}