def launch_executable(file_name: str, args: List[str]) -> subprocess.Popen: """ Launches a Unity executable and returns the process handle for it. :param file_name: the name of the executable :param args: List of string that will be passed as command line arguments when launching the executable. """ launch_string = validate_environment_path(file_name) if launch_string is None: raise UnityEnvironmentException( f"Couldn't launch the {file_name} environment. Provided filename does not match any environments." ) else: get_logger(__name__).debug( f"This is the launch string {launch_string}") # Launch Unity environment subprocess_args = [launch_string] + args try: return subprocess.Popen( subprocess_args, # start_new_session=True means that signals to the parent python process # (e.g. SIGINT from keyboard interrupt) will not be sent to the new process on POSIX platforms. # This is generally good since we want the environment to have a chance to shutdown, # but may be undesirable in come cases; if so, we'll add a command-line toggle. # Note that on Windows, the CTRL_C signal will still be sent. start_new_session=True, ) except PermissionError as perm: # This is likely due to missing read or execute permissions on file. raise UnityEnvironmentException( f"Error when trying to launch environment - make sure " f"permissions are set correctly. For example " f'"chmod -R 755 {launch_string}"') from perm
def process_side_channel_message(self, data: bytes) -> None: """ Separates the data received from Python into individual messages for each registered side channel and calls on_message_received on them. :param data: The packed message sent by Unity """ offset = 0 while offset < len(data): try: channel_id = uuid.UUID(bytes_le=bytes(data[offset:offset + 16])) offset += 16 message_len, = struct.unpack_from("<i", data, offset) offset = offset + 4 message_data = data[offset:offset + message_len] offset = offset + message_len except (struct.error, ValueError, IndexError): raise UnityEnvironmentException( "There was a problem reading a message in a SideChannel. " "Please make sure the version of MLAgents in Unity is " "compatible with the Python version.") if len(message_data) != message_len: raise UnityEnvironmentException( "The message received by the side channel {0} was " "unexpectedly short. Make sure your Unity Environment " "sending side channel data properly.".format(channel_id)) if channel_id in self._side_channels_dict: incoming_message = IncomingMessage(message_data) self._side_channels_dict[channel_id].on_message_received( incoming_message) else: get_logger(__name__).warning( f"Unknown side channel data received. Channel type: {channel_id}." )
def validate_environment_path(env_path: str) -> Optional[str]: """ Strip out executable extensions of the env_path :param env_path: The path to the executable """ env_path = (env_path.strip().replace(".app", "").replace( ".exe", "").replace(".x86_64", "").replace(".x86", "")) true_filename = os.path.basename(os.path.normpath(env_path)) get_logger(__name__).debug(f"The true file name is {true_filename}") if not (glob.glob(env_path) or glob.glob(env_path + ".*")): return None cwd = os.getcwd() launch_string = None true_filename = os.path.basename(os.path.normpath(env_path)) if get_platform() == "linux" or get_platform() == "linux2": candidates = glob.glob(os.path.join(cwd, env_path) + ".x86_64") if len(candidates) == 0: candidates = glob.glob(os.path.join(cwd, env_path) + ".x86") if len(candidates) == 0: candidates = glob.glob(env_path + ".x86_64") if len(candidates) == 0: candidates = glob.glob(env_path + ".x86") if len(candidates) > 0: launch_string = candidates[0] elif get_platform() == "darwin": candidates = glob.glob( os.path.join(cwd, env_path + ".app", "Contents", "MacOS", true_filename)) if len(candidates) == 0: candidates = glob.glob( os.path.join(env_path + ".app", "Contents", "MacOS", true_filename)) if len(candidates) == 0: candidates = glob.glob( os.path.join(cwd, env_path + ".app", "Contents", "MacOS", "*")) if len(candidates) == 0: candidates = glob.glob( os.path.join(env_path + ".app", "Contents", "MacOS", "*")) if len(candidates) > 0: launch_string = candidates[0] elif get_platform() == "win32": candidates = glob.glob(os.path.join(cwd, env_path + ".exe")) if len(candidates) == 0: candidates = glob.glob(env_path + ".exe") if len(candidates) == 0: # Look for e.g. 3DBall\UnityEnvironment.exe crash_handlers = set( glob.glob(os.path.join(cwd, env_path, "UnityCrashHandler*.exe"))) candidates = [ c for c in glob.glob(os.path.join(cwd, env_path, "*.exe")) if c not in crash_handlers ] if len(candidates) > 0: launch_string = candidates[0] return launch_string
def validate_environment_path(env_path: str) -> Optional[str]: """ Strip out executable extensions of the env_path :param env_path: The path to the executable """ env_path = ( env_path.strip() .replace(".app", "") .replace(".exe", "") .replace(".x86_64", "") .replace(".x86", "") ) true_filename = os.path.basename(os.path.normpath(env_path)) get_logger(__name__).debug("The true file name is {}".format(true_filename)) if not (glob.glob(env_path) or glob.glob(env_path + ".*")): return None cwd = os.getcwd() launch_string = None true_filename = os.path.basename(os.path.normpath(env_path)) if platform == "linux" or platform == "linux2": candidates = glob.glob(os.path.join(cwd, env_path) + ".x86_64") if len(candidates) == 0: candidates = glob.glob(os.path.join(cwd, env_path) + ".x86") if len(candidates) == 0: candidates = glob.glob(env_path + ".x86_64") if len(candidates) == 0: candidates = glob.glob(env_path + ".x86") if len(candidates) > 0: launch_string = candidates[0] elif platform == "darwin": candidates = glob.glob( os.path.join(cwd, env_path + ".app", "Contents", "MacOS", true_filename) ) if len(candidates) == 0: candidates = glob.glob( os.path.join(env_path + ".app", "Contents", "MacOS", true_filename) ) if len(candidates) == 0: candidates = glob.glob( os.path.join(cwd, env_path + ".app", "Contents", "MacOS", "*") ) if len(candidates) == 0: candidates = glob.glob( os.path.join(env_path + ".app", "Contents", "MacOS", "*") ) if len(candidates) > 0: launch_string = candidates[0] elif platform == "win32": candidates = glob.glob(os.path.join(cwd, env_path + ".exe")) if len(candidates) == 0: candidates = glob.glob(env_path + ".exe") if len(candidates) > 0: launch_string = candidates[0] return launch_string
def __init__( self, trainer_factory: TrainerFactory, output_path: str, run_id: str, param_manager: EnvironmentParameterManager, train: bool, training_seed: int, ): """ :param output_path: Path to save the model. :param summaries_dir: Folder to save training summaries. :param run_id: The sub-directory name for model and summary statistics :param param_manager: EnvironmentParameterManager object which stores information about all environment parameters. :param train: Whether to train model, or only run inference. :param training_seed: Seed to use for Numpy and Tensorflow random number generation. :param threaded: Whether or not to run trainers in a separate thread. Disable for testing/debugging. """ self.trainers: Dict[str, Trainer] = {} self.brain_name_to_identifier: Dict[str, Set] = defaultdict(set) self.trainer_factory = trainer_factory self.output_path = output_path self.logger = get_logger(__name__) self.run_id = run_id self.train_model = train self.param_manager = param_manager self.ghost_controller = self.trainer_factory.ghost_controller self.registered_behavior_ids: Set[str] = set() self.trainer_threads: List[threading.Thread] = [] self.kill_trainers = False np.random.seed(training_seed) tf.set_random_seed(training_seed)
def __init__(self, trainer_factory: TrainerFactory, output_path: str, run_id: str, meta_curriculum: Optional[MetaCurriculum], train: bool, training_seed: int, sampler_manager: SamplerManager, resampling_interval: Optional[int]): """ :param output_path: Path to save the model. :param summaries_dir: Folder to save training summaries. :param run_id: The sub-directory name for model and summary statistics :param meta_curriculum: MetaCurriculum object which stores information about all curricula. :param train: Whether to train model, or only run inference. :param training_seed: Seed to use for Numpy and Tensorflow random number generation. :param sampler_manager: SamplerManager object handles samplers for resampling the reset parameters. :param resampling_interval: Specifies number of simulation steps after which reset parameters are resampled. :param threaded: Whether or not to run trainers in a separate thread. Disable for testing/debugging. """ self.trainers: Dict[str, Trainer] = {} self.brain_name_to_identifier: Dict[str, Set] = defaultdict(set) self.trainer_factory = trainer_factory self.output_path = output_path self.logger = get_logger(__name__) self.run_id = run_id self.train_model = train self.meta_curriculum = meta_curriculum self.sampler_manager = sampler_manager self.resampling_interval = resampling_interval self.ghost_controller = self.trainer_factory.ghost_controller self.trainer_threads: List[threading.Thread] = [] self.kill_trainers = False np.random.seed(training_seed) tf.set_random_seed(training_seed)
def __init__( self, trainer_factory: TrainerFactory, model_path: str, summaries_dir: str, run_id: str, save_freq: int, meta_curriculum: Optional[MetaCurriculum], train: bool, training_seed: int, sampler_manager: SamplerManager, resampling_interval: Optional[int], ): """ :param model_path: Path to save the model. :param summaries_dir: Folder to save training summaries. :param run_id: The sub-directory name for model and summary statistics :param save_freq: Frequency at which to save model :param meta_curriculum: MetaCurriculum object which stores information about all curricula. :param train: Whether to train model, or only run inference. :param training_seed: Seed to use for Numpy and Tensorflow random number generation. :param sampler_manager: SamplerManager object handles samplers for resampling the reset parameters. :param resampling_interval: Specifies number of simulation steps after which reset parameters are resampled. """ self.trainers: Dict[str, Trainer] = {} self.brain_name_to_identifier: Dict[str, Set] = defaultdict(set) self.trainer_factory = trainer_factory self.model_path = model_path self.summaries_dir = summaries_dir self.logger = get_logger(__name__) self.run_id = run_id self.save_freq = save_freq self.train_model = train self.meta_curriculum = meta_curriculum self.sampler_manager = sampler_manager self.resampling_interval = resampling_interval np.random.seed(training_seed) tf.set_random_seed(training_seed)
from mlagents_envs.communicator_objects.unity_rl_input_pb2 import UnityRLInputProto from mlagents_envs.communicator_objects.unity_rl_output_pb2 import UnityRLOutputProto from mlagents_envs.communicator_objects.agent_action_pb2 import AgentActionProto from mlagents_envs.communicator_objects.unity_output_pb2 import UnityOutputProto from mlagents_envs.communicator_objects.capabilities_pb2 import UnityRLCapabilitiesProto from mlagents_envs.communicator_objects.unity_rl_initialization_input_pb2 import ( UnityRLInitializationInputProto, ) from mlagents_envs.communicator_objects.unity_input_pb2 import UnityInputProto from .rpc_communicator import RpcCommunicator import signal logger = get_logger(__name__) class UnityEnvironment(BaseEnv): # Communication protocol version. # When connecting to C#, this must be compatible with Academy.k_ApiVersion. # We follow semantic versioning on the communication version, so existing # functionality will work as long the major versions match. # This should be changed whenever a change is made to the communication protocol. API_VERSION = "1.0.0" # Default port that the editor listens on. If an environment executable # isn't specified, this port will be used. DEFAULT_EDITOR_PORT = 5004 # Default base port for environments. Each environment will be offset from this
from typing import Set, Dict, Any, TextIO import os import yaml from mlagents.trainers.exception import TrainerConfigError from mlagents_envs.environment import UnityEnvironment import argparse from mlagents_envs import logging_util logger = logging_util.get_logger(__name__) class RaiseRemovedWarning(argparse.Action): """ Internal custom Action to raise warning when argument is called. """ def __init__(self, nargs=0, **kwargs): super().__init__(nargs=nargs, **kwargs) def __call__(self, arg_parser, namespace, values, option_string=None): logger.warning( f"The command line argument {option_string} was removed.") class DetectDefault(argparse.Action): """ Internal custom Action to help detect arguments that aren't default. """ non_default_args: Set[str] = set() def __call__(self, arg_parser, namespace, values, option_string=None):
def test_set_logging_level(): for level in [INFO, ERROR, FATAL, CRITICAL, DEBUG]: set_log_level(level) assert get_logger("test").level == level