- Home /
How to instantiate and delete spheres based on incomming coordinates stream
Hello everyone, Iam new to Unity scripting and c# and would like to do the following:
I constantly get x y coordinates which represent laser scan data. I would like to draw spheres on each of the x y points.
Basically the laserscan data consist of up to 100 points which will repeat themselves if the laserscanner is not moved. So i will constantly get a x y data stream (if laser is not movied, then always the same points). If I move the scanner, I will get new points and some old points will not be sent because they are not visible for the laser scanner anymore.
Now for each x y point i would like to draw a sphere and at the same time, if the laser scanner moves and gives new points i want to display the new points and delete old ones which are not visible.
For now I have implemented that for every xy point to draw the spheres but struggled with the deletion algorithm. So now it draws spheres at new points but also keep the old ones on the scene I want to delete the ones which coordinates are not being sent anymore.
my script is as following:
public GameObject point;
public Vector3 spawnValues;
public float spawnWait;
public float spawnMostWait;
public float spawnLeastWait;
public int startWait;
public bool stop;
public GameObject sensorf;
public SensorReceive sensorscript;
public GeometryPoint pt
private void Start()
{
// get x and y from sensorReceive script
sensorf = GameObject.Find("SensorReceiver");
sensorscript = sensorf.GetComponent<SensorReceive>();
StartCoroutine(waitSpawner());
}
private void Update()
{
spawnWait = 0.00f;
}
IEnumerator waitSpawner()
{
while (!stop)
{
// spawn spheres at x and y position from sensorscript
Vector3 spawnPosition = new Vector3(sensorscript.x, 1, sensorscript.y);
Instantiate(point, spawnPosition + transform.TransformPoint(0, 0, 0), gameObject.transform.rotation);
yield return new WaitForSeconds(spawnWait);
}
}
Please help me because I struggled for 2 days and also searched for examples. Big thanks in advance! Best regards
Answer by JVene · Jul 16, 2018 at 11:10 PM
@neymar4138, A review of waitSpawner suggests that whether you see them or not, new spheres are constantly being created in the same place if the data streams as you describe.
Since it seems obvious from your description that the software can't sense when the scanner is moved, it must sense, instead, when data points already exist (so it doesn't create duplicates as it appears to be now), and can age the data so as to remove items no longer coming in from the data.
You'll need a container that can find points by x,y location (so as to recognize duplicate incoming data). When a duplicate data point is recognized, there should be a time stamp it can update, "refreshing" that point of data to indicate it is still coming in.
Next, there must be an occasional sweep of this container to identify old data. If the time stamp isn't being refreshed, then the scanner is no longer reading those points in - those are the candidates for removal.
The classic container of choice here is the balanced height binary tree. This container is indicated because it performs well for frequent insertion and removal, provides lookup by key (the x,y pair) and dynamically expands to whatever volume is required. Unfortunately that container isn't in the C# library, so you'd have to build one yourself to use it.
C# provides the Dictionary, the Hashtable and sorted arrays. These will work because your volume is low. Insertion and deletion performance are these containers weak points, but they are much faster for sweeping through the entire container than binary trees, and are a little more efficient on memory consumption. If the volume were in the several thousands, the deletion/insertion performance would suffer sufficiently that constructing a binary tree might be a reasonable choice (and that's even more important as the volume increases), but for 100 or so there won't be a problem.
I'll choose Dictionary for an example.
Please note, this is proposed example code, untested and not checked. You'll need to interpret, perhaps research a bit on the specifics.
There's a potential catch. Vectors (2 and 3) use floats for location. When identifying exact matches, floats have a tendency to truncate (some say round, but it doesn't do 4/5 rounding, it truncates). If that isn't a problem, you may ignore this point. However, if you do want to control the precision of the incoming data, you'll need to create an IEqualityComparer to control how keys are compared. This should suffice:
class DataPointComp : IEqualityComparer<Vector3>
{
private static int rounding = 4;
// this rounds the points for comparison, accepts points as matching
// when they are within 'rounding' decimal points of accuracy
public bool Equals( Vector3 d1, Vector3 d2 )
{
if ( Mathf.Round( d1.x, rounding ) != Mathf.Round( d2.x, rounding ) ) return false;
if ( Mathf.Round( d1.y, rounding ) != Mathf.Round( d2.y, rounding ) ) return false;
if ( Mathf.Round( d1.z, rounding ) != Mathf.Round( d2.z, rounding ) ) return false;
return true;;
}
// this creates hashcodes based on rounded values, so close
// points (within 'rounding' of precision) have the same hash code
public int GetHashCode( DataPoint d )
{
return Math.Round( d.x, rounding ).GetHashCode()
^ Math.Round( d.y, rounding ).GetHashCode() << 2
^ Math.Round( d.z, rounding ).GetHashCode() >> 2;
}
}
You'll need a few members in your class. A container, for example (using the rounding comparer)
var dataPoints = new Dictionary< Vector3, DataPoint >( new DataPointComp() );
You'll need a DataPoint class. This stores the time stamp, a copy of the Transform of the sphere you create for the data point (so it knows what to delete later), and the data point. Something like:
class DataPoint
{
Vector3 position;
Transform trans;
float timeStamp;
DataPoint( x, y, Transform t )
{
position = new Vector3( x, 1, y );
trans = t;
timeStamp = Time.time;
}
}
You need an "executive" level function to run this. I'll assume your waitSpawner, but I can't tell the correct choice. Something like:
IEnumerator waitSpawner()
{
while( !stop )
{
Vector3 dataPoint = GetDataPoint();
UpdateDataPoints( dataPoint );
if ( Time.time > nextCullTime )
{
PruneData();
}
}
}
The idea here is to use a few functions to represent important steps.
This assumes a float, nextCullTime, is set to the value Time.time will reach when the next cull should run. This will sweep through the data to find old points and remove them. When that executes, calculate and set the nextCullTime for scheduling removal. You'll choose a time that represents how often, and therefore how soon, old data should be removed.
This is an example "prune" function that does something like that:
void PruneData()
{
// set ageCutoff as member of the class, this is how old data
// can be before it should be pruned
float cutoffAge = Time.time - ageCutoff;
List<Vector3> toBeRemoved = new List<Vector3>();
foreach( KeyValuePair< Vector3, float > d in dataPoints )
{
if ( d.Value.time < cutoffAge )
{
DestroyDataPoint( d.Value ); // provides the transform
toBeRemoved.Add( d.Key );
}
}
foreach( Vector3 d in toBeRemoved )
{
dataPoints.Remove( d );
}
}
You'll note that I create a list collecting what is to be removed, and remove them from the dictionary after the sweep completes. This is necessary in some containers because when something is removed while a loop is running through its contents, the loop can be confused by stuff disappearing (the current pointer can be lost). I don't know C# Dictionary's behavior in this regard, so I'm playing it safe by moving the removal to a second step.
This uses yet another member you'll create and tune to your liking, the ageCutoff. This is time in seconds (a float, so it can be short, like .1 seconds) declaring what is considered old data. It can be used to schedule the nextCullTime.
Also note this calls "DestroyDataPoint", a function I've not exampled. This is what removes the sphere from your gameObjects. How you do that is up to you (you're creating them), but the d.Value passed is a DataPoint object, which contains a transform of the sphere, from which you can get gameObject and do whatever works.
I'd suggest you consider not destroying the game objects, but disabling them from rendering, hold them in a pool of unused gameObjects, and reuse them by moving them to new positions and turning them back on when SpawnNewData is called (another proposed function).
Maybe my use of GetDataPoint as a function seems overkill since it's just one little thing, but here's an example:
Vector3 GetDataPoint()
{
return new Vector3( sensorscript.x, 1, sensorscript.y );
}
Here's the meat of it all, though.
bool UpdateDataPoints( Vector3 d )
{
DataPoint p;
if ( dataPoints.TryGetValue( d, out p ) == true )
{
p.timeStamp = Time.time; // updates the time for this key
// inform caller data isn't new (but has been refreshed by time)
return false;
}
// the key doesn't exist, so create it, and inform the caller
// the key is new data
Transform t = SpawnNewData( d ); // should return the transform of the new sphere
dataPoints[ d ] = new DataPoint( d.x, d.y, t ); // stamps time in the constructor
return true;
}
The objective here is to query the dictionary dataPoints to see if a key of d is already there. If it is, get the dataPoint (in p) and update the timeStamp (so it stays fresh and prune won't destroy it).
That would return false to indicate to the executive that this dataPoint already existed. I don't use the return in my example waitSpawner, so this is optional.
If the data point was not found, it's new. This calls SpawnNewData, which takes the Vector3 indicating where it should spawn. How you spawn is up to you, but you may consider a pool of reusable objects. Whatever the case, return the Transform representing the object so that can be stored in the DataPoint of the dictionary (so it can tell you the Transform and therefore the GameObject that is to be deleted later).
So, there's the plan.
Details are still up to you.
Answer by neymar4138 · Jul 17, 2018 at 10:19 AM
@Jvene Many Thanks for your help Sir !! Iam very glad that you found time to think about my problem.
Your idea is very good and I nearly understand all the steps that you proposed.
Still I dont know how to work with transforms to instantiate and destroy objects. I looked at the definition of Transform but there are so many variables to set (one is the position). What do you mean with provide the transform with the function SpawnNewData(d) and DestroyDataPoint(d) and how should I do that? Because I only have the position Information. I tried to write the Functions as below but its not working yet:
public bool UpdateDataPoints(Vector3 d)
{
DataPoint p;
if (dataPoints.TryGetValue(d, out p) == true)
{
p.timeStamp = Time.time; // updates the time for this key
// inform caller data isn't new (but has been refreshed by time)
return false;
}
// the key doesn't exist, so create it, and inform the caller
// the key is new data
Transform t = SpawnNewData(d); // should return the transform of the new sphere
dataPoints[d] = new DataPoint(d.x, d.y, t); // stamps time in the constructor
//now get the position from transform and instantiate the object 'point'
//would it be easier to instantly get the position from d instead of going through the transform ?
Instantiate(point, t.position + transform.TransformPoint(0, 0, 0), gameObject.transform.rotation);
return true;
}
Transform SpawnNewData(Vector3 position)
{
transform.position = position;
return transform;
}
For the destruction of old points I have this :
void PruneData()
{
// set ageCutoff as member of the class, this is how old data
// can be before it should be pruned
float cutoffAge = Time.time - ageCutoff;
List<Vector3> toBeRemoved = new List<Vector3>();
foreach (KeyValuePair<Vector3, DataPoint> d in dataPoints)
{
if (d.Value.timeStamp < cutoffAge)
{
DestroyDataPoint(d.Value); // provides the transform
toBeRemoved.Add(d.Key);
}
}
foreach (Vector3 d in toBeRemoved)
{
dataPoints.Remove(d);
}
}
void DestroyDataPoint(DataPoint objectInformation)
{
Destroy(objectInformation.gameObject);
}
Is this the right way to work with transforms to instantiate and destroy objects? Because before that I only did instantiation with simple
Instantiate(point, spawnPosition , gameObject.transform.rotation);
but I think with transform its the better way because you can somehow work with the gameObject. If I start unity then nothing happens. So no spheres are spawned.
Another question is where I should put the dictionary within my code ?
var dataPoints = new Dictionary< Vector3, DataPoint >( new DataPointComp() );
Because I have to make this global so that the functions can access it, but var s cant be public. So I fist declare the Dictionary globally at the top of my class and within the start() function make it var (see below overall code at the beginning). But Iam not sure if this works. So our overall code looks like this: The main class 'SpawnPoints' which I will attach to unity (see picture)
using System;
using UnityEngine;
using System.Collections;
using System.Threading;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace RosSharp.RosBridgeClient
{
public class SpawnPoints : MonoBehaviour
{
public GameObject point;
public Vector3 spawnValues;
public float spawnWait;
public float spawnMostWait;
public float spawnLeastWait;
public int startWait;
public bool stop;
public GameObject sensorf;
public SensorReceive sensorscript;
public double x, y, z;
public float ageCutoff = 4;
public float nextCullTime = 2;
//Position Arrays
public Vector3[] spawnPositions;
public Vector3[] sPos;
public Vector3[] newPositions;
public Dictionary<Vector3, DataPoint> dataPoints;
public GeometryPoint pt;
private void Start()
{
sensorf = GameObject.Find("SensorReceiver");
sensorscript = sensorf.GetComponent<SensorReceive>();
//right to put it here?
var dataPoints = new Dictionary<Vector3, DataPoint>();
StartCoroutine(waitSpawner());
}
private void Update()
{
spawnWait = 0.0001f;
}
IEnumerator waitSpawner()
{
while (!stop)
{
Vector3 dataPoint = GetDataPoint();
UpdateDataPoints(dataPoint);
if (Time.time > nextCullTime)
{
PruneData();
}
yield return new WaitForSeconds(spawnWait);
}
}
Vector3 GetDataPoint()
{
return new Vector3(sensorscript.x, 1, sensorscript.y);
}
public bool UpdateDataPoints(Vector3 d)
{
DataPoint p;
if (dataPoints.TryGetValue(d, out p) == true)
{
p.timeStamp = Time.time; // updates the time for this key
// inform caller data isn't new (but has been refreshed by time)
return false;
}
// the key doesn't exist, so create it, and inform the caller
// the key is new data
Transform t = SpawnNewData(d); // should return the transform of the new sphere
dataPoints[d] = new DataPoint(d.x, d.y, t); // stamps time in the constructor
//now get the position from transform and instantiate the object 'point'
//would it be easier to instantly get the position from d instead of going through the transform ?
Instantiate(point, t.position + transform.TransformPoint(0, 0, 0), gameObject.transform.rotation);
return true;
}
Transform SpawnNewData(Vector3 position)
{
transform.position = position;
return transform;
}
void DestroyDataPoint(DataPoint objectInformation)
{
Destroy(objectInformation.gameObject);
}
void PruneData()
{
// set ageCutoff as member of the class, this is how old data
// can be before it should be pruned
float cutoffAge = Time.time - ageCutoff;
List<Vector3> toBeRemoved = new List<Vector3>();
foreach (KeyValuePair<Vector3, DataPoint> d in dataPoints)
{
if (d.Value.timeStamp < cutoffAge)
{
DestroyDataPoint(d.Value); // provides the transform
toBeRemoved.Add(d.Key);
}
}
foreach (Vector3 d in toBeRemoved)
{
dataPoints.Remove(d);
}
}
}
}
If I try to run my unity scene nothing is spawned cause there is an NullReferenceException: Object reference not set to an instance of an object at line 99 where UpdateDataPoints(vector3 d) is called.
Also i would like to ask if you could provide examples on how i should implement the algorithm where we dont destroy the spheres but asign them to some other places. That seems to be a very nice idea! But Iam still to beginner to figure out how to achieve that.
I hope for your answer and thank you very much!
Best regards
I may have misunderstood, it seemed you were perhaps more experienced than I assumed. I’ve taken you into a deeper dive than what I’d wish on most new programmers, but you do seem up to the task and motivated, so let’s continue.
About Transforms. The Transform is a member of a gameObject once it is created, so you don’t need to figure out all that it means in order to create a new sphere, your instantiation from the example you posted worked so stay with that, but let SpawnNewData do it. What you get is an object that owns a configured Transform, so return that with something like:
Transform SpawnNewData(Vector3 spawnPosition)
{
GameObject o = Instantiate(point, spawnPosition, gameObject.transform.rotation) as GameObject;
return o.transform;
}
Then, leave UpdateDataPoints more like I posted, by removing the call to Instantiate where you put it (SpawNewData does that).
In line with my point that what I’ve posted is not tested code, and that I’d leave it to you to complete it, I may have assumed that TryGetValue created a DataPoint if required, but perhaps it doesn’t and it gives you an exception because of that.
Try adding:
DataPoint p = new DataPoint();
Which may also require creating a default constructor, if the compiler complains, for DataPoint with something like:
DataPoint() {}
Which doesn't have to do anything, leaving it all zeroed.
Your method for destroying and instantiating objects is, as far as I can tell without actually building this, ok. In your Instantiate you’re using the gameObject from your script to fill in a Quaternion, which really could be any valid Quaternion since a sphere doesn’t show what rotation it has without a texture that shows a sense of orientation.
Transform is a study by itself, so you’ll need to read example code that uses it, and study the documentation on it’s members. Transform is used by Unity as the basis for containing objects, and from the Transform you can access the gameObject and all else. You’ll need to read and understand the difference between position and localPosition, rotation and localRotation.
As to placement of the dictionary, your last code post is correct, though it isn’t global. Global refers to data available to all classes (in all scripts), but this is contained with this class as you’ve placed it. If you wanted it (or any data) accessible to all classes in all scripts, you make it static (that’s what C# uses to implement global data, where otherwise C# doesn’t actually support global declarations like C++ does).
BTW, experiencing these kinds of errors and design difficulties is common to software development in any language. $$anonymous$$y own "native" and preferred language is C++, and I've been at this since the late 70's as a teen. C# is new to me, but at this point I've forgotten at least 10 languages I once used, and new ones come quickly.
Replies are limited in size. I'll think about object pooling, but you might research that, it's a common design pattern. For that matter, what I've proposed is a not so common design pattern, or plan (it might not be an entry in the formal texts on design patterns).
For object pooling like your example, you'd basically make 100 spheres, but turn off rendering for all of them. I'm assu$$anonymous$$g you always have exactly 100 data points, but you could make more for a buffer, maybe 200 or 300, one for previous and another for current sampling.
As you make these spheres, you take the notion I've exampled here with Dictionary on storing the transform of the sphere, but store them in a Dequeue. This container will function as a FIFO queue (it can do LIFO too). You stuff all the disabled spheres into the Dequeue at initialization, then remove one at a time when data comes in, assign the position, turn on rendering for that sphere and, as the existing proposal provides, put it in the dictionary.
When data is removed, ins$$anonymous$$d of destroying the sphere, it is removed from the dictionary , then the sphere's rendering is disable (making it vanish from view), and put it back into the Dequeue.
The Dequeue is, then, the object pool.
Wait until you have the dictionary working to try this, but once the dictionary works, back up your project, then see if you can figure out how to do it. Like I said before, this is a deep dive for a new programmer, but it seems you're not a kid or timid about this.
Another thought came to $$anonymous$$d as I was grabbing groceries. As new data comes in, it might be useful to place new, inco$$anonymous$$g spheres slightly above and in a different color. When the data is found in the dictionary, which is a repeated input, move them into the plane you have them now (you're setting Y at 1, which BTW is one meter in Unity, and your spheres are probably 1 meter in diameter, so maybe new spheres come in at 1.5 or 1.25 or even 2), and set the color to indicate repeated reads.
Thanks for your replies, tipps and ideas! I am an electrical eng. student from germany who is learning to code through such projects but I first started with Unity and C# only about 2 month ago! Unity is as fascinating and as its complicated. Iam very glad to meet you and would like to learn as much as possible from excperienced coders like you!
Refering to the NullExceptionReference Problem: i thinks its because of the argument d from UpdateDataPoints(Vector3 d) to which dataPoint is passed in UpdateDataPoints(dataPoint), for some reason it cannot do that and says that the object Reference of d which i guess is dataPoint is not set to an instance of an object.
(NullReferenceException: Object reference not set to an instance of an object RosSharp.RosBridgeClient.SP2.UpdateDataPoints (Vector3 d) (at Assets/RosSharp/Scripts/PCLVisualize/SP2.cs:104))
Edit: After some testing, uncommenting and Debugging lines the problem definitely has to do something with the dictionary. The argument dataPoint is definitely passed to d in UpdateDataPoints. So thats not the problem. But everytime I try to work with or access the dictionary it will come up with that error. I will try to see whats behind this. $$anonymous$$aybe the dictionary is not visible or something like that?
Edit2: Now the thing with the dictionary is working! I had to remove the 'var' word before dataPoints = new Dictionary() Now the points are showing up on the screen but the deletion part has got an NullReferenceException Error at line 149 where i call Destroy(objectInformation.gameObject);
also tried Destroy(objectInformation.transform.gameObject);
Edit3: Okay, something with the Dictionary is still not working. Everytime I
dataPoints[d] = new DataPoint(d.x, d.y, t)
Debug.Log(dataPoints[d])
in line 111, Debug gives null for dataPoints.
Edit4: resolved the issue! The class DataPoints.cs was deriving from $$anonymous$$onobehavior which was not okay! deleted that and now the dictionaries are accessable!
Seeing your debugging process detailed like this tells me "you got this". Not just this particular issue, which from this distance had a solution I could not see (kudos on that), but more generally it is clear you have scientific and engineering training, experience with diagnosis and the methods for finding the problems.
Indeed, $$anonymous$$onoBehaviour (notice the British spelling, a curious hint as to the origins of Unity's code), is a Unity base class. I find a lot of students tend to think in terms of scripts, as if scripts were a program$$anonymous$$g concept that owns things. Script is a poorly defined term originally associated with text files used to customize the behavior of an application. In Unity, however, the term is a bit demeaning (though it is their convention of terms, so I don't argue) in that cs files are source files to the application, as if this were a C# application outside of Unity. So, like you discovered, classes need not derive from Unity bases, you're writing a C# application and have full access to both the language and design freedom such that a stand alone class like DataPoint is appropriate. Sometimes those are structs. In C++ classes and structs are identical in all respects except classes assume members are private, while structs assume they are public. In C#, though, structs are smaller, stand alone encapsulations that can't derive from other structs or classes and from which one can't derive.
I'm delighted to meet you, too. I often find posters here with questions about as detailed and interesting as, "It's broken, how do I fix it", then getting any further detail is like ancient dentistry. Having grown up from childhood to witness what both computers and the Internet have done to change the world, it fascinates me to chat in depth with people from all over the world. As an American I'm motivated to explain most of us are mystified about our President, and short of an apology we simply have no explanation, are about as jaw dropped and concerned. Not just about him, but how more than 5% of our fellow citizens (it's maybe 35% or so) support him. Now I understand why I sense you're fully up to the task of mastering C#. Electrical engineering is near to my own study, but while my college days and related study of electronics and engineering are decades behind me, my career impelled me to continually study software engineering every year since. Lately I volunteered to $$anonymous$$ch program$$anonymous$$g to high school students in the local robotics club where my son attends, which branched into that elementary mechanical engineering associated with designing and building robots such students understand. I point that out because it became increasingly obvious that all engineering disciplines intersect with software engineering, and your unusual application, hinted by your questions, is an example.
@Jvene now its finally working!! The problem was indeed, that the DataPoint Class was mistakenly deriving from $$anonymous$$onoBehavior. I accidently included that. Now I deleted it and everyting is totally working! $$anonymous$$any thanks! The debugging part is really a tough part in coding but when everything is working than its worth it!
Now I will try to optimize that, to draw new points with a different color and slightly above the old ones, afterwards I would also like to implement the object pooling approach because the generation of new points is still slow. The problem is as following: unfortunately the data stream from the laserscanner (wich I receive through websockets) is very unpredictable and random. $$anonymous$$g. it happens that the same point is sent 50 times and only after that a new one arrives (in sum there are only 40 to 100 different points from the laser scanner but with this way the data stream is very long) $$anonymous$$g. lets say the current laser scanner position scanns 60 points. then it could take relatively long for all those 60 points to arrive at my unity, because some points will be sent like 50 or even more times in a row and the generation of the whole point cloud will be slow.
Congratulations! The sequence of repeated points might not be the scanner itself, but of the method the code draws them in. It appears, though I can't see, that sensorscript receives data, but this code is reading from it as fast as a loop can run, which may be quite fast. A few notions to consider is to add a small delay (you might find the scanner supplies data every few ms, so a small delay might help synchronize the loop pulling data in, and improve CPU consumption as a result). The Dictionary is skipping duplicate input from this queue of inco$$anonymous$$g data, but perhaps sensorscript should send a signal that new data is arrived. That can take some complex forms, and it depends on whether or not the scanner sends a synchronization signal, but it might be synthesized by having sensorcript notice that x and y haven't changed since the last sample. If that were running in it's own thread this might be even worse. However, this configuration of the code is rejecting duplicates by search of a Dictionary, which is an efficient data structure, but the comparison is being made against the entire population of extant data. Ins$$anonymous$$d, it may be wise to cache the last datapoint received from sensorscript (say a single Vector2 or Vector3 called lastDataPoint), and skip even attempting to add that to the dictionary until it changes. That represents fewer steps in sensing the transition from one inco$$anonymous$$g datapoint to the next. The dictionary is still required for the overall sense of scanner repositioning, but this notion is focused on point to point transition. In some designs, if we have a synchronization signal co$$anonymous$$g from a sensor (a new point has arrived signal), we can make code simply rest and wait, without consu$$anonymous$$g CPU resources, until data has arrived. This method is sometimes known as overlapped I/O (a poorly named concept commonly associated with reading data from storage - disks and flash drives). If that isn't available we're left with polling, which is the basic construction I see here. Polling can be too fast compared to the device, so polling should be "held" in cycles with very small delays so ins$$anonymous$$d of checking 20 or 50 times per new point of data, we're checking maybe 2 or 3. If the scanner provides data at, say, 10 samples per second, we need not check 100 times per second because that oversampling is wasted CPU work. By adding a wait of 5 ms per test, the code checks 20 times per second, catching every change but not running full blast. Testing might show this is better at 2 or 3 ms because there is a small risk that if the polling is too close to the rate of supply, a transition might happen between samples such that a new point of data is missed, and thus lost. Put another way, the repeated points you're seeing might not be the scanner, but in the functions between the scanner and applying that to the dictionary.