목차
1.
Flowchart
2.
Perception
3.
Decision
4.
Control
Flowchart
DRIVEN의 창작 자동차에 들어갈 자율주행 시스템의 Flowchart는 위의 그림과 같다. 크게 Perception, Decision, Control 단계로 나누었고 각 파트를 threading 방식으로 처리하게 된다. 각 단계에 대해서 자세하게 설명하면 다음과 같다.
Perception
INPUT : Pointcloud of LiDAR VLP-16, 3D array(RGB,camera)
OUTPUT : REALTIME OGM 2D MAP( numpy array )
•
2D array
◦
0 : nothing
◦
1 : object perception
먼저 인지(Perception) 단계이다.이 부분은 자율주행 시스템의 첫 단으로, LiDAR와 Camera의 센서 정보를 받아와서 결정(Decision) 단계에 전달할 데이터를 만든다.
Camera는 라바콘을 탐지(Detection)하기 위해 사용한다. 중요하게 사용 될 부분은 주황색 라바콘으로 된 지점 앞에서 정지를 하는 기능을 하게 될 것이다. 또한 주행 중에 좌우측 라바콘의 색이 다르기 때문에 이를 탐지하여 자동차의 조향각을 설정하는데 보조적인 역할을 하게 될 것이다. 해당 기술로는 YOLO v5 모델을 사용할 예정이고 라바콘에 대한 데이터로 파인튜닝(fine-tuning)을 하여 사용할 예정이다. YOLO를 통해 라바콘을 네모 박스로 탐지를 하게 된다면 해당 박스 안의 라바콘 이미지를 활용하여 라바콘의 색상을 판단하게 된다. 해당 과정은 룰 베이스로 박스의 중앙 부분 픽셀들의 평균 값을 계산, 아주 가벼운 CNN 모델 사용 또는 K-means와 같은 머신러닝 기법을 사용하여 색상 값을 판단할 예정이다. 해당 방법들 중 실제 환경에서의 비교를 통해 성능이 좋은 방법론을 택한다.
LiDAR는 16채널의 Pointcloud를 받아와서 SLAM을 실행하게 되는데 SLAM의 여러 알고리즘 중 LeGO-LOAM 이라는 LOAM 계열의 알고리즘을 선택하였다. 해당 알고리즘의 선택 이유로는 먼저 현재 DRIVEN 자율주행 팀에서 사용하고 있는 Velodyne LiDAR PUCK(VLP-16) 그리고 nvidia사의 Jetson Xavier AGX와 같은 환경에서 해당 알고리즘이 구현되었고, 어느 정도 보장된 성능을 지녔다는 점과 기존 LOAM에 비해 계산량을 줄인 알고리즘이라는 점에서 선택을 하게 되었다.
LeGO-LOAM의 Output data는 6 DOF (x, y, z, pitch, roll, yaw)의 형태이다. 해당 데이터는 3차원 정보를 가지고 있는데, 이번 대회의 환경 상 방지턱같은 차체가 기울어질 만한 상황은 없기 때문에 pitch, roll 데이터는 필요가 없다고 판단하였다. 따라서 yaw값만 고려하여 자동차의 방향에 따른 맵 구성에 도움을 주도록 하였고 데이터 역시 3차원(x, y, z)을 2차원(x, y)으로 변형하여 사용해도 충분히 좋은 성능을 낼 수 있을 것이라 판단하여 최종적으로 z 값을 제외한 (x, y, yaw)의 형태로 변환하여 맵을 구성하고 Decision 단계의 Path planning에 사용이 된다.
Decision
INPUT : REALTIME 2D OGM MAP ( numpy array )
OUTPUT : MOTION CONTROL MESSAGE ( queue, variable )
•
first message
◦
yawrate_now
◦
yawrate_target
◦
velocity_now
◦
velocity_target
•
second message
◦
yawrate_now
◦
yawrate_target
◦
velocity_now
◦
velocity_target
•
state control
◦
priority_ mission
◦
next_mission
첫번째 메시지와 두번째 메시지를 구분한 이유는 motion control 명령 전달이후 feedback 전달까지의 시간차이로 벽에 들이받는 상황을 예방하기 위함임.
queue 의 형태로 전달 될 예정이나, 테스트주행 이후 두번째 메시지까지만 한번에 전달하는것이 차량상태 feedback 에 이상이 없다면 두개의 변수만을 활용하여 motion control 할 예정임.
Perception 단계에서 생성된 2차원의 맵을 받아서 Decision 단계에서는 자동차가 이동할 경로를 만들고 현재 위치를 지속적으로 확인하여 자동차가 도착 지점에 왔는지 판단을 하게 작동한다. 기본적인 흐름은 다음과 같다.
시범 주행을 통해서 현재 대회에서 제공하는 맵의 좌표 데이터를 추출해줘야 한다. 이 과정에서 gps뿐만 아니라 imu의 yaw데이터도 함께 추출해서 출발점과 출발 방향도 잘 잡아줄 수 있도록 해준다. 이 과정에서 gps의 경우 오차가 클 수록 경기를 완주하는데 큰 어려움을 불러올 수 있기에 가능한 오차를 줄이기 위해 ‘Real Time Kinematic’을 연결할 것이다. 위성을 통하여 실시간으로 현재 위치를 잡아주는 역할을 하는 RTK는 1m의 오차가 나는 gps도 1~2cm로 오차범위를 줄일 수도 있다. 초당 1회에서 2회 정도 값을 저장시켜서 실제 주행 때 입력된 데이터들의 값들을 통하여 완주할 수 있다.
OGM 기반 A* 알고리즘을 통해 지속적으로 path 를 갱신한다. 현재 위치에서 이동하게 될 지점의 경로를 통해 이동해야 할 각도를 계산한다.
atan(way_point[0][0]-way_point[0][1],way_point[1][0]-way_point[1][1])
### 추가 argv, ogm map 과 현실세계의 Scale 이 얼마나 차이가 있는지를 고려하여, 조향각을 설정해야함.
Python
복사
현재 위치에서 이동해야하는 좌표의 각도를 계산하여, CONTROL 로 넘겨주는 yawrate_target 변수를 설정한다. 시험주행을 통해 결정되어야 하는 사항은 path planning 을 통해 결정 된 way_point 는 현실세계의 SCALE 을 반영하여야 하는 점이다.
Control
INPUT :IMU Data(EBMotion V5 (EBIMU24GV5(2) & EBRCV24GV5))
•
Default value
◦
vehicle_mass: 차량의 무게
◦
brake_deadband: 브레이크 페달 민감도
◦
decel_limit: 출발 정지 시 감속 제한
◦
accel_limit: 최대 가속도
◦
wheel_radius: 바퀴 반지름
◦
wheel_base:" 차량 바퀴 사이의 거리
◦
steer_ratio: 조향 장치의 비율
◦
max_lat_accel: 차량의 최대 측면 가속도
◦
max_steer_angle: 조향 장치의 최대 회전각도
PID 제어값
◦
self.min_angle = -max_steer_angle(최소 조향각)
◦
self.max_angle = max_steer_angle(최대 조향각)
◦
•
first message
◦
yawrate_now
◦
yawrate_target
◦
velocity_now
◦
velocity_target
•
second message
◦
yawrate_now
◦
yawrate_target
◦
velocity_now
◦
velocity_target
•
state control
◦
priority_ mission
◦
next_mission
OUTPUT :
•
Steering value
◦
brake
◦
throttle
◦
steering
차량 모션 플래닝을 통한 차량 거동 결정사항에 대해 control부에서는 Roll, Pitch 컨트롤, 및 Yaw컨트롤로 쪼개어 조금 더 섬세한 차량 제어 요소들을 결정하고자 하였다. 드리븐에서 설계한 차량의 스티어링 구현부는 ackermann-steering을 따름에 있어서 모션 플래닝에서 결정한 조향각을 하드웨어로 이행 시킬 때 ‘스티어링 휠을 얼마나 조향해야 하는가?’와 동시에 이때 속력은 어느 정도이며 브레이킹 압은 얼마나 주어야 하는 것인가 등등에 대해 연산하였다. 이때 결정된 사항을 하드웨어로 옮기기 위한 제어 수단으로써 Pid제어를 활용하였다. 너무 잦은 Pid제어 연산을 하게 되면 직선 주행을 하는 과정에서도 차량이 직선으로 나아가지 못하고 떨면서 주행하는 현상이 발생하는데 이때 Pid제어값을 smoothing하는 과정이 필요하였다. 이 부분은 자율주행팀 내부에서만 해결할 수 있는 문제는 아니라 판단하여 제어팀과 동시에 문제를 해결 중에 있다. 자율주행팀 내부적으로 낸 해결 방안에는 결정된 사항을 Lowpassfilter를 거침으로써 노이즈를 줄이고자 하였다.
속도 및 조향각을 하드웨어로 옮기는 과정에 있어서 imu를 통해 현재 속도를 계속 피드백 받고 모션 플래닝을 통한 목표 조향 및 가속값을 목표로 현재 차량 거동상태를 옮기는 형태로 차량 구동을 구현하고자 하였다. 이때 차량이
원하는 선회각이 있음에도 현재 속도가 과하게 빨라 차량 스티어링 각도를 통해 선회각을 만들 수 없는 경우 차량의 각속도를 범위 내로 제한 하는 등 여러 상황을 상정하여 자율주행팀의 생각을 control부에 녹여내었다.
def get_angle(self, radius):
#아크탄젠트
angle = atan(self.wheel_base / radius) * self.steer_ratio
'''
최대 조향각과 최소 조향각 사이 값인지 확인
if angle >= self.max_angle:
angle=self.max_angle
return self.max_angle
else:
if angle <= self.min_angle:
return self.min_angle
'''
return max(self.min_angle, min(self.max_angle, angle
Python
복사
def get_steering(self, linear_velocity, angular_velocity, current_velocity):
#이동 방향으로 향하는 머리를 유지하기 위한 조향각 결정(yaw contrller)
#선속도, 각속도, 현재 속도를 포함하는 차체에서 주행조건에 맞게 조향각 평가
#선속도가 양수인 경우, 각속도를 조정하여 현재 속도와 비례하게 제어
if abs(linear_velocity) > 0.0:
angular_velocity = current_velocity * angular_velocity / linear_velocity
else:
angular_velocity = 0.0
# 현재 차량이 움직이는 상태일때 최대 허용 스티어링 각도를 계산하고
# 각속도를 범위 내로 제한
if abs(current_velocity) > 0.1:
max_yaw_rate = abs(self.max_lat_accel / current_velocity)
angular_velocity = max(-max_yaw_rate, min(max_yaw_rate, angular_velocity))
# 계산된 각속도가 양수일 경우, 각속도에 대응하는 조향 각도로 변환
if abs(angular_velocity) > 0.0:
return self.get_angle(max(current_velocity, self.min_speed) / angular_velocity)
return 0.0
Python
복사