本帖最后由 meiyao 于 2026-1-20 14:06 编辑
一、功能 使用摄像头实现AI手识检测识别,使用树莓派摄像头捕捉实时视频流。对视频流进行处理,然后再进行对比,是否是正确的手势,使用背景减除算法去除背景干扰,利用OpenCV的手势识别算法检测和跟踪手势,根据手势识别结果进行相应的操作控制灯光,或者对应的执行设备,舵机等装置。 进行物体识别,或者是进行场景灯光调节,当正在看书时,出现阅读模式,点亮阅读灯,如果正在玩手机时,点切换为游戏模式,高亮白光灯。
二、模式介绍 1、手识:1,2,3 2、模式:无人模式,阅读模式,手机模式 3、灯光模式:白光,蓝光,**光
设置开机自动启动 上电后等几十秒就可以手势操作了 对准摄像头 手正对摄像头30厘米左右就行 这个识别范围比较大 手势1 读书模式 暖色灯 舵机动 手势2 手机模式 冷色灯 舵机不动 其他手势 保持灯的亮度和颜色 无手势 无人模式 灯光会暗
三、主要器件 树莓派5-- PI5(4核2.4GHz Cortex-A76)-- 支持4K视频解码、硬件PWM、原生USB3.0(高速摄像头数据传输) 舵机--SG90(9g微型舵机)-- 扭矩2.5kg·cm(5V供电)、响应速度0.12s/60°、控制信号周期20ms(50Hz) 灯--WS2812(5050 RGB LED)--单线通信(NeoPixel协议)、24位真彩色(1677万色)、支持级联(最大100颗) 串口屏--TJC804X550_011(4.3寸)--分辨率480×272、TJC自定义协议、支持触摸交互、预留UART接口(波特率115200) 摄像头--九目4K USB免驱动--分辨率3840×2160@30FPS、自动对焦、UVC协议兼容(Linux原生支持)
舵机 物理引脚:GPIO 12 树莓派40针引脚:引脚32(GPIO 12) 功能:PWM信号输出,控制舵机角度 电压:3.3V PWM信号 串口屏 树莓派默认串口引脚: TXD (发送) GPIO14 引脚8 RXD (接收) GPIO15 引脚10 GND 引脚6(或任何GND)
四、连接原理图 graph LR PI5[树莓派5] -->|GPIO18(PWM)| SG90[舵机] PI5-->|GPIO12(PWM)| WS2812[灯带] PI5-->|UART(TX/RX)| TJC[串口屏] PI5-->|USB3.0| Camera[4K摄像头] 五、软件框架图 框架图
软件工作流程图
六、代码
- 初始化串口通信
- ser = serial.Serial(
- port='/dev/serial0', # 树莓派默认串口设备
- baudrate=9600, # 波特率
- timeout=1 # 读取超时时间(秒)
- )
- 串口通信数据定义
- 格式说明:每条指令以0xFF 0xFF 0xFF结尾
- book_data = bytes([
- 0x70,0x61, 0x67, 0x65, 0x30, 0x2E, 0x70, 0x30, # "page0.p0"
- 0x2E,0x70, 0x69, 0x63, 0x3D, 0x30, # ".pic=0"
- 0xFF,0xFF, 0xFF # 结束标志
- ]) # 对应"书本"命令,显示图片0
- phone_data = bytes([
- 0x70,0x61, 0x67, 0x65, 0x30, 0x2E, 0x70, 0x30, # "page0.p0"
- 0x2E,0x70, 0x69, 0x63, 0x3D, 0x31, # ".pic=1"
- 0xFF,0xFF, 0xFF # 结束标志
- ]) # 对应"手机"命令,显示图片1
- nobody_data = bytes([
- 0x70,0x61, 0x67, 0x65, 0x30, 0x2E, 0x30, 0x30, # "page0.p0"
- 0x2E,0x70, 0x69, 0x63, 0x3D, 0x32, # ".pic=2"
- 0xFF,0xFF, 0xFF # 结束标志
- ]) # 对应"无人"状态,显示图片2
- DEBOUNCE_TIME = 1 # 防抖时间(秒),手势需稳定1秒才触发动作
- #####################################
- --- 舵机(Servo)控制模块 ---
- #####################################
- 初始化舵机,连接到GPIO 12引脚
- 脉冲宽度范围:0.5ms-2.5ms,对应-90°到+90°
- servo = Servo(12, min_pulse_width=0.5/1000,max_pulse_width=2.5/1000)
- def servo_move():
- """
- 控制舵机旋转动作
- 舵机顺时针(CCW)旋转0.25秒后停止
- """
- servo.value = 1 # 设置为最大值,舵机以最快速度逆时针旋转
- print("CCW fast - 舵机逆时针旋转")
- sleep(0.25) # 持续旋转0.25秒
- servo.value = 0 # 设置为0,舵机停止
- print("Stopped - 舵机停止")
- sleep(0.2) # 短暂停顿
- #####################################
- --- LED控制模块(SPI版本)---
- #####################################
- LED配置参数
- LED_COUNT = 4 # LED灯珠数量
- LED_BRIGHTNESS = 0.7 # 正常亮度(范围0.0-1.0)
- DIM_BRIGHTNESS = 0.05 # 休眠模式亮度
- 初始化SPI总线连接NeoPixel LED
- spi_bus = board.SPI()
- pixels = neopixel.NeoPixel_SPI(
- spi_bus, # SPI总线
- LED_COUNT, # LED数量
- brightness=LED_BRIGHTNESS, # 初始亮度
- pixel_order=neopixel.GRB, # LED颜色顺序(Green-Red-Blue)
- auto_write=False # 手动控制刷新
- )
- def set_color(r, g, b):
- """
- 设置所有LED灯的颜色
- 参数:
- r: 红色分量 (0-255)
- g: 绿色分量 (0-255)
- b: 蓝色分量 (0-255)
- """
- for i inrange(LED_COUNT):
- pixels = (r, g, b)
- pixels.show() # 更新LED显示
- def set_brightness(value):
- """
- 设置LED亮度
- 参数:
- value: 亮度值(0.0-1.0)
- """
- pixels.brightness = value
- pixels.show()
- #####################################
- --- Mediapipe手势识别配置 ---
- #####################################
- 初始化Mediapipe手部识别
- mp_hands = mp.solutions.hands
- 手指关键点索引定义
- FINGER_TIPS = [4, 8, 12, 16, 20] # 大拇指、食指、中指、无名指、小指尖端
- FINGER_DIPS = [3, 7, 11, 15, 19] # 对应手指的中间关节
- def is_finger_up(hand, tip, dip):
- """
- 判断手指是否伸直
- 原理: 指尖的y坐标小于中间关节的y坐标时,手指向上
- 注意: Mediapipe坐标系原点在左上角,y轴向下
- """
- returnhand.landmark[tip].y < hand.landmark[dip].y
- #####################################
- --- 主程序循环 ---
- #####################################
- 初始化摄像头
- cap = cv2.VideoCapture(0)
- cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) # 设置分辨率320x240
- cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) # 降低分辨率提高处理速度
- 手势状态变量
- last_gesture = None # 最新检测到的手势
- last_executed_gesture = None # 上一次执行过动作的手势
- gesture_start_time = time.time() # 手势开始时间
- current_color = (255, 255, 255) # 当前LED颜色,初始为白色
- 初始化Mediapipe手部识别模型
- with mp_hands.Hands(
- max_num_hands=1, # 最多识别1只手
- min_detection_confidence=0.5, # 检测置信度阈值
- min_tracking_confidence=0.5 # 跟踪置信度阈值
- ) as hands:
- # 主循环
- whileTrue:
- # 读取摄像头帧
- ok,frame = cap.read()
- ifnot ok:
- continue # 读取失败则跳过
- # 将BGR转换为RGB(Mediapipe需要RGB格式)
- rgb =cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
- # 手势识别处理
- result = hands.process(rgb)
- gesture = None # 当前检测到的手势
- hand_present = False # 是否有手部被检测到
- #-------------------------
- # 1. 手势检测逻辑
- #-------------------------
- ifresult.multi_hand_landmarks: # 检测到手部
- hand_present = True
- hand = result.multi_hand_landmarks[0] # 只处理第一只手
- #计算除大拇指外的手指伸直数量
- fingers_up = 0
- for tip, dip in zip(FINGER_TIPS[1:], FINGER_DIPS[1:]):
- if is_finger_up(hand, tip, dip):
- fingers_up += 1
- #手势映射:
- #1根手指伸直-> 手势1
- #2根手指伸直-> 手势2
- if fingers_up == 1:
- gesture = 1
- elif fingers_up == 2:
- gesture = 2
- #其他手指数量视为无效手势
- #-------------------------
- # 2. 无手状态处理(休眠模式)
- #-------------------------
- ifnot hand_present:
- set_brightness(DIM_BRIGHTNESS) # 降低亮度进入休眠
- last_gesture = None
- last_executed_gesture = None
- ser.write(nobody_data) # 发送无人状态到串口设备
- continue # 跳过**处理
- #-------------------------
- # 3. 手部刚出现处理(唤醒模式)
- #-------------------------
- iflast_gesture is None: # 手部刚出现
- set_brightness(LED_BRIGHTNESS) # 恢复亮度
- #根据手势设置不同颜色
- if gesture == 1:
- current_color = (255, 150, 80) # 橙色
- set_color(*current_color)
- elif gesture == 2:
- current_color = (100, 150, 255) #蓝色
- set_color(*current_color)
- else:
- # 无效手势保持原颜色
- set_color(*current_color)
- #更新手势状态和时间
- last_gesture = gesture
- gesture_start_time = time.time()
- continue
- #-------------------------
- # 4. 手势变化检测
- #-------------------------
- ifgesture != last_gesture: # 手势发生变化
- last_gesture = gesture
- gesture_start_time = time.time() # 重置计时器
- print(f"检测到手势变化 →{gesture}")
- continue
- #-------------------------
- # 5. 手势稳定触发(防抖处理)
- #-------------------------
- iftime.time() - gesture_start_time >= DEBOUNCE_TIME:
- #如果与上次执行的手势相同,不重复执行
- if gesture == last_executed_gesture:
- continue
- #记录本次执行的手势
- last_executed_gesture = gesture
- #根据手势执行对应动作
- if gesture == 1:
- print("执行:手势1动作(书本)")
- ser.write(book_data) # 发送书本命令
- current_color = (255, 150, 80) # 橙色
- set_color(*current_color)
- # 在新线程中控制舵机,避免阻塞主循环
- t = threading.Thread(target=servo_move, daemon=True)
- t.start()
- elif gesture == 2:
- print("执行:手势2动作(手机)")
- ser.write(phone_data) # 发送手机命令
- current_color = (100, 150, 255) #蓝色
- set_color(*current_color)
- else:
- continue # 无效手势不执行
- 释放摄像头资源
- cap.release()
- 释放舵机资源
- servo.angle = None
复制代码
七、实物连接图
八、视频 PI5手识识别_哔哩哔哩_bilibili
|