我们有几个图表显示了来自表面肌电图传感器的过滤数据。此数据通过 TCP 接收并使用事件传播。数据包的类型为数据包,因此被过滤掉。我正在使用缓冲区让数据包以 30FPS 的速度通过。我正在使用反应式扩展收听此事件,如下所示:
Observable.FromEvent<Packet>(h => this.DataService.PacketReceived += h, h=> this.DataService.PacketReceived -= h)
.OfType<DataPacket>()
.Buffer(TimeSpan.FromSeconds(1.0 / 30))
.ObserveOnDispatcher()
.Subscribe(
packet => this.ReceiveDataPackets(packet.ToList()),
err => this.Log.Error("Error subscribing to data packets", err),
() => this.Log.Info("Finished listening to data packets"));
为了显示EMG数据,我们使用Telerik ChartView。我遇到的问题是数据更新不流畅,图表断断续续。
这可能有几个原因:
- Telerik 图表的速度不够快,无法每秒 1000 个数据点
- 调度程序计时器不以恒定速率触发
- 数据不是以恒定速率接收的
1 通过对输入数据进行采样来解决,以便在图形中仅显示 1000 个点。
不幸的是,第2点无法解决。我尝试提高渲染的优先级,但这根本没有帮助。http://social.msdn.microsoft.com/Forums/en-US/5eea6700-1c79-4da6-9b68-efa480ed3a36/simplify-wpf-dispatcher-calls?forum=rx
第 3 点与第 2 点相关。我尝试使用 System.Debug.Stopwatch
的定时队列来解决这两个问题。DataPackets包含一个时间戳,用于让它们在调度程序线程上以恒定速率通过。我怀疑这无济于事,因为调度程序速率与渲染线程的刷新率无关。
我能做些什么来减少断断续续?我尝试了LightningChart Ultimate,它应该要快得多。它确实具有更好的性能,并且无需进行任何采样,它可以呈现每个数据点。LightningChart提供的示例运行顺利,但它们在主线程中读取数据。当我在我们的多线程程序中实现他们的图表时,它仍然受到第 2 点和第 3 点的组合的影响(事实上它比 Telerik 图表视图贵得多。
[更新]
经典错误。我的数据源正在使用调度程序计时器来收集数据。将其更改为可观察的。间隔大大提高了性能。
这是一个老问题,但它可能会对这里的某人有所帮助。
如果要在 WPF 图表中显示来自传感器的数据,如果使用标准的开源图表组件或大多数商业供应商(如 Telerik(,则会非常慢。
我想建议我自己的组件:SciChart,它在WPF中以毫秒级更新显示数百万个点。
SciChart是一个商业组件,我想透露的是,是的,我是所有者,但是,在可用性或性能方面,没有免费或开源替代品可以接近它。
请查看页面 为什么使用 SciChart - 最佳 WPF 图表,因为它有一些特定的性能演示/视频供您查看。它支持数据的多线程更新,可以处理1,000点/秒。
前我在图表控制方面遇到了类似的麻烦。我也发现大多数图表控件无法处理 1000+ 个数据点。我还发现,试图让它每秒重新渲染超过 5-15 次对调度员来说非常困难。
我给你的建议是:
- 以低得多的帧速率为目标。您希望每 33 毫秒更新一次图表?
- 希望将数据集减少到 1000 点。再次使用数字,看看什么适用于您的目标规格 PC 和您的控件/数据模板。这篇旧帖子可能会对您有所帮助。它提供了一种从集合中选择要呈现的 X 个最有用的点的方法
- 回拨图表上每个点呈现的任何花哨动画或额外 UI。 如果您要添加更多项目,每个项目都有工具提示、画笔、动画和多余的布局面板,您将支付费用。另请注意,您不仅要在创建它们时支付费用,而且在以后 GC 尝试清除这数千个 UI 对象时也要支付费用。
- 减少调度程序的工作。我知道这并不多,但你可以移动那个
ToList()
并在另一个线程上做。此外,不要发送要处理的空列表(如果适用于您的事件( 请考虑使用 D3/DDD 图表。虽然当我审查它们时它们不是很好,但从那时起,同事们就取得了成功。
Observable.FromEvent<Packet>(h => this.DataService.PacketReceived += h, h=> this.DataService.PacketReceived -= h) .OfType<DataPacket>() .Buffer(TimeSpan.FromSeconds(1.0 / 8)) //Reduce the FPS .Select(packet=>packet.ToList()) //Reduce work done on dispatcher .Where(packet=>packet.Count>0) //Dont send empty sets to dispatcher .ObserveOnDispatcher() .Subscribe( packet => this.ReceiveDataPackets(packet), err => this.Log.Error("Error subscribing to data packets", err), () => this.Log.Info("Finished listening to data packets"));
我不确定它是否有帮助,但这是演示文稿中一些代码的另一个链接。演示的一部分是关于如何通过 Rx 将数据流式传输到 WPF 图表。你可能会撕毁整个事情。