本文最后更新于83 天前,其中的信息可能已经过时,如有错误请发送邮件到2275012286@qq.com,或者在下方留言。
想了解更多项目请搜索“项目”标签或者访问我的github仓库。
仓库地址:QianmoNai’s Repositories
基于Maixcam的人体姿态关键点识别(停止维护)
视频演示:stm32+maixcam人体姿态识别
图片展示(点我)





maixcam部分开源,通过网盘分享的文件:human_pose.zip
链接: https://pan.baidu.com/s/1g6yPZRtrQ1n3SAqG8KxyCg?pwd=rd64 提取码: rd64
1.项目背景
本身用于robotcup舞蹈机器人表演赛比赛要求,实现识别特定动作,让机器人模仿被识别人的动作,使用的是人体关键点进行动作的判断和识别,最后能够返回对应动作的串口数据,提供给上位单片机进行处理。
参考官方代码地址:MaixCAM-Pro — 快速落地 AI 视觉、听觉应用 – Sipeed Wiki
2.项目介绍
代码部分(点我)
# 导入maix库中的相关模块,分别用于摄像头、显示、图像处理、神经网络、应用控制、时间、触摸屏和串口通信
from maix import camera, display, image, nn, app, time, touchscreen, uart
# 定义各姿态的计数变量,用于统计不同姿态出现的次数
pose1_ticket = 0 # 姿态1计数(举手)
pose2_ticket = 0 # 姿态2计数(马步)
pose3_ticket = 0 # 姿态3计数(深蹲)
pose4_ticket = 0 # 姿态4计数(弓步)
pose5_ticket = 0 # 姿态5计数(叉腰)
pose6_ticket = 0 # 姿态6计数(单脚)
none_ticket = 0 # 无有效姿态计数
# 定义距离阈值参数,用于姿态判断中的位置关系计算
test_distance = 10 # 通用距离阈值1
test_distance1 = 10 # 距离阈值2(用于深蹲判断)
test_distance2 = 3 # 距离阈值3(用于马步判断)
test_distance3 = 20 # 距离阈值4(用于单脚判断)
# 配置串口设备及参数,使用/dev/ttyS0端口,波特率9600
device = "/dev/ttyS0"
serial0 = uart.UART(device, 9600)
def is_in_button(x, y, btn_pos):
"""检查触摸点(x,y)是否在按钮区域内
参数:
x, y: 触摸点坐标
btn_pos: 按钮区域参数 [x起始, y起始, 宽度, 高度]
返回:
布尔值,True表示在按钮区域内,False反之
"""
return x > btn_pos[0] and x < btn_pos[0] + btn_pos[2] and y > btn_pos[1] and y < btn_pos[1] + btn_pos[3]
def get_pose(points):
"""根据人体关键点坐标判断姿态,并更新对应姿态的计数
参数:
points: 人体关键点坐标列表(由姿态检测模型输出)
返回:
字符串,当前识别的姿态名称
"""
global pose1_ticket, pose2_ticket, pose3_ticket, pose4_ticket, pose5_ticket, pose6_ticket, none_ticket
# 判断"单脚"姿态:基于特定关键点(31和33)的距离是否超过阈值
if (abs(points[31] - points[33]) > test_distance3 and
points[31] != -1 and points[33] != -1): # 确保关键点有效(-1表示未检测到)
pose6_ticket += 1
return "dan_jiao"
# 判断"举手"姿态:基于特定关键点(21、19与1)的上下位置关系
elif (points[21] < points[1] and
points[19] < points[1] and
points[21] != -1 and points[19] != -1 and points[1] != -1):
pose1_ticket += 1
return "raise_hand"
# 判断"叉腰"姿态:基于特定关键点(20与16、18与14)的位置关系
elif (points[20] > points[16] and points[18] < points[14] and
points[20] != -1 and points[16] != -1 and points[18] != -1 and points[14] != -1):
pose5_ticket += 1
return "cha_yao"
# 判断"马步"姿态:基于多个关键点(19与21、30与32、24与22)的距离和位置关系
elif (abs(points[19] - points[21]) < test_distance and
(points[30] - points[32] + points[24] - points[22]) > test_distance2 and
points[30] - points[32] > test_distance2 and points[22] - points[24] > test_distance2 and
points[19] != -1 and points[21] != -1):
pose2_ticket += 1
return "Straddle_Stance"
# 判断"深蹲"姿态:基于特定关键点(19与11、21与13)的距离是否小于阈值
elif ((abs(points[19] - points[11]) < test_distance1 or
abs(points[21] - points[13]) < test_distance1) and
points[19] != -1 and points[11] != -1 and points[21] != -1 and points[13] != -1):
pose3_ticket += 1
return "Squat"
# 判断"弓步"姿态:基于特定关键点(19与11、21与13)的交叉位置关系
elif (((points[19] > points[11] and points[21] < points[13]) or
(points[19] < points[11] and points[21] > points[13])) and
points[19] != -1 and points[11] != -1 and points[21] != -1 and points[13] != -1):
pose4_ticket += 1
return "Lunge"
# 无有效姿态时,更新无姿态计数
else:
none_ticket += 1
return "none"
def main(disp):
"""主函数:初始化设备、处理图像采集、姿态检测、串口通信和用户交互"""
global pose1_ticket, pose2_ticket, pose3_ticket, pose4_ticket, pose5_ticket, pose6_ticket, none_ticket
# 初始化触摸屏模块
ts = touchscreen.TouchScreen()
# 加载YOLOv8人体姿态检测模型(模型路径为/root/models/yolov8n_pose.mud)
detector = nn.YOLOv8(model="/root/models/yolov8n_pose.mud")
# 加载返回按钮的背景图像
img_back = image.load("/maixapp/share/icon/ret.png")
# 定义返回按钮的位置和大小 [x坐标, y坐标, 宽度, 高度]
back_rect = [0, 0, 32, 32]
# 初始化摄像头,分辨率和格式与模型输入匹配
cam = camera.Camera(detector.input_width(), detector.input_height(), detector.input_format())
# 计算按钮在显示设备上的实际位置(考虑图像缩放适配显示屏幕)
back_rect_disp = image.resize_map_pos(
cam.width(), cam.height(), # 原始图像尺寸
disp.width(), disp.height(), # 显示设备尺寸
image.Fit.FIT_CONTAIN, # 缩放模式:保持纵横比并完全包含在目标区域内
back_rect[0], back_rect[1], # 按钮在原始图像中的位置
back_rect[2], back_rect[3] # 按钮在原始图像中的大小
)
try:
data = None # 用于存储串口接收的数据
flag = 0 # 状态标志:0-未开始检测,1-正在检测
while not app.need_exit(): # 主循环,直到应用需要退出
img = cam.read() # 从摄像头读取一帧图像
if img is None: # 如果未获取到图像,短暂休眠后继续
time.sleep_ms(20)
continue
data = serial0.read() # 读取串口数据
# 若接收到0x01指令:重置所有姿态计数,开始检测
if (data == b'\x01'):
print("get!")
pose1_ticket = 0
pose2_ticket = 0
pose3_ticket = 0
pose4_ticket = 0
pose5_ticket = 0
pose6_ticket = 0
none_ticket = 0
flag = 1
# 若接收到0x00指令:结束检测,发送出现次数最多的姿态对应的指令
elif (data == b'\x00'):
max_value = max(pose1_ticket, pose2_ticket, pose3_ticket, pose4_ticket, pose5_ticket, pose6_ticket, none_ticket)
# 根据最大计数判断主导姿态,并发送对应串口指令
if pose1_ticket == max_value and max_value != 0:
for i in range(1):
serial0.write(bytes([0x33]))
print("send!0x33")
elif pose2_ticket == max_value and max_value != 0:
for i in range(1):
serial0.write(bytes([0x31]))
print("send!0x31")
elif pose3_ticket == max_value and max_value != 0:
for i in range(1):
serial0.write(bytes([0x34]))
print("send!0x34")
elif pose4_ticket == max_value and max_value != 0:
for i in range(1):
serial0.write(bytes([0x32]))
print("send!0x32")
elif pose5_ticket == max_value and max_value != 0:
for i in range(1):
serial0.write(bytes([0x37]))
print("1")
print("send!0x37")
elif pose6_ticket == max_value and max_value != 0:
for i in range(1):
serial0.write(bytes([0x38]))
print("send!0x38")
else:
for i in range(1):
serial0.write(bytes([0x36]))
print("send!0x36")
flag = 0 # 重置标志位,结束检测
# 若处于检测状态(flag=1),执行姿态检测并绘制结果
if flag == 1:
# 使用模型检测图像中的人体姿态(置信度阈值0.5,IOU阈值0.45,关键点阈值0.5)
objs = detector.detect(img, conf_th=0.5, iou_th=0.45, keypoint_th=0.5)
# 在图像上绘制检测结果
for obj in objs:
# 绘制人体边界框(红色)
img.draw_rect(obj.x, obj.y, obj.w, obj.h, color=image.COLOR_RED)
# 绘制姿态类别标签
msg = f'{get_pose(obj.points)}'
img.draw_string(obj.x, obj.y, msg, color=image.COLOR_WHITE)
print(obj.points) # 打印关键点坐标(调试用)
# 绘制人体关键点和连接线(绿色,线宽根据图像尺寸自适应)
detector.draw_pose(img, obj.points, 8 if detector.input_width() > 480 else 4, image.COLOR_GREEN)
# 在图像上绘制返回按钮
img.draw_image(back_rect[0], back_rect[1], img_back)
# 调整图像大小以适应显示设备(保持纵横比)
img_display = img.resize(disp.width(), disp.height(), image.Fit.FIT_CONTAIN)
# 在显示设备上显示处理后的图像
disp.show(img_display)
# 读取触摸屏输入(坐标和按压状态)
x, y, pressed = ts.read()
# 检查是否点击了返回按钮,若是则退出应用
if pressed and is_in_button(x, y, back_rect_disp):
app.set_exit_flag(True)
break
time.sleep_ms(20) # 短暂休眠,降低CPU占用
finally:
# 程序结束时释放摄像头资源
if cam:
cam.release()
# 主程序入口
disp = display.Display() # 初始化显示设备
try:
main(disp) # 启动主函数
except Exception as e:
# 异常处理:捕获并显示错误信息
import traceback
msg = traceback.format_exc() # 获取完整的错误堆栈信息
# 创建图像并绘制错误信息
img = image.Image(disp.width(), disp.height())
img.draw_string(0, 0, msg, image.COLOR_WHITE)
disp.show(img)
# 等待用户退出应用
while not app.need_exit():
time.sleep_ms(100)
模型输出的数组对应的人体点位如下:

3.项目重点内容
①检测动作的原理就是通过判断不同关键点之间的位置关系,就能得到当前的人体姿态是什么。
②通过一段时间内的得票制,来判断所识别到的动作,大大减小了误识别的次数。










