浏览代码

[WIP] Side Channel Design Changes (#3807)

* Make EnvironmentParameters a first-class citizen in the API

Missing: Python conterparts and testing.

* Minor comment fix to Engine Parameters

* A second minor fix.

* Make EngineConfigChannel Internal and add a singleton/sealed accessor

* Make StatsSideChannel Internal and add a singleton/sealed accessor

* Changes to SideChannelUtils

- Disallow two sidechannels of the same type to be added
- Remove GetSideChannels that return a list as that is now unnecessary
- Make most methods except (register/unregister) internal to limit users impacting the “system-level” side channels
- Add an improved comment to SideChannel.cs

* Added Dispose methods to system-level sidechannel wrappers

- Specifically to StatsRecorder, EnvironmentParameters and EngineParameters.
- Updated Academy.Dispose to take advantage of these.
- Updated Editor tests to cover all three “system-level” side channels.

Kudos to Unit Tests (TestAcade...
/develop/dockerfile
GitHub 5 年前
当前提交
ea0c6fa0
共有 49 个文件被更改,包括 915 次插入496 次删除
  1. 8
      Project/Assets/ML-Agents/Examples/3DBall/Scripts/Ball3DAgent.cs
  2. 8
      Project/Assets/ML-Agents/Examples/3DBall/Scripts/Ball3DHardAgent.cs
  3. 6
      Project/Assets/ML-Agents/Examples/Bouncer/Scripts/BouncerAgent.cs
  4. 7
      Project/Assets/ML-Agents/Examples/FoodCollector/Scripts/FoodCollectorAgent.cs
  5. 10
      Project/Assets/ML-Agents/Examples/FoodCollector/Scripts/FoodCollectorSettings.cs
  6. 9
      Project/Assets/ML-Agents/Examples/GridWorld/Scripts/GridAgent.cs
  7. 20
      Project/Assets/ML-Agents/Examples/GridWorld/Scripts/GridArea.cs
  8. 2
      Project/Assets/ML-Agents/Examples/GridWorld/Scripts/GridSettings.cs
  9. 18
      Project/Assets/ML-Agents/Examples/PushBlock/Scripts/PushAgentBasic.cs
  10. 13
      Project/Assets/ML-Agents/Examples/Reacher/Scripts/ReacherAgent.cs
  11. 3
      Project/Assets/ML-Agents/Examples/SharedAssets/Scripts/ProjectSettingsOverrides.cs
  12. 8
      Project/Assets/ML-Agents/Examples/Soccer/Scripts/AgentSoccer.cs
  13. 6
      Project/Assets/ML-Agents/Examples/Soccer/Scripts/SoccerFieldArea.cs
  14. 8
      Project/Assets/ML-Agents/Examples/Tennis/Scripts/TennisAgent.cs
  15. 10
      Project/Assets/ML-Agents/Examples/Walker/Scripts/WalkerAgent.cs
  16. 12
      Project/Assets/ML-Agents/Examples/WallJump/Scripts/WallJumpAgent.cs
  17. 23
      com.unity.ml-agents/CHANGELOG.md
  18. 42
      com.unity.ml-agents/Runtime/Academy.cs
  19. 4
      com.unity.ml-agents/Runtime/Communicator/RpcCommunicator.cs
  20. 58
      com.unity.ml-agents/Runtime/SideChannels/EngineConfigurationChannel.cs
  21. 30
      com.unity.ml-agents/Runtime/SideChannels/FloatPropertiesChannel.cs
  22. 2
      com.unity.ml-agents/Runtime/SideChannels/RawBytesChannel.cs
  23. 17
      com.unity.ml-agents/Runtime/SideChannels/SideChannel.cs
  24. 45
      com.unity.ml-agents/Runtime/SideChannels/StatsSideChannel.cs
  25. 2
      com.unity.ml-agents/Runtime/SideChannels/EnvironmentParametersChannel.cs.meta
  26. 16
      com.unity.ml-agents/Tests/Editor/MLAgentsEditModeTest.cs
  27. 32
      com.unity.ml-agents/Tests/Editor/SideChannelTests.cs
  28. 10
      docs/Custom-SideChannels.md
  29. 27
      docs/Migrating.md
  30. 36
      docs/Python-API.md
  31. 4
      docs/Training-Curriculum-Learning.md
  32. 4
      docs/Using-Tensorboard.md
  33. 2
      ml-agents-envs/mlagents_envs/environment.py
  34. 88
      ml-agents-envs/mlagents_envs/side_channel/engine_configuration_channel.py
  35. 5
      ml-agents/mlagents/trainers/env_manager.py
  36. 19
      ml-agents/mlagents/trainers/learn.py
  37. 14
      ml-agents/mlagents/trainers/simple_env_manager.py
  38. 19
      ml-agents/mlagents/trainers/subprocess_env_manager.py
  39. 6
      ml-agents/mlagents/trainers/tests/test_simple_rl.py
  40. 70
      com.unity.ml-agents/Runtime/EnvironmentParameters.cs
  41. 11
      com.unity.ml-agents/Runtime/EnvironmentParameters.cs.meta
  42. 91
      com.unity.ml-agents/Runtime/SideChannels/EnvironmentParametersChannel.cs
  43. 218
      com.unity.ml-agents/Runtime/SideChannels/SideChannelsManager.cs
  44. 11
      com.unity.ml-agents/Runtime/SideChannels/SideChannelsManager.cs.meta
  45. 71
      com.unity.ml-agents/Runtime/StatsRecorder.cs
  46. 11
      com.unity.ml-agents/Runtime/StatsRecorder.cs.meta
  47. 37
      ml-agents-envs/mlagents_envs/side_channel/environment_parameters_channel.py
  48. 238
      com.unity.ml-agents/Runtime/SideChannels/SideChannelUtils.cs
  49. 0
      /com.unity.ml-agents/Runtime/SideChannels/EnvironmentParametersChannel.cs.meta

8
Project/Assets/ML-Agents/Examples/3DBall/Scripts/Ball3DAgent.cs


[Header("Specific to Ball3D")]
public GameObject ball;
Rigidbody m_BallRb;
FloatPropertiesChannel m_ResetParams;
EnvironmentParameters m_ResetParams;
m_ResetParams = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}

public void SetBall()
{
//Set the attributes of the ball by fetching the information from the academy
m_BallRb.mass = m_ResetParams.GetPropertyWithDefault("mass", 1.0f);
var scale = m_ResetParams.GetPropertyWithDefault("scale", 1.0f);
m_BallRb.mass = m_ResetParams.GetWithDefault("mass", 1.0f);
var scale = m_ResetParams.GetWithDefault("scale", 1.0f);
ball.transform.localScale = new Vector3(scale, scale, scale);
}

8
Project/Assets/ML-Agents/Examples/3DBall/Scripts/Ball3DHardAgent.cs


[Header("Specific to Ball3DHard")]
public GameObject ball;
Rigidbody m_BallRb;
FloatPropertiesChannel m_ResetParams;
EnvironmentParameters m_ResetParams;
m_ResetParams = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}

public void SetBall()
{
//Set the attributes of the ball by fetching the information from the academy
m_BallRb.mass = m_ResetParams.GetPropertyWithDefault("mass", 1.0f);
var scale = m_ResetParams.GetPropertyWithDefault("scale", 1.0f);
m_BallRb.mass = m_ResetParams.GetWithDefault("mass", 1.0f);
var scale = m_ResetParams.GetWithDefault("scale", 1.0f);
ball.transform.localScale = new Vector3(scale, scale, scale);
}

6
Project/Assets/ML-Agents/Examples/Bouncer/Scripts/BouncerAgent.cs


int m_NumberJumps = 20;
int m_JumpLeft = 20;
FloatPropertiesChannel m_ResetParams;
EnvironmentParameters m_ResetParams;
public override void Initialize()
{

m_ResetParams = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}

public void SetTargetScale()
{
var targetScale = m_ResetParams.GetPropertyWithDefault("target_scale", 1.0f);
var targetScale = m_ResetParams.GetWithDefault("target_scale", 1.0f);
target.transform.localScale = new Vector3(targetScale, targetScale, targetScale);
}

7
Project/Assets/ML-Agents/Examples/FoodCollector/Scripts/FoodCollectorAgent.cs


public bool contribute;
public bool useVectorObs;
EnvironmentParameters m_ResetParams;
public override void Initialize()
{

m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}

public void SetLaserLengths()
{
m_LaserLength = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>().GetPropertyWithDefault("laser_length", 1.0f);
m_LaserLength = m_ResetParams.GetWithDefault("laser_length", 1.0f);
float agentScale = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>().GetPropertyWithDefault("agent_scale", 1.0f);
float agentScale = m_ResetParams.GetWithDefault("agent_scale", 1.0f);
gameObject.transform.localScale = new Vector3(agentScale, agentScale, agentScale);
}

10
Project/Assets/ML-Agents/Examples/FoodCollector/Scripts/FoodCollectorSettings.cs


using System;
using MLAgents.SideChannels;
public class FoodCollectorSettings : MonoBehaviour
{

public int totalScore;
public Text scoreText;
StatsSideChannel m_statsSideChannel;
StatsRecorder m_Recorder;
m_statsSideChannel = SideChannelUtils.GetSideChannel<StatsSideChannel>();
m_Recorder = Academy.Instance.StatsRecorder;
public void EnvironmentReset()
private void EnvironmentReset()
{
ClearObjects(GameObject.FindGameObjectsWithTag("food"));
ClearObjects(GameObject.FindGameObjectsWithTag("badFood"));

// need to send every Update() call.
if ((Time.frameCount % 100)== 0)
{
m_statsSideChannel?.AddStat("TotalScore", totalScore);
m_Recorder.Add("TotalScore", totalScore);
}
}
}

9
Project/Assets/ML-Agents/Examples/GridWorld/Scripts/GridAgent.cs


const int k_Left = 3;
const int k_Right = 4;
EnvironmentParameters m_ResetParams;
public override void Initialize()
{
m_ResetParams = Academy.Instance.EnvironmentParameters;
}
public override void CollectDiscreteActionMasks(DiscreteActionMasker actionMasker)
{
// Mask the necessary actions if selected by the user.

var positionX = (int)transform.position.x;
var positionZ = (int)transform.position.z;
var maxPosition = (int)SideChannelUtils.GetSideChannel<FloatPropertiesChannel>().GetPropertyWithDefault("gridSize", 5f) - 1;
var maxPosition = (int)m_ResetParams.GetWithDefault("gridSize", 5f) - 1;
if (positionX == 0)
{

20
Project/Assets/ML-Agents/Examples/GridWorld/Scripts/GridArea.cs


public GameObject trueAgent;
FloatPropertiesChannel m_ResetParameters;
Camera m_AgentCam;
public GameObject goalPref;

Vector3 m_InitialPosition;
EnvironmentParameters m_ResetParams;
m_ResetParameters = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
m_Objects = new[] { goalPref, pitPref };

m_InitialPosition = transform.position;
}
public void SetEnvironment()
private void SetEnvironment()
transform.position = m_InitialPosition * (m_ResetParameters.GetPropertyWithDefault("gridSize", 5f) + 1);
transform.position = m_InitialPosition * (m_ResetParams.GetWithDefault("gridSize", 5f) + 1);
for (var i = 0; i < (int)m_ResetParameters.GetPropertyWithDefault("numObstacles", 1); i++)
for (var i = 0; i < (int)m_ResetParams.GetWithDefault("numObstacles", 1); i++)
for (var i = 0; i < (int)m_ResetParameters.GetPropertyWithDefault("numGoals", 1f); i++)
for (var i = 0; i < (int)m_ResetParams.GetWithDefault("numGoals", 1f); i++)
var gridSize = (int)m_ResetParameters.GetPropertyWithDefault("gridSize", 5f);
var gridSize = (int)m_ResetParams.GetWithDefault("gridSize", 5f);
m_Plane.transform.localScale = new Vector3(gridSize / 10.0f, 1f, gridSize / 10.0f);
m_Plane.transform.localPosition = new Vector3((gridSize - 1) / 2f, -0.5f, (gridSize - 1) / 2f);
m_Sn.transform.localScale = new Vector3(1, 1, gridSize + 2);

public void AreaReset()
{
var gridSize = (int)m_ResetParameters.GetPropertyWithDefault("gridSize", 5f);
var gridSize = (int)m_ResetParams.GetWithDefault("gridSize", 5f);
foreach (var actor in actorObjs)
{
DestroyImmediate(actor);

{
numbers.Add(Random.Range(0, gridSize * gridSize));
}
var numbersA = Enumerable.ToArray(numbers);
var numbersA = numbers.ToArray();
for (var i = 0; i < players.Length; i++)
{

2
Project/Assets/ML-Agents/Examples/GridWorld/Scripts/GridSettings.cs


public void Awake()
{
SideChannelUtils.GetSideChannel<FloatPropertiesChannel>().RegisterCallback("gridSize", f =>
Academy.Instance.EnvironmentParameters.RegisterCallback("gridSize", f =>
{
MainCamera.transform.position = new Vector3(-(f - 1) / 2f, f * 1.25f, -(f - 1) / 2f);
MainCamera.orthographicSize = (f + 5f) / 2f;

18
Project/Assets/ML-Agents/Examples/PushBlock/Scripts/PushAgentBasic.cs


/// </summary>
Renderer m_GroundRenderer;
private EnvironmentParameters m_ResetParams;
void Awake()
{
m_PushBlockSettings = FindObjectOfType<PushBlockSettings>();

m_GroundRenderer = ground.GetComponent<Renderer>();
// Starting material
m_GroundMaterial = m_GroundRenderer.material;
m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}

public void SetGroundMaterialFriction()
{
var resetParams = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
groundCollider.material.dynamicFriction = resetParams.GetPropertyWithDefault("dynamic_friction", 0);
groundCollider.material.staticFriction = resetParams.GetPropertyWithDefault("static_friction", 0);
groundCollider.material.dynamicFriction = m_ResetParams.GetWithDefault("dynamic_friction", 0);
groundCollider.material.staticFriction = m_ResetParams.GetWithDefault("static_friction", 0);
var resetParams = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
var scale = resetParams.GetPropertyWithDefault("block_scale", 2);
var scale = m_ResetParams.GetWithDefault("block_scale", 2);
m_BlockRb.drag = resetParams.GetPropertyWithDefault("block_drag", 0.5f);
m_BlockRb.drag = m_ResetParams.GetWithDefault("block_drag", 0.5f);
public void SetResetParameters()
private void SetResetParameters()
{
SetGroundMaterialFriction();
SetBlockProperties();

13
Project/Assets/ML-Agents/Examples/Reacher/Scripts/ReacherAgent.cs


// Frequency of the cosine deviation of the goal along the vertical dimension
float m_DeviationFreq;
private EnvironmentParameters m_ResetParams;
/// <summary>
/// Collect the rigidbodies of the reacher in order to resue them for
/// observations and actions.

m_RbA = pendulumA.GetComponent<Rigidbody>();
m_RbB = pendulumB.GetComponent<Rigidbody>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}

public void SetResetParameters()
{
var fp = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_GoalSize = fp.GetPropertyWithDefault("goal_size", 5);
m_GoalSpeed = Random.Range(-1f, 1f) * fp.GetPropertyWithDefault("goal_speed", 1);
m_Deviation = fp.GetPropertyWithDefault("deviation", 0);
m_DeviationFreq = fp.GetPropertyWithDefault("deviation_freq", 0);
m_GoalSize = m_ResetParams.GetWithDefault("goal_size", 5);
m_GoalSpeed = Random.Range(-1f, 1f) * m_ResetParams.GetWithDefault("goal_speed", 1);
m_Deviation = m_ResetParams.GetWithDefault("deviation", 0);
m_DeviationFreq = m_ResetParams.GetWithDefault("deviation_freq", 0);
}
}

3
Project/Assets/ML-Agents/Examples/SharedAssets/Scripts/ProjectSettingsOverrides.cs


Physics.defaultSolverVelocityIterations = solverVelocityIterations;
// Make sure the Academy singleton is initialized first, since it will create the SideChannels.
var academy = Academy.Instance;
SideChannelUtils.GetSideChannel<FloatPropertiesChannel>().RegisterCallback("gravity", f => { Physics.gravity = new Vector3(0, -f, 0); });
Academy.Instance.EnvironmentParameters.RegisterCallback("gravity", f => { Physics.gravity = new Vector3(0, -f, 0); });
}
public void OnDestroy()

8
Project/Assets/ML-Agents/Examples/Soccer/Scripts/AgentSoccer.cs


BehaviorParameters m_BehaviorParameters;
Vector3 m_Transform;
private EnvironmentParameters m_ResetParams;
public override void Initialize()
{
m_Existential = 1f / MaxStep;

m_LateralSpeed = 0.3f;
m_ForwardSpeed = 1.3f;
}
else
else
{
m_LateralSpeed = 0.3f;
m_ForwardSpeed = 1.0f;

area.playerStates.Add(playerState);
m_PlayerIndex = area.playerStates.IndexOf(playerState);
playerState.playerIndex = m_PlayerIndex;
m_ResetParams = Academy.Instance.EnvironmentParameters;
}
public void MoveAgent(float[] act)

{
timePenalty = 0;
m_BallTouch = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>().GetPropertyWithDefault("ball_touch", 0);
m_BallTouch = m_ResetParams.GetWithDefault("ball_touch", 0);
if (team == Team.Purple)
{
transform.rotation = Quaternion.Euler(0f, -90f, 0f);

6
Project/Assets/ML-Agents/Examples/Soccer/Scripts/SoccerFieldArea.cs


[HideInInspector]
public bool canResetBall;
private EnvironmentParameters m_ResetParams;
void Awake()
{
canResetBall = true;

m_BallController.area = this;
ballStartingPos = ball.transform.position;
m_ResetParams = Academy.Instance.EnvironmentParameters;
}
IEnumerator ShowGoalUI()

ballRb.velocity = Vector3.zero;
ballRb.angularVelocity = Vector3.zero;
var ballScale = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>().GetPropertyWithDefault("ball_scale", 0.015f);
var ballScale = m_ResetParams.GetWithDefault("ball_scale", 0.015f);
ballRb.transform.localScale = new Vector3(ballScale, ballScale, ballScale);
}
}

8
Project/Assets/ML-Agents/Examples/Tennis/Scripts/TennisAgent.cs


Rigidbody m_AgentRb;
Rigidbody m_BallRb;
float m_InvertMult;
FloatPropertiesChannel m_ResetParams;
EnvironmentParameters m_ResetParams;
// Looks for the scoreboard based on the name of the gameObjects.
// Do not modify the names of the Score GameObjects

m_BallRb = ball.GetComponent<Rigidbody>();
var canvas = GameObject.Find(k_CanvasName);
GameObject scoreBoard;
m_ResetParams = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
if (invertX)
{
scoreBoard = canvas.transform.Find(k_ScoreBoardBName).gameObject;

public void SetRacket()
{
angle = m_ResetParams.GetPropertyWithDefault("angle", 55);
angle = m_ResetParams.GetWithDefault("angle", 55);
gameObject.transform.eulerAngles = new Vector3(
gameObject.transform.eulerAngles.x,
gameObject.transform.eulerAngles.y,

public void SetBall()
{
scale = m_ResetParams.GetPropertyWithDefault("scale", .5f);
scale = m_ResetParams.GetWithDefault("scale", .5f);
ball.transform.localScale = new Vector3(scale, scale, scale);
}

10
Project/Assets/ML-Agents/Examples/Walker/Scripts/WalkerAgent.cs


Rigidbody m_ChestRb;
Rigidbody m_SpineRb;
FloatPropertiesChannel m_ResetParams;
EnvironmentParameters m_ResetParams;
public override void Initialize()
{

m_ChestRb = chest.GetComponent<Rigidbody>();
m_SpineRb = spine.GetComponent<Rigidbody>();
m_ResetParams = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}

public void SetTorsoMass()
{
m_ChestRb.mass = m_ResetParams.GetPropertyWithDefault("chest_mass", 8);
m_SpineRb.mass = m_ResetParams.GetPropertyWithDefault("spine_mass", 10);
m_HipsRb.mass = m_ResetParams.GetPropertyWithDefault("hip_mass", 15);
m_ChestRb.mass = m_ResetParams.GetWithDefault("chest_mass", 8);
m_SpineRb.mass = m_ResetParams.GetWithDefault("spine_mass", 10);
m_HipsRb.mass = m_ResetParams.GetWithDefault("hip_mass", 15);
}
public void SetResetParameters()

12
Project/Assets/ML-Agents/Examples/WallJump/Scripts/WallJumpAgent.cs


Vector3 m_JumpTargetPos;
Vector3 m_JumpStartingPos;
FloatPropertiesChannel m_FloatProperties;
EnvironmentParameters m_ResetParams;
public override void Initialize()
{

spawnArea.SetActive(false);
m_FloatProperties = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
}
// Begin the jump sequence

{
localScale = new Vector3(
localScale.x,
m_FloatProperties.GetPropertyWithDefault("no_wall_height", 0),
m_ResetParams.GetWithDefault("no_wall_height", 0),
localScale.z);
wall.transform.localScale = localScale;
SetModel("SmallWallJump", noWallBrain);

localScale = new Vector3(
localScale.x,
m_FloatProperties.GetPropertyWithDefault("small_wall_height", 4),
m_ResetParams.GetWithDefault("small_wall_height", 4),
localScale.z);
wall.transform.localScale = localScale;
SetModel("SmallWallJump", smallWallBrain);

var min = m_FloatProperties.GetPropertyWithDefault("big_wall_min_height", 8);
var max = m_FloatProperties.GetPropertyWithDefault("big_wall_max_height", 8);
var min = m_ResetParams.GetWithDefault("big_wall_min_height", 8);
var max = m_ResetParams.GetWithDefault("big_wall_max_height", 8);
var height = min + Random.value * (max - min);
localScale = new Vector3(
localScale.x,

23
com.unity.ml-agents/CHANGELOG.md


- The `--load` and `--train` command-line flags have been deprecated. Training
now happens by default, and use `--resume` to resume training instead. (#3705)
- The Jupyter notebooks have been removed from the repository.
- Introduced the `SideChannelUtils` to register, unregister and access side
channels.
- `Academy.FloatProperties` was removed, please use
`SideChannelUtils.GetSideChannel<FloatPropertiesChannel>()` instead.
- Removed the multi-agent gym option from the gym wrapper. For multi-agent
scenarios, use the [Low Level Python API](../docs/Python-API.md).
- The low level Python API has changed. You can look at the document

`AgentAction` and `AgentReset` have been removed.
- The GhostTrainer has been extended to support asymmetric games and the
asymmetric example environment Strikers Vs. Goalie has been added.
- The SideChannel API has changed (#3833, #3660) :
- Introduced the `SideChannelManager` to register, unregister and access side
channels.
- `EnvironmentParameters` replaces the default `FloatProperties`.
You can access the `EnvironmentParameters` with
`Academy.Instance.EnvironmentParameters` on C# and create an
`EnvironmentParametersChannel` on Python
- `SideChannel.OnMessageReceived` is now a protected method (was public)
- SideChannel IncomingMessages methods now take an optional default argument,
which is used when trying to read more data than the message contains.
- Added a feature to allow sending stats from C# environments to TensorBoard
(and other python StatsWriters). To do this from your code, use
`Academy.Instance.StatsRecorder.Add(key, value)`(#3660)
- CameraSensorComponent.m_Grayscale and RenderTextureSensorComponent.m_Grayscale
were changed from `public` to `private` (#3808).
- The `UnityEnv` class from the `gym-unity` package was renamed

- Format of console output has changed slightly and now matches the name of the
model/summary directory. (#3630, #3616)
- Added a feature to allow sending stats from C# environments to TensorBoard
(and other python StatsWriters). To do this from your code, use
`SideChannelUtils.GetSideChannel<StatsSideChannel>().AddStat(key, value)`
(#3660)
- SideChannel IncomingMessages methods now take an optional default argument,
which is used when trying to read more data than the message contains.
- The way that UnityEnvironment decides the port was changed. If no port is
specified, the behavior will depend on the `file_name` parameter. If it is
`None`, 5004 (the editor port) will be used; otherwise 5005 (the base

42
com.unity.ml-agents/Runtime/Academy.cs


/// Access the Academy singleton through the <see cref="Instance"/>
/// property. The Academy instance is initialized the first time it is accessed (which will
/// typically be by the first <see cref="Agent"/> initialized in a scene).
///
///
/// At initialization, the Academy attempts to connect to the Python training process through
/// the external communicator. If successful, the training process can train <see cref="Agent"/>
/// instances. When you set an agent's <see cref="BehaviorParameters.behaviorType"/> setting

/// on each side, although we may allow some flexibility in the future.
/// This should be incremented whenever a change is made to the communication protocol.
/// </summary>
const string k_ApiVersion = "0.16.0";
const string k_ApiVersion = "0.17.0";
/// <summary>
/// Unity package version of com.unity.ml-agents.

}
}
private EnvironmentParameters m_EnvironmentParameters;
private StatsRecorder m_StatsRecorder;
/// <summary>
/// Returns the <see cref="EnvironmentParameters"/> instance. If training
/// features such as Curriculum Learning or Environment Parameter Randomization are used,
/// then the values of the parameters generated from the training process can be
/// retrieved here.
/// </summary>
/// <returns></returns>
public EnvironmentParameters EnvironmentParameters
{
get { return m_EnvironmentParameters; }
}
/// <summary>
/// Returns the <see cref="StatsRecorder"/> instance. This instance can be used
/// to record any statistics from the Unity environment.
/// </summary>
/// <returns></returns>
public StatsRecorder StatsRecorder
{
get { return m_StatsRecorder; }
}
/// <summary>
/// Initializes the environment, configures it and initializes the Academy.
/// </summary>

EnableAutomaticStepping();
SideChannelUtils.RegisterSideChannel(new EngineConfigurationChannel());
SideChannelUtils.RegisterSideChannel(new FloatPropertiesChannel());
SideChannelUtils.RegisterSideChannel(new StatsSideChannel());
SideChannelsManager.RegisterSideChannel(new EngineConfigurationChannel());
m_EnvironmentParameters = new EnvironmentParameters();
m_StatsRecorder = new StatsRecorder();
// Try to launch the communicator by using the arguments passed at launch
var port = ReadPortFromArgs();

// If the communicator is not on, we need to clear the SideChannel sending queue
if (!IsCommunicatorOn)
{
SideChannelUtils.GetSideChannelMessage();
SideChannelsManager.GetSideChannelMessage();
}
using (TimerStack.Instance.Scoped("AgentAct"))

Communicator?.Dispose();
Communicator = null;
SideChannelUtils.UnregisterAllSideChannels();
m_EnvironmentParameters.Dispose();
m_StatsRecorder.Dispose();
SideChannelsManager.UnregisterAllSideChannels(); // unregister custom side channels
if (m_ModelRunners != null)
{

4
com.unity.ml-agents/Runtime/Communicator/RpcCommunicator.cs


void UpdateEnvironmentWithInput(UnityRLInputProto rlInput)
{
SideChannelUtils.ProcessSideChannelData(rlInput.SideChannel.ToArray());
SideChannelsManager.ProcessSideChannelData(rlInput.SideChannel.ToArray());
SendCommandEvent(rlInput.Command);
}

message.RlInitializationOutput = tempUnityRlInitializationOutput;
}
byte[] messageAggregated = SideChannelUtils.GetSideChannelMessage();
byte[] messageAggregated = SideChannelsManager.GetSideChannelMessage();
message.RlOutput.SideChannel = ByteString.CopyFrom(messageAggregated);
var input = Exchange(message);

58
com.unity.ml-agents/Runtime/SideChannels/EngineConfigurationChannel.cs


namespace MLAgents.SideChannels
{
public class EngineConfigurationChannel : SideChannel
internal class EngineConfigurationChannel : SideChannel
private enum ConfigurationType : int
{
ScreenResolution = 0,
QualityLevel = 1,
TimeScale = 2,
TargetFrameRate = 3,
CaptureFrameRate = 4
}
const string k_EngineConfigId = "e951342c-4f7e-11ea-b238-784f4387d1f7";
/// <summary>

}
/// <inheritdoc/>
public override void OnMessageReceived(IncomingMessage msg)
protected override void OnMessageReceived(IncomingMessage msg)
var width = msg.ReadInt32();
var height = msg.ReadInt32();
var qualityLevel = msg.ReadInt32();
var timeScale = msg.ReadFloat32();
var targetFrameRate = msg.ReadInt32();
timeScale = Mathf.Clamp(timeScale, 1, 100);
Screen.SetResolution(width, height, false);
QualitySettings.SetQualityLevel(qualityLevel, true);
Time.timeScale = timeScale;
Time.captureFramerate = 60;
Application.targetFrameRate = targetFrameRate;
var messageType = (ConfigurationType)msg.ReadInt32();
switch (messageType)
{
case ConfigurationType.ScreenResolution:
var width = msg.ReadInt32();
var height = msg.ReadInt32();
Screen.SetResolution(width, height, false);
break;
case ConfigurationType.QualityLevel:
var qualityLevel = msg.ReadInt32();
QualitySettings.SetQualityLevel(qualityLevel, true);
break;
case ConfigurationType.TimeScale:
var timeScale = msg.ReadFloat32();
timeScale = Mathf.Clamp(timeScale, 1, 100);
Time.timeScale = timeScale;
break;
case ConfigurationType.TargetFrameRate:
var targetFrameRate = msg.ReadInt32();
Application.targetFrameRate = targetFrameRate;
break;
case ConfigurationType.CaptureFrameRate:
var captureFrameRate = msg.ReadInt32();
Time.captureFramerate = captureFrameRate;
break;
default:
Debug.LogWarning(
"Unknown engine configuration received from Python. Make sure" +
" your Unity and Python versions are compatible.");
break;
}
}
}
}

30
com.unity.ml-agents/Runtime/SideChannels/FloatPropertiesChannel.cs


}
/// <inheritdoc/>
public override void OnMessageReceived(IncomingMessage msg)
protected override void OnMessageReceived(IncomingMessage msg)
{
var key = msg.ReadString();
var value = msg.ReadFloat32();

action?.Invoke(value);
}
/// <inheritdoc/>
public void SetProperty(string key, float value)
/// <summary>
/// Sets one of the float properties of the environment. This data will be sent to Python.
/// </summary>
/// <param name="key"> The string identifier of the property.</param>
/// <param name="value"> The float value of the property.</param>
public void Set(string key, float value)
{
m_FloatProperties[key] = value;
using (var msgOut = new OutgoingMessage())

action?.Invoke(value);
}
public float GetPropertyWithDefault(string key, float defaultValue)
/// <summary>
/// Get an Environment property with a default value. If there is a value for this property,
/// it will be returned, otherwise, the default value will be returned.
/// </summary>
/// <param name="key"> The string identifier of the property.</param>
/// <param name="defaultValue"> The default value of the property.</param>
/// <returns></returns>
public float GetWithDefault(string key, float defaultValue)
{
float valueOut;
bool hasKey = m_FloatProperties.TryGetValue(key, out valueOut);

/// <summary>
/// Registers an action to be performed everytime the property is changed.
/// </summary>
/// <param name="key"> The string identifier of the property.</param>
/// <param name="action"> The action that ill be performed. Takes a float as input.</param>
public IList<string> ListProperties()
/// <summary>
/// Returns a list of all the string identifiers of the properties currently present.
/// </summary>
/// <returns> The list of string identifiers </returns>
public IList<string> Keys()
{
return new List<string>(m_FloatProperties.Keys);
}

2
com.unity.ml-agents/Runtime/SideChannels/RawBytesChannel.cs


}
/// <inheritdoc/>
public override void OnMessageReceived(IncomingMessage msg)
protected override void OnMessageReceived(IncomingMessage msg)
{
m_MessagesReceived.Add(msg.GetRawBytes());
}

17
com.unity.ml-agents/Runtime/SideChannels/SideChannel.cs


/// Side channels provide an alternative mechanism of sending/receiving data from Unity
/// to Python that is outside of the traditional machine learning loop. ML-Agents provides
/// some specific implementations of side channels, but users can create their own.
///
/// To create your own, you'll need to create two, new mirrored classes, one in Unity (by
/// extending <see cref="SideChannel"/>) and another in Python by extending a Python class
/// also called SideChannel. Then, within your project, use
/// <see cref="SideChannelsManager.RegisterSideChannel"/> and
/// <see cref="SideChannelsManager.UnregisterSideChannel"/> to register and unregister your
/// custom side channel.
/// </summary>
public abstract class SideChannel
{

protected set;
}
internal void ProcessMessage(byte[] msg)
{
using (var incomingMsg = new IncomingMessage(msg))
{
OnMessageReceived(incomingMsg);
}
}
public abstract void OnMessageReceived(IncomingMessage msg);
protected abstract void OnMessageReceived(IncomingMessage msg);
/// <summary>
/// Queues a message to be sent to Python during the next simulation step.

45
com.unity.ml-agents/Runtime/SideChannels/StatsSideChannel.cs


namespace MLAgents.SideChannels
{
/// <summary>
/// Determines the behavior of how multiple stats within the same summary period are combined.
/// A Side Channel for sending <see cref="StatsRecorder"/> data.
public enum StatAggregationMethod
{
/// <summary>
/// Values within the summary period are averaged before reporting.
/// Note that values from the same C# environment in the same step may replace each other.
/// </summary>
Average = 0,
/// <summary>
/// Only the most recent value is reported.
/// To avoid conflicts between multiple environments, the ML Agents environment will only
/// keep stats from worker index 0.
/// </summary>
MostRecent = 1
}
/// <summary>
/// Add stats (key-value pairs) for reporting. The ML Agents environment will send these to a StatsReporter
/// instance, which means the values will appear in the Tensorboard summary, as well as trainer gauges.
/// Note that stats are only written every summary_frequency steps; See <see cref="StatAggregationMethod"/>
/// for options on how multiple values are handled.
/// </summary>
public class StatsSideChannel : SideChannel
internal class StatsSideChannel : SideChannel
/// Initializes the side channel with the provided channel ID.
/// The constructor is internal because only one instance is
/// supported at a time, and is created by the Academy.
/// Initializes the side channel. The constructor is internal because only one instance is
/// supported at a time.
/// </summary>
internal StatsSideChannel()
{

/// <summary>
/// Add a stat value for reporting. This will appear in the Tensorboard summary and trainer gauges.
/// You can nest stats in Tensorboard with "/".
/// Note that stats are only written to Tensorboard each summary_frequency steps; if a stat is
/// received multiple times, only the most recent version is used.
/// To avoid conflicts between multiple environments, only stats from worker index 0 are used.
/// Add a stat value for reporting.
/// <param name="value">The stat value. You can nest stats in Tensorboard by using "/". </param>
/// <param name="value">The stat value.</param>
public void AddStat(
string key, float value, StatAggregationMethod aggregationMethod = StatAggregationMethod.Average
)
public void AddStat(string key, float value, StatAggregationMethod aggregationMethod)
{
using (var msg = new OutgoingMessage())
{

}
/// <inheritdoc/>
public override void OnMessageReceived(IncomingMessage msg)
protected override void OnMessageReceived(IncomingMessage msg)
{
throw new UnityAgentsException("StatsSideChannel should never receive messages.");
}

2
com.unity.ml-agents/Runtime/SideChannels/EnvironmentParametersChannel.cs.meta


fileFormatVersion: 2
guid: 2506dff31271f49298fbff21e13fa8b6
guid: a849760d5bec946b884984e35c66fcfa
MonoImporter:
externalObjects: {}
serializedVersion: 2

16
com.unity.ml-agents/Tests/Editor/MLAgentsEditModeTest.cs


Assert.AreEqual(0, aca.EpisodeCount);
Assert.AreEqual(0, aca.StepCount);
Assert.AreEqual(0, aca.TotalStepCount);
Assert.AreNotEqual(null, SideChannelUtils.GetSideChannel<FloatPropertiesChannel>());
Assert.AreNotEqual(null, SideChannelsManager.GetSideChannel<EnvironmentParametersChannel>());
Assert.AreNotEqual(null, SideChannelsManager.GetSideChannel<EngineConfigurationChannel>());
Assert.AreNotEqual(null, SideChannelsManager.GetSideChannel<StatsSideChannel>());
// Check that Dispose is idempotent
aca.Dispose();

[Test]
public void TestAcademyDispose()
{
var floatProperties1 = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
var envParams1 = SideChannelsManager.GetSideChannel<EnvironmentParametersChannel>();
var engineParams1 = SideChannelsManager.GetSideChannel<EngineConfigurationChannel>();
var statsParams1 = SideChannelsManager.GetSideChannel<StatsSideChannel>();
var floatProperties2 = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
var envParams2 = SideChannelsManager.GetSideChannel<EnvironmentParametersChannel>();
var engineParams2 = SideChannelsManager.GetSideChannel<EngineConfigurationChannel>();
var statsParams2 = SideChannelsManager.GetSideChannel<StatsSideChannel>();
Assert.AreNotEqual(floatProperties1, floatProperties2);
Assert.AreNotEqual(envParams1, envParams2);
Assert.AreNotEqual(engineParams1, engineParams2);
Assert.AreNotEqual(statsParams1, statsParams2);
}
[Test]

32
com.unity.ml-agents/Tests/Editor/SideChannelTests.cs


ChannelId = new Guid("6afa2c06-4f82-11ea-b238-784f4387d1f7");
}
public override void OnMessageReceived(IncomingMessage msg)
protected override void OnMessageReceived(IncomingMessage msg)
{
messagesReceived.Add(msg.ReadInt32());
}

intSender.SendInt(5);
intSender.SendInt(6);
byte[] fakeData = SideChannelUtils.GetSideChannelMessage(dictSender);
SideChannelUtils.ProcessSideChannelData(dictReceiver, fakeData);
byte[] fakeData = SideChannelsManager.GetSideChannelMessage(dictSender);
SideChannelsManager.ProcessSideChannelData(dictReceiver, fakeData);
Assert.AreEqual(intReceiver.messagesReceived[0], 4);
Assert.AreEqual(intReceiver.messagesReceived[1], 5);

strSender.SendRawBytes(Encoding.ASCII.GetBytes(str1));
strSender.SendRawBytes(Encoding.ASCII.GetBytes(str2));
byte[] fakeData = SideChannelUtils.GetSideChannelMessage(dictSender);
SideChannelUtils.ProcessSideChannelData(dictReceiver, fakeData);
byte[] fakeData = SideChannelsManager.GetSideChannelMessage(dictSender);
SideChannelsManager.ProcessSideChannelData(dictReceiver, fakeData);
var messages = strReceiver.GetAndClearReceivedMessages();

var dictSender = new Dictionary<Guid, SideChannel> { { propB.ChannelId, propB } };
propA.RegisterCallback(k1, f => { wasCalled++; });
var tmp = propB.GetPropertyWithDefault(k2, 3.0f);
var tmp = propB.GetWithDefault(k2, 3.0f);
propB.SetProperty(k2, 1.0f);
tmp = propB.GetPropertyWithDefault(k2, 3.0f);
propB.Set(k2, 1.0f);
tmp = propB.GetWithDefault(k2, 3.0f);
byte[] fakeData = SideChannelUtils.GetSideChannelMessage(dictSender);
SideChannelUtils.ProcessSideChannelData(dictReceiver, fakeData);
byte[] fakeData = SideChannelsManager.GetSideChannelMessage(dictSender);
SideChannelsManager.ProcessSideChannelData(dictReceiver, fakeData);
tmp = propA.GetPropertyWithDefault(k2, 3.0f);
tmp = propA.GetWithDefault(k2, 3.0f);
propB.SetProperty(k1, 1.0f);
propB.Set(k1, 1.0f);
fakeData = SideChannelUtils.GetSideChannelMessage(dictSender);
SideChannelUtils.ProcessSideChannelData(dictReceiver, fakeData);
fakeData = SideChannelsManager.GetSideChannelMessage(dictSender);
SideChannelsManager.ProcessSideChannelData(dictReceiver, fakeData);
var keysA = propA.ListProperties();
var keysA = propA.Keys();
var keysB = propA.ListProperties();
var keysB = propA.Keys();
Assert.AreEqual(2, keysB.Count);
Assert.IsTrue(keysB.Contains(k1));
Assert.IsTrue(keysB.Contains(k2));

10
docs/Custom-SideChannels.md


You can create your own side channel in C# and Python and use it to communicate
custom data structures between the two. This can be useful for situations in
which the data to be sent is too complex or structured for the built-in
`FloatPropertiesChannel`, or is not related to any specific agent, and therefore
`EnvironmentParameters`, or is not related to any specific agent, and therefore
inappropriate as an agent observation.
## Overview

`base.QueueMessageToSend(msg)` method inside the side channel, and call the
`OutgoingMessage.Dispose()` method.
To register a side channel on the Unity side, call `SideChannelUtils.RegisterSideChannel` with the side channel
To register a side channel on the Unity side, call `SideChannelManager.RegisterSideChannel` with the side channel
as only argument.
### Python side

// When a Debug.Log message is created, we send it to the stringChannel
Application.logMessageReceived += stringChannel.SendDebugStatementToPython;
// The channel must be registered with the SideChannelUtils class
SideChannelUtils.RegisterSideChannel(stringChannel);
// The channel must be registered with the SideChannelManager class
SideChannelManager.RegisterSideChannel(stringChannel);
}
public void OnDestroy()

if (Academy.IsInitialized){
SideChannelUtils.UnregisterSideChannel(stringChannel);
SideChannelManager.UnregisterSideChannel(stringChannel);
}
}

27
docs/Migrating.md


- The signature of `Agent.Heuristic()` was changed to take a `float[]` as a
parameter, instead of returning the array. This was done to prevent a common
source of error where users would return arrays of the wrong size.
- The SideChannel API has changed (#3833, #3660) :
- Introduced the `SideChannelManager` to register, unregister and access side
channels.
- `EnvironmentParameters` replaces the default `FloatProperties`.
You can access the `EnvironmentParameters` with
`Academy.Instance.EnvironmentParameters` on C# and create an
`EnvironmentParametersChannel` on Python
- `SideChannel.OnMessageReceived` is now a protected method (was public)
- SideChannel IncomingMessages methods now take an optional default argument,
which is used when trying to read more data than the message contains.
- Added a feature to allow sending stats from C# environments to TensorBoard
(and other python StatsWriters). To do this from your code, use
`Academy.Instance.StatsRecorder.Add(key, value)`(#3660)
- `num_updates` and `train_interval` for SAC have been replaced with `steps_per_update`.
- The `UnityEnv` class from the `gym-unity` package was renamed
`UnityToGymWrapper` and no longer creates the `UnityEnvironment`. Instead,

- To force-overwrite files from a pre-existing run, add the `--force`
command-line flag.
- The Jupyter notebooks have been removed from the repository.
- `Academy.FloatProperties` was removed.
- `Academy.RegisterSideChannel` and `Academy.UnregisterSideChannel` were
removed.
- Replace `Academy.FloatProperties` with
`SideChannelUtils.GetSideChannel<FloatPropertiesChannel>()`.
- Replace `Academy.RegisterSideChannel` with
`SideChannelUtils.RegisterSideChannel()`.
- Replace `Academy.UnregisterSideChannel` with
`SideChannelUtils.UnregisterSideChannel`.
- If you used `SideChannels` you must:
- Replace `Academy.FloatProperties` with `Academy.Instance.EnvironmentParameters`.
- `Academy.RegisterSideChannel` and `Academy.UnregisterSideChannel` were
removed. Use `SideChannelManager.RegisterSideChannel` and
`SideChannelManager.UnregisterSideChannel` instead.
- Set `steps_per_update` to be around equal to the number of agents in your environment,
times `num_updates` and divided by `train_interval`.
- Replace `UnityEnv` with `UnityToGymWrapper` in your code. The constructor

36
docs/Python-API.md


`EngineConfigurationChannel` has two methods :
* `set_configuration_parameters` which takes the following arguments:
* `width`: Defines the width of the display. Default 80.
* `height`: Defines the height of the display. Default 80.
* `quality_level`: Defines the quality level of the simulation. Default 1.
* `time_scale`: Defines the multiplier for the deltatime in the simulation. If set to a higher value, time will pass faster in the simulation but the physics may perform unpredictably. Default 20.
* `target_frame_rate`: Instructs simulation to try to render at a specified frame rate. Default -1.
* `width`: Defines the width of the display. (Must be set alongside height)
* `height`: Defines the height of the display. (Must be set alongside width)
* `quality_level`: Defines the quality level of the simulation.
* `time_scale`: Defines the multiplier for the deltatime in the simulation. If set to a higher value, time will pass faster in the simulation but the physics may perform unpredictably.
* `target_frame_rate`: Instructs simulation to try to render at a specified frame rate.
* `capture_frame_rate` Instructs the simulation to consider time between updates to always be constant, regardless of the actual frame rate.
* `set_configuration` with argument config which is an `EngineConfig`
NamedTuple object.

...
```
#### FloatPropertiesChannel
The `FloatPropertiesChannel` will allow you to get and set pre-defined numerical values in the environment. This can be useful for adjusting environment-specific settings, or for reading non-agent related information from the environment. You can call `get_property` and `set_property` on the side channel to read and write properties.
#### EnvironmentParameters
The `EnvironmentParameters` will allow you to get and set pre-defined numerical values in the environment. This can be useful for adjusting environment-specific settings, or for reading non-agent related information from the environment. You can call `get_property` and `set_property` on the side channel to read and write properties.
`FloatPropertiesChannel` has three methods:
`EnvironmentParametersChannel` has one methods:
* `set_property` Sets a property in the Unity Environment.
* `set_float_parameter` Sets a float parameter in the Unity Environment.
* `get_property` Gets a property in the Unity Environment. If the property was not found, will return None.
* key: The string identifier of the property.
* `list_properties` Returns a list of all the string identifiers of the properties
from mlagents_envs.side_channel.float_properties_channel import FloatPropertiesChannel
from mlagents_envs.side_channel.environment_parameters_channel import EnvironmentParametersChannel
channel = FloatPropertiesChannel()
channel = EnvironmentParametersChannel()
channel.set_property("parameter_1", 2.0)
channel.set_float_parameter("parameter_1", 2.0)
readout_value = channel.get_property("parameter_2")
...
```

var sharedProperties = SideChannelUtils.GetSideChannel<FloatPropertiesChannel>();
float property1 = sharedProperties.GetPropertyWithDefault("parameter_1", 0.0f);
var envParameters = Academy.Instance.EnvironmentParameters;
float property1 = envParameters.GetWithDefault("parameter_1", 0.0f);
```
#### Custom side channels

4
docs/Training-Curriculum-Learning.md


In order to define the curricula, the first step is to decide which parameters of
the environment will vary. In the case of the Wall Jump environment,
the height of the wall is what varies. We define this as a `Shared Float Property`
that can be accessed in `SideChannelUtils.GetSideChannel<FloatPropertiesChannel>()`, and by doing
the height of the wall is what varies. We define this as a `Environment Parameters`
that can be accessed in `Academy.Instance.EnvironmentParameters`, and by doing
so it becomes adjustable via the Python API.
Rather than adjusting it by hand, we will create a YAML file which
describes the structure of the curricula. Within it, we can specify which

4
docs/Using-Tensorboard.md


StatsSideChannel:
```csharp
var statsSideChannel = SideChannelUtils.GetSideChannel<StatsSideChannel>();
statsSideChannel.AddStat("MyMetric", 1.0);
var statsRecorder = Academy.Instance.StatsRecorder;
statsSideChannel.Add("MyMetric", 1.0);
```

2
ml-agents-envs/mlagents_envs/environment.py


# Currently we require strict equality between the communication protocol
# on each side, although we may allow some flexibility in the future.
# This should be incremented whenever a change is made to the communication protocol.
API_VERSION = "0.16.0"
API_VERSION = "0.17.0"
# Default port that the editor listens on. If an environment executable
# isn't specified, this port will be used.

88
ml-agents-envs/mlagents_envs/side_channel/engine_configuration_channel.py


from mlagents_envs.side_channel import SideChannel, OutgoingMessage, IncomingMessage
from mlagents_envs.exception import UnityCommunicationException
from mlagents_envs.exception import (
UnityCommunicationException,
UnitySideChannelException,
)
from typing import NamedTuple
from typing import NamedTuple, Optional
from enum import IntEnum
class EngineConfig(NamedTuple):

time_scale: float
target_frame_rate: int
capture_frame_rate: int
return EngineConfig(80, 80, 1, 20.0, -1)
return EngineConfig(80, 80, 1, 20.0, -1, 60)
class EngineConfigurationChannel(SideChannel):

- int qualityLevel;
- float timeScale;
- int targetFrameRate;
- int captureFrameRate;
class ConfigurationType(IntEnum):
SCREEN_RESOLUTION = 0
QUALITY_LEVEL = 1
TIME_SCALE = 2
TARGET_FRAME_RATE = 3
CAPTURE_FRAME_RATE = 4
def __init__(self) -> None:
super().__init__(uuid.UUID("e951342c-4f7e-11ea-b238-784f4387d1f7"))

def set_configuration_parameters(
self,
width: int = 80,
height: int = 80,
quality_level: int = 1,
time_scale: float = 20.0,
target_frame_rate: int = -1,
width: Optional[int] = None,
height: Optional[int] = None,
quality_level: Optional[int] = None,
time_scale: Optional[float] = None,
target_frame_rate: Optional[int] = None,
capture_frame_rate: Optional[int] = None,
:param width: Defines the width of the display. Default 80.
:param height: Defines the height of the display. Default 80.
:param width: Defines the width of the display. (Must be set alongside height)
:param height: Defines the height of the display. (Must be set alongside width)
Default 1.
simulation but the physics might break. Default 20.
simulation but the physics might break.
specified frame rate. Default -1.
specified frame rate.
:param capture_frame_rate: Instructs the simulation to consider time between
updates to always be constant, regardless of the actual frame rate.
msg = OutgoingMessage()
msg.write_int32(width)
msg.write_int32(height)
msg.write_int32(quality_level)
msg.write_float32(time_scale)
msg.write_int32(target_frame_rate)
super().queue_message_to_send(msg)
if (width is None and height is not None) or (
width is not None and height is None
):
raise UnitySideChannelException(
"You cannot set the width/height of the screen resolution without also setting the height/width"
)
if width is not None and height is not None:
screen_msg = OutgoingMessage()
screen_msg.write_int32(self.ConfigurationType.SCREEN_RESOLUTION)
screen_msg.write_int32(width)
screen_msg.write_int32(height)
super().queue_message_to_send(screen_msg)
if quality_level is not None:
quality_level_msg = OutgoingMessage()
quality_level_msg.write_int32(self.ConfigurationType.QUALITY_LEVEL)
quality_level_msg.write_int32(quality_level)
super().queue_message_to_send(quality_level_msg)
if time_scale is not None:
time_scale_msg = OutgoingMessage()
time_scale_msg.write_int32(self.ConfigurationType.TIME_SCALE)
time_scale_msg.write_float32(time_scale)
super().queue_message_to_send(time_scale_msg)
if target_frame_rate is not None:
target_frame_rate_msg = OutgoingMessage()
target_frame_rate_msg.write_int32(self.ConfigurationType.TARGET_FRAME_RATE)
target_frame_rate_msg.write_int32(target_frame_rate)
super().queue_message_to_send(target_frame_rate_msg)
if capture_frame_rate is not None:
capture_frame_rate_msg = OutgoingMessage()
capture_frame_rate_msg.write_int32(
self.ConfigurationType.CAPTURE_FRAME_RATE
)
capture_frame_rate_msg.write_int32(capture_frame_rate)
super().queue_message_to_send(capture_frame_rate_msg)
def set_configuration(self, config: EngineConfig) -> None:
"""

5
ml-agents/mlagents/trainers/env_manager.py


def external_brains(self) -> Dict[BehaviorName, BrainParameters]:
pass
@property
@abstractmethod
def get_properties(self) -> Dict[BehaviorName, float]:
pass
@abstractmethod
def close(self):
pass

19
ml-agents/mlagents/trainers/learn.py


help="The target frame rate of the Unity environment(s). Equivalent to setting "
"Application.targetFrameRate in Unity.",
)
eng_conf.add_argument(
"--capture-frame-rate",
default=60,
type=int,
help="The capture frame rate of the Unity environment(s). Equivalent to setting "
"Time.captureFramerate in Unity.",
)
return argparser

quality_level: int = parser.get_default("quality_level")
time_scale: float = parser.get_default("time_scale")
target_frame_rate: int = parser.get_default("target_frame_rate")
capture_frame_rate: int = parser.get_default("capture_frame_rate")
@staticmethod
def from_argparse(args: argparse.Namespace) -> "RunOptions":

options.env_path, options.no_graphics, run_seed, port, options.env_args
)
engine_config = EngineConfig(
options.width,
options.height