import ctypes import functools import multiprocessing import numpy as np import time from multiprocessing import Pipe, Array from openpilot.tools.sim.bridge.common import QueueMessage, QueueMessageType from openpilot.tools.sim.bridge.metadrive.metadrive_process import (metadrive_process, metadrive_simulation_state, metadrive_vehicle_state) from openpilot.tools.sim.lib.common import SimulatorState, World from openpilot.tools.sim.lib.camerad import W, H class MetaDriveWorld(World): def __init__(self, status_q, config, test_duration, test_run, dual_camera=False): super().__init__(dual_camera) self.status_q = status_q self.camera_array = Array(ctypes.c_uint8, W*H*3) self.road_image = np.frombuffer(self.camera_array.get_obj(), dtype=np.uint8).reshape((H, W, 3)) self.wide_camera_array = None if dual_camera: self.wide_camera_array = Array(ctypes.c_uint8, W*H*3) self.wide_road_image = np.frombuffer(self.wide_camera_array.get_obj(), dtype=np.uint8).reshape((H, W, 3)) self.controls_send, self.controls_recv = Pipe() self.simulation_state_send, self.simulation_state_recv = Pipe() self.vehicle_state_send, self.vehicle_state_recv = Pipe() self.exit_event = multiprocessing.Event() self.op_engaged = multiprocessing.Event() self.test_run = test_run self.first_engage = None self.last_check_timestamp = 0 self.distance_moved = 0 self.metadrive_process = multiprocessing.Process(name="metadrive process", target= functools.partial(metadrive_process, dual_camera, config, self.camera_array, self.wide_camera_array, self.image_lock, self.controls_recv, self.simulation_state_send, self.vehicle_state_send, self.exit_event, self.op_engaged, test_duration, self.test_run)) self.metadrive_process.start() self.status_q.put(QueueMessage(QueueMessageType.START_STATUS, "starting")) print("----------------------------------------------------------") print("---- Spawning Metadrive world, this might take awhile ----") print("----------------------------------------------------------") self.vehicle_last_pos = self.vehicle_state_recv.recv().position # wait for a state message to ensure metadrive is launched self.status_q.put(QueueMessage(QueueMessageType.START_STATUS, "started")) self.steer_ratio = 15 self.vc = [0.0,0.0] self.reset_time = 0 self.should_reset = False def apply_controls(self, steer_angle, throttle_out, brake_out): if (time.monotonic() - self.reset_time) > 2: self.vc[0] = steer_angle if throttle_out: self.vc[1] = throttle_out else: self.vc[1] = -brake_out else: self.vc[0] = 0 self.vc[1] = 0 self.controls_send.send([*self.vc, self.should_reset]) self.should_reset = False def read_state(self): while self.simulation_state_recv.poll(0): md_state: metadrive_simulation_state = self.simulation_state_recv.recv() if md_state.done: self.status_q.put(QueueMessage(QueueMessageType.TERMINATION_INFO, md_state.done_info)) self.exit_event.set() def read_sensors(self, state: SimulatorState): while self.vehicle_state_recv.poll(0): md_vehicle: metadrive_vehicle_state = self.vehicle_state_recv.recv() curr_pos = md_vehicle.position state.velocity = md_vehicle.velocity state.bearing = md_vehicle.bearing state.steering_angle = md_vehicle.steering_angle state.gps.from_xy(curr_pos) state.valid = True is_engaged = state.is_engaged if is_engaged and self.first_engage is None: self.first_engage = time.monotonic() self.op_engaged.set() # check moving 5 seconds after engaged, doesn't move right away after_engaged_check = is_engaged and time.monotonic() - self.first_engage >= 5 and self.test_run x_dist = abs(curr_pos[0] - self.vehicle_last_pos[0]) y_dist = abs(curr_pos[1] - self.vehicle_last_pos[1]) dist_threshold = 1 if x_dist >= dist_threshold or y_dist >= dist_threshold: # position not the same during staying still, > threshold is considered moving self.distance_moved += x_dist + y_dist time_check_threshold = 15 current_time = time.monotonic() since_last_check = current_time - self.last_check_timestamp if since_last_check >= time_check_threshold: if after_engaged_check and self.distance_moved == 0: self.status_q.put(QueueMessage(QueueMessageType.TERMINATION_INFO, {"vehicle_not_moving" : True})) self.exit_event.set() self.last_check_timestamp = current_time self.distance_moved = 0 self.vehicle_last_pos = curr_pos def read_cameras(self): pass def tick(self): pass def reset(self): self.should_reset = True def close(self, reason: str): self.status_q.put(QueueMessage(QueueMessageType.CLOSE_STATUS, reason)) self.exit_event.set() self.metadrive_process.join()