-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Refactor DemonstrationStore/Recorder #3354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
ffc6df1
b22e734
2a41bf4
9890f16
df37277
cf11310
b0b2d9c
670ed47
1e13dcd
21a7631
e66a3b8
2d2c995
c59636f
0f40840
e275121
29bd4be
cfd1d3f
9bd123d
a450de3
dcac289
6da9433
c883ab0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ namespace MLAgents | |
| /// Struct that contains all the information for an Agent, including its | ||
| /// observations, actions and current status, that is sent to the Brain. | ||
| /// </summary> | ||
| public struct AgentInfo | ||
| internal struct AgentInfo | ||
| { | ||
| /// <summary> | ||
| /// Keeps track of the last vector action taken by the Brain. | ||
|
|
@@ -145,7 +145,6 @@ public abstract class Agent : MonoBehaviour | |
| /// Whether or not the agent requests a decision. | ||
| bool m_RequestDecision; | ||
|
|
||
|
|
||
| /// Keeps track of the number of steps taken by the agent in this episode. | ||
| /// Note that this value is different for each agent, and may not overlap | ||
| /// with the step counter in the Academy, since agents reset based on | ||
|
|
@@ -168,6 +167,14 @@ public abstract class Agent : MonoBehaviour | |
| /// </summary> | ||
| DemonstrationRecorder m_Recorder; | ||
|
|
||
| /// <summary> | ||
| /// Set of DemonstrationStores that the Agent will write its step information to. | ||
| /// If you use a DemonstrationRecorder component, this will automatically register its DemonstrationStore. | ||
| /// You can also add your own DemonstrationStores; the Agent is not responsible for creating or closing the | ||
| /// stores, only opening them. | ||
| /// </summary> | ||
| public ISet<DemonstrationStore> DemonstrationStores = new HashSet<DemonstrationStore>(); | ||
|
||
|
|
||
| /// <summary> | ||
| /// List of sensors used to generate observations. | ||
| /// Currently generated from attached SensorComponents, and a legacy VectorSensor | ||
|
|
@@ -199,8 +206,6 @@ public void LazyInitialize() | |
| // Grab the "static" properties for the Agent. | ||
| m_EpisodeId = EpisodeIdCounter.GetEpisodeId(); | ||
| m_PolicyFactory = GetComponent<BehaviorParameters>(); | ||
| m_Recorder = GetComponent<DemonstrationRecorder>(); | ||
|
|
||
|
|
||
| m_Info = new AgentInfo(); | ||
| m_Action = new AgentAction(); | ||
|
|
@@ -221,6 +226,8 @@ public void LazyInitialize() | |
| /// becomes disabled or inactive. | ||
| void OnDisable() | ||
| { | ||
| DemonstrationStores.Clear(); | ||
|
|
||
| // If Academy.Dispose has already been called, we don't need to unregister with it. | ||
| // We don't want to even try, because this will lazily create a new Academy! | ||
| if (Academy.IsInitialized) | ||
|
|
@@ -491,9 +498,10 @@ void SendInfoToBrain() | |
|
|
||
| m_Brain.RequestDecision(m_Info, sensors); | ||
|
|
||
| if (m_Recorder != null && m_Recorder.record && Application.isEditor) | ||
| // If we have any DemonstrationStores, write the AgentInfo and sensors to them. | ||
| foreach(var demoWriter in DemonstrationStores) | ||
| { | ||
| m_Recorder.WriteExperience(m_Info, sensors); | ||
| demoWriter.Record(m_Info, sensors); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -505,6 +513,7 @@ void UpdateSensors() | |
| } | ||
| } | ||
|
|
||
|
|
||
| /// <summary> | ||
| /// Collects the vector observations of the agent. | ||
| /// The agent observation describes the current environment from the | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| using System.IO.Abstractions; | ||
| using System.Text.RegularExpressions; | ||
| using UnityEngine; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
|
|
||
| namespace MLAgents | ||
| { | ||
|
|
@@ -12,23 +12,24 @@ namespace MLAgents | |
| [AddComponentMenu("ML Agents/Demonstration Recorder", (int)MenuGroup.Default)] | ||
| public class DemonstrationRecorder : MonoBehaviour | ||
| { | ||
| [Tooltip("Whether or not to record demonstrations.")] | ||
| public bool record; | ||
|
|
||
| [Tooltip("Base demonstration file name. Will have numbers appended to make unique.")] | ||
| public string demonstrationName; | ||
| string m_FilePath; | ||
|
|
||
| [Tooltip("Base directory to write the demo files. If null, will use {Application.dataPath}/Demonstrations.")] | ||
| public string demonstrationDirectory; | ||
|
|
||
| DemonstrationStore m_DemoStore; | ||
| public const int MaxNameLength = 16; | ||
| internal const int MaxNameLength = 16; | ||
|
|
||
| void Start() | ||
| { | ||
| if (Application.isEditor && record) | ||
| { | ||
| InitializeDemoStore(); | ||
| } | ||
| } | ||
| const string k_ExtensionType = ".demo"; | ||
| IFileSystem m_FileSystem; | ||
|
|
||
| void Update() | ||
| { | ||
| if (Application.isEditor && record && m_DemoStore == null) | ||
| if (record) | ||
| { | ||
| InitializeDemoStore(); | ||
chriselion marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
@@ -37,22 +38,49 @@ void Update() | |
| /// <summary> | ||
| /// Creates demonstration store for use in recording. | ||
| /// </summary> | ||
| public void InitializeDemoStore(IFileSystem fileSystem = null) | ||
| internal DemonstrationStore InitializeDemoStore(IFileSystem fileSystem = null) | ||
| { | ||
| m_DemoStore = new DemonstrationStore(fileSystem); | ||
| if (m_DemoStore != null) | ||
| { | ||
| return m_DemoStore; | ||
| } | ||
|
|
||
| m_FileSystem = fileSystem ?? new FileSystem(); | ||
| var behaviorParams = GetComponent<BehaviorParameters>(); | ||
| if (string.IsNullOrEmpty(demonstrationName)) | ||
| { | ||
| demonstrationName = behaviorParams.behaviorName; | ||
| } | ||
| if (string.IsNullOrEmpty(demonstrationDirectory)) | ||
| { | ||
| demonstrationDirectory = Path.Combine(Application.dataPath, "Demonstrations"); | ||
| } | ||
|
|
||
| demonstrationName = SanitizeName(demonstrationName, MaxNameLength); | ||
| var filePath = MakeDemonstrationFilePath(m_FileSystem, demonstrationDirectory, demonstrationName); | ||
| var stream = m_FileSystem.File.Create(filePath); | ||
| m_DemoStore = new DemonstrationStore(stream); | ||
|
|
||
| m_DemoStore.Initialize( | ||
| demonstrationName, | ||
| behaviorParams.brainParameters, | ||
| behaviorParams.fullyQualifiedBehaviorName); | ||
| behaviorParams.fullyQualifiedBehaviorName | ||
| ); | ||
|
|
||
| var agent = GetComponent<Agent>(); | ||
| if (agent != null) | ||
| { | ||
| agent.DemonstrationStores.Add(m_DemoStore); | ||
| } | ||
|
|
||
| return m_DemoStore; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Removes all characters except alphanumerics from demonstration name. | ||
| /// Shorten name if it is longer than the maxNameLength. | ||
| /// </summary> | ||
| public static string SanitizeName(string demoName, int maxNameLength) | ||
| internal static string SanitizeName(string demoName, int maxNameLength) | ||
| { | ||
| var rgx = new Regex("[^a-zA-Z0-9 -]"); | ||
| demoName = rgx.Replace(demoName, ""); | ||
|
|
@@ -65,31 +93,63 @@ public static string SanitizeName(string demoName, int maxNameLength) | |
| } | ||
|
|
||
| /// <summary> | ||
| /// Forwards AgentInfo to Demonstration Store. | ||
| /// Gets a unique path for the demonstrationName in the demonstrationDirectory. | ||
| /// </summary> | ||
| public void WriteExperience(AgentInfo info, List<ISensor> sensors) | ||
| /// <param name="fileSystem"></param> | ||
| /// <param name="demonstrationDirectory"></param> | ||
| /// <param name="demonstrationName"></param> | ||
| /// <returns></returns> | ||
| internal static string MakeDemonstrationFilePath( | ||
| IFileSystem fileSystem, string demonstrationDirectory, string demonstrationName | ||
| ) | ||
| { | ||
| m_DemoStore.Record(info, sensors); | ||
| // Create the directory if it doesn't already exist | ||
| if (!fileSystem.Directory.Exists(demonstrationDirectory)) | ||
| { | ||
| fileSystem.Directory.CreateDirectory(demonstrationDirectory); | ||
| } | ||
|
|
||
| var literalName = demonstrationName; | ||
| var filePath = Path.Combine(demonstrationDirectory, literalName + k_ExtensionType); | ||
| var uniqueNameCounter = 0; | ||
| while (fileSystem.File.Exists(filePath)) | ||
| { | ||
| // TODO should we use a timestamp instead of a counter here? This loops an increasing number of times | ||
| // as the number of demos increases. | ||
| literalName = demonstrationName + "_" + uniqueNameCounter; | ||
| filePath = Path.Combine(demonstrationDirectory, literalName + k_ExtensionType); | ||
| uniqueNameCounter++; | ||
| } | ||
|
|
||
| return filePath; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Close the DemonstrationStore and remove it from the Agent. | ||
| /// Has no effect if the DemonstrationStore is already closed (or wasn't opened) | ||
| /// </summary> | ||
| public void Close() | ||
| { | ||
| if (m_DemoStore != null) | ||
| { | ||
| var agent = GetComponent<Agent>(); | ||
| if (agent != null) | ||
| { | ||
| agent.DemonstrationStores.Remove(m_DemoStore); | ||
| } | ||
|
|
||
| m_DemoStore.Close(); | ||
| m_DemoStore = null; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Closes Demonstration store. | ||
| /// Clean up the DemonstrationStore when shutting down. | ||
| /// </summary> | ||
| void OnApplicationQuit() | ||
|
||
| { | ||
| if (Application.isEditor && record) | ||
| { | ||
| Close(); | ||
| } | ||
| Close(); | ||
| } | ||
|
|
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍