프로젝트 명 : 딥러닝을 활용한 스마트 자율주행 시스템

목적 : 캡스톤 디자인 & LINK+ 경진대회 & 한미르 경진대회 & 공학혁신센터 경진대회

참여 인원 : 3 명

참여 기간 : 2020. 9 ~ 2021. 6

기관 : 부경대학교 공과대학, 산학협력단, 공학혁신센터

담당 업무

  1. 한정된 자원을 통해 Road_Following + Collision_Avodiance 가 동시에 가능한 모델 구현 및 성능 유지 보수
  2. YOLOv5 Custom 데이터 셋을 통해 Object_Following
  3. 자율주행체 동작제어
  4. 데이터 전처리 및 수집, 유지보수
  5. AI 모델 최적화를 위한 Tensor RT 적용

사용 언어 및 프레임워크 : Python, Pytorch


프로젝트 설명

  1. 인공지능의 발달과 더불어, 자율주행 기술의 발달 또한 같이 이루어짐. 특히 최근에는 V2X(Vehicle To Everything)이라는 용어의 등장과 함께, 자율주행 차량과 도로, 인프라가 구축 된 사물과 정보를 교환하는데 사용되는 기술들의 표준이 정의되고, IEEE 802.11p(WAVE, Wireless Access in Vehicle Environment) 와 같은 통신 표준들은 통신 지연에 민감한 V2X의 기반 기술로 사용되고 있다.
  2. 자율주행의 발달과 더불어, 여러 대의 차량을 동시다발적으로 제어하여 하나의 호송대로 구성해 운행하는 군집 자율주행이 주목된다. 군집 자율주행은 자율주행 기술과 함께 차량 연결 기술이 복합적으로 적용된 주행방법이다. 군집 자율주행에서는 선두 차량이 Host 차량으로 리더역할을 수행하며, 후행 차량들은 선두 차량이 주행한 경로를 이행하여 운행한다.
  3. 자율주행체에 사용되는 IoT 단말들은 배터리 또는 저전력으로 작동하여, 기존의 인공지능 연산 장비들보다 낮은 연산 능력을 가진다. 이는 최신 인공지능 기술 구현에 가장 큰 걸림돌로 작용하여, 5G 시스템에서는 단말의 연산 업무를 클라우드가 대신 수행한다. 따라서 MEC 네트워크 구조를 지향하고, 스몰셀 기지국 등의 계산 능력을 본떠 한정된 자원에서의 연산을 최대화 한다.
  4. 따라서 위의 시스템들을 종합하여 시험용으로 AI를 통한 군집 주행 및 자율 주행이 가능한 로봇을 만들고 서로 저지연으로 통신이 가능한 네트워크를 형성하여 다양한 산업 현장에서 사용 가능한 운영 가능한 시스템을 구성하는 것이 목표이다.

MEC 서버 흐름도

출처 : 딥러닝을 활용한 스마트 자율주행 시스템

출처 : 딥러닝을 활용한 스마트 자율주행 시스템

특정 신호에 반응하는 자율 주행체 서버 흐름도

Untitled

장비

  1. NVIDIA Jetson Nano 4GB Developer Kit * 3EA - Autonomous vehicle
  2. NVIDIA Jetson Xavier NX Developer Kit * 1EA - Edge Server
  3. Desktop PC(i5-9400F, RTX 2060, DDR4 32GB RAM) - Main Server

담당 구현

한정된 자원을 통해 Road_Following + Collision_Avodiance 가 동시에 가능한 모델 구현 및 성능 유지 보수

Untitled

YOLOv5 Custom 데이터 셋을 통해 Object_Following

Untitled

자율주행체 동작제어

#코드의 가장 핵심인 excute 부분. change에 맞추어 값이 변화한다.

def execute(change):
    global angle_last, count, left, right
    image = change['new']
    xy = model_trt(preprocess(image)).detach().float().cpu().numpy().flatten()
    x = round(xy[0], 3)
    y = round((0.5 - xy[1]), 3)
    
    x_slider.value = x
    y_slider.value = y
    
    angle = round(np.arctan2(x, y),3) #radian
    
    if botLine.botLineObject.isStop is False:
        #gpio.output(7, gpio.HIGH)
        #gpio.output(11, gpio.LOW)
        
        if( y <= 0.001) and ( abs(x) <= 0.2 ) :
            count = count + 1
            left = max(left - (left * (count * 0.03)), 0)
            right = max(right - (right * (count * 0.03)), 0)

            if( count >= 15 ):
                robot.stop()
                left = 0
                right = 0

        else: # 장애물 x
            count = 0
            
            if ( (abs(angle - angle_last) > abs(angle_last)) and ( angle_last != 0) and (abs(angle) > pid_value.value)) :
                angle = angle_last + ( angle * diff_slider.value )
    
                pid = round(angle ,2)
                
                if pid > 0 :
                    left = round(pid * add_motor.value * speed_slider.value + speed_slider.value , 2)
                    right = round(-pid * dec_motor.value * speed_slider.value + speed_slider.value, 2)
                else:
                    left = round(pid * dec_motor.value * speed_slider.value + speed_slider.value , 2)
                    right = round(-pid * add_motor.value * speed_slider.value + speed_slider.value, 2)

                    
            else:
                pid = round(angle ,2) #0.95 etc..
                left = round(max(pid, 0)* add_motor.value * speed_slider.value + speed_slider.value , 2)
                right = round(max(-pid, 0)* add_motor.value * speed_slider.value + speed_slider.value, 2)

                
                speed_slider.value = speed_gain_slider.value
                steering_slider.value = pid

            #전역변수 설정
        angle_last = angle
        robot.left_motor.value = left
        robot.right_motor.value = right
        
    else:
        robot.stop()
        #gpio.output(7, gpio.LOW)
        #gpio.output(11, gpio.HIGH)
    botLine.onUpdate()
    
        
execute({'new': camera.value})

데이터 전처리 및 수집, 유지보수

                                                           그림 1

                                                       그림 1 

                         그림 2

                     그림 2 

                          그림 3

                      그림 3 

                          그림 3

                      그림 3 
def get_x(path, width):
    """Gets the x value from the image filename"""
    return (float(int(path.split("_")[1])) - width/2) / (width/2)

def get_y(path, height):
    """Gets the y value from the image filename"""
    return (float(int(path.split("_")[2])) - height/2) / (height/2)

class XYDataset(torch.utils.data.Dataset):
    
    def __init__(self, directory, random_hflips=False):
        self.directory = directory
        self.random_hflips = random_hflips
        self.image_paths = glob.glob(os.path.join(self.directory, '*.jpg'))
        self.color_jitter = transforms.ColorJitter(0.3, 0.3, 0.3, 0.3)
    
    def __len__(self):
        return len(self.image_paths)
    
    def __getitem__(self, idx):
        image_path = self.image_paths[idx]
        
        image = PIL.Image.open(image_path)
        width, height = image.size
        x = float(get_x(os.path.basename(image_path), width))
        y = float(get_y(os.path.basename(image_path), height))
      
        if float(np.random.rand(1)) > 0.5:
            image = transforms.functional.hflip(image)
            x = -x
        
        image = self.color_jitter(image)
        image = transforms.functional.resize(image, (224, 224))
        image = transforms.functional.to_tensor(image)
        image = image.numpy()[::-1].copy()
        image = torch.from_numpy(image)
        image = transforms.functional.normalize(image, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        
        return image, torch.tensor([x, y]).float()

성과