Sorry for late reply, my job prevented me from trying this earlier. So I have applied a moving average with size of 3 to my values that I send to motors and result was this:
Now I see there are actually two levels of smoothing here: one is smoothing out the actual readings from the engine, ie if engine ramped up rpm, we expect that needle would only move up. While this, in essence, is happening (rpm in increasing), it could have those small jerks or dips where rpm would would jump up and down a bit multiple times. This would cause needle to not move smoothly up but go down a bit, then up again then down again a bit before reaching it final position where rpm would settle.
The other smoothing is interpolating missing values between readings. In its basic form the needle controller has no idea what should be between two values, it just assumes needle must move from point A to point B and does this without any regard for how this actually looks (cosmetically). This then looks like jerky incremental movements (as seen in video). The solution must be something in line with controller delaying for one value so that it knows where next needle position will be in advance and then calculates the needed acceleration and speed. This way the controller knows not to decelerate if next value will not land in vicinity of previous value and just continue on, thus movement will look smooth. Or if next value lands close to previous value, just a bit more forward, it will know to start decelerating a bit.
Now this sounds all nice and dandy but how to actually do this is another story or if it even has merit
The library Iām using atm has something called accelTable where you can define how needle accelerates. I tried changing it a bit and I think some improvement can be seen but still way off the nice smooth looking movement on real clusters
before
// This table defines the acceleration curve.
// 1st value is the speed step, 2nd value is delay in microseconds
// 1st value in each row must be > 1st value in subsequent row
// 1st value in last row should be == maxVel, must be <= maxVel
static unsigned short defaultAccelTable[2] = {
{20, 800},
{50, 400},
{100, 280},
{150, 170},
{300, 120}};
after
static unsigned short defaultAccelTable[2] = {
{10, 20000},
{20, 10000},
{30, 5000},
{50, 1000},
{100, 600},
{150, 450},
{300, 350},
{500, 290},
{700, 200},
{1000, 120}};
The awesome guy who wrote this library also suggested to try to slow down the needle so that it would travel the desired distance in exactly 180ms (in time for next reading)