def test_engine_configuration(): sender = EngineConfigurationChannel() # We use a raw bytes channel to interpred the data receiver = RawBytesChannel(sender.channel_id) config = EngineConfig.default_config() sender.set_configuration(config) data = SideChannelManager([sender]).generate_side_channel_messages() SideChannelManager([receiver]).process_side_channel_message(data) received_data = receiver.get_and_clear_received_messages() assert len(received_data) == 5 # 5 different messages one for each setting sent_time_scale = 4.5 sender.set_configuration_parameters(time_scale=sent_time_scale) data = SideChannelManager([sender]).generate_side_channel_messages() SideChannelManager([receiver]).process_side_channel_message(data) message = IncomingMessage(receiver.get_and_clear_received_messages()[0]) message.read_int32() time_scale = message.read_float32() assert time_scale == sent_time_scale with pytest.raises(UnitySideChannelException): sender.set_configuration_parameters(width=None, height=42) with pytest.raises(UnityCommunicationException): # try to send data to the EngineConfigurationChannel sender.set_configuration_parameters(time_scale=sent_time_scale) data = SideChannelManager([sender]).generate_side_channel_messages() SideChannelManager([sender]).process_side_channel_message(data)
def _create_engine_channel(self): engine_channel = EngineConfigurationChannel() engine_config = EngineConfig(80, 80, 1, 4.0, 30 * 4) if self.train_mode else EngineConfig( 1280, 720, 1, 1.0, 60) engine_channel.set_configuration(engine_config) return engine_channel
def _make_unity_env( env_path: Optional[str] = None, port: int = UnityEnvironment.BASE_ENVIRONMENT_PORT, seed: int = -1, env_args: Optional[List[str]] = None, engine_config: Optional[EngineConfig] = None, side_channels: Optional[List[SideChannel]] = None) -> UnityEnvironment: """ Create a UnityEnvironment. """ # Use Unity Editor if env file is not provided. if env_path is None: port = UnityEnvironment.DEFAULT_EDITOR_PORT else: launch_string = UnityEnvironment.validate_environment_path(env_path) if launch_string is None: raise UnityEnvironmentException( f"Couldn't launch the {env_path} environment. Provided filename does not match any environments." ) logger.info(f"Starting environment from {env_path}.") # Configure Unity Engine. if engine_config is None: engine_config = EngineConfig.default_config() engine_configuration_channel = EngineConfigurationChannel() engine_configuration_channel.set_configuration(engine_config) if side_channels is None: side_channels = [engine_configuration_channel] else: side_channels.append(engine_configuration_channel) # Find an available port to connect to Unity environment. while True: try: env = UnityEnvironment( file_name=env_path, seed=seed, base_port=port, args=env_args, side_channels=side_channels, ) except UnityWorkerInUseException: logger.debug(f"port {port} in use.") port += 1 else: logger.info(f"Connected to environment using port {port}.") break return env
def create_engine_config_side_channel(self) -> EngineConfigurationChannel: if self.play or self.inference: engine_configuration = EngineConfig( width=self.WINDOW_WIDTH.play, height=self.WINDOW_HEIGHT.play, quality_level=self.QUALITY_LEVEL.play, time_scale=self.TIMESCALE.play, target_frame_rate=self.TARGET_FRAME_RATE.play, ) else: engine_configuration = EngineConfig( width=self.WINDOW_WIDTH.train, height=self.WINDOW_HEIGHT.train, quality_level=self.QUALITY_LEVEL.train, time_scale=self.TIMESCALE.train, target_frame_rate=self.TARGET_FRAME_RATE.train, ) engine_configuration_channel = EngineConfigurationChannel() engine_configuration_channel.set_configuration(engine_configuration) return engine_configuration_channel
def worker( parent_conn: Connection, step_queue: Queue, pickled_env_factory: str, worker_id: int, engine_configuration: EngineConfig, ) -> None: env_factory: Callable[[int, List[SideChannel]], UnityEnvironment] = cloudpickle.loads( pickled_env_factory) shared_float_properties = FloatPropertiesChannel() engine_configuration_channel = EngineConfigurationChannel() engine_configuration_channel.set_configuration(engine_configuration) stats_channel = StatsSideChannel() env: BaseEnv = env_factory( worker_id, [shared_float_properties, engine_configuration_channel, stats_channel], ) def _send_response(cmd_name, payload): parent_conn.send(EnvironmentResponse(cmd_name, worker_id, payload)) def _generate_all_results() -> AllStepResult: all_step_result: AllStepResult = {} for brain_name in env.get_agent_groups(): all_step_result[brain_name] = env.get_step_result(brain_name) return all_step_result def external_brains(): result = {} for brain_name in env.get_agent_groups(): result[brain_name] = group_spec_to_brain_parameters( brain_name, env.get_agent_group_spec(brain_name)) return result try: while True: cmd: EnvironmentCommand = parent_conn.recv() if cmd.name == "step": all_action_info = cmd.payload for brain_name, action_info in all_action_info.items(): if len(action_info.action) != 0: env.set_actions(brain_name, action_info.action) env.step() all_step_result = _generate_all_results() # The timers in this process are independent from all the processes and the "main" process # So after we send back the root timer, we can safely clear them. # Note that we could randomly return timers a fraction of the time if we wanted to reduce # the data transferred. # TODO get gauges from the workers and merge them in the main process too. env_stats = stats_channel.get_and_reset_stats() step_response = StepResponse(all_step_result, get_timer_root(), env_stats) step_queue.put( EnvironmentResponse("step", worker_id, step_response)) reset_timers() elif cmd.name == "external_brains": _send_response("external_brains", external_brains()) elif cmd.name == "get_properties": reset_params = shared_float_properties.get_property_dict_copy() _send_response("get_properties", reset_params) elif cmd.name == "reset": for k, v in cmd.payload.items(): shared_float_properties.set_property(k, v) env.reset() all_step_result = _generate_all_results() _send_response("reset", all_step_result) elif cmd.name == "close": break except (KeyboardInterrupt, UnityCommunicationException, UnityTimeOutException): logger.info( f"UnityEnvironment worker {worker_id}: environment stopping.") step_queue.put(EnvironmentResponse("env_close", worker_id, None)) finally: # If this worker has put an item in the step queue that hasn't been processed by the EnvManager, the process # will hang until the item is processed. We avoid this behavior by using Queue.cancel_join_thread() # See https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread for # more info. logger.debug(f"UnityEnvironment worker {worker_id} closing.") step_queue.cancel_join_thread() step_queue.close() env.close() logger.debug(f"UnityEnvironment worker {worker_id} done.")
def worker( parent_conn: Connection, step_queue: Queue, pickled_env_factory: str, worker_id: int, engine_configuration: EngineConfig, log_level: int = logging_util.INFO, ) -> None: env_factory: Callable[ [int, List[SideChannel]], UnityEnvironment ] = cloudpickle.loads(pickled_env_factory) env_parameters = EnvironmentParametersChannel() engine_configuration_channel = EngineConfigurationChannel() engine_configuration_channel.set_configuration(engine_configuration) stats_channel = StatsSideChannel() env: BaseEnv = None # Set log level. On some platforms, the logger isn't common with the # main process, so we need to set it again. logging_util.set_log_level(log_level) def _send_response(cmd_name: EnvironmentCommand, payload: Any) -> None: parent_conn.send(EnvironmentResponse(cmd_name, worker_id, payload)) def _generate_all_results() -> AllStepResult: all_step_result: AllStepResult = {} for brain_name in env.behavior_specs: all_step_result[brain_name] = env.get_steps(brain_name) return all_step_result try: env = env_factory( worker_id, [env_parameters, engine_configuration_channel, stats_channel] ) while True: req: EnvironmentRequest = parent_conn.recv() if req.cmd == EnvironmentCommand.STEP: all_action_info = req.payload for brain_name, action_info in all_action_info.items(): if len(action_info.action) != 0: env.set_actions(brain_name, action_info.action) env.step() all_step_result = _generate_all_results() # The timers in this process are independent from all the processes and the "main" process # So after we send back the root timer, we can safely clear them. # Note that we could randomly return timers a fraction of the time if we wanted to reduce # the data transferred. # TODO get gauges from the workers and merge them in the main process too. env_stats = stats_channel.get_and_reset_stats() step_response = StepResponse( all_step_result, get_timer_root(), env_stats ) step_queue.put( EnvironmentResponse( EnvironmentCommand.STEP, worker_id, step_response ) ) reset_timers() elif req.cmd == EnvironmentCommand.BEHAVIOR_SPECS: _send_response(EnvironmentCommand.BEHAVIOR_SPECS, env.behavior_specs) elif req.cmd == EnvironmentCommand.ENVIRONMENT_PARAMETERS: for k, v in req.payload.items(): if isinstance(v, ParameterRandomizationSettings): v.apply(k, env_parameters) elif req.cmd == EnvironmentCommand.RESET: env.reset() all_step_result = _generate_all_results() _send_response(EnvironmentCommand.RESET, all_step_result) elif req.cmd == EnvironmentCommand.CLOSE: break except ( KeyboardInterrupt, UnityCommunicationException, UnityTimeOutException, UnityEnvironmentException, UnityCommunicatorStoppedException, ) as ex: logger.info(f"UnityEnvironment worker {worker_id}: environment stopping.") step_queue.put( EnvironmentResponse(EnvironmentCommand.ENV_EXITED, worker_id, ex) ) _send_response(EnvironmentCommand.ENV_EXITED, ex) finally: # If this worker has put an item in the step queue that hasn't been processed by the EnvManager, the process # will hang until the item is processed. We avoid this behavior by using Queue.cancel_join_thread() # See https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread for # more info. logger.debug(f"UnityEnvironment worker {worker_id} closing.") step_queue.cancel_join_thread() step_queue.close() if env is not None: env.close() logger.debug(f"UnityEnvironment worker {worker_id} done.")
def worker( parent_conn: Connection, step_queue: Queue, pickled_env_factory: str, worker_id: int, run_options: RunOptions, log_level: int = logging_util.INFO, ) -> None: env_factory: Callable[ [int, List[SideChannel]], UnityEnvironment ] = cloudpickle.loads(restricted_loads(pickled_env_factory)) env_parameters = EnvironmentParametersChannel() engine_config = EngineConfig( width=run_options.engine_settings.width, height=run_options.engine_settings.height, quality_level=run_options.engine_settings.quality_level, time_scale=run_options.engine_settings.time_scale, target_frame_rate=run_options.engine_settings.target_frame_rate, capture_frame_rate=run_options.engine_settings.capture_frame_rate, ) engine_configuration_channel = EngineConfigurationChannel() engine_configuration_channel.set_configuration(engine_config) stats_channel = StatsSideChannel() training_analytics_channel: Optional[TrainingAnalyticsSideChannel] = None if worker_id == 0: training_analytics_channel = TrainingAnalyticsSideChannel() env: UnityEnvironment = None # Set log level. On some platforms, the logger isn't common with the # main process, so we need to set it again. logging_util.set_log_level(log_level) def _send_response(cmd_name: EnvironmentCommand, payload: Any) -> None: parent_conn.send(EnvironmentResponse(cmd_name, worker_id, payload)) def _generate_all_results() -> AllStepResult: all_step_result: AllStepResult = {} for brain_name in env.behavior_specs: all_step_result[brain_name] = env.get_steps(brain_name) return all_step_result try: side_channels = [env_parameters, engine_configuration_channel, stats_channel] if training_analytics_channel is not None: side_channels.append(training_analytics_channel) env = env_factory(worker_id, side_channels) if ( not env.academy_capabilities or not env.academy_capabilities.trainingAnalytics ): # Make sure we don't try to send training analytics if the environment doesn't know how to process # them. This wouldn't be catastrophic, but would result in unknown SideChannel UUIDs being used. training_analytics_channel = None if training_analytics_channel: training_analytics_channel.environment_initialized(run_options) while True: req: EnvironmentRequest = parent_conn.recv() if req.cmd == EnvironmentCommand.STEP: all_action_info = req.payload for brain_name, action_info in all_action_info.items(): if len(action_info.agent_ids) > 0: env.set_actions(brain_name, action_info.env_action) env.step() all_step_result = _generate_all_results() # The timers in this process are independent from all the processes and the "main" process # So after we send back the root timer, we can safely clear them. # Note that we could randomly return timers a fraction of the time if we wanted to reduce # the data transferred. # TODO get gauges from the workers and merge them in the main process too. env_stats = stats_channel.get_and_reset_stats() step_response = StepResponse( all_step_result, get_timer_root(), env_stats ) step_queue.put( EnvironmentResponse( EnvironmentCommand.STEP, worker_id, step_response ) ) reset_timers() elif req.cmd == EnvironmentCommand.BEHAVIOR_SPECS: _send_response(EnvironmentCommand.BEHAVIOR_SPECS, env.behavior_specs) elif req.cmd == EnvironmentCommand.ENVIRONMENT_PARAMETERS: for k, v in req.payload.items(): if isinstance(v, ParameterRandomizationSettings): v.apply(k, env_parameters) elif req.cmd == EnvironmentCommand.TRAINING_STARTED: behavior_name, trainer_config = req.payload if training_analytics_channel: training_analytics_channel.training_started( behavior_name, trainer_config ) elif req.cmd == EnvironmentCommand.RESET: env.reset() all_step_result = _generate_all_results() _send_response(EnvironmentCommand.RESET, all_step_result) elif req.cmd == EnvironmentCommand.CLOSE: break except ( KeyboardInterrupt, UnityCommunicationException, UnityTimeOutException, UnityEnvironmentException, UnityCommunicatorStoppedException, ) as ex: logger.info(f"UnityEnvironment worker {worker_id}: environment stopping.") step_queue.put( EnvironmentResponse(EnvironmentCommand.ENV_EXITED, worker_id, ex) ) _send_response(EnvironmentCommand.ENV_EXITED, ex) except Exception as ex: logger.exception( f"UnityEnvironment worker {worker_id}: environment raised an unexpected exception." ) step_queue.put( EnvironmentResponse(EnvironmentCommand.ENV_EXITED, worker_id, ex) ) _send_response(EnvironmentCommand.ENV_EXITED, ex) finally: logger.debug(f"UnityEnvironment worker {worker_id} closing.") if env is not None: env.close() logger.debug(f"UnityEnvironment worker {worker_id} done.") parent_conn.close() step_queue.put(EnvironmentResponse(EnvironmentCommand.CLOSED, worker_id, None)) step_queue.close()
class StorageEnvController(ConFormSimUnityEnvController): _BASE_PORT = 5004 def __init__(self, config=DEFAULT_ENV_CONFIG): """ Environment initialization :param config: Configuration of the environment. """ # create side channels self.env_param_channel = EnvironmentParametersChannel() self.engine_channel = EngineConfigurationChannel() self.color_pool_channel = IntListPropertiesChannel() side_channels = [ self.env_param_channel, self.engine_channel, self.color_pool_channel, ] # flag whether the config has been apllied to the environment self.is_already_initialized = False # create environment with config and side channels super().__init__(config, DEFAULT_ENV_CONFIG, side_channels=side_channels) def apply_config(self): # set FloatProperties grid_size_x = self.config.get("grid_size_x") if not isinstance(grid_size_x, list) or len(grid_size_x) != 2: raise ("The provided grid_size_x parameter is no list of type " "[min, max]. Please correct this.") grid_size_y = self.config.get("grid_size_y") if not isinstance(grid_size_y, list) or len(grid_size_y) != 2: raise ("The provided grid_size_y parameter is no list of type " "[min, max]. Please correct this.") vis_obs_size = self.config.get("vis_obs_size") if not isinstance(vis_obs_size, list) or len(vis_obs_size) != 2: raise ("The provided vis_obs_size parameter is no list of type " "[min, max]. Please correct this.") base_size_x = self.config.get("base_size_x") if not isinstance(base_size_x, list) or len(base_size_x) != 2: raise ("The provided base_size_x parameter is no list of type " "[min, max]. Please correct this.") base_size_y = self.config.get("base_size_x") if not isinstance(base_size_x, list) or len(base_size_x) != 2: raise ("The provided base_size_x parameter is no list of type " "[min, max]. Please correct this.") num_per_base_type = self.config.get("num_per_base_type") if not isinstance(num_per_base_type, list) or len(num_per_base_type) != 2: raise ( "The provided num_per_base_type parameter is no list of type " "[min, max]. Please correct this.") num_per_item = self.config.get("num_per_item") if not isinstance(num_per_item, list) or len(num_per_item) != 2: raise ("The provided num_per_item parameter is no list of type " "[min, max]. Please correct this.") color_pool = self.config.get("color_pool") if not isinstance(color_pool, list): raise ("The provided color_pool parameter is not of type list. " "Please correct this.") camera_type = self.config.get("camera_type") camera_type_f: float = CAMERA_TYPES[camera_type] or 0.0 # set properties in reset channel self.env_param_channel.set_float_parameter("minGridSizeX", grid_size_x[0]) self.env_param_channel.set_float_parameter("maxGridSizeX", grid_size_x[1]) self.env_param_channel.set_float_parameter("minGridSizeY", grid_size_y[0]) self.env_param_channel.set_float_parameter("maxGridSizeY", grid_size_y[1]) self.env_param_channel.set_float_parameter("cameraType", camera_type_f) # area settings # check if num train areas should be set if self.is_already_initialized: print("You're trying to change the number of " "train areas, during runtime. This is only possible at " "initialization.") else: self.env_param_channel.set_float_parameter( "numTrainAreas", self.config.get("num_train_areas")) self.env_param_channel.set_float_parameter( "numBaseTypesToUse", self.config.get("num_base_types")) self.env_param_channel.set_float_parameter("numberPerBaseTypeMax", num_per_base_type[1]) self.env_param_channel.set_float_parameter("numberPerBaseTypeMin", num_per_base_type[0]) self.env_param_channel.set_float_parameter("baseSizeXMax", base_size_x[1]) self.env_param_channel.set_float_parameter("baseSizeXMin", base_size_x[0]) self.env_param_channel.set_float_parameter("baseSizeZMax", base_size_y[1]) self.env_param_channel.set_float_parameter("baseSizeZMin", base_size_y[0]) self.env_param_channel.set_float_parameter( "baseInCornersOnly", 1 if self.config.get("base_in_corners_only") else 0) self.env_param_channel.set_float_parameter( "boxesVanish", 1 if self.config.get("boxes_vanish") else 0) self.env_param_channel.set_float_parameter( "boxesNeedDrop", 1 if self.config.get("boxes_need_drop") else 0) self.env_param_channel.set_float_parameter( "sparseReward", 1 if self.config.get("sparse_reward_only") else 0) # color settings self.env_param_channel.set_float_parameter( "noBaseFillColor", 1 if self.config.get("no_base_fill_color") else 0) self.env_param_channel.set_float_parameter( "brighterBases", 1 if self.config.get("brighter_bases") else 0) self.env_param_channel.set_float_parameter( "full_base_line", 1 if self.config.get("fullBaseLine") else 0) # item settings self.env_param_channel.set_float_parameter( "numItemTypesToUse", self.config.get("num_item_types")) self.env_param_channel.set_float_parameter("numberPerItemTypeMax", num_per_item[1]) self.env_param_channel.set_float_parameter("numberPerItemTypeMin", num_per_item[0]) # general settings self.env_param_channel.set_float_parameter( "noDisplay", 1 if self.config.get("no_display") else 0) self.env_param_channel.set_float_parameter("visObsWidth", vis_obs_size[0]) self.env_param_channel.set_float_parameter("visObsHeight", vis_obs_size[1]) self.env_param_channel.set_float_parameter( "useVisual", 1 if self.config.get("use_visual") and not self.config.get("use_object_property_camera") else 0) self.env_param_channel.set_float_parameter( "useRayPerception", 1 if self.config.get("use_ray_perception") else 0) self.env_param_channel.set_float_parameter( "useObjectPropertyCamera", 1 if self.config.get("use_object_property_camera") else 0) self.env_param_channel.set_float_parameter( "maxSteps", self.config.get("max_steps")) self.env_param_channel.set_float_parameter( "taskLevel", self.config.get("task_level")) # Read engine config engine_config = self.config.get("engine_config") # Configure the Engine engine_config = EngineConfig( width=engine_config.get("window_width"), height=engine_config.get("window_height"), quality_level=engine_config.get("quality_level"), time_scale=engine_config.get("sim_speed"), target_frame_rate=engine_config.get("target_frame_rate"), capture_frame_rate=60) self.engine_channel.set_configuration(engine_config) # set list properties self.color_pool_channel.set_property("colorPool", self.config.get("color_pool")) self.is_already_initialized = True