基于树莓派的画板通过opencv+mediapipe进行手势识别控制做画,还可以通过摇杆完成做画,由于树莓派的gpio不支持ADC,所以stm32完成摇杆的ADC转换通过串口将据传输到树莓派。
右手控制,右手食指做出一的形状控制画布中的点移动,当食指和中指一起并拢竖出来时开始做画,可以移动到左边更换颜色,在中间两条线可以控制画的粗细,摇杆也同样可以控制,按下摇杆中间开始做画。
有不懂可以私信我发讲解视频。
摇杆 | 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结束。
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()
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()
#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);
}
#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
#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;
}
#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
#include "system.h"
void system_init(void)
{
USART2_Init();
uar1_init(115200);
ADC_GPIO_Init();
My_ADC_Init();
KEY_GPIO_Init();
}
#ifndef __system_H__
#define __system_H__
#include "stm32f10x.h"
#include "uart.h"
#include "adc.h"
void system_init(void);
#endif
#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]);
}
};
}
点击下载工程文件