【问题标题】:Vispy multiple graphsVispy 多张图
【发布时间】:2021-04-30 20:38:08
【问题描述】:

我是 python 编程的新手,我正在努力使用 Vispy 库。

基本上,我有一个 Raspberry pi 连接到 2 个 Arduinos 加速度计传感器。覆盆子通过 UDP 将两个传感器的 X、Y 和 Z 值发送到我的计算机。然后我的电脑必须显示 9 个图表:6 个表示两个传感器的 x、y 和 z 的演变,3 个表示它们之间的差异(X1-X2、Y1-Y2 和 Z1-Z2),最后,它必须是真实的-时间。

最后一点我想使用 Vispy 库。阅读文档后,我想出了以下代码:

#!/usr/bin/env python3

import numpy as np
from vispy import app
from vispy import gloo
import socket
from itertools import count

# init x, y arrays
x1_vals = []
time_vals = []

#UDP connection from Raspberry pi
UDP_IP = ""
UDP_PORT = 5005

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))

# Initialize the index and set it to 1
index = count()
next(index)

# Initialize the Canvas c
c = app.Canvas(keys='interactive')

vertex = """
attribute vec2 a_position;
void main (void)
{
    gl_Position = vec4(a_position, 0.0, 1.0);
}
"""

fragment = """
void main()
{
    gl_FragColor = vec4(0.0, 0.0, 15.0, 10.0);
}
"""
program = gloo.Program(vertex, fragment)

@c.connect
def on_resize(event):
    gloo.set_viewport(0, 0, *event.size)

@c.connect
def on_draw(event):
    gloo.clear((1,1,1,1))
    program.draw('line_strip')


def on_timer(event):
    
    # next index
    cpt = next(index)
    
    # Get data from UDP
    recv, addr = sock.recvfrom(1024)
    data = recv.decode('UTF-8').split(';')
    
    # We want to display only 100 samples so the graph still readable.
    # So we delete the first value of the x array if there are more than 100 values
    if (cpt > 100):
        del x1_vals[0]
        time_vals = np.linspace(-1.0, +1.0, 100)      
    else:
        time_vals = np.linspace(-1.0, +1.0, cpt)

    # The values must be bound between -1.0 and 1.0
    tmp = float(data[0])*0.5
    if (tmp >= 1):
        tmp = float(0.99)
    elif (tmp <= -1):
        tmp = float(-0.99)
    
    x1_vals.append(tmp)
    
    # Then we concatenate the arrays of x and y
    program['a_position'] = np.c_[time_vals, x1_vals].astype(np.float32)
    c.update()
   
c.timer = app.Timer('auto', connect=on_timer, start=True)
    
c.show()
app.run()

因此,正如 cmets 所描述的,它首先初始化 UDP 连接和画布,然后对于收到的每个值,它都会使用新添加的值更新画布。如果值的个数超过100,则删除数组的第一个值,保持样本数不变。

当我只想显示 X1 加速度计传感器的演变时,它工作得很好。所以现在我选择了the code from the Vispy documentation which demonstrates how to show multiple graphs,但是代码对于我的关卡来说有点太复杂了。

基本上,在我的代码中,我接收到 data 数组中的所有传感器值。我选择第一个值 [0] (X1),但完整的数据如下所示:[x1, y1, z1, dx, dy, dz, x2, y2, z2] 其中 dx = x1 - x2,dy = y1 - y2 和 dz = z1 - z2。 (差值要直接在树莓上计算)。

所以我尝试修改文档中的代码如下:

# Number of cols and rows in the table.
nrows = 3
ncols = 3

# Number of signals.
m = nrows*ncols

# Number of samples per signal.
n = 100

因为我想要 9 个图,每个图只有 100 个样本。

我忽略了索引、颜色并删除了振幅,在我的情况下不需要它。基本上,我几乎保留了整个设置部分的原始代码,然后我用我的替换了def on_timer

现在我正在尝试用我自己的数据从 GLSL 提供 a_position 数组。但我不确定如何准备数据以使其与此代码正常工作。我很难理解这些行的作用:

# GLSL C code
VERT_SHADER = """
    // Compute the x coordinate from the time index.
    float x = -1 + 2*a_index.z / (u_n-1);
    vec2 position = vec2(x - (1 - 1 / u_scale.x), a_position);
    // Find the affine transformation for the subplots.
    vec2 a = vec2(1./ncols, 1./nrows)*.9;
    vec2 b = vec2(-1 + 2*(a_index.x+.5) / ncols,
                  -1 + 2*(a_index.y+.5) / nrows);
    // Apply the static subplot transformation + scaling.
    gl_Position = vec4(a*u_scale*position+b, 0.0, 1.0);
"""

# Python code
def __init__(self):
    self.program['a_position'] = y.reshape(-1, 1)
    
def on_timer(self, event):
    k = 10
    y[:, :-k] = y[:, k:]
    y[:, -k:] = amplitudes * np.random.randn(m, k)

    self.program['a_position'].set_data(y.ravel().astype(np.float32))

我删除了我认为我理解的周围代码。

请注意,即使我从 python 开始,当我在代码中使用裸对象时,我也知道他们正在使用 Canvas 的类定义。我了解self 和其他人的用法。

如何根据我的案例调整 realtime_signals 文档中的代码?

【问题讨论】:

    标签: python glsl vispy


    【解决方案1】:

    免责声明:在我看来,总体而言,实时信号示例有点过时。制作尽可能多的情节是“作弊”,但最终结果很快。

    那段着色器代码正在尝试获取一系列线顶点并找出它们应该进入哪个“子图”。所有线的所有顶点都作为一个数组进入着色器。着色器代码试图说“这个顶点在数组中是第 23 个,这意味着它必须属于子图 5,并且它是该图中的第 3 个点,因为我们知道每个图有 5 个点”(例如)。着色器主要通过a_index 中的信息来执行此操作。比如这个位:

        // Compute the x coordinate from the time index.
        float x = -1 + 2*a_index.z / (u_n-1);
        vec2 position = vec2(x - (1 - 1 / u_scale.x), a_position);
    

    正在根据点所在的子图调整 x 坐标 (a_position)。

    下一个块:

        // Find the affine transformation for the subplots.
        vec2 a = vec2(1./ncols, 1./nrows)*.9;
        vec2 b = vec2(-1 + 2*(a_index.x+.5) / ncols,
                      -1 + 2*(a_index.y+.5) / nrows);
        // Apply the static subplot transformation + scaling.
        gl_Position = vec4(a*u_scale*position+b, 0.0, 1.0);
    

    正在尝试确定每个子图的大小。所以第一块是“这个点落在什么子图中”,这个是“这个点在那个子图中的什么位置”。这段代码通过线性仿射变换 (y = m*x + b) 将线条缩放到适当的大小,以便所有子图的大小相同且不重叠。

    如果不重新遍历整个脚本并尝试准确理解 a_index 中的每个值是什么,我不确定是否可以更详细地介绍。

    编辑:另一个建议,从长远来看,您可能希望将 UDP recv 代码移动到一个单独的线程(如果使用 Qt 后端,则为 QThread),当它发出带有新数据的信号时可用。这样,GUI/主线程保持响应,不会挂起等待数据进入。

    【讨论】:

      猜你喜欢
      • 2021-10-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 2016-09-07
      • 1970-01-01
      • 2020-07-27
      • 1970-01-01
      • 2017-03-21
      相关资源
      最近更新 更多