- 대회 기간:2021.3~2021.8
1. 대회 진행 전략
2. 알고리즘 설명
3. 소스 코드 설명
4. 코드론 DIY 데이터시트
5. 팀원
6. 라이센스
- Beautiful is better than ugly
- Explicit is better than implicit
- Simple is better than complex
- Complex is better than complicated
- Flat is better than nested
- More
- 라즈베리 파이를 통해 PC로 드론 제어
- 파이썬 코드 편집
- Guide
- 파이썬 코드 편집
- Guide
- 라즈베리 파이와 연동
- Guide
- 데이터 파일 전송
- Guide
- 라즈베리 파이를 PC로 디스플레이
- Guide
- github 업데이트
- Guide
- 드론의 위치 제어를 위한 throttle, pitch, roll 및 yaw 제어
def sendControlPosition(self, positionX, positionY, positionZ, velocity, heading, rotationalVelocity):
Variable name | form | range | unit | explain |
---|---|---|---|---|
position X | float | -10.0 ~ 10.0 | meter | forward(+), behind(-) |
position Y | float | -10.0 ~ 10.0 | meter | left(+), right(-) |
position Z | float | -10.0 ~ 10.0 | meter | up(+), down(-) |
velocity | float | 0.5 ~ 2.0 | meter | moving velocity |
heading | Int16 | -360 ~ 360 | degree | left turn(+), right turn(-) |
rotationalVelocity | Int16 | 10 ~ 360 | degree/s | rotational velocity |
컨트롤러를 사용하여 센서를 리셋
안정적인 호버링을 위한 트림 설정
- Go to manual
try-exception 구문을 사용하여 소프트웨어 오류 발생 시 드론이 Landing하도록 설정
except Exception as e:
print(e)
drone.sendLanding()
알고리즘 순서도 설명
- 자식 계층이 없고 부모 계층이 있는 컨투어 중 면적이 가장 큰 컨투어 검출
- 링의 컨투어 인덱스를 반환
def find_ring(cnt, hier): p=0 # 링의 컨투어 인덱스 저장하는 변수 선언 s = cv2.contourArea(cnt[0]) for i in range(len(hier[0])): # len(hier[0]) : 컨투어 개수 area = cv2.contourArea(cnt[i]) if hier[0, i, 2] == -1 and hier[0, i, 3] != -1: # 자식없고 부모 있음 if area != 0 : p = i else: # area==0 pass elif hier[0, i, 2] != -1 and hier[0, i, 3] != -1: # 자식 있고 부모 있음 if area < s: s = area p = i return p
1단계 링 컨투어 | 1단계 표식 컨투어 |
---|
- 링의 중심을 컨투어 처리
- 링의 중심으로 카메라 중심 이동
def focus(x, y): #링의 중심을 카메라의 중심으로 이동 x_= x - 320 y_= y - 240> if x<280 or x>360 or y<200 or y>280 : x__= x_ * 0.0229 y__= y_ * 0.0204 px = round(x__, 3) py = round(y__, 3) print("locate") drone.sendControlPosition16(0, px, py, 1, 0, 0) for i in range(4, 0, -1): print("{0}".format(i)) time.sleep(1)
- 사각형 표식(4cm x 4cm)을 컨투어 처리
- 표식의 중심으로 카메라 중심 이동
def focus2(cx, cy): #사각형 표식의 중점 카메라와 맞추기 cx_= 320 - cx cy_= 240 - cy if cx<250 or cx>390 or cy<170 or cy>310 : # 140*140 cx__= cx_ * 0.0229 cy__= cy_ * 0.0204 px = round(cx__, 3) py = round(cy__, 3) print("locate") drone.sendControlPosition16(0, px, py, 1, 0, 0) for i in range(4, 0, -1): print("{0}".format(i)) time.sleep(1) else : pass
1단계 표식 컨투어 | 2단계 표식 컨투어 |
---|
- radius < 240인 경우 ring 앞까지 전진 후 n에 1을 더함
def shift_ring1(b_pix, b_pix_thr, R_pix): #링 앞까지 전진_링 안잘렸을 때 global n if n==2: print("move") drone.sendControlPosition16(9, 0, 0, 3, 0, 0) for i in range(3, 0, -1): print("{0}".format(i)) time.sleep(1) print("move") drone.sendControlPosition16(9, 0, 0, 3, 0, 0) for i in range(3, 0, -1): print("{0}".format(i)) time.sleep(1) print("Landing") drone.sendLanding() for i in range(5, 0, -1): print("{0}".format(i)) time.sleep(1) else: if b_pix < b_pix_thr: print("move") drone.sendControlPosition16(3, 0, 0, 3, 0, 0) #20cm 씩 이동 for i in range(1, 0, -1): print("{0}".format(i)) time.sleep(1) else: if R_pix > 80: # 1.8m 앞으로 이동 후, turn left print("move") drone.sendControlPosition16(10, 0, 0, 3, 0, 0) for i in range(3, 0, -1): print("{0}".format(i)) time.sleep(1) print("move") drone.sendControlPosition16(8, 0, 0, 3, 0, 0) for i in range(3, 0, -1): print("{0}".format(i)) time.sleep(1) print("heading") drone.sendControlPosition(0, 0, 0, 0, 90, 30) for i in range(4, 0, -1): print("{0}".format(i)) time.sleep(1) n+=1
- ring이 잘려서 검출되므로 ring과 가깝기 때문에 표식 인식 후 전진
def shift_ring2(R_pix): # 링통과 + (좌회전 or 착륙) global n if n==2: print("move") drone.sendControlPosition16(10, 0, 0, 3, 0, 0) for i in range(3, 0, -1): print("{0}".format(i)) time.sleep(1) drone.sendControlPosition16(5, 0, 0, 3, 0, 0) for i in range(1, 0, -1): print("{0}".format(i)) time.sleep(1) print("Landing") drone.sendLanding() for i in range(5, 0, -1): print("{0}".format(i)) time.sleep(1) else: if R_pix>90 : # 1.5m 앞으로 이동 + 좌회전 print("move") drone.sendControlPosition16(10, 0, 0, 3, 0, 0) for i in range(3, 0, -1): print("{0}".format(i)) time.sleep(1) drone.sendControlPosition16(5, 0, 0, 3, 0, 0) for i in range(1, 0, -1): print("{0}".format(i)) time.sleep(1) print("heading") drone.sendControlPosition(0, 0, 0, 0, 90, 30) for i in range(4, 0, -1): print("{0}".format(i)) time.sleep(1) n+=1
- 파란색 크로마키 천 임계값의 최소값과 최대값을 HSV 차원에서 inRange를 이용하여 이진화
- 빨간색 사각 표식 임계값의 최소값과 최대값을 HSV 차원에서 inRange를 이용하여 이진화
# blue flag img_mask = cv2.inRange(image, (th_blue_h - 10, 20, 20), (th_blue_h + 10, 255, 255)) B = np.sum(img_mask == 255, axis=None) # red square img_mask_red = cv2.inRange(image, (th_red_h - 10, 20, 20), (th_red_h + 10, 255, 255)) R = np.sum(img_mask_red == 255, axis=None)
1단계 링 이진화 | 1단계 표식 이진화 |
---|
- erode는 노이즈로 인한 불규칙한 외곽선을 침식
- dilate는 erode로 인한 데이터 손실을 복구
# erosion과 dilate를 이용한 외곽 정리 k = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) img_mask2 = cv2.erode(img_mask, k) img_mask2 = cv2.dilate(img_mask2, k) img_mask2 = cv2.dilate(img_mask2, k) img_mask2 = cv2.erode(img_mask2, k)
- 이미지 컨투어 처리
# contour _, contours, hier = cv2.findContours(img_mask2, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) _, contours_red, _ = cv2.findContours(img_mask_red, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
- 파란색 깃발이 보이면 find_ring 함수로 ring의 contour 인덱스 반환
- idx < 240이면 ring이 카메라에 검출됨
- radiux > 240이면 focus함수로 moment를 이용해 무게중심 반환
# main_run if B>20: idx = find_ring(contours, hier) # 링의 컨투어 찾아서 인덱스 리턴 (x, y), radius = cv2.minEnclosingCircle(contours[idx]) # 링의 중심, 반지름 리턴 M = cv2.moments(contours[idx]) cx = int(M['m10'] / M['m00']) cy = int(M['m01'] / M['m00']) if radius < 240: # 링이 안잘렸을때 focus(x, y) # 링 중점 위치 조절 print("move") #20cm 이동 drone.sendControlPosition16(3, 0, 0, 3, 0, 0) for i in range(1, 0, -1): print("{0}".format(i)) time.sleep(1) # shift_ring1(B, b_pix_thr, R) else: # 링이 잘렸을 때 focus(cx, cy) # 모멘트 무게중심으로 초점 잡기 shift_ring2(R)
- Go to Details
Team leader: Kang Hwan Kwon
Team members: Seong Ha Park, Eun Sun Park
This project is licensed under the MIT License - see the LICENSE file for details