当前位置: 首页 > news >正文

树莓派,mediapipe,Picamera2利用舵机云台追踪人手(PID控制)

一、项目目标

追踪人手大拇指指尖:
当人手移动时,摄像头通过控制两个伺服电机(分别是偏航和俯仰)把大拇指指尖放到视界的中心位置,本文采用了PID控制伺服电机

  • Mediapipe Hand简介

MediaPipe 手部标志任务可检测图像中手部的标志。 您可以使用此任务来定位手的关键点并在其上渲染视觉效果。 该任务使用机器学习(ML)模型作为静态数据或连续流对图像数据进行操作,并输出图像坐标中的手部标志、世界坐标中的手部标志以及多个检测到的手的惯用手(左/右手)。
在这里插入图片描述

二、 需要准备的软、硬件

  1. Raspiberry Pi 4b
  2. 两个SG90 180度舵机(注意舵机的角度,最好是180度且带限位的,切勿选360度舵机)
  3. 二自由度舵机云台(如下图)
  4. Raspiberry CSI 摄像头
  5. mediapipe库, 安装方法可以参照此链接
    组装后的效果:
    组装后的效果

三、具体步骤

  1. 创建“hand_tracking_PID.py”文件,代码如下,我在本文中追踪的是大拇指指尖,如果你想追踪其它部位,只须将fingerID参数设置成你想追踪的数字即可。具体数字分布如下图。

hand landmark模型

#-*- coding: UTF-8 -*-	
# 调用必需库
#hand_tracking_PID.py
from multiprocessing import Manager
from multiprocessing import Process
from handobj import HandObj
from pid import PID
from servo import Servo
import signal
import time
import sys
import cv2
import mediapipe as mp
from picamera2 import Picamera2# 定义舵机
pan=Servo(pin=19)
tilt=Servo(pin=16)#定义图像尺寸
dispW=1280
dispH=720# 定义手指ID
fingerID=4# 键盘终止函数
def signal_handler(sig, frame):# 输出状态信息print("[INFO] You pressed `ctrl + c`! Exiting...")# 关闭舵机pan.stop()tilt.stop()# 退出sys.exit()def hand_obj(objX,objY,centerX,centerY):# ctrl+c退出进程signal.signal(signal.SIGINT, signal_handler)# 启动视频流并缓冲print("[INFO] waiting for camera to warm up...")cv2.startWindowThread()picam2 = Picamera2()preview_config = picam2.create_preview_configuration(main={"size": (dispW, dispH),"format":"RGB888"})picam2.configure(preview_config)picam2.start()time.sleep(2.0)#初始化手掌对象探测器hand=HandObj(fingerID)#进入循环while True:# 从视频流抓取图像并旋转frame = picam2.capture_array()frame = cv2.flip(frame, 1)# 找到图像中心(H, W) = frame.shape[:2]centerX.value = W // 2centerY.value = H // 2# 画出图像中心点cv2.circle(frame, (centerX.value, centerY.value), 5, (0, 0, 255), -1)# 找到手指对象点objectLoc = hand.update(frame, (centerX.value, centerY.value))((objX.value, objY.value), handlms) = objectLoc# 画出手指关注的对象点,这是里前面定义的ID:4,即大拇指指尖if handlms is not None:      cv2.circle(frame, (objX.value, objY.value), 15, (255, 0, 255), cv2.FILLED)cv2.imshow('Hand', frame)cv2.waitKey(1)def pid_process(output, p, i, d, objCoord, centerCoord):# ctrl+c退出进程signal.signal(signal.SIGINT, signal_handler)# 创建一个PID类的对象并初始化p = PID(p.value, i.value, d.value)p.initialize()# 进入循环while True:# 计算误差error = centerCoord.value - objCoord.value# 更新输出值,当error小于50时,误差设为0,以避免云台不停运行。if abs(error) < 50:error = 0output.value = p.update(error)def set_servos(panAngle, tiltAngle):# ctrl+c退出进程signal.signal(signal.SIGINT, signal_handler)#进入循环while True:# 偏角变号yaw = -1 * panAngle.valuepitch = -1 * tiltAngle.value# 设置舵机角度。pan.set_angle(yaw)tilt.set_angle(pitch)# 启动主程序
if __name__ == "__main__":# 启动多进程变量管理with Manager() as manager:  # 相当于manager=Manager(),with as 语句操作上下文管理器(context manager),它能够帮助我们自动分配并且释放资源。# 舵机角度置零pan.set_angle(0)tilt.set_angle(0)# 为图像中心坐标赋初值centerX = manager.Value("i", 0)  # "i"即为整型integercenterY = manager.Value("i", 0)# 为人脸中心坐标赋初值objX = manager.Value("i", 0)objY = manager.Value("i", 0)# panAngle和tiltAngle分别是两个舵机的PID控制输出量panAngle = manager.Value("i", 0)tiltAngle = manager.Value("i", 0)# 设置一级舵机的PID参数panP = manager.Value("f", 0.015)  # "f"即为浮点型floatpanI = manager.Value("f", 0.01)panD = manager.Value("f", 0.0008)# 设置二级舵机的PID参数tiltP = manager.Value("f", 0.025)tiltI = manager.Value("f", 0.01)tiltD = manager.Value("f", 0.008)# 创建4个独立进程# 1. objectCenter  - 探测人脸# 2. panning       - 对一级舵机进行PID控制,控制偏航角# 3. tilting       - 对二级舵机进行PID控制,控制俯仰角# 4. setServos     - 根据PID控制的输出驱动舵机processObjectCenter = Process(target=hand_obj, args=(objX, objY, centerX, centerY))processPanning = Process(target=pid_process, args=(panAngle, panP, panI, panD, objX, centerX))processTilting = Process(target=pid_process, args=(tiltAngle, tiltP, tiltI, tiltD, objY, centerY))processSetServos = Process(target=set_servos, args=(panAngle, tiltAngle))# 开启4个进程processObjectCenter.start()processPanning.start()processTilting.start()processSetServos.start()# 添加4个进程processObjectCenter.join()processPanning.join()processTilting.join()processSetServos.join()
  1. 创建“handobj.py”,代码如下:
#handobj.py
#-*- coding: UTF-8 -*-
# 调用必需库
import mediapipe as mpclass HandObj:def __init__(self,fingerID):# 初始化手掌关键点坐标self.myHands=mp.solutions.hands# 初始化手掌关键点坐标和手掌关键点连接情况self.hands=self.myHands.Hands()# 初始化手掌关键点绘制库self.mpDraw=mp.solutions.drawing_utils# 初始化手掌关键点IDself.fingerID=fingerIDdef update(self, frame, frameCenter):# 处理视频流results = self.hands.process(frame)if results.multi_hand_landmarks:for handLms in results.multi_hand_landmarks:# 绘制手掌关键点self.mpDraw.draw_landmarks(frame, handLms, self.myHands.HAND_CONNECTIONS)for id, lm in enumerate(handLms.landmark):h, w, c = frame.shapecx, cy = int(lm.x * w), int(lm.y * h)if id == self.fingerID:#绘制手掌关键点并返回手掌关键点坐标return ((cx, cy), handLms)return(frameCenter,None)
  1. 创建“pid.py”,代码如下:
#-*- coding: UTF-8 -*-
# 调用必需库
import timeclass PID:def __init__(self, kP=1, kI=0, kD=0):# 初始化参数self.kP = kPself.kI = kIself.kD = kDdef initialize(self):# 初始化当前时间和上一次计算的时间self.currTime = time.time()self.prevTime = self.currTime# 初始化上一次计算的误差self.prevError = 0# 初始化误差的比例值,积分值和微分值self.cP = 0self.cI = 0self.cD = 0def update(self, error, sleep=0.5):# 暂停time.sleep(sleep)# 获取当前时间并计算时间差self.currTime = time.time()deltaTime = self.currTime - self.prevTime# 计算误差的微分deltaError = error - self.prevError# 比例项self.cP = error# 积分项self.cI += error * deltaTime# 微分项self.cD = (deltaError / deltaTime) if deltaTime > 0 else 0# 保存时间和误差为下次更新做准备self.prevTime = self.currTimeself.prevError = error# 返回输出值return sum([self.kP * self.cP,self.kI * self.cI,self.kD * self.cD])
  1. 上述代码中的from servo import Servo导入servo,这个库是没有的,我们要手动创建这个库,在object_tracking.py所在的目录下新建servo.py文件,复制下面的代码到文件中
#!/usr/bin/env python3
import pigpio
from time import sleep
# Start the pigpiod daemon
import subprocess
result = None
status = 1
for x in range(3):p = subprocess.Popen('sudo pigpiod', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)result = p.stdout.read().decode('utf-8')status = p.poll()if status == 0:breaksleep(0.2)
if status != 0:print(status, result)
'''
> Use the DMA PWM of the pigpio library to drive the servo
> Map the servo angle (0 ~ 180 degree) to (-90 ~ 90 degree)'''class Servo():MAX_PW = 1250  # 0.5/20*100MIN_PW = 250 # 2.5/20*100_freq = 50 # 50 Hz, 20msdef __init__(self, pin, min_angle=-90, max_angle=90):self.pi = pigpio.pi()self.pin = pin self.pi.set_PWM_frequency(self.pin, self._freq)self.pi.set_PWM_range(self.pin, 10000)      self.angle = 0self.max_angle = max_angleself.min_angle = min_angleself.pi.set_PWM_dutycycle(self.pin, 0)def set_angle(self, angle):if angle > self.max_angle:angle = self.max_angleelif angle < self.min_angle:angle = self.min_angleself.angle = angleduty = self.map(angle, -90, 90, 250, 1250)self.pi.set_PWM_dutycycle(self.pin, duty)def get_angle(self):return self.angledef stop(self):self.pi.set_PWM_dutycycle(self.pin, 0)self.pi.stop()# will be called automatically when the object is deleted# def __del__(self):#     passdef map(self, x, in_min, in_max, out_min, out_max):return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_minif __name__ =='__main__':from vilib import Vilib# Vilib.camera_start(vflip=True,hflip=True) # Vilib.display(local=True,web=True)pan = Servo(pin=13, max_angle=90, min_angle=-90)tilt = Servo(pin=12, max_angle=30, min_angle=-90)panAngle = 0tiltAngle = 0pan.set_angle(panAngle)tilt.set_angle(tiltAngle)sleep(1)while True:for angle in range(0, 90, 1):pan.set_angle(angle)tilt.set_angle(angle)sleep(.01)sleep(.5)for angle in range(90, -90, -1):pan.set_angle(angle)tilt.set_angle(angle)sleep(.01)sleep(.5)for angle in range(-90, 0, 1):pan.set_angle(angle)tilt.set_angle(angle)sleep(.01)sleep(.5)
  1. 运行效果如下图,如果不想在运行过程中显示网格与关注的手指节点,可以把相应的代码注释掉

在这里插入图片描述

相关文章:

树莓派,mediapipe,Picamera2利用舵机云台追踪人手(PID控制)

一、项目目标 追踪人手大拇指指尖&#xff1a; 当人手移动时&#xff0c;摄像头通过控制两个伺服电机&#xff08;分别是偏航和俯仰&#xff09;把大拇指指尖放到视界的中心位置&#xff0c;本文采用了PID控制伺服电机 Mediapipe Hand简介 MediaPipe 手部标志任务可检测图像…...

DQL查询数据(超重点)以及distinct(去重)

DQL(Data Query Language:数据查询语言) 1.所有查询操作都用 SELECT 2.无论是简单的查询还是复杂的查询它都能做 3.数据库中最核心的语言&#xff0c;最重要的语句 4.使用频率最高的语句 语法&#xff1a; SELECT 字段1&#xff0c;字段2&#xff0c;……FROM 表 有时候…...

【网络奇缘】——奈氏准则和香农定理从理论到实践一站式服务|计算机网络

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 失真 - 信号的变化 影响信号失真的因素&#xff1a; ​编辑 失真的一种现象&#xff1a;码间…...

MongoDB 根据 _id 获取记录的创建时间并回填记录中

MongoDB 集合 test1,有字段 _id&#xff0c;createTime&#xff0c;createTimeStr&#xff0c;name字段 &#xff0c; 查询createTime不为空的&#xff0c;根据 _id 生成该条记录的创建时间时间戳并填写到字段 createTime 字段中 &#xff0c;并打印时间戳 // 查询 createTime…...

【开源】基于JAVA语言的独居老人物资配送系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询社区4.2 新增物资4.3 查询物资4.4 查询物资配送4.5 新增物资配送 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的独居老人物资配送系统&#xff0c;包含了社区档案、…...

网络7层架构

网络 7 层架构 什么是OSI七层模型&#xff1f; OSI模型用于定义并理解数据从一台计算机转移到另一台计算机&#xff0c;在最基本的形式中&#xff0c;两台计算机通过网线和连接器相互连接&#xff0c;在网卡的帮助下共享数据&#xff0c;形成一个网络&#xff0c;但是一台计算…...

【Arthas】Arthas线上trace匿名函数/Lambda表达式/函数式接口

前言 Arthas是一个非常牛B的东西&#xff0c;我非常喜欢用&#xff0c;特别是在定位线上问题的时候&#xff0c;牛逼大发&#xff01; 非常建议所有Java玩家都去学习一下 阅读对象 了解并使用过Arthas了解并使用过trace命令 先说结论 先说结论&#xff0c;lambda表达式的追…...

阿里云“块存储”是系统盘和数据盘的意思

阿里云“块存储”是什么意思&#xff1f;块存储是指阿里云服务器的系统盘或数据盘。块存储EBS&#xff08;Elastic Block Storage&#xff09;是为云服务器ECS提供的低时延、持久性、高可靠的块级随机存储。块存储支持在可用区内自动复制您的数据&#xff0c;防止意外硬件故障导…...

AI赋能金融创新:ChatGPT引领量化交易新时代

文章目录 一、引言二、ChatGPT与量化交易的融合三、实践应用&#xff1a;ChatGPT在量化交易中的成功案例四、挑战与前景五、结论《AI时代Python量化交易实战&#xff1a;ChatGPT让量化交易插上翅膀》&#x1f4da;→ [当当](http://product.dangdang.com/29658180.html) | [京东…...

数字化时代的探索:学生为何对数据可视化趋之若鹜?

随着信息时代的迅猛发展&#xff0c;数据已经成为我们生活中不可或缺的一部分。而在这个数字化浪潮中&#xff0c;越来越多的学生开始关注数据可视化&#xff0c;这并非偶然。下面&#xff0c;我就从可视化从业者的角度出发&#xff0c;简单聊聊为什么越来越多的学生开始关注数…...

vue2、vue3实现用aws s3协议操作minio进行文件存储和读取

亚马逊s3 API文档 最开始安装了aws-sdk/client-s3&#xff0c;但是不知道为什么一直报错&#xff0c;所以用了aws-sdk 准备工作&#xff1a; 需要已经搭建好minio、创建好桶 1. vue2 安装插件 yarn add aws-sdk s3配置 var AWS require("aws-sdk"); AWS.co…...

宏集应用 | 如何通过振动传感器防止造纸工业中的意外故障?

来源&#xff1a;宏集科技 工业物联网 宏集应用 | 如何通过振动传感器防止造纸工业中的意外故障&#xff1f; 原文链接&#xff1a;https://mp.weixin.qq.com/s/Z2qSdJnPLdOxJuG5qz-JJA 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 一 应用背景 在造纸工业中&…...

【华为OD题库-110】反转每对括号间的子串-java

题目 给出一个字符串s(仅含有小写英文字母和括号)。 请你按照从括号内到外的顺序&#xff0c;逐层反转每对匹配括号中的字符串&#xff0c;并返回最终的结果。注意&#xff0c;您的结果中不应包含任何括号。 示例1: 输入: s “(abcd)” 输出: “dcba” 示例2: 输入: s “(u(l…...

如何搭建一个高效的Python开发环境

“工欲善其事&#xff0c;必先利其器”&#xff0c;这里我们来搭建一套高效的 Python 开发环境&#xff0c;为后续的数据分析做准备。 关于高效作业&#xff0c;对于需要编写 Python 代码进行数据分析的工作而言&#xff0c;主要涉及两个方面。 1. 一款具备强大的自动完成和错…...

Reactor 和 Proactor模式,IO复用与epoll、同步IO,异步IO与协程

汽车软件中的CPU密集与IO密集任务 在汽车软件中&#xff0c;涉及到ADAS的长期占用CPU的计算任务可以算的上是CPU密集型。 另外的&#xff0c;众多SOA原子服务或者各种数据收集、处理、分发、log系统&#xff0c;应该算是IO密集型任务。 寻求一些手段优化IO性能的原因 在过去…...

nginx反向代理服务器及负载均衡服务配置

一、正向代理与反向代理 正向代理&#xff1a;是一个位于客户端和原始服务器(oricin server)之间的服务器&#xff0c;为了从原始服务器取得内容&#xff0c;客户端向代理发送一个请求并指定目标(原始服务器)&#xff0c;然后代理向原始服务器转交请求并将获得的内容返回给客户…...

【Log4j2】Log4j2最佳实践:Log4j2配置超过7天压缩,超过3个月删除文件的滚动日志,分别定义info文件和error文件,按照每小时存储

目录 Log4j2配置 springboot多环境日志配置 参考资料 Log4j2配置 如果你想要在控制台输出美化的日志信息&#xff0c;你可以使用Log4j2的ConsoleAppender和AnsiColorConverter来实现。下面是相应的配置示例&#xff1a; <Configuration status"WARN"><…...

windows和Linux如何做强制域名解析

首先我们了解两个问题&#xff1a; 一、域名解析是什么&#xff1f; 域名解析是让我们可以通过网站的域名来找到它对应的IP地址&#xff0c;以便更加方便的访问我们所需访问的网站的一种服务。 它通过DNS服务器来进行&#xff0c;我们输入所想要访问的域名&#xff0c;将会通过…...

5G NTN:通信新天地,卫星通信的奇妙探索

导言 嗨&#xff0c;大家好&#xff01;今天我们要深入了解一项让通信更强大的技术——5G NTN。它和卫星通信结合在一起&#xff0c;为我们带来了通信的新时代。在这篇文章中&#xff0c;我们将用白话文揭示5G NTN和卫星通信的关系&#xff0c;探索这个通信世界的奇妙之旅。 5…...

RabbitMQ的基础使用

/*** 使用rabbitMQ* 1.引用amqp场景 RabbitAutoConfiguration就会自动生效* 2.给容器中自动配置了各种api RabbitTemplate AmqpAdmin CachingConnectionFactory RabbitMessagingTemplate* 所有属性都是 spring.rabbitmq开头* 3.通过注解EnableRabbit使用* 4.监听消息 使用Rabbi…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...