Skip to main content
Skip table of contents

gRPC API 사용하기

본 섹션에서는 gRPC API를 사용하여 MORAI Simulator를 제어하는 방법에 대하여 설명한다.

사용자는 gRPC API를 통해 UI를 사용하지 않아도, 시뮬레이터에서 맵 로드, NPC 차량 생성 및 제어 등을 수행할 수 있고, 사용자가 원하는 테스트 시나리오 환경을 구성할 수 있다.


사전 요구 사항

Example code 다운로드

  • 22.R1.2 / 22.R1.3 버전과 호환되는 예제 코드

gRPC_Example.zip

Python 환경 설정

  • python 버전

    • example code는 python 3.7.11에서 작성 및 테스트 진행

  • 설치 필요 python 모듈

    • grpcio : 1.39.0

    • grpcio-tools : 1.39.0

gRPC API 사용 순서

1] Morai Launcher 실행 후 로그인

2] Morai Launcher에서 특정 시뮬레이터 버전 설치 및 실행

3] 시뮬레이터의 맵 선택 및 차량 선택 화면에 위치한 상태에서 example.py 스크립트 실행

4] 결과

  • 맵이 로드되고 Ego 차량이 Cruise 모드로 주행하는 것을 확인

  • NPC 1, 2 차량이 생성되어 원을 그리며 주행하는 것을 확인

  • 장애물이 2개 생성된 것을 확인

예제 코드 설명

파일 구조

  • example 코드

    • example.py

  • gRPC 통신에 사용되는 proto 파일

    • morai_openscenario_base_pb2.py

    • morai_openscenario_base_pb2_grpc.py

    • morai_openscenario_msgs_pb2.py

    • morai_openscenario_msgs_pb2_grpc.py

    • geometry_msgs_pb2.py

    • geometry_msgs_pb2_grpc.py

예제 코드 동작 방식

main 함수에서 GRPCClient 클래스를 생성하여 해당 클래스를 통해 gRPC API를 호출하여 시뮬레이터와 gRPC 통신을 수행

  • 현재 시뮬레이터와 gRPC 통신을 하기 위해서는 아래와 같은 IP, Port를 사용

    • localhost:7789

프로그램은 바로 종료되지 않기 위해 무한 루프를 돌다가 ‘Ctrl + C’를 누르면 프로그램 종료

예제 코드의 동작 순서는 아래와 같다.

1] start command 호출하여 gRPC 통신을 시작

2] map load command 호출하여 시뮬레이터에 사용자가 원하는 맵을 로드

3] ego control command를 호출하여 ego를 원하는 위치로 이동

4] object pause 함수를 호출하여 ego 차량의 주행 시작

5] create npc vehicle command를 호출하여 npc 차량 생성

6] npc vehicle control command를 호출하여 npc 제어 명령 수행

7] create obstacle command를 호출하여 장애물 생성

8] start__status_worker를 thread로 생성하여 ego 및 npc 차량의 상태 정보를 주기적으로 수신

PY
import os, sys
from trace import Trace

current_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.normpath(os.path.join(current_path, './')))

import morai_openscenario_base_pb2
import morai_openscenario_base_pb2_grpc
import morai_openscenario_msgs_pb2
import morai_openscenario_msgs_pb2_grpc
import geometry_msgs_pb2
import geometry_msgs_pb2_grpc

import grpc
import asyncio

import math
import threading
import time
import json

class GRPCClient():
    def __init__(self, parent=None):
        self.cnt = 0
        self.event_loop = None

    async def get_vehicle_state(self):
        async with grpc.aio.insecure_channel('localhost:7789') as channel:
            stub = morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub(channel)
            req = morai_openscenario_base_pb2.ServiceRequest()
            req.service_name = ""
            req.msg = bytes("", 'utf-8')
            call = stub.Connect(req)
            async for resp in call:
                if resp.service_name == "/morai_msgs/MultiEgoState" :
                    info = json.loads(str(resp.msg))
                elif resp.service_name == "/morai_msgs/EgoState" :
                    info = json.loads(str(resp.msg))

    def start__status_worker(self):
        self.event_loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.event_loop)

        try:
            asyncio.get_event_loop().run_until_complete(self.get_vehicle_state())
        except:            
            return

    def send_ego_ctrl_cmd(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub) :
        k_city = {"x": 200.93956082984351, "y": 1766.8720364740955, "z": 0.0, "roll": 0.0, "pitch": 0.0, "yaw": -90.0}
       
        request = morai_openscenario_msgs_pb2.EgoCtrlCmd()
        request.service_name = "/morai_msgs/EgoCtrlCmd"

        request.position.x = k_city["x"]
        request.position.y = k_city["y"]
        request.position.z = k_city["z"]
        request.rotation.x = k_city["roll"]
        request.rotation.y = k_city["pitch"]
        request.rotation.z = k_city["yaw"]

        request.cruise_settings.cruise_on = True
        request.cruise_settings.cruise_type = morai_openscenario_msgs_pb2.EgoCruiseCtrl.CONSTANT
        request.cruise_settings.link_speed_ratio = 100
        request.cruise_settings.constant_velocity = 50

        request.velocity = 50
        request.pause = True
        
        try:
            response = stub.SendEgoCtrlCmd(request)
            print("SendEgoCtrlCmd - " + str(response))
        except:
            return

    def set_multi_ego_ctrl_cmd(self, ctrl_info, unique_id, longCmdType, accel, brake, steering, velocity, acceleration) :
        ctrl_info.unique_id = unique_id
        ctrl_info.longCmdType = longCmdType
        ctrl_info.accel = accel
        ctrl_info.brake = brake
        ctrl_info.steering = steering
        ctrl_info.velocity = velocity
        ctrl_info.acceleration = acceleration

        return ctrl_info

    def send_multi_ego_ctrl_cmd(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub) :
        while self.cnt < 15:
            self.cnt += 1
            request = morai_openscenario_msgs_pb2.MultiEgoCtrlCmdList()
            request.service_name = "/morai_msgs/MultiEgoCtrlCmd"
            
            ctrl_info = morai_openscenario_msgs_pb2.MultiEgoCtrlCmd()            
            ctrl_info = self.set_multi_ego_ctrl_cmd(ctrl_info, "NPC_1", 1, 0.9, 0, 1, 66, 77)
            request.multi_ego_ctrl_cmd.append(ctrl_info)
           
            ctrl_info_2 = morai_openscenario_msgs_pb2.MultiEgoCtrlCmd()
            ctrl_info_2 = self.set_multi_ego_ctrl_cmd(ctrl_info_2, "NPC_2", 2, 0.6, 0, 1, 30, 77)            
            request.multi_ego_ctrl_cmd.append(ctrl_info_2)

            try:
                response = stub.SendMultiEgoCtrlCmd(request)
            except:
                return

            time.sleep(1)
            
    
    def create_multi_ego_vehicle(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub) :       
        request = morai_openscenario_msgs_pb2.CreateMultiEgoVehicleRequestList()
        request.service_name = "/morai_msgs/CreateMultiEgoVehicle"
       
        create_info = morai_openscenario_msgs_pb2.CreateMultiEgoVehicleRequest()
        create_info.unique_id = "NPC_1"
        create_info.position.x = 201.1550653096617
        create_info.position.y = 1726.8726365584039
        create_info.position.z = 0.0
        create_info.rotation.x = 0.0
        create_info.rotation.y = 0.0
        create_info.rotation.z = -90.0
        create_info.velocity = 50
        create_info.vehicleName = "2016_Hyundai_Genesis_DH"
        create_info.pause = False
        
        request.req_list.append(create_info)

        create_info_2 = morai_openscenario_msgs_pb2.CreateMultiEgoVehicleRequest()
        create_info_2.unique_id = "NPC_2"
        create_info_2.position.x = 201.70380861467328
        create_info_2.position.y = 1620.374109200712
        create_info_2.position.z = 0.0
        create_info_2.rotation.x = 0.0
        create_info_2.rotation.y = 0.0
        create_info_2.rotation.z = -90.0
        create_info_2.velocity = 50
        create_info_2.vehicleName = "2016_Hyundai_Ioniq"
        create_info_2.pause = False
        
        request.req_list.append(create_info_2)

        try:
            response = stub.SendCreateMultiEgoVehicle(request)
            print("SendCreateMultiEgoVehicle" + str(response))
        except:
            return

    def start_cmd(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub):
        request = morai_openscenario_msgs_pb2.StartRequest()
        request.service_name = "/morai_msgs/StartCmd"
        request.cmd_start = True

        try:
            response = stub.Start(request)
        except:
            return
        finally:
            time.sleep(2)

    def stop_cmd(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub):
        request = morai_openscenario_msgs_pb2.StopRequest()
        request.service_name = "/morai_msgs/StopCmd"
        request.cmd_stop = True

        try:
            response = stub.Stop(request)
        except:
            return
        finally:
            time.sleep(2)
    
    def load_map(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub):
        request = morai_openscenario_msgs_pb2.Map()

        # extra asset bundle map을 로드하려는 경우
        # extra_asset_bundle_name = "las_test"
        # request.map_name = f"V_Extra_Scene,{extra_asset_bundle_name}"

        request.map_name = "R_KR_PG_KATRI"
        request.ego_name = "2016_Hyundai_Ioniq"

        try:
            response = stub.LoadMap(request)
            print(response)
        except:
            return

    def send_delete_multi_ego_vehicle(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub):
        request = morai_openscenario_msgs_pb2.DeleteMultiEgoVehicleRequest()
        request.service_name = "/morai_msgs/DeleteMultiEgoVehicle"
        request.req_delete = True

        try:
            response = stub.SendDeleteMultiEgoVehicle(request)
            print(response)
        except:
            return
        
    def object_pause(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub, object_list):
        request = morai_openscenario_msgs_pb2.ObjectPauseList()

        for object in object_list:
            msg = morai_openscenario_msgs_pb2.ObjectPause()
            msg.unique_id = object['unique_id']
            msg.obj_type = morai_openscenario_msgs_pb2.ObjectPause.EGO if object['is_ego'] else morai_openscenario_msgs_pb2.ObjectPause.MULTIEGO
            msg.set_pause = object['is_pause']

            request.req_list.append(msg)

        try:
            response = stub.ObjectPause(request)
        except BaseException as e:
            return     

    def create_obstacle(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub):
        request = morai_openscenario_msgs_pb2.ObstacleSpawnList()

        create_info = morai_openscenario_msgs_pb2.ObstacleSpawn()
        create_info.unique_id = 'obstacle_1'
        create_info.position.x = 208.68639761359265
        create_info.position.y = 1661.4038564971388
        create_info.position.z = 0
        create_info.rotation.x = 0.0
        create_info.rotation.y = 0.0
        create_info.rotation.z = 0.0
        create_info.scale.x = 1.0
        create_info.scale.y = 1.0
        create_info.scale.z = 1.0
        create_info.obstacle_name = 'CargoBox'

        request.req_list.append(create_info)

        create_info_2 = morai_openscenario_msgs_pb2.ObstacleSpawn()
        create_info_2.unique_id = 'obstacle_2'
        create_info_2.position.x = 212.4813728324434
        create_info_2.position.y = 1605.9207673354679
        create_info_2.position.z = 0
        create_info_2.rotation.x = 0.0
        create_info_2.rotation.y = 0.0
        create_info_2.rotation.z = 0.0
        create_info_2.scale.x = 1.0
        create_info_2.scale.y = 1.0
        create_info_2.scale.z = 1.0
        create_info_2.obstacle_name = 'WoodBox'

        request.req_list.append(create_info_2)

        # send message & get response
        try:
            response = stub.CreateObstacle(request)
        except BaseException as e:  
            return

    def delete_objects(self, stub : morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub):
        """Spawn Obstacles 제거"""
        req = morai_openscenario_msgs_pb2.CategoryObstacles()
        req.vehicle = True
        req.pedestrian = True
        req.obstacle = True
        req.spawn_point = True
        req.map_object = True

        try:
            stub.DeleteSpawnObstacles(req)
        except BaseException as e:
            return


if __name__ == "__main__":
    print('start example.')
    print('press ctrl + c for exit.')

    client = GRPCClient()
    channel = grpc.insecure_channel('localhost:7789')
    stub = morai_openscenario_base_pb2_grpc.MoraiOpenScenarioBaseServiceStub(channel)

    # start command
    client.start_cmd(stub)

    # load map command
    client.load_map(stub)

    # ego Control
    client.send_ego_ctrl_cmd(stub)

    # resume ego vehicle
    ego_object_list = []
    ego_object = {
        'unique_id': 'ego',
        'is_ego': True,
        'is_pause': False
    }
    ego_object_list.append(ego_object)
    client.object_pause(stub, ego_object_list)

    # create npc vehicle
    client.create_multi_ego_vehicle(stub)

    # control npc vehicle
    t_stop = threading.Thread(target=client.send_multi_ego_ctrl_cmd, daemon=True, args=(stub,))
    t_stop.start()

    # create obstacle
    client.create_obstacle(stub)

    # start to receive vehicle status
    t_car_status = threading.Thread(target=client.start__status_worker)
    t_car_status.start()
    
    try:
        while True:
            pass
    # press ctrl + c for exit
    except KeyboardInterrupt:      
        # delete all scenario objects
        client.delete_objects(stub)
        
        # stop receiving vehicle status
        if client.event_loop != None:
            client.event_loop.stop()

        # stop command
        client.stop_cmd(stub)

        # gRPC channel close
        channel.close()

        sys.exit()

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.