在 C# 中以 10-15 毫秒的间隔记录来自结构的数据
Logging data from structs in interval 10-15 ms in C#
我必须准备 Logger class,它将以 10-15 毫秒的间隔从 3 个结构中保存数据。我解决这个问题的方法如下:
public class Logger
{
// Variables
private Task loggerTask;
public bool IsActive { get; set; }
// Constructor
public Logger()
{
}
private void Logging()
{
#if DEBUG
System.Diagnostics.Debug.WriteLine("Logging has been started.");
#endif
FileStream fs = new FileStream($"./log {DateTime.Now.ToString("dd.MM HH.mm.ss")}.txt", FileMode.Create, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs, Encoding.Default);
try
{
Queue<double> times = new Queue<double>();
Queue<Attitude> attitudes = new Queue<Attitude>();
Queue<LocalPositionNed> positions = new Queue<LocalPositionNed>();
Queue<SetPositionTargetLocalNed> setpoints = new Queue<SetPositionTargetLocalNed>();
// Logs data
DateTime start = DateTime.Now;
DateTime last = start;
DateTime now;
while (IsActive)
{
now = DateTime.Now;
if ((now - last).TotalMilliseconds < 16)
continue;
last = now;
times.Enqueue((now - start).TotalMilliseconds);
attitudes.Enqueue(GCS.Instance.Drone.Attitude);
positions.Enqueue(GCS.Instance.Drone.LocalPositionNed);
setpoints.Enqueue(GCS.Instance.Offboard.SetPoint);
}
// Save data
for(int i = 0; i < times.Count; i++)
{
sw.WriteLine($"{times.ElementAt(i)}\t" +
$"{attitudes.ElementAt(i).Pitch}\t" +
$"{attitudes.ElementAt(i).Roll}\t" +
$"{attitudes.ElementAt(i).Yaw}\t" +
$"{attitudes.ElementAt(i).Pitchspeed}\t" +
$"{attitudes.ElementAt(i).Rollspeed}\t" +
$"{attitudes.ElementAt(i).Yawspeed}\t" +
$"{positions.ElementAt(i).X}\t" +
$"{positions.ElementAt(i).Y}\t" +
$"{positions.ElementAt(i).Z}\t" +
$"{positions.ElementAt(i).Vx}\t" +
$"{positions.ElementAt(i).Vy}\t" +
$"{positions.ElementAt(i).Vz}\t" +
$"{setpoints.ElementAt(i).Vx}\t" +
$"{setpoints.ElementAt(i).Vy}\t" +
$"{setpoints.ElementAt(i).Vz}\t");
}
}
catch (Exception ex)
{
#if DEBUG
System.Diagnostics.Debug.WriteLine($"Logging exception: {ex.Message}");
#endif
}
finally
{
sw.Dispose();
fs.Dispose();
}
#if DEBUG
System.Diagnostics.Debug.WriteLine("Logging has been ended.");
#endif
}
// Static method
public void Start()
{
IsActive = true;
loggerTask = new Task(Logging);
loggerTask.Start();
}
public void Stop()
{
IsActive = false;
}
}
我对间隔有疑问,因为它们大约有 5-8 毫秒的变化。我的项目需要 1-2 毫秒的最大变化。有谁知道我可以如何改进我的方法。
感谢您的回复。
最大的问题可能是使用 DateTime.Now
,分辨率很差,不适合这种任务。
一个更合适的简单替代方案是 stopwatch。
var delay = 16;
var stopwatch = Stopwatch.StartNew();
long current = 0;
long previous;
var next = stopwatch.ElapsedMilliseconds + delay;
for (int i = 0; i < 100; i++)
{
while (next > stopwatch.ElapsedMilliseconds)
{
// Spin
}
next = stopwatch.ElapsedMilliseconds + delay;
previous = current;
current = stopwatch.ElapsedMilliseconds;
var delta = current - previous;
Console.WriteLine("Delta: " + delta);
}
如评论中所述,这不提供任何强有力的保证,只是尽力而为。还有更好的方法可以做到这一点,不会浪费整个线程什么都不做,例如使用 multi media timers。但只要 CPU 上的负载很轻,它就可以满足 testing/debugging 的目的。
我必须准备 Logger class,它将以 10-15 毫秒的间隔从 3 个结构中保存数据。我解决这个问题的方法如下:
public class Logger
{
// Variables
private Task loggerTask;
public bool IsActive { get; set; }
// Constructor
public Logger()
{
}
private void Logging()
{
#if DEBUG
System.Diagnostics.Debug.WriteLine("Logging has been started.");
#endif
FileStream fs = new FileStream($"./log {DateTime.Now.ToString("dd.MM HH.mm.ss")}.txt", FileMode.Create, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs, Encoding.Default);
try
{
Queue<double> times = new Queue<double>();
Queue<Attitude> attitudes = new Queue<Attitude>();
Queue<LocalPositionNed> positions = new Queue<LocalPositionNed>();
Queue<SetPositionTargetLocalNed> setpoints = new Queue<SetPositionTargetLocalNed>();
// Logs data
DateTime start = DateTime.Now;
DateTime last = start;
DateTime now;
while (IsActive)
{
now = DateTime.Now;
if ((now - last).TotalMilliseconds < 16)
continue;
last = now;
times.Enqueue((now - start).TotalMilliseconds);
attitudes.Enqueue(GCS.Instance.Drone.Attitude);
positions.Enqueue(GCS.Instance.Drone.LocalPositionNed);
setpoints.Enqueue(GCS.Instance.Offboard.SetPoint);
}
// Save data
for(int i = 0; i < times.Count; i++)
{
sw.WriteLine($"{times.ElementAt(i)}\t" +
$"{attitudes.ElementAt(i).Pitch}\t" +
$"{attitudes.ElementAt(i).Roll}\t" +
$"{attitudes.ElementAt(i).Yaw}\t" +
$"{attitudes.ElementAt(i).Pitchspeed}\t" +
$"{attitudes.ElementAt(i).Rollspeed}\t" +
$"{attitudes.ElementAt(i).Yawspeed}\t" +
$"{positions.ElementAt(i).X}\t" +
$"{positions.ElementAt(i).Y}\t" +
$"{positions.ElementAt(i).Z}\t" +
$"{positions.ElementAt(i).Vx}\t" +
$"{positions.ElementAt(i).Vy}\t" +
$"{positions.ElementAt(i).Vz}\t" +
$"{setpoints.ElementAt(i).Vx}\t" +
$"{setpoints.ElementAt(i).Vy}\t" +
$"{setpoints.ElementAt(i).Vz}\t");
}
}
catch (Exception ex)
{
#if DEBUG
System.Diagnostics.Debug.WriteLine($"Logging exception: {ex.Message}");
#endif
}
finally
{
sw.Dispose();
fs.Dispose();
}
#if DEBUG
System.Diagnostics.Debug.WriteLine("Logging has been ended.");
#endif
}
// Static method
public void Start()
{
IsActive = true;
loggerTask = new Task(Logging);
loggerTask.Start();
}
public void Stop()
{
IsActive = false;
}
}
我对间隔有疑问,因为它们大约有 5-8 毫秒的变化。我的项目需要 1-2 毫秒的最大变化。有谁知道我可以如何改进我的方法。
感谢您的回复。
最大的问题可能是使用 DateTime.Now
,分辨率很差,不适合这种任务。
一个更合适的简单替代方案是 stopwatch。
var delay = 16;
var stopwatch = Stopwatch.StartNew();
long current = 0;
long previous;
var next = stopwatch.ElapsedMilliseconds + delay;
for (int i = 0; i < 100; i++)
{
while (next > stopwatch.ElapsedMilliseconds)
{
// Spin
}
next = stopwatch.ElapsedMilliseconds + delay;
previous = current;
current = stopwatch.ElapsedMilliseconds;
var delta = current - previous;
Console.WriteLine("Delta: " + delta);
}
如评论中所述,这不提供任何强有力的保证,只是尽力而为。还有更好的方法可以做到这一点,不会浪费整个线程什么都不做,例如使用 multi media timers。但只要 CPU 上的负载很轻,它就可以满足 testing/debugging 的目的。