ASAM OpenSCENARIO 1.2.0

장승현·2023년 4월 18일
0

Standard

목록 보기
1/2
post-thumbnail

OpenSCENARIO

OpenSCENARIO에 대해 알기 전 먼저 OpenX에 대해 알아야 한다. OpenX는 독일의 비영리 조직 ASAM(Association for Standardiztion of Automation and Measuring Systems)이 자동차 개발 및 테스트에서 툴체인(Tool Chain)의 표준화를 촉진하고자 만들었다. 여기서 툴체인은 소프트웨어 개발에 사용되는 프로그래밍 도구의 집합을 의미한다. 즉, OpenX는 자율주행 또는 운전자 보조 기능의 가상 개발, 테스트 및 검증에 사용되는 표준과 같다.
OpenSCENARIO는 그 중 시나리오를 위한 데이터 모델을 정의하는 내용이다. 시나리오는 운전 시뮬레이션에서 차량, 보행자 및 기타 교통 참여자와 같은 여러 개체가 관련된 복잡하고 동기화 된 상황을 설명하는 것으로 자율주행 알고리즘 검증에 중요한 역할을 한다.

ASAM OpenSCNEARIO : User Guide

시나리오 구성 요소

OpenSCNEARIO는 계층 구조로 시나리오를 설명하기에 전체 구조와 각 항목의 의미를 정확히 이해하고 있어야 한다. 항목에 대한 설명은 아래 링크에서 참고할 수 있다.

ASAM OpenSCENARIO : ALL Classes


OpenSCENARIO에서 가장 상위 항목은 위와 같다. FileHeader에는 시나리오 파일의 저자, 작성 일자, 버전 등 시나리오의 전반적인 정보를 포함하고 있다. RoadNetwork는 시뮬레이션이 수행될 도로에 대한 정보를 포함한다. Entities는 시나리오에 등장하는 객체에 대한 정보를 포함하며, Storyboard는 Entities들이 수행할 행동들에 대한 정보를 나타낸다.


Entities는 Vehicle, Pedestrian, MiscObject로 총 3가지 유형의 ScenarioObject를 가질 수 있다. 각 유형은 하나의 객체를 설명하기 위한 속성(Axles, BoudingBox 등)을 설정할 수 있다.


Storyboard는 전체 시나리오에서 '누가', '무엇을', '언제', '어떻게'라는 설명을 다룬다.

Init은 Entity의 초기 위치, 속도 등 초기 상태를 설정하는 요소이다. Story는 다양한 시나리오를 상위 계층 구조로 그룹화 한 요소이다. StopTrigger는 시나리오의 끝을 결정하는 요소이다.

Story 의 하위 요소인 Act는 특정 시작 시점(StartTrigger)를 기준으로 한 여러 ManeuverGroup의 집합이다.

ManeuverGroup은 Actors와 Maneuver로 구성된다. 여기서 Actors는 Maneuver를 수행할 Entities를 의미하며, Maneuver는 Actors가 수행할 행동인 Event의 집합을 의미한다.

Event는 하위 요소로 Action과 StartTrigger를 갖는다. Action은 실질적인 행동이며, StartTrigger는 이 행동을 언제 수행할지에 대한 조건이다. 즉. StartTrigger가 충족되면 Actors는 Action을 수행한다. 이때, 각 Event마다 priority를 정해 실행 우선 순위를 결정할 수 있다.

Action은 3가지 유형이 있다. 시간, 날씨, 신호 등 Entity와 관련되지 않은 환경에 대한 행동을 결정할 수 있는 Global Action, Entity 개인의 행동을 결정할 수 있는 Private Action, 사용자가 직접 정의할 수 있는 User-defined Action이 있다.

지금까지 시나리오의 전체 구성 요소에 대해 간략하게 알아보았다. 하지만 실제 시나리오는 각 요소마다 가지고 있는 하위 클래스가 다양하기 때문에 위의 ALL Classes 링크를 참고하며 각 클래스의 의미를 이해할 수 있어야 한다.

시나리오 예제

위에서 살펴본 구성을 토대로 시나리오 예제를 한번 살펴보자.

시나리오 구성

위 그림은 예제 시나리오의 구성을 보여주고 있다. Ego 차량은 100km/h의 속도로 주행하고 있다. Ego 차량의 좌측 후방에서 달리는 TV 차량은 120km/h의 속도로 주행하고 있다. TV 차량이 Ego 차량을 추월하게 되면 차선 변경을 하고자 한다. 이때 두 차량 간의 간격은 Ego 차량이 TV 차량의 위치에 도달하는데까지 0.4초의 시간이 걸리는 거리이다. 차선 변경이 끝나면 5초 후에 시나리오를 종료한다.
이를 OpenSCENARIO 형식으로 나타내면 다음과 같다.

<?xml version="1.0" encoding="UTF-8"?>
<OpenSCENARIO>
   <FileHeader revMajor="1" revMinor="1" date="2023-04-28T22:00:00" description="cut-in" author="seunghyun"/> <!-- 파일 정보 -->
   <ParameterDeclarations> <!-- 변수를 선언하여 중복되는 값을 쉽게 사용할 수 있다. $name으로 사용한다. -->
      <ParameterDeclaration name="WhiteCar" parameterType="string" value="car_white"/>
      <ParameterDeclaration name="RedCar" parameterType="string" value="car_red"/>
   </ParameterDeclarations>
   <CatalogLocations> <!-- 미리 정의해둔 카탈로그를 불러와 사용할 수 있다. -->
      <VehicleCatalog>
         <Directory path="../xosc/Catalogs/Vehicles"/>
      </VehicleCatalog>
   </CatalogLocations>
   <RoadNetwork> <!-- OpenDRIVE 형식으로 저장된 도로 정보를 불러온다. -->
      <LogicFile filepath="../xodr/e6mini.xodr"/>
      <SceneGraphFile filepath="../models/e6mini.osgb"/>
   </RoadNetwork>
   <Entities> <!-- 시나리오에 등장하는 객체를 정의한다. -->
      <ScenarioObject name="Ego">
        <CatalogReference catalogName="VehicleCatalog" entryName="$WhiteCar"/>
      </ScenarioObject>
      <ScenarioObject name="TargetVehicle">
         <CatalogReference catalogName="VehicleCatalog" entryName="$RedCar"/>
      </ScenarioObject>
   </Entities>
   <Storyboard>
      <Init> <!-- Entities의 초기 상태를 정의한다. -->
         <Actions>
            <Private entityRef="Ego">
               <PrivateAction>
                  <TeleportAction>
                     <Position> <!-- 위치를 정의한다. -->
                        <LanePosition roadId="0" laneId="-3" offset="0" s="50"/> <!-- 위에서 불러온 도로 정보를 기반으로 정의한다. -->
                     </Position>
                  </TeleportAction>
               </PrivateAction>
               <PrivateAction>
                  <LongitudinalAction>
                     <SpeedAction> <!-- 속도를 정의한다. -->
                        <SpeedActionDynamics dynamicsShape="step" dynamicsDimension="time" value="0.0"/> 
                        <SpeedActionTarget>
                           <AbsoluteTargetSpeed value="${100 / 3.6}"/>
                        </SpeedActionTarget>
                     </SpeedAction>
                  </LongitudinalAction>
               </PrivateAction>
            </Private>
            <Private entityRef="TargetVehicle">
               <PrivateAction>
                  <TeleportAction>
                     <Position>
                        <LanePosition roadId="0" laneId="-2" offset="0" s="25"/>
                     </Position>
                  </TeleportAction>
               </PrivateAction>
               <PrivateAction>
                  <LongitudinalAction>
                     <SpeedAction>
                        <SpeedActionDynamics dynamicsShape="step" dynamicsDimension="time" value="0.0"/>
                        <SpeedActionTarget>
                           <AbsoluteTargetSpeed value="${100 / 3.6 * 1.2}"/>
                        </SpeedActionTarget>
                     </SpeedAction>
                  </LongitudinalAction>
               </PrivateAction>
            </Private>
         </Actions>
      </Init>
      <Story name="CutInStory"> <!-- Entities가 수행할 행동을 정의한다. -->
         <Act name="CutInAct">
            <ManeuverGroup maximumExecutionCount="1" name="CutInSequence">
               <Actors selectTriggeringEntities="false">
                  <EntityRef entityRef="TargetVehicle"/>
               </Actors>
               <Maneuver name="CutInManeuver">
                  <Event name="CutInEvent" priority="overwrite">
                     <Action name="CutInAction"> <!-- EntityRef에서 정의된 Entities가 수행할 행동을 정의한다. -->
                        <PrivateAction>
                           <LateralAction>
                              <LaneChangeAction> <!-- 횡방향 행동 중 차선 변경 행동을 의미힌다. -->
                                 <LaneChangeActionDynamics dynamicsShape="sinusoidal" value="3" dynamicsDimension="time"/>
                                 <LaneChangeTarget>
                                    <RelativeTargetLane entityRef="Ego" value="0"/> <!-- Ego 차량과 동일한 차선을 의미한다. -->
                                 </LaneChangeTarget>
                              </LaneChangeAction>
                           </LateralAction>
                        </PrivateAction>
                     </Action>
                     <StartTrigger> <!-- 위에서 정의한 Action을 수행할 시기를 정의한다. -->
                        <ConditionGroup> 
                           <Condition name="CutInStartCondition" delay="0" conditionEdge="rising">
                              <ByEntityCondition>
                                 <TriggeringEntities triggeringEntitiesRule="any">
                                    <EntityRef entityRef="Ego"/>
                                 </TriggeringEntities>
                                 <EntityCondition> <!-- TimeHeadwayCondition은 트리거 Entity가 참조 Entity 위치에 도달하는 데 걸리는 시간으로 조건을 정의한다.  -->
                                    <TimeHeadwayCondition entityRef="TargetVehicle" value="0.4" freespace="false" coordinateSystem="road" relativeDistanceType="longitudinal" rule="greaterThan"/>
                                 </EntityCondition>
                              </ByEntityCondition>
                           </Condition>
                        </ConditionGroup>
                     </StartTrigger>
                  </Event>
               </Maneuver>
            </ManeuverGroup>
            <StartTrigger> <!-- Act가 시작되는 시기를 정의한다. -->
               <ConditionGroup>
                  <Condition name="CutInActStart" delay="0" conditionEdge="none">
                     <ByValueCondition>
                        <SimulationTimeCondition value="0" rule="greaterThan"/> <!-- Act는 시뮬레이션이 시작되고 0초 이상에서 시작된다. -->
                     </ByValueCondition>
                  </Condition>
               </ConditionGroup>
            </StartTrigger>
            <StopTrigger> <!-- Act가 끝나는 시기를 정의한다. -->
                <ConditionGroup>
                   <Condition name="ActStopCondition" delay="5" conditionEdge="rising">
                      <ByValueCondition> <!-- 위에서 정의한 CutInManeuver가 끝나고 delay 5초 후에 종료된다. -->
                         <StoryboardElementStateCondition storyboardElementType="maneuver" storyboardElementRef="CutInManeuver" state="endTransition"/>
                      </ByValueCondition>
                   </Condition>
                </ConditionGroup>
            </StopTrigger>
         </Act>
      </Story>
      <StopTrigger/>
   </Storyboard>
</OpenSCENARIO>

Esmini

이제 위에서 예제로 만든 시나리오 파일이 정상적으로 작동하는지 확인이 필요하다. 하지만 이렇게 xml만 봐서는 정확하게 동작하는지 확인하기가 어렵다. 이때 우리는 esmini라는 오픈 소스를 사용하여 직접 시나리오를 재생해보고 쉽게 확인해볼 수 있다. esmini란 OpenSCENARIO 형식의 파일을 재생할 수 있는 소프트웨어 툴이다. 다운 및 빌드는 아래 링크를 참고하면 된다.
Esmini

다운로드가 끝나면 esmini root 폴더로 이동한다. 여기서 resources/xosc로 이동해 새로운 시나리오 파일을 삽입한다. 다시 root 폴더로 돌아와 아래 명령어를 통해 시나리오를 재생한다.

./bin/esmini --window 60 60 800 400 --osc ./resources/xosc/file_name.xosc

이렇게 시나리오 파일이 정상적으로 재생되는 것을 확인할 수 있다.

profile
늦더라도 끝이 강한 내가 되자

3개의 댓글

comment-user-thumbnail
2024년 3월 14일

안녕하세요! 해당 자료 참고하여 공부하고 있습니다. Esmini 본문 내용 중
다운로드가 끝나면 esmini root 폴더로 이동한다. 여기서 resources/xosc로 이동해 새로운 시나리오 파일을 삽입한다.
새로운 시나리오 파일을 삽입하는 방법을 조금 더 서술해주실 수 있으실까요...? ㅜㅜ 본문 따라서 코드 작성은 했는데 실행에 어려움을 겪고 있습니다,, 감사합니다!

2개의 답글