基于STM32与K210的云台追踪装置

目录

一、摘要

二、K210端

 1.摄像头配置

2.颜色预处理

3.串口通信(发送)

4.寻找色块

三、STM32端

1.串口通信(接收)

2.舵机(定时器配置)

3.PID


一、摘要

        本文通过K210作为是识别模块去识别被测物体(以红色小球为例),当其识别到红色小球后,判断小球中心点所在的区域信息,并将其区域标志位通过串口发送给STM32,当STM32接收到位置信息后对x轴、y轴的两个舵机参数进行操作,最后通过定时器输出合适的PWM波,控制舵机旋转相应的角度,使K210摄像头对准被测物体,以实现物体追踪功能。

二、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.颜色预处理

        寻找最大色块的函数:

#寻找最大色块
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

        在追踪识别的过程中,可能出现背景或是其他区域出现小面积的红色区域,这会对识别造成影响,所以需要用程序过滤掉那些小的红色区域。

颜色阈值可通过IDE右侧的帧缓冲区框选要识别的物体如图所示:

框取红色物块后下面会显示其LAB的最大值和最小值,将thresholds列表修改即可

3.串口通信(发送)

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).

4.寻找色块

 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

        调整好红色阈值后,赋值LAB阈值的参数,并赋值给red_threshold,调用MicroPython函数库中的image.find_blobs()函数,对该色域进行识别。

blob是色块的对象:

blob.rect() 返回这个色块的外框——矩形元组(x, y, w, h),可以直接在image.draw_rectangle中使用。

1.blob.x() 返回色块的外框的x坐标(int),也可以通过blob[0]来获取。

2.blob.y() 返回色块的外框的y坐标(int),也可以通过blob[1]来获取。

3.blob.w() 返回色块的外框的宽度w(int),也可以通过blob[2]来获取。

4.blob.h() 返回色块的外框的高度h(int),也可以通过blob[3]来获取。

5.blob.pixels() 返回色块的像素数量(int),也可以通过blob[4]来获取。

6.blob.cx() 返回色块的外框的中心x坐标(int),也可以通过blob[5]来获取。

7.blob.cy() 返回色块的外框的中心y坐标(int),也可以通过blob[6]来获取。
8.blob.area() 返回色块的外框的面积。应该等于(w * h)

blobs列表里包含很多blob对象,blobs对象就是色块,每个blobs对象包含一个色块的信息。blobs就是很多色块。

三、STM32端

1.串口通信(接收)

usart3.c

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);
}
u8 flag;
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;
				USART_ClearITPendingBit(USART3,USART_IT_RXNE);   //Çå³ýÖжϱêÖ¾
				com_data = USART_ReceiveData(USART3);
				if(RxState==0&&com_data==0x2C)  //0x2cÖ¡Í·
				{
					
					RxState=1;
					RxBuffer1[RxCounter1++]=com_data;
				}
		
				else if(RxState==1&&com_data==0x12)  //0x12Ö¡Í·
				{
					RxState=2;
					RxBuffer1[RxCounter1++]=com_data;
				}
		
				else if(RxState==2)
				{
					RxBuffer1[RxCounter1++]=com_data;

					if(RxCounter1==5 && com_data == 0x5B)       //RxBuffer1½ÓÊÜÂúÁË,½ÓÊÕÊý¾Ý½áÊø
					{
						
						Openmv_X=RxBuffer1[RxCounter1-3];//[2]
						Openmv_Y=RxBuffer1[RxCounter1-2];//[3]
					
						RxCounter1 = 0;
						RxState = 0;	
					}
					else if(RxCounter1 > 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;      //½«´æ·ÅÊý¾ÝÊý×éÇåÁã
						}
				}

		}
		
}

注意:1.K210要与STM32共地。

           2.K210要与STM32波特率一致,一般为9600。

2.舵机(定时器配置)

一个定时器有多个通道,不同通道可以设置不同的CCR寄存器的值来控制不同的舵机角度。

TIM3_PWM_Init(2000-1,720-1); 	//72M/2000/720=50Hz~20ms 
TIM_SetCompare1(TIM3,out_x);
TIM_SetCompare2(TIM3,out_y);	

舵机角度与PWM参数关系的推导:

servo.c函数封装:

#define ARR 1999//TIM3 ARR值
#define MAX_Angle 270//270的舵机

float ccr1,ccr2;
void Servo1_SetAngle(float Angle)
{
	ccr1=ARR*Angle/10/MAX_Angle+ARR/40;
	TIM_SetCompare1(TIM3,ccr1);
}
void Servo2_SetAngle(float Angle)
{
	ccr2=ARR*Angle/10/MAX_Angle+ARR/40;
	TIM_SetCompare2(TIM3,ccr2);
}

由上面推导不难将PWM.c进一步封装可以直接在主函数中输入角度,免去了多次计算TIM_SetCompare1()的参数。

注意:舵机供电电压要求5V,不要使用单片机供电(舵机电流较大),单片机与外接电源(给舵机供电)要共地

3.PID

TIM2_Int_Init(3000-1,840-1);	//30ms 定时器中断采样

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)//ÅжÏÖжÏ
	{	
		implement();
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//ÊÖ¶¯Çå³ýÖжϱê־λ
	}
}

void implement()
{
	int ccrx,ccry;

	if(flag)
	{
		flag = 0;
		
		ccrx = PID_Level(Openmv_X);
		ccry = PID_vertical(Openmv_Y);
		
		out_x = out_x - ccrx;
		out_y = out_y - ccry;

		
		TIM_SetCompare1(TIM3,out_x);
		TIM_SetCompare2(TIM3,out_y);
	}
}

TIM2每30ms根据串口接收到的坐标进行PID运算调整TIM3的CCR使舵机进行稳定追踪

pid.c

#include "pid.h"
PID pid;

void PID_Init()
{
	pid.X_Kp = 0.5;
	pid.X_Ki = 0;
	pid.X_Kd = 0.01;
	pid.X_err = 0;
	pid.X_err_sum = 0;
	pid.X_err_last = 0;
	
	pid.Y_Kp=0.5;
	pid.Y_Ki=0;
	pid.Y_Kd=0.01;
	pid.Y_err=0;
	pid.Y_err_sum=0;
	pid.Y_err_last=0;
}

//ˮƽ·½Ïò
int PID_Level(int x)
{
	int out;
	
	pid.X_err = x-50 ;
	pid.X_err_sum += pid.X_err;
	out = pid.X_Kp*(pid.X_err)+ pid.X_Ki*(pid.X_err_sum)+ pid.X_Kd*(pid.X_err - pid.X_err_last);
	pid.X_err_last = pid.X_err;
	
	return out;
}

//´¹Ö±·½Ïò
int PID_vertical(int y)
{
	int out;
		
	pid.Y_err = y-50 ;
	pid.Y_err_sum += pid.Y_err;
	out = pid.Y_Kp*(pid.Y_err)+ pid.Y_Ki*(pid.Y_err_sum)+ pid.Y_Kd*(pid.Y_err - pid.Y_err_last);
	pid.Y_err_last = pid.Y_err;
	
	return out;
}

pid.h

typedef struct 
{
	float X_Kp;
	float X_Ki;
	float X_Kd;
	float X_err;
	float X_err_sum;
	float X_err_last;	
	
	float Y_Kp;
	float Y_Ki;
	float Y_Kd;
	float Y_err;
	float Y_err_sum;
	float Y_err_last;	
}PID;

void PID_Init(void);
int PID_Level(int x);
int PID_vertical(int y);

  • 6
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经
基于STM32K210小车是一种具备人脸检测和环境监测功能的智能小车。它使用STM32单片机作为控制部分,通过串口与K210模组进行通信。K210模组采用了RISC-V架构,具备1TOPS的算力和低功耗特性。在人脸检测方面,K210模组使用了yolo算法,并将检测结果通过串口传输给STM32单片机。而STM32单片机使用TVOC气体传感器来监测车内空气质量,如甲醛浓度、CO2浓度、PM2.5浓度。当车内环境较差时,STM32单片机控制语音模块进行语音播报,以提醒用户。所有信息会通过OLED屏幕进行显示。此外,该小车还具备监测车内是否有小孩的功能,当车内温度升高到一定阈值时,会进行报警提醒,以避免小孩被遗忘或留在车内的情况发生。\[2\]\[3\] #### 引用[.reference_title] - *1* [stm32+k210视觉小车——来拒去留+多位串口通信](https://blog.csdn.net/weixin_53173179/article/details/126438007)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [B42 - 基于STM32单片机K210人脸识别模块的儿童滞留小车报警系统](https://blog.csdn.net/qq_20467929/article/details/126119688)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值