目录
一、预想功能
制作基于沁恒CH32V103最小系统和openmv的颜色追踪小车,他可以识别到所需要的颜色进而追踪所需颜色。
二、系统框架
三、硬件介绍
openmv:由openmv识别所需的色块,并且把所识别的色块中心xy坐标以数据包的形式发送给沁恒CH32V103最小系统。
沁恒CH32V103最小系统:接收openmv所发送过来的数据包,经过对xy坐标的判断和计算去通过pwm控制电机。
l298n:电机驱动模块,接收沁恒CH32V103最小系统的pwm来控制两边的两个电机。
四、软件介绍
openmv程序
import sensor, image, time, math
from pyb import UART
import json
import ustruct
blue_threshold = (25, 67, -37, 26, -63, -26)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
clock = time.clock()
uart = UART(3,115200) #定义串口3变量
uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
def find_max(blobs): #定义寻找色块面积最大的函数
max_size=0
for blob in blobs:
if blob.pixels() > max_size:
max_blob=blob
max_size = blob.pixels()
return max_blob
def sending_data(cx,cy):
global uart;
#frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
#data = bytearray(frame)
data = ustruct.pack("<bbhhb", #格式为俩个字符俩个短整型(2字节)
0x2C, #帧头1
0x12, #帧头2
int(cx), # up sample by 4 #数据1
int(cy), # up sample by 4 #数据2
0x5B)
uart.write(data); #必须要传入一个字节数组
def recive_data():
global uart
if uart.any():
tmp_data = uart.readline();
print(tmp_data)
#mainloop
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
# pixels_threshold=100, area_threshold=100
blobs = img.find_blobs([blue_threshold], area_threshold=150);
cx=0;cy=0;
if blobs:
#如果找到了目标颜色
max_b = find_max(blobs);
# Draw a rect around the blob.
img.draw_rectangle(max_b[0:4]) # rect
#用矩形标记出目标颜色区域
img.draw_cross(max_b[5], max_b[6]) # cx, cy
img.draw_cross(160, 120) # 在中心点画标记
#在目标颜色区域的中心画十字形标记
cx=max_b[5];
cy=max_b[6];
img.draw_line((160,120,cx,cy), color=(127));
#img.draw_string(160,120, "(%d, %d)"%(160,120), color=(127));
img.draw_string(cx, cy, "(%d, %d)"%(cx,cy), color=(127));
sending_data(cx,cy); #发送点位坐标
recive_data();
print(cx,cy)
#time.sleep(1000)
#pack各字母对应类型
#x pad byte no value 1
#c char string of length 1 1
#b signed char integer 1
#B unsigned char integer 1
#? _Bool bool 1
#h short integer 2
#H unsigned short integer 2
#i int integer 4
#I unsigned int integer or long 4
#l long integer 4
#L unsigned long long 4
#q long long long 8
#Q unsilong long long 8
#f float float 4
#d double float 8
#s char[] string 1
#p char[] string 1
#P void * long
沁恒CH32V103最小系统
串口接收函数
#include "Uart.H"
#include "Motor.H"
//u8 x_coordinate;
u8 data_Correctness;
u16 openmv_data[7];
u8 number=0;
void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
//========================================================================
// 函数: void USARTx_CFG(void)
// 描述: 串口初始化函数
// 参数: 无
// 返回: 无
// 版本:
// 日期:
// 备注:USART2 TX-->A.2 RX-->A.3
//========================================================================
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
USART_DeInit(USART2);
/* USART2 TX-->A.2 RX-->A.3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置PA2为复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置PA3为浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200; //设置串口波特率为115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送和接收模式
USART_Init(USART2, &USART_InitStructure); //初始化串口
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
USART_Cmd(USART2, ENABLE); //使能串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启中断
}
//========================================================================
// 函数: void USART2_IRQHandler(void)
// 描述: 串口2中断函数
// 参数: 无
// 返回: 无
// 版本:
// 日期:
// 备注:接收openmv传来的数据
//========================================================================
void USART2_IRQHandler(void)
{
u8 temp;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
temp = USART_ReceiveData(USART2); //接收数据
Openmv_Receive_Data(temp);
}
}
void Openmv_Receive_Data(u16 data)//接收Openmv传过来的数据
{
static u8 state = 0;
if(state==0&&data==0x2C)
{
state=1;
openmv_data[number++]=data;
}
else if(state==1&&data==18)
{
state=2;
openmv_data[number++]=data;
}
else if(state==2)
{
openmv_data[number++]=data;
if(number>19||data == 0x5B) state=3; //the last of char is openmv[19]
}
else if(state==3) //state == 3 检测是否接受到结束标志
{
if(openmv_data[number-1] == 0x5B)
{
state = 0;
data_Correctness = 1;
USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);
}
else //wrong thing
{
state = 0;
number=0;
}
}
else //wrong thing
{
state = 0;
number=0;
}
}
void USART2_Rx_Task(void)
{
u16 posX,posY;
if(data_Correctness == 1)
{
posX = openmv_data[3]<<8 | openmv_data[2];
posY = openmv_data[5]<<8 | openmv_data[4];
data_Correctness = 0;
number = 0;
car_control(posX,posY);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
}
}
pwm初始化
#include "pwm.h"
//========================================================================
// 函数: TIM1_PWMOut_Init( u16 arr, u16 psc, u16 ccp )
// 描述: pwm初始化函数
// 参数: arr 周期,psc 预分频器值,ccp 占空比
// 返回: 无
// 版本:
// 日期:
// 备注:
//========================================================================
void TIM1_PWMOut_Init( u16 arr, u16 psc, u16 ccp )
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );//使能GPIOA外设时钟和TIM1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9; //配置PA8引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置为复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置输出速度:50MHz
GPIO_Init( GPIOA, &GPIO_InitStructure ); //GPIO初始化
TIM_TimeBaseInitStructure.TIM_Period = arr; //指定下次更新事件时要加载到活动自动重新加载寄存器中的周期值。
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //指定用于划分TIM时钟的预分频器值。
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频因子
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM计数模式,向上计数模式
TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //指定TIM模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//指定TIM输出比较状态,即使能比较输出
TIM_OCInitStructure.TIM_Pulse = ccp; //指定要加载到捕获比较寄存器中的脉冲值。
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //指定输出极性。
TIM_OC1Init( TIM1, &TIM_OCInitStructure ); //根据TIM_OCInitStruct中指定的参数初始化TIM1 Channel1。
TIM_OC2Init( TIM1, &TIM_OCInitStructure );
TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable ); //使能CCR1上的TIM1外设预加载寄存器
TIM_OC2PreloadConfig( TIM1, TIM_OCPreload_Disable );
TIM_CtrlPWMOutputs(TIM1, ENABLE ); //启用定时器1PWM输出
TIM_ARRPreloadConfig( TIM1, ENABLE ); //使能ARR上TIM1外设预加载寄存器
TIM_Cmd( TIM1, ENABLE ); //使能TIM1
}
小车位置判断和控制
#include "Motor.H"
u8 motor;
u8 left = 0;
u8 right = 1;
u8 run = 2;
void car_control(u16 x_coordinate,u16 y_coordinate)
{
if(x_coordinate < 50 && x_coordinate != 0)
{
motor = left;
}
else if (x_coordinate >= 200 && x_coordinate != 0)
{
motor = right;
}
else if (x_coordinate > 50 && x_coordinate < 200 && y_coordinate >0)
{
motor = run;
}
switch(motor)
{
case 0:TIM_SetCompare1(TIM1,400);
TIM_SetCompare2(TIM1,100);
break;
case 1:TIM_SetCompare1(TIM1,100);
TIM_SetCompare2(TIM1,400);
break;
case 2:TIM_SetCompare1(TIM1,x_coordinate/2-y_coordinate/4);
TIM_SetCompare2(TIM1,x_coordinate/2+y_coordinate/4);
break;
}
}
五、实际效果
openmv追踪小车
学习引用文章:
(4条消息) 使用openMV3与stm32进行通讯_qq_43243338的博客-CSDN博客_openmv与stm32通信