25. [ROS RC์นด] Object Following - 3 & ๊ฐ์ ๋ง๋ฌด๋ฆฌ์ Future Work ์๊ฐ
๐ ์ง๋ ์๊ฐ์ ์ด์ด์, find_ball node๋ฅผ ๋ถ์ํด ๋ณด๊ฒ ์ต๋๋ค. ๐
- ๊ทธ๋ผ ์ด๋ฒ์, main๋ฌธ์ ์๋ ์ด ์๋ง์ ์ฝ๋๋ค์ ๋ฌด์์ผ๊น์?
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 0
params.maxThreshold = 200
# Filter by Area.
params.filterByArea = True
params.minArea = 2000
params.maxArea = 70000 # 640 * 480 = 307,200
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.1
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.2
# Filter by Inertia
params.filterByInertia = True
params.minInertiaRatio = 0.7
rospy.init_node("blob_detector", anonymous=True)
ic = BlobDetector(green_min, green_max, blur, params, detection_window)
-
๋ฌผ์ฒด๋ก ์ธ์๋๊ธฐ ์ํด์๋ ์ต์, ์ต๋ ์ด๋์ ๋ ํฝ์ ๋ฉด์ ์ ๊ฐ์ ธ์ผ ํ๋์ง,
-
์ฌ๊ฐํ ๋ฌผ์ฒด, ์ํ ๋ฌผ์ฒด๋ง ์ธ์ํ ๊ฒ์ธ์ง,
-
์ผ๋ง๋ ํํฐ์ ์ ํฉํด์ผ ๋ฌผ์ฒด๋ก ์ธ์ํ ๊ฒ์ธ์ง ๋ฑ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ค์ ํ๋ ๋ถ๋ถ์ ๋๋ค.
- ๊ฐ ๋งค๊ฐ๋ณ์๋ค์ ๋ํ ์์ธํ ์ค๋ช ์ ๋ ํผ๋ฐ์ค๋ฅผ ๋จ๊ธฐ๊ฒ ์ต๋๋ค.
์ด ๋งค๊ฐ๋ณ์๋ค์, cv2.SimpleBlobDetector_create
์๊ฒ
๋๊ฒจ์ฃผ๋ฉด, ํฝ์
์ฐ์ฐ์ ๊ฑฐ์ณ keypoints
(ํน์ง์ )๋ฅผ ๋ฐํํฉ๋๋ค. ์ด ํน์ง์ ์ด ๋ฐ๋ก ์ฐ๋ฆฌ๊ฐ ์ถ์ ํ๊ณ ์ ํ๋ ๋ฌผ์ฒด์ ์ด๋ฏธ์ง์์์ ์์น๊ฐ
๋์ง์.
- blob_detector
#- Apply blob detection
detector = cv2.SimpleBlobDetector_create(params)
# Reverse the mask: blobs are black on white
reversemask = 255-mask
if imshow:
cv2.imshow("Reverse Mask", reversemask)
cv2.waitKey(0)
keypoints = detector.detect(reversemask)
return keypoints, reversemask
image_blob
ํ ํฝ์
๋๋ค.
- ๋ฌผ์ฒด๋ก ์ธ์๋์๋ค๋ฉด, ์ฌ์ง๊ณผ ๊ฐ์ด ๋นจ๊ฐ ๋๊ทธ๋ผ๋ฏธ๊ฐ ์๊ธฐ๋ ๊ฒ์ด ๋ณด์ด๋ฉฐ,
- ํ๋ ์ฌ๊ฐํ ๋ฐ๊นฅ ๋ถ๋ถ์ ํ๋ฆฌ๊ฒ ์ฒ๋ฆฌ๋์์ง์?
์ฐ์ฐ๋์ ์ค์ด๊ธฐ ์ํด, ๊ทธ๋ฆฌ๊ณ ์ธ๊ณก์ด ์ฌํ ํ ๋๋ฆฌ ๋ถ๋ถ์์์ ์ค์ฐจ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ์ฒ๋ฆฌ์ ๋๋ค.
์ค์ ์ฝ๋์์๋ ์ด๋ป๊ฒ ๊ตฌํ๋์ด ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
- find_ball.py
draw_keypoints#--- Detect blobs
keypoints, mask = blob_detect(cv_image, self._threshold[0], self._threshold[1], self._blur,
blob_params=self._blob_params, search_window=self.detection_window )
#--- Draw search window and blobs
cv_image = blur_outside(cv_image, 10, self.detection_window)
cv_image = draw_window(cv_image, self.detection_window, line=1)
cv_image = draw_frame(cv_image)
cv_image = draw_keypoints(cv_image, keypoints)
try:
self.image_pub.publish(self.bridge.cv2_to_imgmsg(cv_image, "bgr8"))
self.mask_pub.publish(self.bridge.cv2_to_imgmsg(mask, "8UC1"))
- draw_window : ๊ด์ฌ ์ง์ญ์ธ ํ๋ ์ฌ๊ฐํ์ ๊ทธ๋ ค์ค๋๋ค. ์ฌ๊ฐํ์ ํฌ๊ธฐ๋ main์ ๋น์จ ํํ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- blur_outside : ํ๋ ์ฌ๊ฐํ ๋ฐ๊นฅ ๋ถ๋ถ์ ํ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ํด์ค๋๋ค.
- draw_frame : ์ด๋ฏธ์ง ์ค์ฌ์ ์นด๋ฉ๋ผ ํ๋ ์์ ๊ทธ๋ ค์ค๋๋ค.
- draw_keypoints : ์ต์ข
์ ์ผ๋ก, ์ฐ๋ฆฌ๊ฐ ์ถ์ ํ๊ณ ์ ํ๋
keypoints
๋ฅผ ๋นจ๊ฐ ๋๊ทธ๋ผ๋ฏธ๋ก ํ์ํด์ค๋๋ค.
๊ฐ ํจ์๋ค์ ๋ํด์๋
include/blob_detector.py
๋ฅผ ์ฐธ์กฐํ์๊ธธ ๋ฐ๋๋๋ค.
๋๋ง์ publish
for i, keyPoint in enumerate(keypoints):
#--- Here you can implement some tracking algorithm to filter multiple detections
#--- We are simply getting the first result
x = keyPoint.pt[0]
y = keyPoint.pt[1]
s = keyPoint.size
print ("kp %d: s = %3d x = %3d y= %3d"%(i, s, x, y))
#--- Find x and y position in camera adimensional frame
x, y = get_blob_relative_position(cv_image, keyPoint)
self.blob_point.x = x
self.blob_point.y = y
self.blob_pub.publish(self.blob_point)
break
๊ทธ๋ผ ์ต์ข ์ ์ผ๋ก ๋ค์ node์ publish๋๋ ๊ฐ์ ๋ฌด์์ผ๊น์??
์ฌ๋ฌ ๋ฌผ์ฒด๊ฐ ์๋ ๊ฒฝ์ฐ์ ๋๋นํ ์ฝ๋๊ฐ ๋ณด์ด๋ฉฐ,
get_blob_relative_position
๋ก๋ถํฐ์ output์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ด ํจ์๋ฅผ
์ดํด์ผ๊ฒ ์ง์.
- get_blob_relative_position
#---------- Obtain the camera relative frame coordinate of one single keypoint
#-- return(x,y)
def get_blob_relative_position(image, keyPoint):
cols = float(image.shape[0]) # 480
rows = float(image.shape[1]) # 640
# print(rows, cols)
center_x = 0.5*rows # 320
center_y = 0.5*cols # 240
# print(center_x)
x = (keyPoint.pt[0] - center_x)/(center_x)
y = (keyPoint.pt[1] - center_y)/(center_y)
return(x,y)
์... ์ ์๋ฟ์ง ์์ง์? ์์๋ฅผ ํตํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.
ํ์ฌ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉ์ค์ธ ์ด๋ฏธ์ง๋ [640 * 480]์ ํฌ๊ธฐ๋ฅผ ๊ฐ์ต๋๋ค.
๋์ ์ค์ฌ ํฝ์ ์ [320, 240]์ ์์น๋ฅผ ๊ฐ์ต๋๋ค.
์ด ์ํฉ์์, keypoint์ ์์น๊ฐ [370, 240]์ด๋ผ๊ณ ํ๋ค๋ฉด
โ
x = (keyPoint.pt[0] - center_x)/(center_x)
โ x = (370 - 320) / 320
์ด๋ ๊ฒ ๊ณ์ฐํ ์ ์์ต๋๋ค.
์ฆ, publish๋๋ blob_point.x, blob_point.y๋ ์ค์ฌ์ผ๋ก๋ถํฐ keypoint๊ฐ ์ด๋์ ๋ ๋จ์ด์ ธ ์๋์ง๋ฅผ -1 ~ 1 ์ฌ์ด์ ๊ฐ์ผ๋ก ๋ณํ์ํจ ๊ฒฐ๊ณผ๋ผ๊ณ ํ ์ ์์ต๋๋ค.
- ๋ค์ ํฐ๊ทธ๋ฆผ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
- csi_pub : ์นด๋ฉ๋ผ ์ด๋ฏธ์ง๋ฅผ ์ก์ถํด์ฃผ๋ ๋ ธ๋
- blob_detector : ์ปดํจํฐ ๋น์ ์ด ์ ์ฉ๋ ๋ ธ๋, ๋ น์ ๋ฌผ์ฒด๋ฅผ ์ฐพ์๋ด๊ณ , ์ง์์ ์ผ๋ก ์ถ์
- chase_ball : blob_point.x, blob_point.y๋ฅผ ์ ์ด๊ฐ์ธ Twist ํ์์ผ๋ก ๋ณํ
- image_view : ์ด๋ฏธ์ง ํ ํฝ ๋ทฐ์ด
- blob_chase_node : ์ค์ง์ ์ธ PWM ์ ์ด
๊ธธ๊ณ ๊ธธ์์ต๋๋ค, ์ฌ๊ธฐ๊น์ง ๋ฐ๋ผ์์ฃผ์ ์ฌ๋ฌ๋ถ๋ค ๋ชจ๋ ๊ณ ์ ๋ง์ผ์ จ์ต๋๋ค. ๐
์ง๊ธ๊น์ง ์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๊ฒ
๊ธฐ์ด ์ด๋ก ๋ถํฐ ์๋ฎฌ๋ ์ด์ , ๊ทธ๋ฆฌ๊ณ ์ค์ ํ๋์จ์ด ํ๋ก์ ํธ๊น์ง!! ์ ๋ง ๋ง์ ๊ฒ๋ค์ ์ดํด๋ณด์์ต๋๋ค.
์ด ๊ฐ์๋ฅผ ์ ์ดํดํ์ จ๋ค๋ฉด, ์ด์ ROS๋ฅผ ์ฌ์ฉํ ์ค ์๋ค๊ณ ๋งํ ์ ์์ ๊ฒ์ ๋๋ค. ๐ค
โ์ฝ๋์์ ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค๋ฉดโ
- ๋ฒ๊ทธ, ์๋ฌ์ ๊ฒฝ์ฐ ์ฌ๋ฌ๋ถ์ ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ํจ๊ป ๋ฌธ์ ๊ฐ ๋ ์ํฉ์ ์ต๋ํ ์์ธํ ์๋ ค์ฃผ์ธ์!!
- ํน์ ๋ค๋ฅธ ์ฌ๋๋ ์ด๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋? ๊ณต์ ํ๊ณ , ํจ๊ป ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ถ๋ค๋ฉด,
Discussion
์ ์ฌ์ฉํด ์ฃผ์ธ์.
- ๋ค๊ฐ์ด ๋๋ฒ๊น
์ ํด๋ด๋ ๋์ ํ ๋ชจ๋ฅด๊ฒ ๋ค. ๋์์ฃผ์ธ์!! โ
Issues
โ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ฐ๋ฐํ๊ฒฝ, ์ต๋ํ ์์ธํ!!!
๋ค์์ผ๋ก, ๋ญ ํด๋ณผ๊น์?
- Robotics Roadmap
- ROS2
- Navigation Stack
- Moveit
- rosserial_arduino
์ด๋ ๊ฒ ๊ณต๋ถํ ํจํค์ง๋ค์ด ์ ๋ง ๋ง์ต๋๋ค. ์ด๋ฟ๋ง ์๋๋ผ, ํ๋ก์ ํธ๋ฅผ ํด๋ณผ ์๋ ์๊ฒ ์ง์
- Robotis
- Spot Micro ROS
- PixHawk
...
๐์ง๊ธ๊น์ง ROS ๊ฐ์๋ฅผ ์๊ฐํด์ฃผ์ ์ ์ ๋ง ๊ฐ์ฌํฉ๋๋ค ๐
๋๋ถ์ด, ์ดฌ์์ ๋์์ ์ฃผ์ G Camp, AI Robotics KR ๋ถ๋ค๊ป๋ ๊ฐ์ฌ๋๋ฆฝ๋๋ค ๐