Skip to content
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

Radar does not detect NPCs with waypoints #1899

Open
pantonante opened this issue Dec 1, 2021 · 13 comments
Open

Radar does not detect NPCs with waypoints #1899

pantonante opened this issue Dec 1, 2021 · 13 comments
Labels
answered Waiting for response

Comments

@pantonante
Copy link

Hi, when running a scenario created with the Visual Scenario Editor containing NPCs following waypoints, the radar does not detect the vehicles.
If I create a scenario with a static vehicle, the radar works fine. The radar works fine also with the Random Traffic template.

I get the same behavior with SVL version 2021.1/2.2/3.

@EricBoiseLGSVL
Copy link
Contributor

We have fixes that we are pushing soon to master to address this

@EricBoiseLGSVL EricBoiseLGSVL added answered Waiting for response next release Feature/bug fix will be included in next release labels Dec 2, 2021
@pantonante
Copy link
Author

That's great! Thank you!

@pantonante
Copy link
Author

@EricBoiseLGSVL do you have an estimate on when this will happen? I'm working on a project where this feature is very important and I would like to plan around it.

@EricBoiseLGSVL
Copy link
Contributor

We are hoping to push changes to master within the next few weeks.

@pantonante
Copy link
Author

In light of LG sunsetting SVL, do you still plan to push the fix? If not, can you suggest what is likely causing the problem so I can try to contribute? (I'm not an experienced Unity developer)

@pantonante pantonante reopened this Jan 24, 2022
@EricBoiseLGSVL
Copy link
Contributor

Latest radar sensor.cs

/**
 * Copyright (c) 2019-2021 LG Electronics, Inc.
 *
 * This software contains code licensed as described in LICENSE.
 *
 */

using System.Linq;
using System.Collections.Generic;
using Simulator.Bridge;
using Simulator.Bridge.Data;
using Simulator.Utilities;
using UnityEngine;
using UnityEngine.AI;
using Simulator.Sensors.UI;

namespace Simulator.Sensors
{
    using System;

    [SensorType("Radar", new[] { typeof(DetectedRadarObjectData) })]
    public class RadarSensor : SensorBase
    {
        [SensorParameter]
        [Range(1.0f, 100f)]
        public float Frequency = 13.4f;
        public LayerMask RadarBlockers;

        private List<RadarMesh> radars = new List<RadarMesh>();
        private WireframeBoxes WireframeBoxes;

        private uint seqId;
        private float nextPublish;

        private BridgeInstance Bridge;
        private Publisher<DetectedRadarObjectData> Publish;
        private Rigidbody SelfRigidBody;

        /// <summary>
        /// Dictionary of all agents in the range and their semaphore locks
        /// Semaphore is required because radar can use multiple trigger colliders
        /// Agent has to be cached until any trigger containing it is in the range
        /// </summary>
        private Dictionary<ITriggerAgent, int> AgentsInRange = new Dictionary<ITriggerAgent, int>();
        private Dictionary<ITriggerAgent, DetectedRadarObject> Detected = new Dictionary<ITriggerAgent, DetectedRadarObject>();
        private Dictionary<ITriggerAgent, Box> Visualized = new Dictionary<ITriggerAgent, Box>();

        [AnalysisMeasurement(MeasurementType.Count)]
        public int MaxTracked = -1;
        
        public override SensorDistributionType DistributionType => SensorDistributionType.MainOrClient;
        public override float PerformanceLoad { get; } = 0.2f;
        
        struct Box
        {
            public Vector3 Size;
            public Color Color;
        }

        private void Awake()
        {
            radars.AddRange(GetComponentsInChildren<RadarMesh>());
            foreach (var radar in radars)
            {
                radar.Init();
            }
            SimulatorManager.Instance.NPCManager.RegisterDespawnCallback(OnExitRange);
        }

        protected override void Initialize()
        {
            Debug.Assert(SimulatorManager.Instance != null);
            WireframeBoxes = SimulatorManager.Instance.WireframeBoxes;
            foreach (var radar in radars)
            {
                radar.SetCallbacks(OnEnterRange, OnExitRange);
            }
            nextPublish = Time.time + 1.0f / Frequency;

            SelfRigidBody = gameObject.GetComponentInParent<Rigidbody>();    
            SimulatorManager.Instance.TriggersManager.AgentUnregistered += TriggersManagerOnAgentUnregistered;
        }

        protected override void Deinitialize()
        {
            var agents = AgentsInRange.Keys.ToList();
            for (var i = agents.Count - 1; i >= 0; i--)
            {
                var triggerAgent = agents[i];
                OnExitRange(triggerAgent, true);
            }
            SimulatorManager.Instance.TriggersManager.AgentUnregistered -= TriggersManagerOnAgentUnregistered;
        }

        private void Update()
        {
            if (Bridge == null || Bridge.Status != Status.Connected)
            {
                return;
            }

            if (Time.time < nextPublish)
            {
                return;
            }
                        
            nextPublish = Time.time + 1.0f / Frequency;
            MaxTracked = Mathf.Max(MaxTracked, Detected.Count);
            Publish(new DetectedRadarObjectData()
            {
                Name = Name,
                Frame = Frame,
                Time = SimulatorManager.Instance.CurrentTime,
                Sequence = seqId++,
                Data = Detected.Values.ToArray(),
            });
        }

        private void FixedUpdate()
        {
            foreach (var detectedAgent in AgentsInRange)
            {
                WhileInRange(detectedAgent.Key);
            }
        }

        private void TriggersManagerOnAgentUnregistered(ITriggerAgent iTriggerAgent)
        {
            OnExitRange(iTriggerAgent, true);
        }

        public override void OnBridgeSetup(BridgeInstance bridge)
        {
            Bridge = bridge;
            Publish = Bridge.AddPublisher<DetectedRadarObjectData>(Topic);
        }
        
        void WhileInRange(ITriggerAgent triggerAgent)
        {
            if (CheckBlocked(triggerAgent))
            {
                if (Detected.ContainsKey(triggerAgent))
                {
                    Detected.Remove(triggerAgent);
                }
                if (Visualized.ContainsKey(triggerAgent))
                {
                    Visualized.Remove(triggerAgent);
                }
                return;
            }

            if (Detected.TryGetValue(triggerAgent, out var detected)) // update existing data
            {
                Vector3 objectVelocity = GetObjectVelocity(triggerAgent);

                var otherPosition = triggerAgent.AgentTransform.position;
                var thisTransform = transform;
                var thisPosition = thisTransform.position;
                detected.SensorPosition = thisPosition;
                detected.SensorAim = thisTransform.forward;
                detected.SensorRight = thisTransform.right;
                detected.SensorVelocity = GetSensorVelocity();
                detected.SensorAngle = GetSensorAngle(triggerAgent);
                detected.Position = otherPosition;
                detected.Velocity = objectVelocity;
                detected.RelativePosition = otherPosition - thisPosition;
                detected.RelativeVelocity = GetSensorVelocity() - objectVelocity;
                detected.ColliderSize = triggerAgent.AgentBounds.size;
                detected.State = GetAgentState(triggerAgent);
                detected.NewDetection = false;
            }
            else
            {
                
                Box box = GetVisualizationBox(triggerAgent);
                if (box.Size == Vector3.zero) // Empty box returned if tag is not right
                {
                    return;
                }

                Vector3 objectVelocity = GetObjectVelocity(triggerAgent);

                Transform thisTransform;
                Bounds otherBounds;
                Visualized.Add(triggerAgent, box);
                Detected.Add(triggerAgent, new DetectedRadarObject()
                {
                    Id = triggerAgent.AgentGameObject.GetInstanceID(),
                    SensorPosition = (thisTransform = transform).position,
                    SensorAim = thisTransform.forward,
                    SensorRight = thisTransform.right,
                    SensorVelocity = GetSensorVelocity(),
                    SensorAngle = GetSensorAngle(triggerAgent),
                    Position = (otherBounds = triggerAgent.AgentBounds).center,
                    Velocity = objectVelocity,
                    RelativePosition = otherBounds.center - transform.position,
                    RelativeVelocity = GetSensorVelocity() - objectVelocity,
                    ColliderSize = triggerAgent.AgentBounds.size,
                    State = GetAgentState(triggerAgent),
                    NewDetection = true,
                });
            }
        }

        void OnEnterRange(Collider other, RadarMesh radar)
        {
            if (other.isTrigger)
                return;

            if (!other.enabled)
                return;
            
            var triggerAgent = SimulatorManager.Instance.TriggersManager.TryGetTriggerAgent(other);
            if (triggerAgent == null)
                return;

            if (AgentsInRange.TryGetValue(triggerAgent, out var locks))
            {
                AgentsInRange[triggerAgent] = locks + 1;
            }
            else
            {
                AgentsInRange.Add(triggerAgent, 1);
            }
        }

        void OnExitRange(NPCController controller)
        {
            OnExitRange(controller.MainCollider);
        }

        void OnExitRange(Collider other, RadarMesh radar)
        {
            OnExitRange(other);
        }

        void OnExitRange(Collider other)
        {
            if (other.isTrigger)
                return;

            if (!other.enabled)
                return;
            
            OnExitRange(SimulatorManager.Instance.TriggersManager.TryGetTriggerAgent(other));
        }
        
        void OnExitRange(ITriggerAgent triggerAgent, bool forceRemove = false)
        {
            if (triggerAgent == null)
                return;
            
            if (!AgentsInRange.TryGetValue(triggerAgent, out var locks))
            {
                return;
            }
                
            AgentsInRange[triggerAgent] = --locks;
            if (!forceRemove && locks > 0)
            {
                return;
            }

            // Remove agent from all cache collections if no radar collider overlaps them
            AgentsInRange.Remove(triggerAgent);
            if (Detected.ContainsKey(triggerAgent))
            {
                Detected.Remove(triggerAgent);
            }
            if (Visualized.ContainsKey(triggerAgent))
            {
                Visualized.Remove(triggerAgent);
            }

        }

        private bool CheckBlocked(ITriggerAgent triggerAgent)
        {
            bool isBlocked = false;
            var orig = transform.position;
            var end = triggerAgent.AgentBounds.center;
            var dir = end - orig;
            var dist = (end - orig).magnitude;
            if (Physics.Raycast(orig, dir, out RaycastHit hit, dist, RadarBlockers)) // ignore if blocked
            {
                var hitAgent = SimulatorManager.Instance.TriggersManager.TryGetTriggerAgent(hit.collider);
                if (hitAgent != triggerAgent)
                {
                    isBlocked = true;
                }
            }
            return isBlocked;
        }

        private Box GetVisualizationBox(ITriggerAgent triggerAgent)
        {
            var bbox = new Box();

            if (triggerAgent.AgentGameObject.layer == LayerMask.NameToLayer("NPC"))
            {
                bbox.Color = Color.green;
            }
            else if (triggerAgent.AgentGameObject.layer == LayerMask.NameToLayer("Pedestrian"))
            {
                bbox.Color = Color.yellow;
            }
            else if (triggerAgent.AgentGameObject.layer == LayerMask.NameToLayer("Bicycle"))
            {
                bbox.Color = Color.cyan;
            }
            else if (triggerAgent.AgentGameObject.layer == LayerMask.NameToLayer("Agent"))
            {
                bbox.Color = Color.magenta;
            }
            else
            {
                return bbox;
            }

            bbox.Size = triggerAgent.AgentBounds.size;

            return bbox;
        }

        private Vector3 GetSensorVelocity()
        {
            return SelfRigidBody == null ? Vector3.zero : SelfRigidBody.velocity;
        }

        private double GetSensorAngle(ITriggerAgent triggerAgent)
        {
            // angle is orientation of the obstacle in degrees as seen by radar, counterclockwise is positive
            double angle = -Vector3.SignedAngle(transform.forward, triggerAgent.AgentTransform.forward, transform.up);
            if (angle > 90)
            {
                angle -= 180;
            }
            else if (angle < -90)
            {
                angle += 180;
            }
            return angle;
        }

        private Vector3 GetObjectVelocity(ITriggerAgent triggerAgent)
        {
            if (triggerAgent == null)
                return Vector3.zero;
            
            switch (triggerAgent.AgentType)
            {
                case AgentType.Unknown:
                    throw new ArgumentException();
                case AgentType.Ego:
                    if (triggerAgent is IAgentController agentController)
                    {
                        return agentController.Velocity;
                    }
                    break;
                case AgentType.Npc:
                    if (triggerAgent is NPCController npc)
                    {
                        return npc.simpleVelocity;
                    }
                    break;
                case AgentType.Pedestrian:
                    if (triggerAgent is PedestrianController ped)
                    {
                        if (ped.ActiveBehaviour is PedestrianAutomaticBehaviour pedActiveBehaviour)
                        {
                            return pedActiveBehaviour.Agent.desiredVelocity;
                        }

                        var rb = ped.RB;
                        if (rb != null)
                        {
                            return rb.velocity;
                        }
                    }

                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            return Vector3.zero;
        }

        private int GetAgentState(ITriggerAgent triggerAgent)
        {
            int state = 1;
            if (triggerAgent != null && triggerAgent.MovementSpeed > 1f)
            {
                state = 0;
            }
            return state;
        }

        public override void OnVisualize(Visualizer visualizer)
        {
            foreach (var v in Visualized)
            {
                var triggerAgent = v.Key;
                var box = v.Value;
                if (triggerAgent.AgentGameObject.activeInHierarchy)
                {
                    WireframeBoxes.Draw(triggerAgent.AgentTransform.localToWorldMatrix, new Vector3(0f, triggerAgent.AgentBounds.extents.y, 0f), box.Size, box.Color);
                }
            }

            foreach (var radar in radars)
            {
                Graphics.DrawMesh(radar.RadarMeshFilter.sharedMesh, transform.localToWorldMatrix, radar.RadarMeshRenderer.sharedMaterial, LayerMask.NameToLayer("Sensor"));
            }
        }

        public override void OnVisualizeToggle(bool state) {}
    }
}

This may help if the sensor changes don't solve the issue. It was the change needed for GT2D detection of add agent in api mode.
PedestrianManager.cs (NPCManager.cs might need the same changes.)

298 SimulatorManager.Instance.UpdateSegmentationColors(pedController.gameObject, pedController.GTID);
before
return pedController

and 
322
// Segmentation ID does not change on re-pool, so segmentation colors don't have to be updated
// Just make sure GTID is tracked again after re-spawning from pool
SimulatorManager.Instance.SegmentationIdMapping.AddOrGetSegmentationId(CurrentPooledPeds[i].gameObject, CurrentPooledPeds[i].GTID);

before
foreach (var callback in SpawnCallbacks)

@pantonante
Copy link
Author

Is it possible that you also changed the ITriggerAgent interface and the SimulatorManager?

I'm getting these errors when I try to compile:

'ITriggerAgent' does not contain a definition for 'AgentGameObject' and no accessible extension method 'AgentGameObject' accepting a first argument of type 'ITriggerAgent' could be found (are you missing a using directive or an assembly reference?)
'ITriggerAgent' does not contain a definition for 'AgentBounds' and no accessible extension method 'AgentBounds' accepting a first argument of type 'ITriggerAgent' could be found (are you missing a using directive or an assembly reference?)
'SimulatorManager' does not contain a definition for 'TriggersManager' and no accessible extension method 'TriggersManager' accepting a first argument of type 'SimulatorManager' could be found (are you missing a using directive or an assembly reference?)

@EricBoiseLGSVL
Copy link
Contributor

Yes, since our current repo is ahead of latest release we have quite a few changes in simulator that will be missing.

@pantonante
Copy link
Author

Any chance you can share these changes in some branch or here?

@EricBoiseLGSVL
Copy link
Contributor

Sorry, with simulator being sunset we will not be pushing any changes. With the current state internally, it will cause too many issues if we share or push latest.

@EricBoiseLGSVL EricBoiseLGSVL removed the next release Feature/bug fix will be included in next release label Jan 26, 2022
@alperenbayraktar
Copy link

@pantonante did you manage to send the radar data after all?

@pantonante
Copy link
Author

No, I decided to generate the radar from ground truth data outside SVL because it was easier in my project. You can read the code here https://github.com/pantonante/apollo/blob/master/verification/bridge/fake_radar.py

@alperenbayraktar
Copy link

I see, deleting the isTrigger check at the start of the WhileInRange method also fixes the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
answered Waiting for response
Projects
None yet
Development

No branches or pull requests

3 participants