라즈베리파이로 yolov5와 서보모터 연동

라즈베리파이에서 yolov5와 mg996r 서보모터를 연동하고 싶습니다 모터 드라이버는 pca9685를 사용하고 로버플로우를 통해서 플라스틱,캔,페트,종이 4가지를 이미지 학습을 진행했고 분류하는거까지 확인했고 서보모터코드도 따로 실행시켜서 동작하는거 확인했습니다 그런데 서보모터 동작하는 코드와 객체인식 코드를 합치니 객체 인식은 되는데 서보모터가 동작하질 않습니다
터미널 창에는
Setting servo 0 to angle 40 (duty cycle: 4177)
Setting servo 1 to angle 100 (duty cycle: 6844)
Setting servo 2 to angle 100 (duty cycle: 6844)
Current classes: {'Can'}
Current classes: {'Can'}
Current classes: {'Can'}
Detected: Can
Exit program
이러고 서보모터는 동작을 안하고 프로그램이 종료됩니다
밑에는 전체 코드입니다

import torch
import cv2
from PIL import Image
import time
import board
import busio
from adafruit_pca9685 import PCA9685
import threading

모델 로드

model = torch.hub.load('ultralytics/yolov5', 'custom', path='/home/JDH/pr/yolov5/best.pt') # custom 모델 경로 지정

카메라 설정

cap = cv2.VideoCapture(0) # 라즈베리파이 카메라 일반적으로 0번 디바이스 사용
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # 프레임 너비 설정
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 프레임 높이 설정

PCA9685 PWM 컨트롤러 초기화

i2c = busio.I2C(board.SCL, board.SDA)
pca9685 = PCA9685(i2c)
pca9685.frequency = 60

서보모터 초기 설정 (서보1번 40도, 서보2,3번 100도)

def initialize_servos():
print("Initializing servos")
set_servo_angle(0, 40)
set_servo_angle(1, 100)
set_servo_angle(2, 100)

서보모터 제어 함수

def set_servo_angle(channel, angle):
pulse = (650 - 150) * angle / 180 + 150 # 각도에 따른 펄스 계산
duty_cycle = int(pulse * 0xFFFF / 4096) # 듀티 사이클 계산
print(f"Setting servo {channel} to angle {angle} (duty cycle: {duty_cycle})")
pca9685.channels[channel].duty_cycle = duty_cycle # 해당 채널에 듀티 사이클 설정

전역 변수

detected_class = None
consecutive_detections = {}
running = True

초기화

initialize_servos()

def read_frames():
global detected_class, running
detection_counts = {}

while running:
    ret, frame = cap.read()
    if not ret:
        print("Failed to capture frame")
        break
    
    # OpenCV 이미지를 PIL 이미지로 변환
    img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    results = model(img)

    # 결과 분석
    current_classes = set()
    for detection in results.pred[0]:
        _, _, _, _, _, cls = detection
        class_name = results.names[int(cls)]
        current_classes.add(class_name)

    # 디버깅을 위한 출력
    print(f"Current classes: {current_classes}")

    # 연속 감지 카운트
    for class_name in current_classes:
        if class_name in detection_counts:
            detection_counts[class_name] += 1
        else:
            detection_counts[class_name] = 1

        # 3회 연속 감지 확인
        if detection_counts[class_name] >= 3:
            detected_class = class_name
            running = False
            break

    # 감지되지 않은 클래스는 카운트 초기화
    for class_name in detection_counts:
        if class_name not in current_classes:
            detection_counts[class_name] = 0

    # 매 프레임마다 키 입력 확인
    if cv2.waitKey(1) & 0xFF == ord('q'):  # q를 누르면 종료
        running = False
        break

def control_servos():
global detected_class, running
while running:
time.sleep(0.1)

if detected_class:
    print(f"Detected: {detected_class}")  # 감지된 객체 출력
    if detected_class == 'pet':
        set_servo_angle(0, 0)
        time.sleep(2)
        set_servo_angle(0, 40)
        time.sleep(2)
        set_servo_angle(1, 25)
        time.sleep(2)
        set_servo_angle(1, 100)
    elif detected_class == 'plastic':
        set_servo_angle(0, 80)
        time.sleep(2)
        set_servo_angle(0, 40)
        time.sleep(2)
        set_servo_angle(1, 175)
        time.sleep(2)
        set_servo_angle(1, 100)
    elif detected_class == 'paper':
        set_servo_angle(0, 0)
        time.sleep(2)
        set_servo_angle(0, 40)
        time.sleep(2)
        set_servo_angle(2, 25)
        time.sleep(2)
        set_servo_angle(2, 100)
    elif detected_class == 'can':
        set_servo_angle(0, 80)
        time.sleep(2)
        set_servo_angle(0, 40)
        time.sleep(2)
        set_servo_angle(2, 175)
        time.sleep(2)
        set_servo_angle(2, 100)

프레임 읽기와 처리 스레드 시작

read_thread = threading.Thread(target=read_frames)
servo_thread = threading.Thread(target=control_servos)
read_thread.start()
servo_thread.start()

read_thread.join()
servo_thread.join()

cap.release()
pca9685.deinit() # PCA9685 리소스 정리
print("Exit program")

  • 아래 답변은 Anthropic의 Claude-3 Opus 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요! 라즈베리파이에서 YOLOv5와 서보모터를 연동하는 과정에서 겪고 있는 어려움을 잘 이해했습니다. 객체 인식은 잘 되지만 서보모터가 동작하지 않는 문제를 해결해 보도록 하겠습니다.

먼저, 코드를 살펴보니 객체 인식 후 서보모터를 제어하는 부분에서 개선이 필요해 보입니다. 다음과 같이 수정해 보시기 바랍니다:

def control_servos():
    global detected_class, running
    while running:
        if detected_class:
            print(f"Detected: {detected_class}")  # 감지된 객체 출력
            if detected_class == 'pet':
                set_servo_angle(0, 0)
                time.sleep(2)
                set_servo_angle(0, 40)
                time.sleep(2)
                set_servo_angle(1, 25)
                time.sleep(2)
                set_servo_angle(1, 100)
            elif detected_class == 'plastic':
                set_servo_angle(0, 80)
                time.sleep(2)
                set_servo_angle(0, 40)
                time.sleep(2)
                set_servo_angle(1, 175)
                time.sleep(2)
                set_servo_angle(1, 100)
            elif detected_class == 'paper':
                set_servo_angle(0, 0)
                time.sleep(2)
                set_servo_angle(0, 40)
                time.sleep(2)
                set_servo_angle(2, 25)
                time.sleep(2)
                set_servo_angle(2, 100)
            elif detected_class == 'can':
                set_servo_angle(0, 80)
                time.sleep(2)
                set_servo_angle(0, 40)
                time.sleep(2)
                set_servo_angle(2, 175)
                time.sleep(2)
                set_servo_angle(2, 100)
            
            # 객체 감지 후 detected_class 초기화
            detected_class = None
        else:
            time.sleep(0.1)

위 코드에서는 객체 감지 후 해당 동작을 수행한 뒤 detected_classNone으로 초기화합니다. 이렇게 하면 서보모터가 연속적으로 동작하는 것을 방지할 수 있습니다.

추가로, 프로그램 종료 시 서보모터를 초기 위치로 되돌리는 것이 좋습니다. 마지막 부분에 다음 코드를 추가해 보세요:

# 서보모터 초기 위치로 복귀
initialize_servos()

cap.release()
pca9685.deinit()  # PCA9685 리소스 정리
print("Exit program")

이렇게 수정한 후 다시 실행해 보시기 바랍니다. 객체 인식 후 서보모터가 의도한 대로 동작할 것으로 예상됩니다.

더 자세한 내용은 아래 PyTorch 튜토리얼을 참고하시면 도움이 될 것 같습니다:

제 답변이 문제 해결에 도움이 되었기를 바랍니다. 혹시 추가 질문이나 어려움이 있다면 언제든 말씀해 주세요. 함께 해결방안을 찾아보도록 하겠습니다. 감사합니다!


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 OpenAI의 GPT-4o 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요! 라즈베리파이에서 YOLOv5와 서보모터를 연동하는 작업을 하시느라 고생이 많으시네요. 문제를 요약해보면, YOLOv5를 사용하여 객체를 인식하고, 인식된 객체에 따라 서보모터를 제어하려고 하는데, 객체 인식은 잘 되지만 서보모터가 동작하지 않는 문제가 발생하고 있는 것 같습니다.

이 문제를 해결하기 위해 몇 가지 점검해볼 사항과 개선할 방법을 제안드리겠습니다.

  1. 스레드 동기화 문제: 현재 코드에서는 두 개의 스레드가 동시에 실행되는데, detected_class 변수를 공유하고 있습니다. 이 변수에 대한 접근이 동기화되지 않으면 예상치 못한 동작이 발생할 수 있습니다.

  2. 서보모터 제어 코드의 위치: 서보모터 제어 코드가 detected_class가 설정된 후에만 실행되도록 되어 있습니다. 하지만 detected_class가 설정된 후에도 running 변수가 False로 설정되면 서보모터 제어 코드가 실행되지 않을 수 있습니다.

  3. 디버깅 출력: 서보모터가 동작하지 않는 이유를 파악하기 위해 디버깅 출력을 추가하는 것이 좋습니다.

아래는 개선된 코드입니다. 스레드 동기화를 위해 threading.Lock을 사용하고, 서보모터 제어 코드가 제대로 실행되는지 확인하기 위해 디버깅 출력을 추가했습니다.

import torch
import cv2
from PIL import Image
import time
import board
import busio
from adafruit_pca9685 import PCA9685
import threading

# 모델 로드
model = torch.hub.load('ultralytics/yolov5', 'custom', path='/home/JDH/pr/yolov5/best.pt')

# 카메라 설정
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

# PCA9685 PWM 컨트롤러 초기화
i2c = busio.I2C(board.SCL, board.SDA)
pca9685 = PCA9685(i2c)
pca9685.frequency = 60

# 서보모터 초기 설정
def initialize_servos():
    print("Initializing servos")
    set_servo_angle(0, 40)
    set_servo_angle(1, 100)
    set_servo_angle(2, 100)

# 서보모터 제어 함수
def set_servo_angle(channel, angle):
    pulse = (650 - 150) * angle / 180 + 150
    duty_cycle = int(pulse * 0xFFFF / 4096)
    print(f"Setting servo {channel} to angle {angle} (duty cycle: {duty_cycle})")
    pca9685.channels[channel].duty_cycle = duty_cycle

# 전역 변수
detected_class = None
consecutive_detections = {}
running = True
lock = threading.Lock()

# 초기화
initialize_servos()

def read_frames():
    global detected_class, running
    detection_counts = {}

    while running:
        ret, frame = cap.read()
        if not ret:
            print("Failed to capture frame")
            break

        img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        results = model(img)

        current_classes = set()
        for detection in results.pred[0]:
            _, _, _, _, _, cls = detection
            class_name = results.names[int(cls)]
            current_classes.add(class_name)

        print(f"Current classes: {current_classes}")

        for class_name in current_classes:
            if class_name in detection_counts:
                detection_counts[class_name] += 1
            else:
                detection_counts[class_name] = 1

            if detection_counts[class_name] >= 3:
                with lock:
                    detected_class = class_name
                running = False
                break

        for class_name in detection_counts:
            if class_name not in current_classes:
                detection_counts[class_name] = 0

        if cv2.waitKey(1) & 0xFF == ord('q'):
            running = False
            break

def control_servos():
    global detected_class, running
    while running:
        time.sleep(0.1)
        with lock:
            if detected_class:
                print(f"Detected: {detected_class}")
                if detected_class == 'pet':
                    set_servo_angle(0, 0)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(1, 25)
                    time.sleep(2)
                    set_servo_angle(1, 100)
                elif detected_class == 'plastic':
                    set_servo_angle(0, 80)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(1, 175)
                    time.sleep(2)
                    set_servo_angle(1, 100)
                elif detected_class == 'paper':
                    set_servo_angle(0, 0)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(2, 25)
                    time.sleep(2)
                    set_servo_angle(2, 100)
                elif detected_class == 'can':
                    set_servo_angle(0, 80)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(2, 175)
                    time.sleep(2)
                    set_servo_angle(2, 100)
                detected_class = None

read_thread = threading.Thread(target=read_frames)
servo_thread = threading.Thread(target=control_servos)
read_thread.start()
servo_thread.start()

read_thread.join()
servo_thread.join()

cap.release()
pca9685.deinit()
print("Exit program")

이 코드는 detected_class 변수를 접근할 때마다 lock을 사용하여 스레드 동기화를 보장합니다. 또한, 서보모터 제어 코드가 실행될 때마다 detected_classNone으로 설정하여 반복 실행되지 않도록 했습니다.

추가로, PyTorch 공식 튜토리얼을 참고하시면 더 많은 정보를 얻으실 수 있습니다:

혹시 더 궁금한 점이 있거나 문제가 해결되지 않으면 언제든지 질문해 주세요. 제 답변이 틀릴 수도 있으니, 추가적인 확인도 부탁드립니다. 화이팅입니다!


  • 이 답변이 도움이 되셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 Google의 Gemini Pro 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요. 라즈베리파이에서 YOLOv5와 서보모터를 연동하시는 중에 문제가 생기셨군요. 서보모터가 동작하지 않는 문제를 해결해 드리겠습니다.

먼저 코드를 살펴보니 read_frames() 함수와 control_servos() 함수가 별도의 스레드로 실행되고 있음을 알 수 있습니다. 이러한 스레드 간의 동기화 문제로 인해 서보모터가 제대로 동작하지 않을 수 있습니다. 문제를 해결하려면 두 스레드 간의 공유 데이터에 대해 명시적으로 동기화를 수행해야 합니다.

다음은 두 스레드 간의 동기화를 수행한 수정된 코드입니다.

import threading
import time
import board
import busio
from adafruit_pca9685 import PCA9685

# 공유 데이터
detected_class = None
running = True
lock = threading.Lock()  # 동기화용 잠금 객체

def read_frames():
    global detected_class, running, lock

    while running:
        ret, frame = cap.read()
        if not ret:
            print("Failed to capture frame")
            break

        # OpenCV 이미지를 PIL 이미지로 변환
        img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        results = model(img)

        # 결과 분석
        current_classes = set()
        for detection in results.pred[0]:
            _, _, _, _, _, cls = detection
            class_name = results.names[int(cls)]
            current_classes.add(class_name)

        # 디버깅을 위한 출력
        print(f"Current classes: {current_classes}")

        # 연속 감지 카운트
        with lock:
            for class_name in current_classes:
                if class_name in detection_counts:
                    detection_counts[class_name] += 1
                else:
                    detection_counts[class_name] = 1

                # 3회 연속 감지 확인
                if detection_counts[class_name] >= 3:
                    detected_class = class_name
                    running = False
                    break

        # 감지되지 않은 클래스는 카운트 초기화
        with lock:
            for class_name in detection_counts:
                if class_name not in current_classes:
                    detection_counts[class_name] = 0

        # 매 프레임마다 키 입력 확인
        if cv2.waitKey(1) & 0xFF == ord('q'):  # q를 누르면 종료
            with lock:
                running = False
            break


def control_servos():
    global detected_class, running, lock

    while running:
        time.sleep(0.1)

        with lock:  # 잠금 객체를 사용하여 동기화
            if detected_class:
                print(f"Detected: {detected_class}")  # 감지된 객체 출력
                if detected_class == 'pet':
                    set_servo_angle(0, 0)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(1, 25)
                    time.sleep(2)
                    set_servo_angle(1, 100)
                elif detected_class == 'plastic':
                    set_servo_angle(0, 80)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(1, 175)
                    time.sleep(2)
                    set_servo_angle(1, 100)
                elif detected_class == 'paper':
                    set_servo_angle(0, 0)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(2, 25)
                    time.sleep(2)
                    set_servo_angle(2, 100)
                elif detected_class == 'can':
                    set_servo_angle(0, 80)
                    time.sleep(2)
                    set_servo_angle(0, 40)
                    time.sleep(2)
                    set_servo_angle(2, 175)
                    time.sleep(2)
                    set_servo_angle(2, 100)

# ... 나머지 코드는 동일 ...

이 코드에서는 lock 객체를 사용하여 두 스레드가 detected_class 변수에 동시에 액세스하는 것을 방지합니다. read_frames() 함수에서 객체가 감지되면 detected_class 변수에 객체 클래스가 설정되고 running 플래그가 False로 설정됩니다. control_servos() 함수에서는 lock 객체를 사용하여 detected_class 변수에 액세스하고 서보모터를 제어합니다.

이 수정된 코드를 사용하면 서보모터가 올바르게 동작할 것입니다.


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 OpenAI의 GPT-4-Turbo 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요! 라즈베리파이에서 YOLOv5와 서보모터(mg996r)를 연동하는 과정에서 발생한 문제에 대해 질문해주셨군요. 객체 인식은 성공적으로 이루어지지만, 서보모터가 동작하지 않는 상황에 대해 도움을 요청하셨습니다. 제공해주신 코드와 설명을 바탕으로 문제를 정확히 이해했습니다. 이제 해결 방안을 제시해보겠습니다.

문제의 핵심은 서보모터 제어 코드와 객체 인식 코드를 합쳤을 때, 서보모터가 동작하지 않는 것입니다. 코드를 살펴본 결과, 스레드를 사용하여 프레임 읽기와 서보모터 제어를 병렬로 처리하고 있습니다. 이 경우, 가장 흔한 문제는 스레드 간의 동기화 문제입니다. detected_class 변수가 업데이트 되었을 때, control_servos 함수 내에서 해당 변경을 적절히 감지하지 못하고 있을 가능성이 있습니다.

해결 방안

  1. 변수 동기화 확인: detected_class 변수가 업데이트 되었을 때, 이를 control_servos 스레드가 제대로 감지하고 있는지 확인해야 합니다. Python의 threading 모듈에서 제공하는 EventCondition 객체를 사용하여 스레드 간의 동기화를 개선할 수 있습니다.

  2. 서보모터 제어 로직 검토: 서보모터 제어 로직이 정상적으로 동작하는지 독립적으로 다시 한번 확인해보세요. PCA9685 모듈의 초기화 및 서보모터 제어 부분에 문제가 없는지 점검합니다.

  3. 전역 변수 사용 최소화: 전역 변수의 사용을 최소화하고, 스레드 간 데이터를 전달하는 다른 방법을 고려해보세요. 예를 들어, Queue를 사용하여 스레드 간에 객체 인식 결과를 안전하게 전달할 수 있습니다.

아래는 수정된 코드의 예시 중 일부입니다. 이 예시에서는 Queue를 사용하여 객체 인식 결과를 control_servos 스레드로 전달하는 방법을 보여줍니다.

from queue import Queue
import threading

# 객체 인식 결과를 저장할 큐 생성
detection_queue = Queue()

def control_servos():
    global running
    while running:
        if not detection_queue.empty():
            detected_class = detection_queue.get()  # 큐에서 객체 인식 결과 가져오기
            print(f"Detected: {detected_class}")
            # 여기에 서보모터 제어 로직 추가
            # 예: if detected_class == 'pet': ...

def read_frames():
    global running
    while running:
        # 프레임 처리 및 객체 인식 로직
        # 예: detected_class = 'pet'
        detection_queue.put(detected_class)  # 큐에 객체 인식 결과 추가

이 방법을 사용하면, 스레드 간의 동기화 문제를 해결하고, 데이터 전달이 보다 안전하게 이루어질 수 있습니다.

추가적으로, PyTorch와 관련된 더 자세한 정보나 튜토리얼이 필요하시다면, PyTorch 공식 문서를 참고하시기 바랍니다: PyTorch Tutorials.

제시한 해결 방안이 문제를 해결하는 데 도움이 되길 바랍니다. 만약 문제가 계속된다면, 추가적인 정보와 함께 다시 질문해주시기 바랍니다. 제 답변이 정확하지 않을 수도 있으니, 이 점 양해 부탁드립니다.


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!