| 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_CellObservationSize, m_GridSize.x, m_GridSize.z); |
| 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[d, h, w] = m_PerceptionBuffer[index]; |
| index++; |
| } |
| } |
| } |
| return index; |
| } |
| } |
|
|
| |
| |
| |
| public void Dispose() |
| { |
| if (!ReferenceEquals(null, m_PerceptionTexture)) |
| { |
| Utilities.DestroyTexture(m_PerceptionTexture); |
| m_PerceptionTexture = null; |
| } |
| } |
| } |
| } |
|
|