/Ta-Yo

Self-driving Model Car with RPi

Primary LanguagePython

라즈베리파이를 이용한 교육용 자율주행 자동차 개발

Self-driving Model Car with RPi

주요 기능 및 작동 방식

  • 실시간 차선 인식을 통한 차로이탈 방지
  • 신호등, 표지판등의 객체 인식을 통한 자율주행
  • 초음파 센서를 이용한 긴급 제동
  • GPU 서버와의 실시간 소켓통신을 통한 RPi의 성능 한계 극복

실시간 차선 인식(with OpenCV in RPi)

  • 차선 인식 과정 : Gray - Canny - ROI - HoughLines - Weighted

    • Gray(흑백화)
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

    • Canny(모서리 검출): Canny 알고리즘 이용.
    can = cv2.Canny(gray, 50, 200, None, 3)

    • HoughLines(직선 검출)
    line_arr = cv2.HoughLinesP(masked_image, 1, np.pi / 180, 20, minLineLength=10, maxLineGap=10)

    • ROI(Region of Interst, 관심 구역 설정)
    rectangle = np.array([[(0, height), (120, 300), (520, 300), (640, height)]]) ##[upper_left, lower_left, upper_right, lower_right]

    • Weighted(원본 영상에 합성)
    mimg = cv2.addWeighted(src, 1, ccan, 1, 0)

  • 차선 인식 알고리즘

    def DetectLineSlope(src):
    ...
    return mimg, degree_L, degree_R ##대표선이 그려진 영상의 프레임, 왼쪽 대표선의 기울기, 오른쪽 대표선의 기울기
    • 대표선 추출: /Raspberry-Pi/line_detect.py의 DetectLineSlope(src)에 정의, 인식 되는 양 차선의 안쪽차선을 대표선으로 결정.
    • 대표선의 기울기 구하기: 대표선으로 차선으로 인식되는 직선의 좌표(x1,y1,x2,y2)를 통해 왼쪽, 오른쪽 대표선의 기울기를 구함, 인식되는 차선이 없을 경우 0을 반환.
    • line_detect.py(예제)의 알고리즘 흐름도

  • 실습

  1. RPi(Rasbian)에 예제 소스코드 복제
$ git clone https://github.com/lky9620/Ta-Yo.git
  1. Raspberry-Pi 디렉토리로 이동
$ cd /Ta-Yo/Raspberry-Pi 
  1. 자신의 환경에 맞게 servo.py, line_detect.py 수정 후 실행
~Ta-Yo/Raspberry-Pi$ python line_detect.py #or python3 line_detect.py
  • 실습 동영상(아래 이미지 클릭 시, Youtube로 이동합니다.)

SelfDriving Car

Yolo를 이용한 신호등, 표지판 등의 객체인식

  • Yolo(실시간 객체 인식)을 통해서 청색, 적색 신호등, 사람, 자동차 뒷모습, 여러 표지판 등을 미리 학습하여 가중치 모델(학습 모델) 생성
    • 방법은 추후에 notebook 파일 업로드 예정
    • 데이터셋은 저작권 등의 문제로 비공개

  • Raspberry-Pi의 성능을 극복하기 위해 GPU 서버(Pytorch)와 TCP 소켓통신 사용

  • In Server(GPU server(Pytorch))

    • 딥 러닝 프레임워크로 pytorch 사용, GPU 서버는 CUDA 연산이 가능해야함.
    • 미리 학습시켜 놓은 Yolo 모델(Weight)파일을 이용하여, 라즈베리파이가 보내주는 영상의 객체를 인식.
    • 인식되는 객체에 따라 Client(Rpi)에서 수행해야 하는 일을 위험순위에 따라 리스트 자료구조에 저장 후, 위험순위가 가장 높은 행위를 전달함.
    class_name = list(map(lambda x: write(x, frame)[1], output)) # 프레임마다 인식되는 객체를 리스트 자료구조로 저장.
        if class_name.count('redlight') >= 1:
          cslist.append(1)
      elif class_name.count('greenlight') >= 1:
          cslist.append(4)
      #...
      elif class_name.count('kidzone') >= 1:
          cslist.append(3)
      elif class_name.count('kidzoneout') >= 1:
          cslist.append(4)
      else:
          cslist.append(4)
    def csend(direction):
      if direction == 0:
          conn.send(sendData_None.encode('utf-8'))
      elif direction == 1:
          conn.send(sendData_Stop.encode('utf-8')) ## 정지 명령 데이터
      elif direction == 2:
          conn.send(sendData_Sign.encode('utf-8')) ## 정지 표지판 명령 데이터
      elif direction == 3:
          conn.send(sendData_Slow.encode('utf-8')) ## 감속 구간 명령 데이터
      elif direction == 4:
          conn.send(sendData_Return.encode('utf-8')) ## 속도 복귀 명령 데이터
    • redlight, stop(sign)에서 Client에 정지 명령
    • 어린이 보호구역, 혼잡지역에서 Client에 감속 명령
    • kidzoneout, greenlight에서 Client에 속도 복귀 명령
  • In Client(RPi)

    • 적색 신호등에서 차량 정지 후 청색 신호등 변경 시, 이전의 속도로 재주행
    elif recVData == 'T':  ## 'T' is sendData_Stop in Server
      motor.Stop()
      print('Redlight Stop')
    
    elif recVData =='R': ## 'R' is sendData_Return in Server
      motor.Forward(speed)
      print('Normal Driving')
      pre_data = '' ## Pre Data initialize.
    • 정지 표지판에서 차량 정지 후 정지표지판 미인식 시 이전의 속도로 재주행
    elif recvData == 'G':
      motor.Stop()
      print('Stop sign Stop')
      pre_data='G' ## Pre Data memorize in Variable pre_data
    
    elif pre_data=='G' and recvData =='N':  ## Pre Data is 'G' and recvData is None
      motor.Forward(25)
      print('Normal Driving')
      pre_data = ''  ## pre_data initialize
    • 어린이 보호구역, 혼잡지역에서 자동차 감속, 감속구간 벗어날 시 원래의 속도로 재주행.
    if recvData == 'S':
      motor.Forward(low_speed)
      print('Driving Slow')
    
    elif recvData == 'R':
      motor.Forward(speed)
      print('Normal Driving')
    • 어린이 보호구역, 혼잡지역과 같은 감속 구간 진입 시, 긴급제동 기능 활성화. 일정 거리 이내의 물체 감지 시 긴급제동.
  if pre_data == 'S': ## Variable pre_data is 'S' when enter deceleration section
   dist = choeumpa.distance()  ## Activate ultrasonic wave senor
   if dist <=threshold:  
     print('Force Stop!!')    
     motor.Stop()
  • 객체 인식 실습
  • In Server(GPU server(Pytorch))
  1. Pytorch 프레임워크가 설치되어 있고, CUDA 연산이 가능한 GPU 서버에서 수행 되어야 함.
  2. Server(GPU server(Pytorch))에 예제 소스코드 복제
$ git clone https://github.com/lky9620/Ta-Yo.git
  1. server 디렉토리로 이동
$ cd /Ta-Yo/server
  1. 아래 주소의 .weight, .names, .cfg 파일을 모두 해당 디렉토리에 저장 https://drive.google.com/drive/folders/1HyhbhdyAGmOdNXJiGvToS7LLb5e1TObU?usp=sharing
  2. server.py의 ip 및 포트번호 정의 후 server.py 실행 (ip는 빈칸이여도 무관)
HOST = '' # your IP address
PORT =  # your port number(except Well-knwon port number)
s.bind(HOST,PORT)
~/Ta-Yo/server$ python server.py #or python3 server.py 
  • In Client(Rpi)
  1. Client에 예제 소스코드 복제
$ git clone https://github.com/lky9620/Ta-Yo.git
  1. Raspberry-Pi 디렉토리로 이동
$ cd /Ta-Yo/Raspberry-Pi
  1. client.py의 ip 및 포트번호 정의 후 client.py실행
HOST = '' # your IP address
PORT =  # your port number(except Well-knwon port number)
c.connect((HOST,PORT))
~/Ta-Yo/server$ python server.py #or python3 server.py 
  • 객체 인식 실습 동영상(아래 이미지 클릭 시, Youtube로 이동합니다.)

SelfDriving Car-ObjectDetection

해당 프로젝트는 단국대학교 공학교육혁신센터에서 진행하는 2020 캡스톤디자인 Echo+ Project의 지원을 받았으며, (주)3DEMP사와 산학 협력하여 수행하였음.