一、功能介绍
基于树莓派平台,集成摄像头模块实现实时视频流捕捉手势识别,通过手势识别算法完成动态手势检测与跟踪,最终根据识别结果控制灯光模式或执行设备联动。
硬件配置方面,采用树莓派官方USB摄像头,通过40针GPIO接口连接,摄像头捕捉30厘米范围内的手势动作,支持1080P@30fps视频流处理,确保手势识别的灵敏度与准确性。软件实现层面,系统采用背景减除算法分离前景手部区域,结合肤色检测与轮廓分析实现手势特征提取。通过凸包缺陷点计数实现三种基础手势1/2/3的分类识别,对应模式1、模式2、模式3切换。
模式1下点亮暖光阅读灯,模式2则激活高亮白光模式,模式3自动调兰灯光。
二、硬件系统配置与接口说明
1. 核心控制单元树莓派5(PI5)处理器:4核2.4GHz Cortex-A76,支持4K视频硬件解码,提供高效AI手势识别与场景处理能力。接口优势:原生USB3.0接口高速传输摄像头数据、40针GPIO扩展接口。2. 灯光控制模块WS2812(5050 RGB LED)通信协议:单线NeoPixel协议,24位真彩色,支持级联扩展。控制方式:通过树莓派GPIO引脚输出PWM信号驱动,实现动态灯光模式切换(暖光、冷光)。3. 人机交互界面串口屏(TJC8432X350_011)规格参数:4.3寸TFT屏,分辨率480×272,支持触摸交互,预留UART接口(波特率115200)。功能定位:实时显示当前手势模式,模式123、灯光状态及系统信息,支持触摸反馈操作。4. 视频输入设备九目4K USB摄像头性能参数:3840×2160@30FPS分辨率,自动对焦,UVC协议兼容,Linux原生驱动支持。应用场景:捕捉实时视频流,为手势识别与场景检测提供高精度图像数据。硬件接口与连接方案1. 树莓派40针引脚分配预留未使用的GPIO引脚,支持后续添加传感器。
三、40引脚定义

四、原理图
graph LRPI5[树莓派5] PI5 -->|GPIO12(PWM)| WS2812[灯带]PI5 -->|UART(TX/RX)| TJC[串口屏]PI5 -->|USB3.0| Camera[4K摄像头]


六、代码
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秒才触发动作
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 in range(LED_COUNT):
pixels[i] = (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轴向下
"""
return hand.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:
# 主循环
while True:
# 读取摄像头帧
ok, frame = cap.read()
if not ok:
continue # 读取失败则跳过
# 将BGR转换为RGB(Mediapipe需要RGB格式)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 手势识别处理
result = hands.process(rgb)
gesture = None # 当前检测到的手势
hand_present = False # 是否有手部被检测到
# -------------------------
# 1. 手势检测逻辑
# -------------------------
if result.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. 无手状态处理(休眠模式)
# -------------------------
if not hand_present:
set_brightness(DIM_BRIGHTNESS) # 降低亮度进入休眠
last_gesture = None
last_executed_gesture = None
ser.write(nobody_data) # 发送无人状态到串口设备
continue # 跳过后续处理
# -------------------------
# 3. 手部刚出现处理(唤醒模式)
# -------------------------
if last_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. 手势变化检测
# -------------------------
if gesture != last_gesture: # 手势发生变化
last_gesture = gesture
gesture_start_time = time.time() # 重置计时器
print(f"检测到手势变化 → {gesture}")
continue
# -------------------------
# 5. 手势稳定触发(防抖处理)
# -------------------------
if time.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
视频
我要赚赏金
