[树莓派课设]基于树莓派的画板(opencv+mediapipe+stm32+摇杆)

系统介绍

基于树莓派的画板通过opencv+mediapipe进行手势识别控制做画,还可以通过摇杆完成做画,由于树莓派的gpio不支持ADC,所以stm32完成摇杆的ADC转换通过串口将据传输到树莓派。

功能介绍

[树莓派课设]基于树莓派的画板(opencv+mediapipe+stm32+摇杆)_第1张图片
右手控制,右手食指做出一的形状控制画布中的点移动,当食指和中指一起并拢竖出来时开始做画,可以移动到左边更换颜色,在中间两条线可以控制画的粗细,摇杆也同样可以控制,按下摇杆中间开始做画。
有不懂可以私信我发讲解视频。

硬件引脚连接

[树莓派课设]基于树莓派的画板(opencv+mediapipe+stm32+摇杆)_第2张图片

摇杆 STM32 树莓派
GND GND GND
5V 5V ×
SW PA6 ×
VRX PA5 ×
VRY PA4 ×
× PA3 GPIO14
× PA2 GPIO15

×代表不用连接

树莓派端的代码

STM32端采用UART传输,协议以0XA0开头,然后两位为VRX进行ADC的高八位和第八位,再两位为VRY进行ADC的高八位和第八位,再一位为SW,然后以0XB0结束。

Uart.py

import serial
import time
#open serial

def Recv_Uart():
    ser = serial.Serial("/dev/ttyAMA0", 115200)#set up serial
    # 获得接收缓冲区字符

    while True:
        recv = ser.read()  #树莓派串口接收数据
        if recv ==b'\xa0':
            break
    data=list(ser.read(6))
    ADC_X=data[0]*256+data[1]
    ADC_Y=data[2]*256+data[3]
    SW=data[4]
    if data[5]==0xb0 and ADC_X<=4095 and ADC_Y<=4095:
        return ADC_X,ADC_Y,SW

def Get_data():
    all_data_x=[]
    all_data_y=[]
    key=0
    while True:
        data=Recv_Uart()
        if data:
            key=data[2]
            all_data_x.append(data[0])
            all_data_y.append(data[1])
            if len(all_data_x)==10:
                averge_x=int(sum(all_data_x)/10)
                averge_y=int(sum(all_data_y)/10)
                all_data_x=[]
                all_data_y=[]
                return averge_x,averge_y,key
     
             
if __name__ == '__main__':
    try:
        all_data_x=[]
        all_data_y=[]
        while True:
            
            #for i in range(Recv_Uart())
            data=Get_data()
            if data:
                print(data)
                    #print(averge_x,averge_y)
    except KeyboardInterrupt:
        if ser != None:
            ser.close()

Draw.py

import cv2
import mediapipe as mp
import numpy as np
from Uart import Get_data,Recv_Uart

def detect():
    cap = cv2.VideoCapture(0)
    w = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    h = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    mp_drawing = mp.solutions.drawing_utils
    mp_hands = mp.solutions.hands
    hands = mp_hands.Hands(
        static_image_mode=False,
        max_num_hands=1,
        min_detection_confidence=0.75,
        min_tracking_confidence=0.75)
    canvas=np.zeros([int(h),int(w),3],dtype=np.uint8)
    canvas+=255
    Blue=[255,0,0]
    Green=[0,255,0]
    Red=[0,0,255]
    Black=[0,0,0]
    Eraser=[255,255,255]
    TraceBack=[100, 100, 100]
    canvas[:95,0:100,:]=Blue
    canvas[95:191, 0:100, :] = Green
    canvas[191:287, 0:100, :] = Red
    canvas[287:383, 0:100, :] = Black
    canvas[383:479, 0:100, :] = Eraser
    canvas[:, 100:102, :] =Black
    canvas[:, 130:132, :] = Black
    Color=Black
    Draw_Width=3
    x0=y0=300
    flag=0
    Trace_Width=3
    while True:
        ret, fimg = cap.read()
        fimg = cv2.cvtColor(fimg, cv2.COLOR_BGR2RGB)
        fimg = cv2.flip(fimg, 1)
        results = hands.process(fimg)
        frame = cv2.cvtColor(fimg, cv2.COLOR_RGB2BGR)
        img = np.concatenate((frame, canvas), axis=1)
        if results.multi_hand_landmarks:
            if results.multi_handedness:
                for hand_label in results.multi_handedness:
                    hand_jugg = str(hand_label).split('"')[1]
            if results.multi_hand_landmarks and hand_jugg == "Right":
                for hand_landmarks in results.multi_hand_landmarks:
                    #mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
                    x0, y0 = int(hand_landmarks.landmark[8].x * frame.shape[1]), int(
                        hand_landmarks.landmark[8].y * frame.shape[0])
                    x1, y1 = int(hand_landmarks.landmark[12].x * frame.shape[1]), int(
                        hand_landmarks.landmark[12].y * frame.shape[0])
                    distance = (x1 - x0) ** 2 + (y1 - y0) ** 2
                if x0 >= 132 :
                    if distance <= 2000 and y1<y0:
                        canvas[y0 - Draw_Width:y0 + Draw_Width, x0 - Draw_Width:x0 + Draw_Width, :] = Color
                        cv2.imshow('Draw', canvas)
                        #img = np.concatenate((frame, canvas), axis=1)
                        #cv2.imshow('Draw', img)
                    else:
                        Last = np.copy(canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :])
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = TraceBack
                        #img = np.concatenate((frame, canvas), axis=1)
                        cv2.imshow('Draw', canvas)
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = Last
                else:
                    if distance <= 2000:
                        if x0 <= 100:
                            if y0 <= 95:
                                Color = Blue
                            elif y0 <= 191:
                                Color = Green
                            elif y0 <= 287:
                                Color = Red
                            elif y0 <= 383:
                                Color = Black
                            else:
                                Color = Eraser
                        else:
                            Draw_Width = int(y0 * 0.0104) + 1
                    else:
                        Last = np.copy(canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :])
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = TraceBack
                        #img = np.concatenate((frame, canvas), axis=1)
                        cv2.imshow('Draw', canvas)
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = Last
        else:
            data=Recv_Uart()
            if data :
                if data[2]==0 :
                    if flag==0:
                        flag=1
                    else:
                        flag=0 
                dx=int((data[0]-3010)*0.005)
                dy=int((data[1]-2995)*0.002)
                if dx<0:
                    dx=dx//3
                if dy>0:
                    dy=int(dy*2.5)
                x0=x0+dx
                y0=y0+dy
                if x0<0:
                    x0=0
                if y0<0:
                    y0=0
                if x0>=640:
                    x0=640
                if y0>=480:
                    y0=480
                if x0 >= 132 :
                    if flag==0:
                        Last = np.copy(canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :])
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = TraceBack
                        #img = np.concatenate((frame, canvas), axis=1)
                        cv2.imshow('Draw', canvas)
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = Last
                    elif flag==1:
                        canvas[y0 - Draw_Width:y0 + Draw_Width, x0 - Draw_Width:x0 + Draw_Width, :] = Color
                        cv2.imshow('Draw', canvas)
                else:
                    if flag==1:
                        if x0 <= 100:
                            if y0 <= 95:
                                Color = Blue
                            elif y0 <= 191:
                                Color = Green
                            elif y0 <= 287:
                                Color = Red
                            elif y0 <= 383:
                                Color = Black
                            else:
                                Color = Eraser
                        else:
                            Draw_Width = int(y0 * 0.0104) + 1
                    else:
                        Last = np.copy(canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :])
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = TraceBack
                        #img = np.concatenate((frame, canvas), axis=1)
                        cv2.imshow('Draw', canvas)
                        canvas[y0 - Trace_Width:y0 + Trace_Width, x0 - Trace_Width:x0 + Trace_Width, :] = Last                   
        #cv2.imshow('Draw', canvas)
        if cv2.waitKey(1) & 0xFF == 27:
            break
    cap.release()
    cv2.destroyAllWindows()

detect()

STM32端的代码

ADC.c

#include "ADC.h"
void My_ADC_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 启用ADC1时钟

    ADC_InitTypeDef ADC_InitStruct;
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 独立模式
    ADC_InitStruct.ADC_ScanConvMode = DISABLE; // 单通道模式
    ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; // 连续转换模式
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 无外部触发
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐
    ADC_InitStruct.ADC_NbrOfChannel = 2; // 两个通道

    ADC_Init(ADC1, &ADC_InitStruct);
    ADC_Cmd(ADC1, ENABLE); // 启动ADC1

    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_1Cycles5); // 通道4,采样时间28.5周期
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_1Cycles5); // 通道5,采样时间28.5周期
}

void ADC_GPIO_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 启用GPIOA时钟

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; // 引脚PA4和PA5
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void KEY_GPIO_Init(void)
{
    // 初始化GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    // 配置PA6引脚为输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);	
}

ADC.h

#ifndef __ADC_h__
#define __ADC_h__

#include "system.h"

void My_ADC_Init(void);
void ADC_GPIO_Init(void);
void KEY_GPIO_Init(void);

#endif


UART.c

#include "uart.h"


void USART2_Init(void)
{
    // 使能USART2和GPIOA的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 配置USART2的引脚
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; // USART2_TX
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 配置USART2
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 115200; // 波特率为115200
    USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位数据位
    USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1位停止位
    USART_InitStruct.USART_Parity = USART_Parity_No; // 无奇偶校验
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控制
    USART_InitStruct.USART_Mode = USART_Mode_Tx; // 仅发送模式
    USART_Init(USART2, &USART_InitStruct);

    // 使能USART2
    USART_Cmd(USART2, ENABLE);
}

void USART2_SendByte(uint8_t byte)
{
    // 等待发送缓冲区为空
    while (!USART_GetFlagStatus(USART2, USART_FLAG_TXE));

    // 将数据发送到USART2
    USART_SendData(USART2, byte);
}

void uar1_init(u32 baudrate)
{
	GPIO_InitTypeDef gpiotype;
	NVIC_InitTypeDef nvictype;
	USART_InitTypeDef uarttype;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);
	gpiotype.GPIO_Speed = GPIO_Speed_50MHz;		
	
  gpiotype.GPIO_Pin = GPIO_Pin_9;				
  gpiotype.GPIO_Mode = GPIO_Mode_AF_PP; 	
	GPIO_Init(GPIOA,&gpiotype);	
  gpiotype.GPIO_Pin = GPIO_Pin_10;				
  gpiotype.GPIO_Mode = GPIO_Mode_IN_FLOATING; 	
	GPIO_Init(GPIOA,&gpiotype);	
	
	uarttype.USART_BaudRate=baudrate;
	uarttype.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	uarttype.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
	uarttype.USART_Parity=USART_Parity_No;
	uarttype.USART_StopBits=USART_StopBits_1;
	uarttype.USART_WordLength=USART_WordLength_8b;
	USART_Init(USART1,&uarttype);
	
	nvictype.NVIC_IRQChannel=USART1_IRQn;
	nvictype.NVIC_IRQChannelCmd=ENABLE;
	nvictype.NVIC_IRQChannelPreemptionPriority=2;
	nvictype.NVIC_IRQChannelSubPriority=2;
	NVIC_Init(&nvictype);
	NVIC_PriorityGroupConfig(2);
	USART_Cmd(USART1,ENABLE);
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
}


int fputc(int ch, FILE *f)
{
	USART_SendData(USART1, (uint8_t) ch);

	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	
   
    return ch;
}


UART.h

#ifndef __uart_H__
#define __uart_H__

#include 
#include "system.h"

void USART2_Init(void);
void uar1_init(u32 baudrate);
void USART2_SendByte(uint8_t byte);


#endif


system.c

#include "system.h"


void system_init(void)
{
	USART2_Init();
	uar1_init(115200);
	ADC_GPIO_Init();
	My_ADC_Init();
	KEY_GPIO_Init();
}

system.h

#ifndef __system_H__
#define __system_H__

#include "stm32f10x.h"

#include "uart.h"
#include "adc.h"

void system_init(void);

#endif

main.c

#include "system.h"


int main(void)
{
	uint16_t ADC_X=0,ADC_Y=0;
	system_init();
	uint8_t data[7]={0xa0,0,0,0,0,0,0xb0};
	uint8_t i=0;
	while(1)
	{
		ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_28Cycles5); // 配置通道4
		ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 启动ADC转换
		while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待转换完成

		ADC_X = ADC_GetConversionValue(ADC1); // 获取PA4转换结果,12位精度

		// 处理ADC值...

		ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_28Cycles5); // 配置通道5
		ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 启动ADC转换
		while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待转换完成

		ADC_Y = ADC_GetConversionValue(ADC1); // 获取PA5转换结果,12位精度
//		printf("%d,%d\r\n",ADC_X,ADC_Y);
//		ADC_X_H=ADC_X/256;
//		ADC_X_L=ADC_X%256;
//		ADC_Y_H=ADC_Y/256;
//		ADC_Y_L=ADC_Y%256;
		data[1]=ADC_X/256;
		data[2]=ADC_X%256;
		data[3]=ADC_Y/256;
		data[4]=ADC_Y%256;
		//USART2_SendByte(0x1a);
		if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == Bit_SET) data[5]=1;
		else data[5]=0;
		//printf("%d\r\n",data[5]);
		for(i=0;i<7;i++)
		{
			//printf("%d\r\n",data[i]);
			USART2_SendByte(data[i]);
		}
	};
}


工程文件

点击下载工程文件

你可能感兴趣的:(opencv,stm32,人工智能,嵌入式硬件,python)