當我從 GPS 傳感器讀取資料時,會稍有延遲。您沒有得到像 0,1 0,2 0,3 0,4 0,5 等這樣的值,但它們會像 1 一樣出現,然后突然變成 5 或 9 或 12。在這種情況下,針頭來回跳動。有人知道如何使針平穩移動嗎?我想需要某種延遲?
類似的東西,取自另一個控制元件:
async void animateProgress(int progress)
{
sweepAngle = 1;
// Looping at data interval of 5
for (int i = 0; i < progress; i=i 5)
{
sweepAngle = i;
await Task.Delay(3);
}
}
但是我有點困惑如何實作它。
這是在畫布上畫針的代碼:
private void OnDrawNeedle()
{
using (var needlePath = new SKPath())
{
//first set up needle pointing towards 0 degrees (or 6 o'clock)
var widthOffset = ScaleToSize(NeedleWidth / 2.0f);
var needleOffset = ScaleToSize(NeedleOffset);
var needleStart = _center.Y - needleOffset;
var needleLength = ScaleToSize(NeedleLength);
needlePath.MoveTo(_center.X - widthOffset, needleStart);
needlePath.LineTo(_center.X widthOffset, needleStart);
needlePath.LineTo(_center.X, needleStart needleLength);
needlePath.LineTo(_center.X - widthOffset, needleStart);
needlePath.Close();
//then calculate needle position in degrees
var needlePosition = StartAngle ((Value - RangeStart) / (RangeEnd - RangeStart) * SweepAngle);
//finally rotate needle to actual value
needlePath.Transform(SKMatrix.CreateRotationDegrees(needlePosition, _center.X, _center.Y));
using (var needlePaint = new SKPaint())
{
needlePaint.IsAntialias = true;
needlePaint.Color = NeedleColor.ToSKColor();
needlePaint.Style = SKPaintStyle.Fill;
_canvas.DrawPath(needlePath, needlePaint);
}
}
}
編輯:
仍然很難理解這個程序。
假設我不想應用此過濾器進行控制,而是在 ViewModel 中使用它來過濾值。我有一個從中獲取資料的類,例如 GPSTracker。GPSTracker 提供速度值,然后我在 HomeViewModel 中訂閱 EventListener 并希望過濾傳入值。
根據亞當斯的回答:
uj5u.com熱心網友回復:
來自控制背景,為了模仿模擬設備的行為,您可以使用指數(又名低通)濾波器。
您可以使用兩種型別的低通濾波器,具體取決于您希望看到的行為型別:一階或二階濾波器。簡而言之,如果您的讀數穩定在 0,然后突然變為 10,并穩定在 10(一個階躍變化),則第一個訂單會慢慢變為 10,永遠不會超過它,然后保持在 10,而第二個order 將加快向 10 的進度,通過它,然后向 10 振蕩。
指數濾波器的功能很簡單:
public void Exp_Filt(ref double filtered_value, double source_value, double time_passed, double time_constant)
{
if (time_passed > 0.0)
{
if (time_constant > 0.0)
{
source_value = (filtered_value - source_value) * Math.Exp(-time_passed / time_constant);
}
filtered_value = source_value;
}
}
filtered_value是源的過濾版本source_value,time_passed是從最后一次呼叫此函式到 filter的時間filtered_value,并且time_constant是過濾器的時間常數(僅供參考,對階躍變化做出反應,filtered_value將獲得 63% 的source_value時間time_constant時間已經過去,99% 已經過去了 5 倍)。的單位filtered_value將與 相同source_value。time_passed和的單位time_constant必須相同,無論是秒、微秒還是 jiffy。此外,time_passed應該明顯小于time_constant所有時間,否則過濾器行為將變得不確定。有多種獲取 的方法time_passed,例如Stopwatch,請參閱如何計算已經過去了多少時間?
在使用過濾器功能之前,您需要初始化filtered_value以及您用于獲取的任何內容time_passed。對于這個例子,我將使用stopwatch.
var stopwatch = new System.Diagnostics.Stopwatch();
double filtered_value, filtered_dot_value;
...
filtered_value = source_value;
filtered_dot_value = 0.0;
stopwatch.Start();
要將此函式用于一階過濾器,您可以使用計時器或類似的東西回圈以下內容
double time_passed = stopwatch.ElapsedMilliseconds;
stopwatch.Restart();
Exp_Filt(ref filtered_value, source_value, time_passed, time_constant);
要將此函式用于二階濾波器,您可以使用計時器或類似的東西回圈以下內容
double time_passed = stopwatch.ElapsedMilliseconds;
stopwatch.Restart();
if (time_passed > 0.0)
{
double last_value = filtered_value;
filtered_value = filtered_dot_value * time_passed;
Exp_Filt(ref filtered_value, source_value, time_passed, time_constant);
Exp_Filt(ref filtered_dot_value, (filtered_value - last_value) / time_passed, time_passed, dot_time_constant);
}
二階濾波器通過考慮一階濾波值的一階導數來作業。另外,我建議制作time_constant < dot_time_constant- 開始,我會設定dot_time_constant = 2 * time_constant
就我個人而言,我會在由執行緒定時器控制的后臺執行緒中呼叫這個過濾器,并且有time_passed一個等于定時器周期的常量,但我將把實作細節留給你。
編輯:
Below is example class to create first and second order filters. To operate the filter, I use a threading timer set to process every 100 milliseconds. Being that this timer is rather consistent, making time_passed constant, I optimized the filter equation by pre-calculating Math.Exp(-time_passed / time_constant) and not dividing/multiplying 'dot' term by time_passed.
For first-order filter, use var filter = new ExpFilter(initial_value, time_constant). For second-order filter, use var filter = new ExpFilter(initial_value, time_constant, dot_time_constant). Then, to read latest filtered value, call double value = filter.Value. To set value to filter towards, call filter.Value = value.
public class ExpFilter : IDisposable
{
private double _input, _output, _dot;
private readonly double _tc, _tc_dot;
private System.Threading.Timer _timer;
/// <summary>
/// Initializes first-order filter
/// </summary>
/// <param name="value">initial value of filter</param>
/// <param name="time_constant">time constant of filter, in seconds</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="time_constant"/> must be positive</exception>
public ExpFilter(double value, double time_constant)
{
// time constant must be positive
if (time_constant <= 0.0) throw new ArgumentOutOfRangeException(nameof(time_constant));
// initialize filter
_output = _input = value;
_dot = 0.0;
// calculate gain from time constant
_tc = CalcTC(time_constant);
// disable second-order
_tc_dot = -1.0;
// start filter timer
StartTimer();
}
/// <summary>
/// Initializes second-order filter
/// </summary>
/// <param name="value">initial value of filter</param>
/// <param name="time_constant">time constant of primary filter, in seconds</param>
/// <param name="dot_time_constant">time constant of secondary filter, in seconds</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="time_constant"/> and <paramref name="dot_time_constant"/> must be positive</exception>
public ExpFilter(double value, double time_constant, double dot_time_constant)
{
// time constant must be positive
if (time_constant <= 0.0) throw new ArgumentOutOfRangeException(nameof(time_constant));
if (dot_time_constant <= 0.0) throw new ArgumentOutOfRangeException(nameof(dot_time_constant));
// initialize filter
_output = _input = value;
_dot = 0.0;
// calculate gains from time constants
_tc = CalcTC(time_constant);
_tc_dot = CalcTC(dot_time_constant);
// start filter timer
StartTimer();
}
// the following two functions must share the same time period
private double CalcTC(double time_constant)
{
// time period = 0.1 s (100 ms)
return Math.Exp(-0.1 / time_constant);
}
private void StartTimer()
{
// time period = 100 ms
_timer = new System.Threading.Timer(Filter_Timer, this, 100, 100);
}
~ExpFilter()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_timer.Dispose();
}
}
/// <summary>
/// Get/Set filter value
/// </summary>
public double Value
{
get => _output;
set => _input = value;
}
private static void Filter_Timer(object stateInfo)
{
var _filter = (ExpFilter)stateInfo;
// get values
double _input = _filter._input;
double _output = _filter._output;
double _dot = _filter._dot;
// if second-order, adjust _output (no change if first-order as _dot = 0)
// then use filter function to calculate new filter value
_input = (_output _dot - _input) * _filter._tc;
_filter._output = _input;
if (_filter._tc_dot >= 0.0)
{
// calculate second-order portion of filter
_output = _input - _output;
_output = (_dot - _output) * _filter._tc_dot;
_filter._dot = _output;
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/446865.html
標籤:C# xamarin xamarin.forms skiasharp
