从多线程数据源更新平滑图表

时间:2013-10-07 11:35:48

标签: c# wpf graph telerik system.reactive

我们有几张图表显示来自表面EMG传感器的过滤数据。此数据通过TCP接收并使用事件传播。数据包是DataPackets类型,因此被过滤掉了。我正在使用缓冲区让数据包以30FPS通过。我正在使用Reactive Extensions监听此事件,如下所示:

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。我遇到的问题是数据更新不顺畅,图表不稳定。

这可能有几个原因:

  1. Telerik图表速度不够快,每秒1000个数据点
  2. DispatcherTimer不会以恒定速率触发
  3. 未按固定费率收到数据
  4. 通过对输入数据进行采样来解决点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包含一个时间戳,用于在Dispatcher线程上以恒定速率通过它们。我怀疑这不会有太大帮助,因为DispatcherRate与渲染的刷新率无关。

    我可以做些什么来减少波动?我尝试过LightningChart Ultimate,它应该要快得多。它确实具有更好的性能,并且不需要进行任何采样,它可以渲染每个数据点。随LightningChart提供的样本运行黄油顺利,但他们在主线程中读取他们的数据。当我在我们的多线程程序中实现他们的图表时,它仍然受到第2点和第3点的组合(以及它比Telerik图表视图贵得多的事实。)

    [更新]

    经典错误。我的数据源使用DispatcherTimer来收集数据。将此更改为Observable.Interval可大幅提高性能。

2 个答案:

答案 0 :(得分:1)

我遇到了类似图表控件的问题a while ago。我也发现大多数图表控件都无法处理1000多个数据点。我还发现尝试让它每秒重新渲染超过5-15次对于调度员来说非常困难。

我建议你的事情是:

  1. 目标是降低帧速率。您希望每33毫秒获得一次图表更新吗?
  2. 查看将数据集减少到1000个点。再次使用数字来查看适用于您的目标规格PC和控件/数据模板的数据。 This old post可能会对您有所帮助。它提供了一种从集合中选择X最有用的渲染点的方法
  3. 回拨任何花哨的动画或图表上每个点呈现的额外UI。如果您要添加更多项目,每个项目都有工具提示,画笔,动画和多余的布局面板,您将支付费用。另请注意,您不仅要在创建时支付费用,还要在GC尝试清除这些数千个UI对象时支付费用。
  4. 减少对调度员的工作。我知道它并不多,但你可以移动ToList()并在另一个线程上执行。此外,不要发送要处理的空列表(如果适用于您的活动)
  5. 考虑使用D3/DDD charts。虽然我在评论它们时并不是很好,但从那时起,同事们就取得了成功。

    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"));
    
  6. 我不确定它是否有帮助,但这里有another link来自演示文稿的某些代码。演示文稿的一部分是关于如何通过Rx将数据流式传输到WPF图表。你可能会撕掉整件事。

答案 1 :(得分:1)

我知道这是一个老问题,但这可能会对这里的人有所帮助。

如果要在WPF图表中显示来自传感器的数据,则使用标准的开源图表组件或大多数商业供应商(例如Telerik)会非常慢。

我想建议我自己的组件:SciChart,该组件在WPF中以毫秒为单位显示数百万个点。

SciChart是商业组件,我想透露是的,我是所有者,但是,就可用性或性能而言,没有任何免费或开源替代品可以与之接近。

请查看页面Why SciChart - the Best WPF Chart,因为该页面上有一些特定的性能演示/视频可供您观看。它支持数据的多线程更新,并且可以应付1,000点/秒。