1回答

0收藏

[项目提交] 汽车大赛+AI手识识别提交贴

2025 DigiKey AI应用创意挑战赛 2025 DigiKey AI应用创意挑战赛 115 人阅读 | 1 人回复 | 2026-01-20

本帖最后由 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摄像头]
五、软件框架图
框架图

软件工作流程图

六、代码


  1. 初始化串口通信
  2. ser = serial.Serial(
  3.    port='/dev/serial0',  # 树莓派默认串口设备
  4.    baudrate=9600,        # 波特率
  5.    timeout=1             # 读取超时时间(秒)
  6. )

  7. 串口通信数据定义
  8. 格式说明:每条指令以0xFF 0xFF 0xFF结尾
  9. book_data = bytes([
  10.     0x70,0x61, 0x67, 0x65, 0x30, 0x2E, 0x70, 0x30, # "page0.p0"
  11.     0x2E,0x70, 0x69, 0x63, 0x3D, 0x30,             # ".pic=0"
  12.     0xFF,0xFF, 0xFF                                 # 结束标志
  13.     ])  # 对应"书本"命令,显示图片0

  14. phone_data = bytes([
  15.     0x70,0x61, 0x67, 0x65, 0x30, 0x2E, 0x70, 0x30, # "page0.p0"
  16.     0x2E,0x70, 0x69, 0x63, 0x3D, 0x31,             # ".pic=1"
  17.     0xFF,0xFF, 0xFF                                 # 结束标志
  18.     ])  # 对应"手机"命令,显示图片1

  19. nobody_data = bytes([
  20.     0x70,0x61, 0x67, 0x65, 0x30, 0x2E, 0x30, 0x30, # "page0.p0"
  21.     0x2E,0x70, 0x69, 0x63, 0x3D, 0x32,             # ".pic=2"
  22.     0xFF,0xFF, 0xFF                                 # 结束标志
  23.     ])  # 对应"无人"状态,显示图片2

  24. DEBOUNCE_TIME = 1 # 防抖时间(秒),手势需稳定1秒才触发动作

  25. #####################################
  26. --- 舵机(Servo)控制模块 ---
  27. #####################################

  28. 初始化舵机,连接到GPIO 12引脚
  29. 脉冲宽度范围:0.5ms-2.5ms,对应-90°到+90°
  30. servo = Servo(12, min_pulse_width=0.5/1000,max_pulse_width=2.5/1000)

  31. def servo_move():
  32.    """
  33.     控制舵机旋转动作
  34.     舵机顺时针(CCW)旋转0.25秒后停止
  35.    """
  36.    servo.value = 1       # 设置为最大值,舵机以最快速度逆时针旋转
  37.    print("CCW fast - 舵机逆时针旋转")
  38.    sleep(0.25)           # 持续旋转0.25秒

  39.    servo.value = 0       # 设置为0,舵机停止
  40.    print("Stopped - 舵机停止")
  41.    sleep(0.2)            # 短暂停顿

  42. #####################################
  43. --- LED控制模块(SPI版本)---
  44. #####################################

  45. LED配置参数
  46. LED_COUNT = 4             # LED灯珠数量
  47. LED_BRIGHTNESS = 0.7      # 正常亮度(范围0.0-1.0)
  48. DIM_BRIGHTNESS = 0.05     # 休眠模式亮度

  49. 初始化SPI总线连接NeoPixel LED
  50. spi_bus = board.SPI()
  51. pixels = neopixel.NeoPixel_SPI(
  52.    spi_bus,               # SPI总线
  53.    LED_COUNT,             # LED数量
  54.    brightness=LED_BRIGHTNESS,  # 初始亮度
  55.    pixel_order=neopixel.GRB,   # LED颜色顺序(Green-Red-Blue)
  56.    auto_write=False       # 手动控制刷新
  57. )

  58. def set_color(r, g, b):
  59.    """
  60.     设置所有LED灯的颜色
  61.     参数:
  62.         r: 红色分量 (0-255)
  63.         g: 绿色分量 (0-255)
  64.         b: 蓝色分量 (0-255)
  65.    """
  66.     for i inrange(LED_COUNT):
  67.        pixels = (r, g, b)
  68.    pixels.show()  # 更新LED显示

  69. def set_brightness(value):
  70.    """
  71.     设置LED亮度
  72.     参数:
  73.        value: 亮度值(0.0-1.0)
  74.    """
  75.    pixels.brightness = value
  76.    pixels.show()

  77. #####################################
  78. --- Mediapipe手势识别配置 ---
  79. #####################################

  80. 初始化Mediapipe手部识别
  81. mp_hands = mp.solutions.hands

  82. 手指关键点索引定义
  83. FINGER_TIPS = [4, 8, 12, 16, 20]   # 大拇指、食指、中指、无名指、小指尖端
  84. FINGER_DIPS = [3, 7, 11, 15, 19]   # 对应手指的中间关节

  85. def is_finger_up(hand, tip, dip):
  86.    """
  87.     判断手指是否伸直
  88.     原理: 指尖的y坐标小于中间关节的y坐标时,手指向上
  89.     注意: Mediapipe坐标系原点在左上角,y轴向下
  90.    """
  91.     returnhand.landmark[tip].y < hand.landmark[dip].y

  92. #####################################
  93. --- 主程序循环 ---
  94. #####################################

  95. 初始化摄像头
  96. cap = cv2.VideoCapture(0)
  97. cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)   # 设置分辨率320x240
  98. cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)  # 降低分辨率提高处理速度

  99. 手势状态变量
  100. last_gesture = None             # 最新检测到的手势
  101. last_executed_gesture = None    # 上一次执行过动作的手势
  102. gesture_start_time = time.time()  # 手势开始时间

  103. current_color = (255, 255, 255)  # 当前LED颜色,初始为白色

  104. 初始化Mediapipe手部识别模型
  105. with mp_hands.Hands(
  106.    max_num_hands=1,              # 最多识别1只手
  107.    min_detection_confidence=0.5,  # 检测置信度阈值
  108.    min_tracking_confidence=0.5    # 跟踪置信度阈值
  109. ) as hands:

  110.     # 主循环
  111.     whileTrue:
  112.         # 读取摄像头帧
  113.         ok,frame = cap.read()
  114.         ifnot ok:
  115.            continue  # 读取失败则跳过

  116.         # 将BGR转换为RGB(Mediapipe需要RGB格式)
  117.         rgb =cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

  118.         # 手势识别处理
  119.        result = hands.process(rgb)

  120.        gesture = None      # 当前检测到的手势
  121.        hand_present = False  # 是否有手部被检测到

  122.         #-------------------------
  123.         # 1. 手势检测逻辑
  124.         #-------------------------
  125.         ifresult.multi_hand_landmarks:  # 检测到手部
  126.            hand_present = True
  127.            hand = result.multi_hand_landmarks[0] # 只处理第一只手

  128.             #计算除大拇指外的手指伸直数量
  129.            fingers_up = 0
  130.            for tip, dip in zip(FINGER_TIPS[1:], FINGER_DIPS[1:]):
  131.                if is_finger_up(hand, tip, dip):
  132.                    fingers_up += 1

  133.             #手势映射:
  134.             #1根手指伸直-> 手势1
  135.             #2根手指伸直-> 手势2
  136.            if fingers_up == 1:
  137.                gesture = 1
  138.            elif fingers_up == 2:
  139.                gesture = 2
  140.             #其他手指数量视为无效手势

  141.         #-------------------------
  142.         # 2. 无手状态处理(休眠模式)
  143.         #-------------------------
  144.         ifnot hand_present:
  145.            set_brightness(DIM_BRIGHTNESS)  # 降低亮度进入休眠
  146.            last_gesture = None
  147.            last_executed_gesture = None
  148.            ser.write(nobody_data)  # 发送无人状态到串口设备
  149.            continue  # 跳过**处理

  150.         #-------------------------
  151.         # 3. 手部刚出现处理(唤醒模式)
  152.         #-------------------------
  153.         iflast_gesture is None:  # 手部刚出现
  154.            set_brightness(LED_BRIGHTNESS)  # 恢复亮度

  155.             #根据手势设置不同颜色
  156.            if gesture == 1:
  157.                current_color = (255, 150, 80)  # 橙色
  158.                set_color(*current_color)
  159.            elif gesture == 2:
  160.                current_color = (100, 150, 255)  #蓝色
  161.                set_color(*current_color)
  162.            else:
  163.                # 无效手势保持原颜色
  164.                set_color(*current_color)

  165.             #更新手势状态和时间
  166.            last_gesture = gesture
  167.            gesture_start_time = time.time()
  168.            continue

  169.         #-------------------------
  170.         # 4. 手势变化检测
  171.         #-------------------------
  172.         ifgesture != last_gesture:  # 手势发生变化
  173.            last_gesture = gesture
  174.            gesture_start_time = time.time() # 重置计时器
  175.            print(f"检测到手势变化 →{gesture}")
  176.            continue

  177.         #-------------------------
  178.         # 5. 手势稳定触发(防抖处理)
  179.         #-------------------------
  180.         iftime.time() - gesture_start_time >= DEBOUNCE_TIME:
  181.             #如果与上次执行的手势相同,不重复执行
  182.            if gesture == last_executed_gesture:
  183.                continue

  184.             #记录本次执行的手势
  185.            last_executed_gesture = gesture

  186.             #根据手势执行对应动作
  187.            if gesture == 1:
  188.                print("执行:手势1动作(书本)")
  189.                ser.write(book_data)  # 发送书本命令
  190.                current_color = (255, 150, 80)  # 橙色
  191.                set_color(*current_color)
  192.                # 在新线程中控制舵机,避免阻塞主循环
  193.                t = threading.Thread(target=servo_move, daemon=True)
  194.                t.start()

  195.            elif gesture == 2:
  196.                print("执行:手势2动作(手机)")
  197.                ser.write(phone_data)  # 发送手机命令
  198.                current_color = (100, 150, 255)  #蓝色
  199.                set_color(*current_color)
  200.            else:
  201.                continue  # 无效手势不执行
  202. 释放摄像头资源
  203. cap.release()
  204. 释放舵机资源
  205. servo.angle = None
复制代码


七、实物连接图



八、视频
PI5手识识别_哔哩哔哩_bilibili
分享到:
回复

使用道具 举报

回答|共 1 个

倒序浏览

沙发

eefocus_3914144

发表于 6 天前 | 只看该作者

牛呀,太强了!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /2 下一条