Module7. CARLA Simulator 로 Controller 성능 확인하기(와 수료!!!)

JB·2022년 5월 3일
0
post-thumbnail

CARLA를 윈도우에 설치해보자!(그리고 수료하자!)

Coursera 자율주행 전문화코스 첫번째 강좌 Introduction to self-driving cars 의 마지막 주차이자 수료 과제인 CARLA 프로그래밍을 위해 윈도우에 Carla를 설치해보겠습니다.

원래는 우분투에 설치해야되는데 저의 할아버지 우분투 노트북(중3~대3 까지 쓴ㅠㅠ)에서는 돌아가지 않을 것 같고 맥에서는 안된다길래 학교 실습실 윈도우에 깔아보려고 합니다. 알고보니 우리학교 실습실에는 리눅스가 센토스만 있고 우분투 깔아도 되냐고 물어보니까 안된다고 하더라고요...

그래서 시작한 윈도우에 CARLA설치하기!(그리고 수료하기!)

*구체적인 것은 CARLA setup for Windows 공식 문서를 참고해주세요!


준비하기

  1. HW : 여러가지 스펙 요구사항이 있었지만 저에겐 어차피 실습실 컴퓨터밖에 답이 없었기 때문에 그냥 했습니다.

  2. Software : CARLA 시뮬레이터 다운로드 받아서 압출 풀고 시작함

    • 윈도우7 64bit 이상

    • 2000~2002 방화벽 포트 접근 가능

    • OpenGL 3.3 이상, DirectX10 이상 → CARLA 바이너리 불러올 때 알아서 됨

    • Python : 3.6.x (***매우중요)

      실습실 컴이라 Python 깔려있는 줄 알고 넘어갔다가 다시 돌아옴...꼭 확인하세요!

      >python —version
      >python -m pip —version

      > python -m pip install -r C:\(위치)\CarlaSimulator\requirements.txt--user

깔아보자!

  1. Default Map 으로 시뮬레이터를 로딩하기

    >CarlaUE4.exe -windowed -carla-no-networking

    ‘-windowed’ 는 전체화면모드 해제

    중간에 DirectX 를 깔지 물어보는데 깔아주시면 됩니다.

    완료!

    A,S,D,W 로 차량을 움직일 수 있고 Q로 기어 바꿈. P는 파일럿모드



1. Race Track Map 으로 시뮬레이터 로딩하기

Coursera 과제에 사용할 맵으로 로딩해봅시다.

>CarlaUE4.exe /Game/Maps/RaceTrack -windowed -carla-server -benchmark -fps=20

2. 과제 파일 수정하기!

여기서 waypoints 는 이미 txt파일로 만들어져있고 이것이 과제예시파일에서 callback 함수로 구현이 되어있다. 종방향, 횡방향 제어기만 설계하면 된다!

나는 2021년 창작자동차대회 자율주행부문에서 횡방향 제어 알고리즘을 구현했고 2022-1학기에 청강하는 시스템모델링제어에서 PID를 배웠으므로 이 두 개를 구현해보도록 하겠다(Coursera에서도 이론을 배웠다!!!). 스탠리는 차량 앞축을 기준으로, PID는 뒷축을 기준으로 에러값을 계산하며 lookahead distance 없이 가장 가까운 포인트를 기준으로 에러값을 계산하여 에러에 따라 휠을 더 많이 꺾는다. waypoints txt에 포함된 속도값이 22kph 수준이므로 purepursuit 으로도 충분히 정확한 추종이 가능할 것이라고 생각하여 purepursuit&PID 로 구현하였다.

#!/usr/bin/env python3

"""
2D Controller Class to be used for the CARLA waypoint follower demo.
"""

import cutils
import numpy as np
import math

class Controller2D(object):
    def __init__(self, waypoints):
        self.vars                = cutils.CUtils()
        self._current_x          = 0
        self._current_y          = 0
        self._current_yaw        = 0
        self._current_speed      = 0
        self._desired_speed      = 0
        self._current_frame      = 0
        self._current_timestamp  = 0
        self._start_control_loop = False
        self._set_throttle       = 0
        self._set_brake          = 0
        self._set_steer          = 0
        self._waypoints          = waypoints
        self._conv_rad_to_steer  = 180.0 / 70.0 / np.pi
        self._pi                 = np.pi
        self._2pi                = 2.0 * np.pi

    def update_values(self, x, y, yaw, speed, timestamp, frame):
        self._current_x         = x
        self._current_y         = y
        self._current_yaw       = yaw
        self._current_speed     = speed
        self._current_timestamp = timestamp
        self._current_frame     = frame
        if self._current_frame:
            self._start_control_loop = True

    def update_desired_speed(self):
        min_idx       = 0
        min_dist      = float("inf")
        desired_speed = 0
        for i in range(len(self._waypoints)):
            dist = np.linalg.norm(np.array([
                    self._waypoints[i][0] - self._current_x,
                    self._waypoints[i][1] - self._current_y]))
            if dist < min_dist:
                min_dist = dist
                min_idx = i
        if min_idx < len(self._waypoints)-1:
            desired_speed = self._waypoints[min_idx][2]
        else:
            desired_speed = self._waypoints[-1][2]
        self._desired_speed = desired_speed

    def update_waypoints(self, new_waypoints):
        self._waypoints = new_waypoints

    def get_commands(self):
        return self._set_throttle, self._set_steer, self._set_brake

    def set_throttle(self, input_throttle):
        # Clamp the throttle command to valid bounds
        throttle           = np.fmax(np.fmin(input_throttle, 1.0), 0.0)
        self._set_throttle = throttle

    def set_steer(self, input_steer_in_rad):
        # Covnert radians to [-1, 1]
        input_steer = self._conv_rad_to_steer * input_steer_in_rad

        # Clamp the steering command to valid bounds
        steer           = np.fmax(np.fmin(input_steer, 1.0), -1.0)
        self._set_steer = steer

    def set_brake(self, input_brake):
        # Clamp the steering command to valid bounds
        brake           = np.fmax(np.fmin(input_brake, 1.0), 0.0)
        self._set_brake = brake

    def update_controls(self):
        # RETRIEVE SIMULATOR FEEDBACK
        x = self._current_x #meter
        y = self._current_y #meter
        yaw = self._current_yaw #radian
        v = self._current_speed #m/s
        self.update_desired_speed()
        v_desired = self._desired_speed #m/s
        t = self._current_timestamp
        waypoints = self._waypoints #x, y, v
        throttle_output = 0 # 0~1
        steer_output = 0 #-1.22~1.22(rad)
        brake_output = 0 #0~1

        # MODULE 7: DECLARE USAGE VARIABLES HERE
        self.vars.create_var('v_prev', 0.0) #velocity
        self.vars.create_var('t_prev', 0.0) #dt = t_prev-t
        self.vars.create_var('e_prev', 0.0) #Error : v_desired-v
        self.vars.create_var('e_iprev', 0.0) #integral Error
        self.vars.create_var('o_tprev', 0.0) #throttle output
        self.vars.create_var('o_sprev', 0.0) #steer output

        # Skip the first frame to store previous values properly
        if self._start_control_loop:
    
            # MODULE 7: IMPLEMENTATION OF LONGITUDINAL CONTROLLER HERE
						# PID Controller

            K_p = 1.0
            K_i = 0.1
            K_d = 0.01

            dt = t-self.vars.t_prev

            throttle_output = 0
            brake_output    = 0

            e_v = v_desired - v
            i_v = self.vars.e_iprev + e_v*dt
            d_v = (e_v - self.vars.e_prev) / dt

            accel = K_p * e_v + K_i*i_v + K_d * d_v

            if(accel >0):
                throttle_output = (np.tanh(accel) + 1)/2
                
                if(throttle_output - self.vars.o_tprev > 0.1):
                    throttle_output = self.vars.o_tprev + 0.1
            
            else:
                throttle_output = 0

            # MODULE 7: IMPLEMENTATION OF LATERAL CONTROLLER HERE
						# PurePursuit

            steer_output = 0
            L = 2.7 #meter - Ford Mustang Wheelbase
            min_ld = 10 #meter
            K_pld = 0.8

            x_desired = x-L*np.cos(yaw)/2
            y_desired = y-L*np.sin(yaw)/2
            l_d = max(min_ld, v*K_pld)
            
            for i in waypoints:
                dist = np.sqrt((i[0] - x_desired)**2 + (i[1] - y_desired)**2)

                if(dist>l_d):
                    target = i
                    break
                else:
                    target = waypoints[0]
                
            alpha = math.atan2(target[1] - y_desired, target[0]-x_desired)-yaw
            delta = math.atan2(2*L*np.sin(alpha)/l_d)
            steer_output = delta

            # SET CONTROLS OUTPUT
            self.set_throttle(throttle_output)  # in percent (0 to 1)
            self.set_steer(steer_output)        # in rad (-1.22 to 1.22)
            self.set_brake(brake_output)        # in percent (0 to 1)

        self.vars.v_prev = v  # Store forward speed to be used in next step
        self.vars.t_prev = t
        self.vars.e_prev = v_desired-v
        self.vars.e_iprev = i_v
        self.vars.o_tprev = throttle_output
        self.vars.o_sprev = steer_output

이 코드를 저장하고 시뮬레이터를 실행시킨 다음 cmd창을 하나 더 열어서 module_7.py(waypoint 추종 및 제어 코드 launch) 를 실행시킨다!

3. 약간의 Error Note

  • 처음에 StopIteration 을 만난다고 떠서 해당 파일로 들어가보았더니 센서값을 못받아오나 해서 학교 인프라팀에 전화해서 시뮬레이터에서 pygame 어쩌고로 접근하고 싶은데 방화벽 포트 2000~2002번을 열어달라고 했다. 학교에서는 외부에서 학교 컴퓨터에 접근할 때 말하는 그 방화벽이라 관련이 없다고 말해주셨다. 그래서 알고보니 파이썬 버전문제였다. 꼬옥 3.5~3.6으로 해야한다.
  • 아래와 같은 import 문제(link...?). matplotlib 3.0.0을 깔아주어야 해결된다!



결론

상당히 잘 나온다. 속도를 22kph에서 25kph까지 올렸는데도 잘 된다. (잘된다 = 추종한 waypoints 저장하여 비교해보았을 때 100% 일치) 하지만 한 50kph이상 올려버리면 다이나믹모델이 달라지므로 스탠리나 MPC모델로 제어를 해야할 것 같다.




3월부터 시작한 Introductiont to Self-Driving Cars 강의를 수료하였다. 다음주부터는 상태 예측과 위치측위 강좌로 바로 들어간다...와아아아...와ㅏ...행복해...
'실무->입문수업' 순서로 진행되었는데,

오히려 좋았다. 왜냐면 배우면서 '그 땐 노가다로 삼중 for문 돌려가면서 튜닝했는데, 역시 이론이 중요하다...'는 것을 깨달았기 때문이다. 그리고 그것에서 멈추지 않고 과제로 코딩하고 시뮬레이터도 돌려보았기 때문에 오히려 좋아...(ㅋㅋㅋ)

다음주부터 상태예측과 위치측위 강좌로 적장의 목을 베고... 돌아오겠다. 내일도 화이팅

profile
자율주행 이동체를 배우고 있는 JB입니다.

1개의 댓글

comment-user-thumbnail
2024년 4월 12일

시뮬레이션 중에 특정 부분에서 지속적으로 프로그램이 종료 되고
ERROR: (localhost:2001) failed to read data: [WinError 10054] 현재 연결은 원격 호스트에 의해 강제로 끊겼습니다
ERROR: (localhost:2000) failed to connect: [WinError 10061] 대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다
ERROR: (localhost:2000) failed to connect: [WinError 10061] 대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다
ERROR: (localhost:2000) failed to connect: [WinError 10061] 대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다
이러한 에러가 발생하는데 혹시 이 문제에 대해서 아시는거 있으신가요..

답글 달기