二维舵机颜色追踪,使用树莓派+opencv+usb摄像头+两个舵机实现颜色追踪,采用pid调控

效果演示

二维云台颜色追踪

使用树莓派+opencv+usb摄像头+两个舵机实现颜色追踪,采用pid调控

python 复制代码
import cv2
import time
import numpy as np
from threading import Thread
from servo import Servo
from pid import PID

# 初始化伺服电机
pan = Servo(pin=19)
tilt = Servo(pin=16)
panAngle = 0
tiltAngle = 0
pan.set_angle(panAngle)
tilt.set_angle(tiltAngle)

# 定义视频流类
class VideoStream:
    def __init__(self, src=0):
        self.stream = cv2.VideoCapture(src)
        self.stream.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
        self.stream.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
        self.stream.set(cv2.CAP_PROP_FPS, 30)
        self.grabbed, self.frame = self.stream.read()
        self.stopped = False

    def start(self):
        Thread(target=self.update, args=()).start()
        return self

    def update(self):
        while not self.stopped:
            self.grabbed, self.frame = self.stream.read()

    def read(self):
        return self.frame

    def stop(self):
        self.stopped = True
        self.stream.release()

# 启动视频流
vs = VideoStream(src=0).start()

# 设置 PID 控制器参数
pan_pid = PID(0.05, 0.01, 0.001)
tilt_pid = PID(0.05, 0.01, 0.001)
pan_pid.initialize()
tilt_pid.initialize()

# 计算帧率
fps = 0
pos = (10, 20)
font = cv2.FONT_HERSHEY_SIMPLEX
height = 0.5
weight = 1
myColor = (0, 0, 255)

def nothing(x):
    pass

cv2.namedWindow('PID Tuner')
cv2.createTrackbar('Pan Kp', 'PID Tuner', int(pan_pid.kP * 100), 100, nothing)
cv2.createTrackbar('Pan Ki', 'PID Tuner', int(pan_pid.kI * 100), 100, nothing)
cv2.createTrackbar('Pan Kd', 'PID Tuner', int(pan_pid.kD * 100), 100, nothing)
cv2.createTrackbar('Tilt Kp', 'PID Tuner', int(tilt_pid.kP * 100), 100, nothing)
cv2.createTrackbar('Tilt Ki', 'PID Tuner', int(tilt_pid.kI * 100), 100, nothing)
cv2.createTrackbar('Tilt Kd', 'PID Tuner', int(tilt_pid.kD * 100), 100, nothing)

last_update = time.time()
update_interval = 0.1  # 控制更新频率

try:
    while True:
        tStart = time.time()
        frame = vs.read()
        if frame is None:
            print("Failed to grab frame")
            break

        frame = cv2.flip(frame, 1)
        frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        cv2.putText(frame, str(int(fps)) + ' FPS', pos, font, height, myColor, weight)

        lowerBound = np.array([0, 147, 114])
        upperBound = np.array([88, 255, 255])

        myMask = cv2.inRange(frameHSV, lowerBound, upperBound)
        contours, _ = cv2.findContours(myMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        if len(contours) > 0:
            contours = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
            contour = contours[0]
            x, y, w, h = cv2.boundingRect(contour)
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 3)

            # 计算误差
            errorX = (x + w / 2) - (320 / 2)
            errorY = (240 / 2) - (y + h / 2)  # 反转误差方向

            if time.time() - last_update > update_interval:
                # 获取PID参数并更新
                pan_pid.kP = cv2.getTrackbarPos('Pan Kp', 'PID Tuner') / 100
                pan_pid.kI = cv2.getTrackbarPos('Pan Ki', 'PID Tuner') / 100
                pan_pid.kD = cv2.getTrackbarPos('Pan Kd', 'PID Tuner') / 100
                tilt_pid.kP = cv2.getTrackbarPos('Tilt Kp', 'PID Tuner') / 100
                tilt_pid.kI = cv2.getTrackbarPos('Tilt Ki', 'PID Tuner') / 100
                tilt_pid.kD = cv2.getTrackbarPos('Tilt Kd', 'PID Tuner') / 100

                panAdjustment = pan_pid.update(errorX, sleep=0)
                tiltAdjustment = tilt_pid.update(errorY, sleep=0)

                panAngle += panAdjustment
                tiltAngle += tiltAdjustment

                # 限制角度范围
                panAngle = max(-90, min(panAngle, 120))
                tiltAngle = max(-90, min(tiltAngle, 90))

                # 设置伺服电机角度
                pan.set_angle(panAngle)
                tilt.set_angle(tiltAngle)
                last_update = time.time()

        # 仅在图形环境中显示图像窗口
        try:
            cv2.imshow('Camera', frame)
            cv2.imshow('Mask', myMask)
        except cv2.error as e:
            print(f"OpenCV error: {e}")

        if cv2.waitKey(1) == ord('q'):
            break

        tEnd = time.time()
        loopTime = tEnd - tStart
        fps = .9 * fps + .1 * (1 / loopTime)

finally:
    vs.stop()
    cv2.destroyAllWindows()

在相同文件路径下创建一个名为pid.py的文件

python 复制代码
# pid.py
# -*- coding: UTF-8 -*-
# 调用必需库
import time

class PID:
    def __init__(self, kP=1, kI=0, kD=0):
        # 初始化参数
        self.kP = kP
        self.kI = kI
        self.kD = kD

    def initialize(self):
        # 初始化当前时间和上一次计算的时间
        self.currTime = time.time()
        self.prevTime = self.currTime

        # 初始化上一次计算的误差
        self.prevError = 0

        # 初始化误差的比例值,积分值和微分值
        self.cP = 0
        self.cI = 0
        self.cD = 0

    def update(self, error, sleep=0.2):
        # 暂停
        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.currTime
        self.prevError = error

        # 返回输出值
        return sum([
            self.kP * self.cP,
            self.kI * self.cI,
            self.kD * self.cD])

    def set_Kp(self, kP):
        self.kP = kP

    def set_Ki(self, kI):
        self.kI = kI

    def set_Kd(self, kD):
        self.kD = kD

在相同文件路径下创建一个名为servo.py的文件

python 复制代码
import pigpio
from time import sleep
import subprocess

# Start the pigpiod daemon
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:
        break
    sleep(0.2)
if status != 0:
    print(status, result)

class Servo():
    MAX_PW = 1250  # 0.5/20*100
    MIN_PW = 250   # 2.5/20*100
    _freq = 50     # 50 Hz, 20ms

    def __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 = 0
        self.max_angle = max_angle
        self.min_angle = min_angle
        self.pi.set_PWM_dutycycle(self.pin, 0)

    def set_angle(self, angle):
        if angle > self.max_angle:
            angle = self.max_angle
        elif angle < self.min_angle:
            angle = self.min_angle
        self.angle = angle
        duty = self.map(angle, -90, 90, 250, 1250)
        self.pi.set_PWM_dutycycle(self.pin, duty)

    def get_angle(self):
        return self.angle

    def stop(self):
        self.pi.set_PWM_dutycycle(self.pin, 0)
        self.pi.stop()

    def map(self, x, in_min, in_max, out_min, out_max):
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min

if __name__ == '__main__':
    pan = Servo(pin=13, max_angle=90, min_angle=-90)
    tilt = Servo(pin=12, max_angle=30, min_angle=-90)
    panAngle = 0
    tiltAngle = 0
    pan.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)
相关推荐
xiaok6 分钟前
docker network create langbot-network这条命令在dify输入还是在langbot中输入
人工智能
It_张8 分钟前
LLM(大语言模型)的工作原理 图文讲解
人工智能·语言模型·自然语言处理
Darach10 分钟前
坐姿检测Python实现
人工智能·python
xiaok10 分钟前
LangBot 和消息平台均运行在 Docker 容器中
人工智能
queeny19 分钟前
Datawhale AI夏令营 科大讯飞AI大赛(大模型技术) Task3 心得
人工智能
ToTensor19 分钟前
Paraformer实时语音识别中的碎碎念
人工智能·语音识别·xcode
陈佬昔没带相机26 分钟前
Mac Mini 玩大模型避坑指南
人工智能·mac
重启的码农26 分钟前
llama.cpp 分布式推理介绍(4) RPC 服务器 (rpc_server)
c++·人工智能·神经网络
柠檬味拥抱27 分钟前
不确定环境下AI Agent的贝叶斯信念更新策略研究
人工智能
Nona996130 分钟前
从零开始学AI——13
人工智能