| | using System; |
| | using System.Collections.Generic; |
| | using UnityEngine; |
| | using UnityEngine.Profiling; |
| |
|
| | namespace Unity.MLAgents.Sensors |
| | { |
| | |
| | |
| | |
| | public enum ProcessCollidersMethod |
| | { |
| | |
| | |
| | |
| | ProcessAllColliders, |
| |
|
| | |
| | |
| | |
| | ProcessClosestColliders |
| | } |
| |
|
| | |
| | |
| | |
| | public class GridSensorBase : ISensor, IBuiltInSensor, IDisposable |
| | { |
| | string m_Name; |
| | Vector3 m_CellScale; |
| | Vector3Int m_GridSize; |
| | string[] m_DetectableTags; |
| | SensorCompressionType m_CompressionType; |
| | ObservationSpec m_ObservationSpec; |
| | internal IGridPerception m_GridPerception; |
| |
|
| | |
| | float[] m_PerceptionBuffer; |
| | Color[] m_PerceptionColors; |
| | Texture2D m_PerceptionTexture; |
| | float[] m_CellDataBuffer; |
| |
|
| | |
| | int m_NumCells; |
| | int m_CellObservationSize; |
| | Vector3 m_CellCenterOffset; |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public GridSensorBase( |
| | string name, |
| | Vector3 cellScale, |
| | Vector3Int gridSize, |
| | string[] detectableTags, |
| | SensorCompressionType compression |
| | ) |
| | { |
| | m_Name = name; |
| | m_CellScale = cellScale; |
| | m_GridSize = gridSize; |
| | m_DetectableTags = detectableTags; |
| | CompressionType = compression; |
| |
|
| | if (m_GridSize.y != 1) |
| | { |
| | throw new UnityAgentsException("GridSensor only supports 2D grids."); |
| | } |
| |
|
| | m_NumCells = m_GridSize.x * m_GridSize.z; |
| | m_CellObservationSize = GetCellObservationSize(); |
| | m_ObservationSpec = ObservationSpec.Visual(m_GridSize.x, m_GridSize.z, m_CellObservationSize); |
| | m_PerceptionTexture = new Texture2D(m_GridSize.x, m_GridSize.z, TextureFormat.RGB24, false); |
| |
|
| | ResetPerceptionBuffer(); |
| | } |
| |
|
| | |
| | |
| | |
| | public SensorCompressionType CompressionType |
| | { |
| | get { return m_CompressionType; } |
| | set |
| | { |
| | if (!IsDataNormalized() && value == SensorCompressionType.PNG) |
| | { |
| | Debug.LogWarning($"Compression type {value} is only supported with normalized data. " + |
| | "The sensor will not compress the data."); |
| | return; |
| | } |
| | m_CompressionType = value; |
| | } |
| | } |
| |
|
| | internal float[] PerceptionBuffer |
| | { |
| | get { return m_PerceptionBuffer; } |
| | } |
| |
|
| | |
| | |
| | |
| | protected string[] DetectableTags |
| | { |
| | get { return m_DetectableTags; } |
| | } |
| |
|
| | |
| | public void Reset() { } |
| |
|
| | |
| | |
| | |
| | public void ResetPerceptionBuffer() |
| | { |
| | if (m_PerceptionBuffer != null) |
| | { |
| | Array.Clear(m_PerceptionBuffer, 0, m_PerceptionBuffer.Length); |
| | Array.Clear(m_CellDataBuffer, 0, m_CellDataBuffer.Length); |
| | } |
| | else |
| | { |
| | m_PerceptionBuffer = new float[m_CellObservationSize * m_NumCells]; |
| | m_CellDataBuffer = new float[m_CellObservationSize]; |
| | m_PerceptionColors = new Color[m_NumCells]; |
| | } |
| | } |
| |
|
| | |
| | public string GetName() |
| | { |
| | return m_Name; |
| | } |
| |
|
| | |
| | public CompressionSpec GetCompressionSpec() |
| | { |
| | return new CompressionSpec(CompressionType); |
| | } |
| |
|
| | |
| | public BuiltInSensorType GetBuiltInSensorType() |
| | { |
| | return BuiltInSensorType.GridSensor; |
| | } |
| |
|
| | |
| | public byte[] GetCompressedObservation() |
| | { |
| | using (TimerStack.Instance.Scoped("GridSensor.GetCompressedObservation")) |
| | { |
| | var allBytes = new List<byte>(); |
| | var numImages = (m_CellObservationSize + 2) / 3; |
| | for (int i = 0; i < numImages; i++) |
| | { |
| | var channelIndex = 3 * i; |
| | GridValuesToTexture(channelIndex, Math.Min(3, m_CellObservationSize - channelIndex)); |
| | allBytes.AddRange(m_PerceptionTexture.EncodeToPNG()); |
| | } |
| |
|
| | return allBytes.ToArray(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | void GridValuesToTexture(int channelIndex, int numChannelsToAdd) |
| | { |
| | for (int i = 0; i < m_NumCells; i++) |
| | { |
| | for (int j = 0; j < numChannelsToAdd; j++) |
| | { |
| | m_PerceptionColors[i][j] = m_PerceptionBuffer[i * m_CellObservationSize + channelIndex + j]; |
| | } |
| | } |
| | m_PerceptionTexture.SetPixels(m_PerceptionColors); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | protected virtual void GetObjectData(GameObject detectedObject, int tagIndex, float[] dataBuffer) |
| | { |
| | dataBuffer[0] = tagIndex + 1; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | protected virtual int GetCellObservationSize() |
| | { |
| | return 1; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | protected virtual bool IsDataNormalized() |
| | { |
| | return false; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | protected internal virtual ProcessCollidersMethod GetProcessCollidersMethod() |
| | { |
| | return ProcessCollidersMethod.ProcessClosestColliders; |
| | } |
| |
|
| | |
| | |
| | |
| | void ValidateValues(float[] dataValues, GameObject detectedObject) |
| | { |
| | if (m_CompressionType != SensorCompressionType.PNG) |
| | { |
| | return; |
| | } |
| |
|
| | for (int j = 0; j < dataValues.Length; j++) |
| | { |
| | if (dataValues[j] < 0 || dataValues[j] > 1) |
| | throw new UnityAgentsException($"When using compression type {m_CompressionType} the data value has to be normalized between 0-1. " + |
| | $"Received value[{dataValues[j]}] for {detectedObject.name}"); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | internal void ProcessDetectedObject(GameObject detectedObject, int cellIndex) |
| | { |
| | Profiler.BeginSample("GridSensor.ProcessDetectedObject"); |
| | for (var i = 0; i < m_DetectableTags.Length; i++) |
| | { |
| | if (!ReferenceEquals(detectedObject, null) && detectedObject.CompareTag(m_DetectableTags[i])) |
| | { |
| | if (GetProcessCollidersMethod() == ProcessCollidersMethod.ProcessAllColliders) |
| | { |
| | Array.Copy(m_PerceptionBuffer, cellIndex * m_CellObservationSize, m_CellDataBuffer, 0, m_CellObservationSize); |
| | } |
| | else |
| | { |
| | Array.Clear(m_CellDataBuffer, 0, m_CellDataBuffer.Length); |
| | } |
| |
|
| | GetObjectData(detectedObject, i, m_CellDataBuffer); |
| | ValidateValues(m_CellDataBuffer, detectedObject); |
| | Array.Copy(m_CellDataBuffer, 0, m_PerceptionBuffer, cellIndex * m_CellObservationSize, m_CellObservationSize); |
| | break; |
| | } |
| | } |
| | Profiler.EndSample(); |
| | } |
| |
|
| | |
| | public void Update() |
| | { |
| | ResetPerceptionBuffer(); |
| | using (TimerStack.Instance.Scoped("GridSensor.Update")) |
| | { |
| | if (m_GridPerception != null) |
| | { |
| | m_GridPerception.Perceive(); |
| | } |
| | } |
| | } |
| |
|
| | |
| | public ObservationSpec GetObservationSpec() |
| | { |
| | return m_ObservationSpec; |
| | } |
| |
|
| | |
| | public int Write(ObservationWriter writer) |
| | { |
| | using (TimerStack.Instance.Scoped("GridSensor.Write")) |
| | { |
| | int index = 0; |
| | for (var h = m_GridSize.z - 1; h >= 0; h--) |
| | { |
| | for (var w = 0; w < m_GridSize.x; w++) |
| | { |
| | for (var d = 0; d < m_CellObservationSize; d++) |
| | { |
| | writer[h, w, d] = m_PerceptionBuffer[index]; |
| | index++; |
| | } |
| | } |
| | } |
| | return index; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | public void Dispose() |
| | { |
| | if (!ReferenceEquals(null, m_PerceptionTexture)) |
| | { |
| | Utilities.DestroyTexture(m_PerceptionTexture); |
| | m_PerceptionTexture = null; |
| | } |
| | } |
| | } |
| | } |
| |
|