1. 목표
Moveit!으로 장애물 회피를 하기 위해 Depth camera를 사용하고 있습니다.
Intel Realsense의 D435 뎁스 카메라는 뎁스 이미지 뿐만 아니라 RGB 이미지도 제공합니다.
이번에는 Moveit!을 작동시키는 동시에 뎁스 카메라의 이미지를 가져와 YOLOv7을 작동시켜보고자 합니다.
이렇게 하면 카메라를 여러 개 사용하지 않아도 되고,
하나의 카메라로 장애물 인식과 검사 대상 인식을 동시에 하기 때문에 좌표계가 일치하므로 정확도가 더욱 좋아집니다.
2. 방법
2-1. 기본 YOLOv7 작동 시키기
위 블로그를 참고해서 YOLOv7을 설치했습니다.
requirement.txt를 실행하면서 많은 오류가 발생할 수 있는데 하나하나 버전을 맞춰줍니다.
2-2. cv_bridge 설치
ROS에서 만든 이미지를 opencv로 가져오기 위해 필요한 라이브러리입니다.
인터넷을 돌아다니면서 저한테 맞도록 설치 방법을 정리해두었습니다.
2-3. 코드 수정
기본적으로 YOLOv7에서 웹캠으로 실시간 인식을 할 때에는 cv2.VideoCapture('카메라 경로')함수를 사용합니다.
하지만 우리는 cv_bridge를 이용해 이미지를 가져오기 때문에 여기에 맞게 코드를 수정해줍니다.
detect.py 파일은 건드리지 않습니다.
굳이 건드리자면 맨 밑에 parser 중 source에 관련된 줄을
parser.add_argument('--source', type=str, default='0', help='source') # file/folder, 0 for webcam
로 바꿔주면 나중에 detect 파일을 실행할 때 뒤에 추가 설정을 안 해줘도 돼서 편합니다.
우리가 건드려야 할 파일은 utils 폴더 안의 datasets.py파일입니다.
폴더를 직접 들어가서 열어도 되고
vscode를 쓰고 있다면 detect파일 맨 위에 import하는 곳에서 ctrl+클릭 or F12로 이동 가능합니다.
먼저 datasets.py 파일 맨 위에 필요한 라이브러리를 추가해줍니다.
import rospy
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
그 밑에 뎁스 이미지를 가져오는 노드를 만들고, ROS 이미지 메시지를 OpenCV 이미지 객체로 변환시켜주는 함수를 만듭니다.
bridge = CvBridge() # ROS 이미지 메시지와 OpenCV 이미지 객체 간 변환을 위한 객체 생성
def img_callback(data):
global cv_image # 다른 def에서 사용하기 위한 전역 변수 설정
cv_image = bridge.imgmsg_to_cv2(data,"bgr8") # ROS 이미지 메시지를 OpenCV 이미지 객체로 변환
rospy.init_node('cam_data', anonymous=True) # ROS 노드 초기화
# 'camera/color/image_raw' 토픽에서 이미지 데이터를 구독하고, 새로운 데이터가 도착할 때마다 image_callback 함수를 호출
rospy.Subscriber("camera/color/image_raw",Image,img_callback)
그리고 밑으로 쭉 내리다보면 Loadstream 클래스로 옵니다. 이 부분이 웹캠 이미지를 받고 업데이트해주는 곳입니다.
여기서 def __init__과 def update를 수정해줍니다.
def __init__(self, sources='streams.txt', img_size=640, stride=32):
self.mode = 'stream'
self.img_size = img_size
self.stride = stride
if os.path.isfile(sources):
with open(sources, 'r') as f:
sources = [x.strip() for x in f.read().strip().splitlines() if len(x.strip())]
else:
sources = [sources]
n = len(sources)
self.imgs = [None] * n
self.sources = [clean_str(x) for x in sources] # clean source names for later
for i, s in enumerate(sources):
# Start the thread to read frames from the video stream
cap = cv_image
self.imgs[i] = cap # guarantee first frame
thread = Thread(target=self.update, args=([i, cap]), daemon=True)
thread.start()
print('') # newline
# check for common shapes
s = np.stack([letterbox(x, self.img_size, stride=self.stride)[0].shape for x in self.imgs], 0) # shapes
self.rect = np.unique(s, axis=0).shape[0] == 1 # rect inference if all shapes equal
if not self.rect:
print('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.')
def update(self, index, cap):
# Read next stream frame in a daemon thread
n = 0
while 1:
self.imgs[index] = cv_image
time.sleep(1 / 30) # wait time
웹캠을 사용하기 위해 있던 cv2.VideoCapture()함수 대신 cv_bridge로 가져온 이미지를 넣어주고
cv2.VideoCapture()를 쓰기 위해 필요했던 함수들은 지우거나 수정을 해줬습니다. (cap 관련 함수)
update 함수도 이에 맞춰 수정해주었습니다.
그리고 실행해주면
잘 작동하는 것을 확인할 수 있습니다 :)
이제 인식하고자 하는 대상을 커스텀 학습해서 저한테 맞는 weight 파일로 바꿔줘야겠네요.
댓글