当前位置:编程学习 > VB >>

一个VB6.0工业实时曲线的问题,高手请进

小弟最近在用到一种可以实时显示从PCI采集卡取回的数据的程序,实际上程序包含预先设计好的OCX控件,由控件完成显示的全部工作,主程序定时向控件ADDDATA方法加入数据,定时间隔为10mm,曲线显示特点是当所有数据显示大于横坐标的缺省值时,程序对横坐标和曲线进行实时缩放以在绘图区完整的显示。控件的实现是建立全局二维数组,每次增加数据都会重新定义数组的大小,将新增的数值赋予数组的最后一个,建立二维数组的目的是绘制多条曲线,以数组的值为纵坐标,时间与一个可以设置的系数积作为横坐标,在Picturebox中用LINE方法绘制。

目前存在的问题是:由于采样频率高(100次/秒),每次绘制需要刷新绘制数组的所有线段,运行时CPU占用很大(20%以上),绘制时间越长CPU占用越大,程序设计的目标是,以100次/秒的绘制速度,连续运行1小时,CPU占用的主要原因就是线条的重绘。以此计算,1小时将会有360000条线程序中绘制。

美国一家公司(iocomp)做得很好,实现同样的效果CP占用很小,就是价太高了,不知道用的是什么技术?真牛,各位高手不妨看看.

我试图使用VB的GDI+进行绘制,先进行Pah的绘制,每次只绘制path的最后增加的线,再对path进行matrix变换以能在窗口中绘制。实验后CP占用依然很大。

真是快要急死了,请各位高手出出主意,这种情况应如何解决. --------------------编程问答-------------------- 一、100fps 人眼根本看不过来,建议改成 10fps,即每10次重绘一次
二、数组不用每次进行扩充,可以 1000次进行扩充,这样 10秒才进行一次缩放,这 10秒内的 1000 个新点可以在原先的缩放比率上添加新点,根本不用重新绘制整个曲线。 --------------------编程问答-------------------- 很感谢VB老鸟的回复,提供的两种方法也很有效。
秉着追求求完美的精神,不妨和各位高手进一步探讨一下,关于实时曲线我在网上也找了不少资料,有不少网友提供了实例,效率也低,多数是只显示一定数量的数据,不能看到整个趋势。
老外的控件为什么能做的那么快,我想在技术上应有本质的区别。 --------------------编程问答-------------------- 我想是这样:
1 数组应当固定大小,不要反复分配内存。

2 采样数组和绘制数组分开,每次采样完成就将数据转存到绘制数组。这样采样数组很小。

3 绘制数组不要保留所有采样数据。绘制数组的大小应以能绘制满屏为限。记录采样数据的个数,或采样数据个数除以满屏线长像素的倍数 n 。只将 n 个数据的平均或众值写入绘制数据数组。 --------------------编程问答-------------------- 我觉得图形缩放不应该是将所有点进行坐标转化,应该是进行电线合并。
比如一小时采样 360000 点,而屏幕上的控件分辨率只有 1000 个 Pixel,那么每 360000 / 1000 = 360 个点其实对应在一个 x 坐标上,应为曲线的连续性,360 个点合并起来就是一条 (x,y1)-(x,y2) 的线段,这样 360000 个点的重绘就变成 1000 个线段的重绘,会快很多吧。

当你的图形缩小得越厉害,新采样点对图形的影响会越小,那么:
一、原先是 10fps 的也许可以降低到 1fps。
二、数组的扩充因子不用固定值,而是在根据原数组的大小按比率放大,比如 360000 / 99% * 1% = 3636,也就是控件有边的 1000 * 1% = 10 个 Pixel 长度的曲线要用 36 秒多才会填满;这样扩充数组的间隔也增大了。 --------------------编程问答-------------------- 每次增加数据都会重新定义数组的大小。。。
每秒执行100次。

不知道楼主能否确定,还是只是自己猜测?

这样做根本就不合理。



其他的关于任意时间段和缩放什么的都不是大问题。 --------------------编程问答-------------------- of123()所说的求平均值的算法我不太同意,因为这样在实时性要求高的情况下将不真实的反映采样结果,比如"毛刺"将被"钝化"。执行次数可以适当降低。
VB老鸟的“电线合并”方法很独特,谢谢
“360 个点合并起来就是一条 (x,y1)-(x,y2) 的线段”,其中y1,y2,应该取n个合并点中的最大值和最小值吧(n是不断变化的),增加求最大最小值算法的开销好像也很大。
各位有兴趣可以留下EMAIL,发个EXE示例。 --------------------编程问答-------------------- 打错字了:
“电线合并”->“点线合并”

求最大最小很简单,纪录在 x 上的最小值 y1、最大值 y2,下次新取得点的 y3 与 y1、y2 比较就可以了。 --------------------编程问答-------------------- 写了个简单的例子:
== UserControl1 ==
Option Explicit

Private Type SamplingPoint
    x As Long
    y As Long
End Type

Private Type SamplingList
    points() As SamplingPoint
    capacity As Long
    count As Long
End Type

Private Type DrawLine
    y1 As Long
    y2 As Long
End Type

Private Type DrawList
    lines() As DrawLine
    capacity As Long
    count As Long
End Type

Private m_SamplingList As SamplingList
Private m_DrawList As DrawList
Private m_Minification As Long

Public Sub Start()
    With m_DrawList
        .capacity = ScaleWidth
        .count = 0
        ReDim .lines(.capacity - 1)
    End With
    
    With m_SamplingList
        .capacity = ScaleWidth
        .count = 0
        ReDim .points(.capacity - 1)
    End With
    
    m_Minification = 1
    
    UserControl.Cls
End Sub

Public Sub Add(ByVal x As Long, ByVal y As Long)
    Dim bZoomed As Boolean
    Dim lNewMinification As Long
    Dim i As Long
    Dim j As Long
    
    With m_SamplingList
        If .count >= .capacity Then
            lNewMinification = m_Minification / 0.99
            If lNewMinification <= m_Minification Then lNewMinification = lNewMinification + 1
            
            .capacity = .capacity * (lNewMinification / m_Minification)
            ReDim Preserve .points(.capacity - 1)
            
            m_Minification = lNewMinification
            bZoomed = True
        End If
        
        .count = .count + 1
        .points(.count - 1).x = x
        .points(.count - 1).y = y
    End With
    
    With m_DrawList
        If bZoomed Then
            For j = 0 To m_SamplingList.count - 1
                y = m_SamplingList.points(j).y
                
                i = j \ m_Minification
                .count = i
                If (j Mod m_Minification) = 0 Then
                    .lines(i).y1 = y
                    .lines(i).y2 = y
                Else
                    If y < .lines(i).y1 Then
                        .lines(i).y1 = y
                    ElseIf y > .lines(i).y2 Then
                        .lines(i).y2 = y
                    End If
                End If
            Next
            
            UserControl.Cls
            For i = 0 To .count - 1
                UserControl.Line (i, .lines(i).y1)-(i, .lines(i).y2)
            Next
        Else
            i = (m_SamplingList.count - 1) \ m_Minification
            
            If i = .count Then
                .count = .count + 1
                .lines(i).y1 = y
                .lines(i).y2 = y
            Else
                If y < .lines(i).y1 Then
                    .lines(i).y1 = y
                ElseIf y > .lines(i).y2 Then
                    .lines(i).y2 = y
                End If
            End If
            
            UserControl.Line (i, .lines(i).y1)-(i, .lines(i).y2), , BF
        End If
    End With
End Sub

Private Sub UserControl_Initialize()
    UserControl.ScaleMode = vbPixels
    UserControl.AutoRedraw = True
    UserControl.BorderStyle = vbFixedSingle
    UserControl.BackColor = vbBlack
    UserControl.ForeColor = vbGreen
End Sub

== Form1 ==
放一个 Timer、一个 UserControl1

Option Explicit

Private Sub Form_Load()
    UserControl11.Start
    Timer1.Interval = 10
End Sub

Private Sub Timer1_Timer()
    Static x As Long
    Static y As Long
    
    If x = 0 Then
        y = 100
        Randomize
    Else
        y = Abs(y + Sgn(Rnd - 0.5))
    End If
    UserControl11.Add x, y
    x = x + 1
End Sub
--------------------编程问答-------------------- 运行一下,在进行了缩放后出现了"大尾巴",可能是y1,y2的取值问题,实际上在缩放1-2倍之间时,会出现一些像素内有2个点,另一些像素内一个点(只有一个y值)的情况,不过速度很快。 --------------------编程问答-------------------- 只为做一个毕业设计的vb菜鸟也遇到了同样的问题,但没有各位大侠讲的那么高深,我只是将实时采集来的数据以数据值和时间的格式显示,只不过有满屏后刷新时间坐标轴的问题,还有就是我不知道怎么在一个时间轴上显示不同的时间。忘各位大侠赐教!小弟在此不甚感激!                                             --------------------编程问答-------------------- 看看这个 --------------------编程问答--------------------
引用 10 楼  的回复:
只为做一个毕业设计的vb菜鸟也遇到了同样的问题,但没有各位大侠讲的那么高深,我只是将实时采集来的数据以数据值和时间的格式显示,只不过有满屏后刷新时间坐标轴的问题,还有就是我不知道怎么在一个时间轴上显示不同的时间。忘各位大侠赐教!小弟在此不甚感激!

我有该方面的源代码,或许可以帮到你 --------------------编程问答-------------------- 干过类似的活。不光是CPU的问题,还有显卡的问题。执行的时候是显卡风扇狂转,电脑假死。
楼上各位说的都有道理。还有一个方面是并行执行。VB这方面是弱项,但是也有勉强的办法。 --------------------编程问答-------------------- 这类绘制是比较简单的。
1 采用双缓冲
2 批量一次绘制,一般50毫秒就行。也就是把50ms内的数据一次绘制上去,人眼基本分辨不出来。

只要2做好,怎么绘制都ok

-----------------
工业控件 www.beaugauge.net --------------------编程问答-------------------- 不错啊! --------------------编程问答-------------------- 用這個控件,你肯定滿意

http://www.iocomp.com


但是注意很多破解不完美。。。OPC,沒破解 --------------------编程问答-------------------- 高手哇 --------------------编程问答-------------------- --------------------编程问答-------------------- 一,取数据的过程,100次/秒是可以的,存放到数组,然后更新最后的INDEX.

二,至于显示的过程,也许10次/秒地检测这个INDEX,发现改变后就进行绘制.

然后绘制过程里,对数据检测一下,看看当前数据量是否大于显示区的物理尺寸数量,再决定要绘哪些线.

比如,显示区域是800x300像素的区域,而目前取到的INDEX是8000,那只需要10个数据间隔地取数据即可,即1,11,21,31,41....

如此一来,最大绘制次数最多仅为物理像素数量.

循环与判断的速度是非常快的,但绘制到屏幕的速度与它们相比就太慢了,所以需要事先决定有哪些数据是有必要绘制的,这样才能提高效率.

PS:

检测数据是否更改并绘制的过程<二>,可以放在定时器里,并将定时器的周期设置为1ms,这样系统会以最低CPU占用状态进行工作,当然性能会比使用死循环低,嘿嘿.但是使用优化后的绘制过程的话,不需要这么大量的绘制代码了!
补充:VB ,  控件
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,