☰
与蛇共舞:基于摄像头姿态识别与百度AI的贪吃蛇体感游戏设计
2026/10/1 5:50:05 网站建设 项目流程

简介:资源包为Python游戏编程课程设计大作业《与蛇共舞》AI贪吃蛇小游戏完整源码,基于标准贪吃蛇逻辑,引入百度AI人体关键点分析,通过摄像头捕捉手臂、头部与肩部姿态,将运动方向转化为上下左右控制指令,并以socket(127.0.0.1:20163)实时驱动小蛇移动。适合Python入门者完成课程设计、项目实践或人工智能体验学习。内附开发文档、代码注释、PPT展示及可运行脚本,主要文件类型有Python源码(py)、IPython笔记本(ipynb)、编译缓存(pyc)、演示图片与PPT、音乐素材等,py与ipynb配合便于边阅读思路边调试运行。整个压缩包共43个文件,大小约9.47MB,目录结构清晰,可按模块学习。已有936人学习下载,除三大主程序(摄像头姿态分析、标准贪吃蛇、音乐播放)外,还提供百度AI封装库、语音识别脚本、作品展示PPT和项目使用方法说明,注释详细,便于理解socket通信、多进程、姿态识别与游戏逻辑的结合,上手即可运行并二次开发。

1. 与蛇共舞:把摄像头姿态识别接进贪吃蛇的课程设计

这份《与蛇共舞》课程设计源码,核心就一句话:小蛇吃苹果,我来做运动。摄像头拍下你的姿态,百度 AI 平台做人体关键点分析,程序根据手臂、头部和肩膀的相对位置判断上下左右,再通过 socket 把方向喂给标准贪吃蛇程序。它既不是纯键盘游戏,也不是算法 demo,而是一条完整的 AI 交互链路:摄像头采集、姿态识别、socket 传参、多进程接收、pgzero 渲染。卡住新手的往往不在贪吃蛇本身,而在姿态到方向的换算,以及 127.0.0.1:20163 那条数据链路。适合三类人:找 Python 游戏编程课程设计大作业的在校生、想复现 AI 体感小游戏的入门者、想研究 AI 智能体感知交互链路的开发者。

2. 三大模块与百度 AI 人体关键点:camera_get_direct.py 的姿态采集和方向判定

2.1 压缩包里到底有什么:主程序、封装库与辅助示例

先看压缩包结构,避免一进来就被一堆 baidu_*.py 吓到。主程序只有三个:camera_get_direct.py 负责摄像头和姿态识别,snake.py 负责贪吃蛇本体,play_music.py 负责背景音乐。真正参与游戏链路的只有这三个文件加 images 素材。

文件 / 目录在项目里的角色
camera_get_direct.py摄像头取帧、百度 AI 人体关键点识别、方向判定、socket 发送
snake.py基于 pgzero 1.2 的贪吃蛇,多进程接收方向并更新运动
play_music.py用 pygame.mixer 循环播放 music_one.mp3
baidu_api_lib/对百度 AI 平台接口的封装库,来自史然飞《中学生都能玩的人工智能》配套封装
baidu_speech_recognition.py、baidu_picture.py、baidu_couplets.py、voice_record.py同一封装库下的语音识别、图片识别、对联生成、录音示例
images/、source_art/蛇头、蛇身、苹果、墙面的绘图素材
music/演示用背景音乐
run_snake.sh一键启动脚本,按蛇端优先的顺序拉起三个程序
作品展示.pptx、项目使用方法.txt、先读我答辩 PPT 和上手说明

这些 baidu_*.py 不是主程序,但值得翻一遍。它们展示了同一个 baidu_api_lib 分别调语音识别、图片识别、对联生成的写法;游戏里的人体关键点识别只是这个封装的其中一个能力。读懂其中一个示例,再回来看 camera_get_direct.py,会发现调用模式完全一致:造请求、传参、解析返回里的某个字段。根目录的「先读我」和项目使用方法.txt 里写了启动顺序和注意事项,比我自己摸索时的经验还全。

2.2 为什么选百度 AI 平台而不是本地人体姿态库

做人体姿态识别,常见方案有 MediaPipe、OpenPose,也确实能本地跑,但这两类库在课程设计场景里都不太合适。OpenPose 要下载模型文件、依赖 CMake 和显卡环境,教室电脑装一遍就是一场灾难;MediaPipe 虽然轻,但版本和 Python 环境的兼容性问题也很容易把时间耗在装环境上。

百度 AI 平台的人体关键点分析是纯 HTTP 请求,机器能联网、有 API Key 就能用。史然飞那本书里的封装库把 access_token 获取、HTTP 请求、错误码处理都包掉了,主程序只需要传 base64 图片和解析返回。课程设计量级每天几十次调用完全在免费额度内,对演示来说,省掉环境折腾,把精力留在游戏交互上,这是最合理的选型。

2.3 camera_get_direct.py:摄像头取帧与人体关键点请求

这个文件的核心流程是:打开摄像头 → 取一帧 → 编码成 base64 → 调封装库 → 解析 body_parts。先看取帧和请求部分:

import base64 import cv2 # 打开默认摄像头,0 表示第一路摄像头设备 cap = cv2.VideoCapture(0) ret, frame = cap.read() if not ret: print('摄像头取帧失败,先检查设备占用或权限') cap.release() exit(1) # 百度 AI 图片类接口统一收 base64 字符串 _, img_encoded = cv2.imencode('.jpg', frame) img_base64 = base64.b64encode(img_encoded).decode('utf-8') # 调用人体关键点识别;方法名以封装库实际版本为准 resp = baidu_ai.body_analysis(img_base64) # 关键点挂在 body_info[0].body_parts 下 parts = resp['body_info'][0]['body_parts'] for name, point in parts.items(): print(name, point['x'], point['y'], 'score=%.2f' % point['score'])

逻辑说明:cv2 负责取帧,imencode 把 BGR 帧转成 jpg 再 base64 编码,这是百度 AI 图片接口要求的输入格式。baidu_ai 对象来自 baidu_api_lib 封装库,它内部已经处理了 access_token,主程序不用关心鉴权细节。返回的 body_parts 是一个字典,每个关键点有 x、y、score 三个值,x、y 是像素坐标,score 是置信度。

参数说明:if not ret 这个分支一定要保留。教室电脑摄像头被会议软件占用时,read 会返回 False,不处理的话后面 imencode 会拿到空数据直接抛异常。body_info[0] 表示识别到的第一个人,画面里同时出现两个人时程序只取第一个,演示时要提醒自己站在画面中央偏前的位置,别让评委或其他同学进入画面。

2.4 方向判定:手臂、头部与肩膀的几何关系

拿到关键点之后,最关键的一步是把坐标换算成上下左右。项目摘要里说的是「通过姿态信息里手臂、头部和肩膀的关系」得到方向键,常见做法是把画面划分成区域:以头部 y 坐标为上界参考、肩膀 y 坐标为下界参考、脖子 x 坐标为中轴,手腕落在哪个区域就发哪个方向。

def calc_direction(parts): head = parts.get('head', {}) neck = parts.get('neck', {}) left_wrist = parts.get('left_wrist', {}) right_wrist = parts.get('right_wrist', {}) # 两只手都在画面里时,取置信度更高的那只做判断 if left_wrist.get('score', 0) >= right_wrist.get('score', 0): wrist = left_wrist else: wrist = right_wrist if wrist.get('score', 0) < 0.5 or head.get('score', 0) < 0.5: return None # 垂直方向:手腕相对头部的位置 dy = wrist['y'] - head['y'] # 水平方向:手腕相对脖子中轴的偏移 dx = wrist['x'] - neck['x'] if abs(dy) < 40: # 手腕和头差不多高,按水平偏移区分左右 if dx > 30: return 'right' if dx < -30: return 'left' return None if dy < -30: return 'up' # 手已举过头顶 if dy > 80: return 'down' # 手明显低于头部,接近肩线以下 return None

判定逻辑说明:dy = wrist.y - head.y,图像坐标系 y 向下增大,所以 dy 为负表示手腕在头顶上方,判 up;dy 很大表示手腕垂在身体下方,判 down。水平方向用 neck 的 x 做中轴,因为脖子基本在画面中线附近,比用 head 的 x 更稳定。手腕高度和头接近时,按 dx 区分左右。

参数说明:30、40、80 这些阈值是经验和演示距离的产物。摄像头离人 1.5 到 2 米、人站直时基本可用;摄像头离人很近或人坐下来,必须现场调。调法很简单:分别举手、放平、垂手,打印 dy、dx 看实际数值范围,再把阈值设在中间。score 过滤放在最前面,低于 0.5 的关键点说明百度 AI 自己都不确定,别拿它当依据。

3. socket 数据链路:127.0.0.1:20163 上的方向传递与进程解耦

3.1 为什么是 socket:两条独立进程的边界

很多人第一次看这个项目会问:摄像头程序直接调用 snake 里的函数改方向不行吗?不行。两个程序是独立进程,各有各的生命周期:摄像头程序要一直抢摄像头设备、循环取帧,蛇程序要一直跑渲染循环,谁都不能阻塞谁。放进同一个进程,要么取帧卡住渲染,要么渲染卡住取帧。用 socket 把两个进程解耦之后,各自启动、各自崩溃、互不影响,调试时还能单独把某一段跑起来验证。

127.0.0.1 是回环地址,只在本机通信,不经过外网,课程设计场景足够。20163 是双方约定的端口,两个程序里写同一个端口就能对上。为什么不用 UDP?UDP 丢包在体感场景确实可以容忍,但 TCP 写起来也就多两行 connect 和 close,而且答辩时 TCP 的「连接-发送-关闭」链路更容易被评委理解。这套方案本质是感知、决策、执行三段式的闭环:摄像头取帧是感知,方向判定是决策,蛇的移动是执行,这也是最简形态的 AI 智能体。

3.2 发送端:camera_get_direct.py 的方向派发

发送端做的是短连接:每判定出一个方向,就新建 socket 连一次、发完立刻关闭。这样发送端永远不需要处理对端掉线的状态,游戏端没启动时 connect 失败,跳过这一帧就行,不会拖慢取帧循环。

import socket VALID_DIRECTIONS = ('up', 'down', 'left', 'right') def send_direction(direction): """方向字符串通过 TCP 短连接发给蛇端""" if direction not in VALID_DIRECTIONS: return s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(1) # 连接失败最多等 1 秒,避免卡住取帧循环 try: s.connect(('127.0.0.1', 20163)) s.sendall(direction.encode('utf-8')) except OSError as e: # 蛇端没启动或端口被占,本帧丢弃即可 print('方向发送失败:', e) finally: s.close()

逻辑说明:每判定完一个方向就调用一次 send_direction,循环回到取帧继续下一轮。settimeout(1) 是给 connect 设置的超时上限;本机回环正常情况下毫秒级完成,设 1 秒只是为了防极端情况卡住取帧循环。finally 里的 s.close() 保证连接一定释放,不会随着循环越积越多。

参数说明:VALID_DIRECTIONS 白名单在发送端和接收端各有一份,这是双保险——发送端不发送非法字符串,接收端也只认这四个方向,任何杂数据都会被丢弃。方向字符串固定用 up/down/left/right,不要改成中文或数字,socket 传的是字节流,统一编码省去一堆编码问题。

3.3 接收端:snake.py 里的多进程常驻监听

接收端不能放在 pgzero 的 update 回调里。update 每帧都跑,而 accept 是阻塞调用,放进去游戏直接卡死。项目摘要里写明 snake.py 是「新建了一个进程」来做这件事,这是标准做法:

import multiprocessing as mp import socket DIRECTION_CODE = {'up': 0, 'down': 1, 'left': 2, 'right': 3} CODE_TO_DIRECT = {0: 'up', 1: 'down', 2: 'left', 3: 'right'} def direction_listener(holder, stop_event): """独立进程:常驻监听 20163,收到合法方向就编码写入共享变量""" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('127.0.0.1', 20163)) server.listen(1) # 同一时刻只服务一个发送端 while not stop_event.is_set(): try: conn, _ = server.accept() data = conn.recv(16).decode('utf-8').strip() if data in DIRECTION_CODE: holder.value = DIRECTION_CODE[data] conn.close() except OSError: break # 主程序入口 if __name__ == '__main__': holder = mp.Value('i', 0) # 进程间共享的方向编码,初始 0 对应 up stop_event = mp.Event() listener = mp.Process( target=direction_listener, args=(holder, stop_event), daemon=True ) listener.start()

逻辑说明:direction_listener 在独立进程里死循环 accept,每收到一个合法方向就编码写入 holder。holder 是 multiprocessing.Value('i'),整型共享变量,读写都带锁、是原子操作;用整数编码而不是字符串共享,是为了避开 ctypes 字符串类型在部分 Python 版本下的长度和同步坑。主进程的 update 每帧读 holder,读到新值就换向。daemon=True 保证游戏窗口关闭时监听进程跟着退出,不会留下孤儿进程占着 20163 端口。

参数说明:listen(1) 表示内核只排一个等待连接,本机单发送端场景够用。recv(16) 一次最多读 16 字节,方向字符串最长 5 个字符(right),绰绰有余。SO_REUSEADDR 是血泪教训换来的:不加它,蛇端进程刚退出紧接着又启动时,TIME_WAIT 状态会让 bind 报 Address already in use,加上之后基本不撞。

另外注意监听端永远只能有一份。摄像头端如果也写成 bind+listen,蛇端先启动时摄像头端 connect 能连上;但反过来蛇端没启动、摄像头端先跑,蛇端后面 bind 就会失败。这个先后顺序问题,第 5 章有对应的排错方法。

4. 改造 pgzero 贪吃蛇:把键盘方向源换成多进程共享变量

4.1 pgzero 1.2 的运行方式和 snake 基础结构

pgzero 是 Pygame Zero 的简称,它把 pygame 的样板代码收掉,只要求开发者实现 update 和 draw 两个回调。运行方式是 pgzrun snake.py,或者 python snake.py(文件末尾调用了 pgzrun.go())。1.2 版本对 Python 3.8 到 3.10 都友好,安装用 pip install pgzero==1.2 即可,装不上先确认 Python 版本对不对,这是最省心的环境组合。

原版 snake 的方向来源是键盘事件,on_key_down 里按上下左右改 direction;要做 AI 控制,本质就是把 direction 的写入者从「键盘事件」换成「socket 共享变量」。项目正文也写明「在 pgzero 1.2 的 snake 基础上」改造,照着这个基准继续改最稳。

4.2 共享变量的读取、反向过滤与运动节拍

update 里读共享变量并更新方向,是整个改造的核心。注意两点:一是反向过滤,二是用帧计数控制移动节拍。

import pgzrun from pgzero.constants import keyboard SPEED = 10 # 每 10 帧移动一格,越小越快 DIRECTIONS = { 'up': (0, -1), 'down': (0, 1), 'left': (-1, 0), 'right': (1, 0), } def update(): global direction, move_timer # 读共享变量里的方向编码,AI 优先,键盘兜底 ai_dir = CODE_TO_DIRECT.get(holder.value) if ai_dir in DIRECTIONS: cur = DIRECTIONS[direction] new = DIRECTIONS[ai_dir] # 禁止 180 度掉头:正在向右时不允许直接向左 if (new[0], new[1]) != (-cur[0], -cur[1]): direction = ai_dir # 按节拍移动,而不是每帧都移 move_timer += 1 if move_timer >= SPEED: move_timer = 0 move_snake()

逻辑说明:holder.value 是整数编码,用 CODE_TO_DIRECT 字典映射回方向字符串。方向合法性校验在写入共享变量时已经做了一轮,update 里再做一轮是双保险:holder 初始值是 0 映射 up,而且多进程写入无法保证完全干净。move_timer 是帧计数,达到 SPEED 才走一格,这样蛇的移动速度、方向更新频率和 AI 判定的节奏解耦,不会因为识别模块连续发方向导致蛇瞬移。

参数说明:反向过滤用向量相加判断,新方向和当前方向的 x、y 分量互为相反数就是掉头,直接忽略。SPEED=10 在 60 帧渲染下约 0.17 秒走一格,体感中等偏快;现场手跟不上就把 SPEED 调到 12 或 14,只改这一处,不影响其它逻辑。

4.3 move_snake 和吃苹果判定

贪吃蛇本体逻辑沿用 pgzero 原版思路:蛇身是一个坐标列表,头部每步按 direction 加一个格子,尾部去掉一格;吃到苹果就保留尾巴实现变长。AI 控制这一层完全不插手位移,只改 direction,所以原版移动代码可以原样保留。

def move_snake(): global snake, food, score head = snake[0] dx, dy = DIRECTIONS[direction] new_head = (head[0] + dx, head[1] + dy) # 撞墙或撞自己判定 if new_head[0] < 0 or new_head[0] >= GRID_WIDTH: game_over() return if new_head[1] < 0 or new_head[1] >= GRID_HEIGHT: game_over() return if new_head in snake: game_over() return snake.insert(0, new_head) if new_head == food: score += 1 spawn_food() # 尾巴这一帧不删,蛇就变长了 else: snake.pop()

逻辑说明:snake 用元组列表表示,索引 0 是蛇头。新头插入列表头部,没吃到苹果就 pop 掉尾部一格,吃到苹果不 pop,长度自然加一。撞墙和撞自己是独立的 game_over 触发条件。AI 控制时反向过滤已经挡掉了最危险的掉头,剩余风险主要是人反应慢导致蛇撞墙。

参数说明:GRID_WIDTH 和 GRID_HEIGHT 是网格数,pgzero 原版默认 20x15 左右,与窗口像素映射后在 draw 里画格子。这两个参数不需要为 AI 改动,保持原版即可。真正要关注的是 SPEED:体感控制比键盘慢一拍,速度越快越容易翻车。

4.4 背景音乐模块:play_music.py 的接入方式

音乐播放是第三块独立程序,用 pygame.mixer 播放 music/music_one.mp3。它不参与 socket 链路,也不读取共享变量,纯粹是演示气氛组。常见做法是:

import pygame def play_bgm(path='music/music_one.mp3'): try: pygame.mixer.init() pygame.mixer.music.load(path) pygame.mixer.music.play(-1) # -1 表示循环播放 except pygame.error as e: # 教室机器没声卡或音频设备被占用,不影响游戏主程序 print('背景音乐播放失败:', e)

逻辑说明:mixer.init 初始化音频设备,load 加载 mp3,play(-1) 循环。try/except 包住整段的意义在于:答辩机器经常没有音频设备,如果这段不兜底,音乐没响还把游戏主程序拖崩,现场就尴尬了。

参数说明:路径用相对路径 music/music_one.mp3,前提是工作目录在项目根目录。用 run_snake.sh 启动时工作目录是脚本所在目录,没问题;手动在 IDE 里运行 snake.py 时,一定要确认当前工作目录,否则路径找不到,音乐会静默失败,而这个失败不会报错,很难察觉。

5. 常见问题与避坑:五个必踩的坑和对应排查手段

5.1 百度 AI 返回 error_code,程序直接退出

现象和原因:一运行 camera_get_direct.py,控制台就打印一串 error_code(最常见的是 110 和 100),程序直接退出。几乎都是同一个原因:baidu_api_lib 里配置的 API Key、Secret Key 还是作者原有的,或者 access_token 已经过期。百度 AI 的 token 有效期约 30 天,课程设计做完隔一个多月才答辩的情况很常见,过期了就是这个表现。

解决:到百度 AI 平台控制台新建应用,拿到自己的 AK/SK,填进封装库对应的配置位置。封装库如果带了刷新 token 的函数,重跑一次即可;没有就删掉本地 token 缓存文件再跑。答辩前一晚务必从头走一遍这个流程,把它当成固定检查项,这是最稳的后悔药。

5.2 蛇端启动就报 Address already in use

现象和原因:snake.py 刚启动,控制台立刻报 [Errno 98] Address already in use,绑定 20163 失败。通常两个原因:上一次运行留下的进程没退干净,或者摄像头端被错误地配成了 listener,蛇端再 bind 同一个端口就撞上。

解决:先确认启动顺序——必须蛇端先起来占端口,摄像头端后起来做连接方。反复出现占用时,Windows 用 netstat -ano | grep 20163 看 PID,Linux 用 lsof -i:20163,找到 PID 直接 kill。socket 上加的 SO_REUSEADDR 能缓解 TIME_WAIT 造成的占用,但解决不了进程还活着的情况。

5.3 蛇突然 180 度掉头,撞到自己身上

现象和原因:蛇正常向右走,收到一个 left 方向后直接反向,一头撞上自己身体,游戏结束。原因是 socket 传来的方向被直接赋值给 direction,update 里没做反向过滤。体感操作时手从右边甩向左边,中间会经过 left 判定区间,这个方向恰好与当前方向互逆。

解决:在 update 里按 4.2 的向量判断,新方向是当前方向的反向就丢弃。这是整个改造里最容易漏的一步,也是翻车率最高的一步。顺带提醒:键盘兜底事件里也要加同样的过滤,否则演示时按错方向一样自杀。这个过滤逻辑放哪都行,就是不能没有。

5.4 手停在原地,方向却在 up、left 之间反复横跳

现象和原因:手保持一个姿势没动,蛇的方向一直在抖,左右摇摆。原因是单帧判定没有去抖:手腕坐标在阈值边界附近轻微抖动,score 也忽高忽低;另一个可能是 score 过滤阈值设太低,百度 AI 的误检点被当成手腕参与判定。

解决:常见做法是加投票窗口,维护最近 5 帧的判定结果,出现 3 次以上才派发:

history = [] def vote(direction): """最近 5 帧中出现 3 次以上的方向才放行,低于阈值的抖动被滤掉""" history.append(direction) if len(history) > 5: history.pop(0) if history.count(direction) >= 3: return direction return None

逻辑说明:每次判定结果先进入 history,超过 5 帧就把最旧的弹出去,然后统计当前方向出现次数,达到 3 才返回,否则返回 None。这样单帧误检会被稀释,不会直接传导到蛇身。

参数说明:窗口 5 帧、阈值 3 次,在 60 帧下大约 0.08 秒的判定窗口,体感上几乎无延迟。如果抖动还明显,把窗口加到 7、阈值提到 4。我一般先把 score 过滤提到 0.6,再叠加这个投票,抖动基本消失。

5.5 摄像头黑屏或取帧失败

现象和原因:cv2.VideoCapture(0) 返回 False,或者画面全黑、卡在某一帧。原因是教室电脑上腾讯会议、钉钉、OBS 等程序占用了摄像头设备,或者笔记本的摄像头权限被系统设置拦掉。

解决:先关掉所有可能占用摄像头的软件再跑。Windows 在「设置-隐私-摄像头」里允许桌面应用访问;macOS 在系统设置的摄像头权限里勾上终端或 Python。排查思路很关键:先单独跑一个只取帧的脚本,如果单独跑也黑,就是设备或权限问题,和游戏代码无关。调试阶段可以用本地视频文件代替摄像头:把 VideoCapture(0) 换成 VideoCapture('test.mp4'),后面的识别和 socket 链路照样能验证。

6. 演示前自检:用 mock 方向脚本验证整条链路

答辩现场翻车率最高的不是识别不准,而是链路没通。我在每次演示前都会做一件事:不接摄像头,用 mock 脚本直接往 20163 发方向,单独验证蛇端的 socket、共享变量、update 读取这一段。

import socket import time # 模拟摄像头端:每 2 秒发一个方向,蛇应按「上-左-下-右」转圈 for direction in ['up', 'left', 'down', 'right', 'up']: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1', 20163)) s.sendall(direction.encode('utf-8')) s.close() print('sent', direction) time.sleep(2)

逻辑说明:脚本逐一发送方向,每发一个停 2 秒,方便肉眼观察蛇的转向。参数说明:sleep(2) 是观察窗口,链路没通时蛇会停在原地或只朝一个方向走,不会瞬移;连接方式与 camera_get_direct.py 完全一致,都是短连接。

先用 run_snake.sh 起蛇端,再跑这个脚本:蛇按顺序转圈说明链路全通;不动就只在 snake.py 的 listener 或 holder 读取里找问题,把摄像头和百度 AI 完全排除在外。链路验证完再做姿态校准:分别举手、平举、垂手,打印 dy、dx 实际范围,把 2.4 里的阈值调到中间,然后才正式演示。

提示:mock 脚本跑通后再起摄像头程序,能把「链路问题」和「识别问题」彻底分开,这是排错的第一步。

现场启动顺序我固定是:蛇端最先(它要 bind 20163),摄像头端其次(连接方晚起无影响),音乐最后(音频设备出问题也不拖核心链路)。摄像头离人 1.5 到 2 米,正面打光,别让其他人进入画面被误识别成第二个人。这些流程在源码包里的 run_snake.sh 和项目使用方法.txt 都有对应说明,完整源码、注释和答辩 PPT 都在压缩包里,做课程设计的可以直接按这份流程复现。从那以后我每次演示前都强制走一遍 mock 脚本,再按蛇端优先的顺序启动,确认蛇先动起来才把脸凑到摄像头前。这套流程帮我躲过了至少三次答辩事故,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询