0

我对 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()

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

当我只想显示 X1 加速度计传感器的演变时,它工作得很好。所以现在我从 Vispy 文档中选择了代码,该文档演示了如何显示多个图形,但是对于我的关卡来说,代码有点太复杂了。

基本上,在我的代码中,我收到了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

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

# 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 文档中的代码适应我的情况?

4

1 回答 1

0

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

那段着色器代码正在做的是尝试获取一系列线顶点并找出它们应该进入哪个“子图”。所有线的所有顶点都作为一个数组进入着色器。着色器代码试图说“这个顶点在数组中是第 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-01-27T15:57:35.627 回答