Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Smoothing Noisy Compass (Android)

Discussion in 'Scripting' started by danielleamya, Nov 26, 2018.

  1. danielleamya

    danielleamya

    Joined:
    Oct 12, 2015
    Posts:
    8
    Hi,

    I am making an AR experience where I would like to use a phone's compass to direct a player toward true north, but I am having problems with smoothing the compass readings on Android devices. The code I have experimented with so far logs a certain number of readings into a queue, copies the queue into a list and then once the list is full it takes the average of all the readings. From here I would like to scale the reading between -1 & 1, where 0 represents South. Then I want this data to be used to rotate a compass image on the GUI layer.

    Here is the code I have so far:
    Code (CSharp):
    1.  
    2.     using System.Collections;
    3.     using System.Collections.Generic;
    4.     using UnityEngine;
    5.     using UnityEngine.UI;
    6.  
    7.     public class CompassSlider : MonoBehaviour
    8.     {
    9.         public float reading;
    10.         public Queue<float> data;
    11.         private float[] dataList;
    12.         public int maxData = 50;
    13.         public int count;
    14.         public float sum, average;
    15.  
    16.         public float dampening = 0.1f;
    17.         public float rotationToNorth;
    18.  
    19.         // Use this for initialization
    20.         void Start()
    21.         {
    22.             data = new Queue<float>();
    23.             dataList = new float[maxData];
    24.         }
    25.  
    26.         // Update is called once per frame
    27.         void Update()
    28.         {
    29.             Input.location.Start();
    30.             Input.compass.enabled = true;
    31.  
    32.             count = data.Count;
    33.  
    34.             if (data.Count > 0)
    35.             {
    36.                 data.CopyTo(dataList, 0);
    37.             }
    38.  
    39.             if (data.Count == maxData)
    40.             {
    41.                 for (int i = 0; i < data.Count; i++)
    42.                 {
    43.                     sum += dataList[i];
    44.                 }
    45.                 if (Mathf.Abs(dataList[maxData - 1]) > 0)
    46.                 {
    47.                     average = sum / maxData;
    48.                     sum = 0;
    49.                     data.Clear();
    50.                     dataList = new float[maxData];
    51.  
    52.                 }
    53.             }
    54.  
    55.             if (data.Count >= maxData)
    56.             {
    57.                 data.Dequeue();
    58.             }
    59.  
    60.             reading = Mathf.Round(Input.compass.trueHeading);
    61.             data.Enqueue(reading);
    62.  
    63.             if (count == maxData) {
    64.                 rotationToNorth = average / 180;
    65.                 rotationToNorth = (Mathf.Round(rotationToNorth * 10)) / 10;
    66.                 rotationToNorth = rotationToNorth - 1;
    67.             }
    68.         }
    69.     }
    70.  
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    Why not just lerp it?

    Code (csharp):
    1. rotationToNorth = Mathf.LerpAngle(rotationToNorth, Input.compass.trueHeading, 0.1f);
    Values closer to zero will lag a little bit behind but be smoother. Values closer to 1 will be more jittery and on point.

    Note lerp will give a sort of asymptotic approach to the target where it shoots towards it and slows down. If you want something with a more controlled velocity you can use smooth damping instead, just with a little more code needed:
    https://docs.unity3d.com/ScriptReference/Mathf.SmoothDampAngle.html

    Code (csharp):
    1. //as a field of the script
    2. private float _headingVelocity = 0f;
    3.  
    4. //in update
    5. rotationToNorth = Mathf.SmoothDampAngle(rotationToNorth, Input.compass.trueHeading, ref _headingVelocity, 0.1f);
     
    Last edited: Nov 26, 2018
    jpined93 likes this.
  3. danielleamya

    danielleamya

    Joined:
    Oct 12, 2015
    Posts:
    8
    Thanks for the speedy response.
    The reason I didn't use Lerp is for the exact reason you described. As values get closer to 1, they are more jittery. I am looking for an even smoothing across all values. Is it possible to apply some sort of filter to the numbers coming in? Can I have the code look for outlier values and remove them from the data set in the queue? Would it be as easy as comparing a previous value to the current one and if their difference is beyond some threshold to ignore the current value and move on? If so, I'm not too sure how I would accomplish that, so any help would be awesome.
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    Did you try SmoothDampAngle?
     
  5. danielleamya

    danielleamya

    Joined:
    Oct 12, 2015
    Posts:
    8
    Yes, I did. Still a little jerky. Definitely better than Lerp, but still not quite there.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    So. What are you hoping to get as a result?

    If you don't like Lerp with smaller values, that says to me you're hoping to have the needle on point and up to date. No delay what so ever.

    So you just want to filter out noise then?

    Is there some other look/feel you're hoping to get?

    Do you have any visual demonstrations of what you might be looking for... or even a visual of what you don't want.

    Are there compromises you're willing to take?

    Are there performance concerns?

    ...

    I only ask because what you're asking for is sort of specific to you. Like you've looked into a couple options already and you didn't like the results. What didn't you like about them? Not just the janky when near '1' (that's a given, putting a value near 1 in a Lerp is like not having a lerp at all). What's the issue when the value is 0.1?

    The alternative is someone spends the time writing up a way to do 'something', and that 'something' isn't what you want specifically and you're like "hrmph, that's not what I want."
     
  7. danielleamya

    danielleamya

    Joined:
    Oct 12, 2015
    Posts:
    8

    Sure, so I what I want is to filter out as much "outlier" data as possible to have the compass smoothly, similar to the way the compass app on one's phone would act. Lerp and SmoothDampAngle work fine when the jittering values are centered around a certain value, but with Android specifically, outlier values that are just completely incorrect read into the data set somewhat often (ex. if the values are jittering around 0.5, sometimes I'll get a random value like -0.6 even though the phone was sitting on a surface completely still).

    All in all, I think I need to take two important steps in changing this code. The first would be to filter out the outliers, and then for the closer jittery values, Lerp/SmoothDampAngle smoothes out those values fairly well. Sorry, I should have been more specific with my first response. Lol, my noob to this forum was showing. Also, thanks for your suggestions so far, I really appreciate the willingness to help out.