我是python编程的新手,正在为Vispy库而苦苦挣扎。
基本上,我有一个树莓派连接到两个Arduinos加速计传感器。raspberry正在通过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加速度计传感器的演变时,它工作得很好。所以现在我选择了the code from the Vispy documentation which demonstrates how to show multiple graphs,但是代码对于我的水平来说有点太复杂了。
基本上,在我的代码中,我接收data数组中的所有感应器值。我选择了第一个值0,但完整的数据如下所示:[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开始,我也知道当我在代码中使用bare对象时,他们正在使用Canvas的类定义。我了解self和其他工具的用法。
如何使realtime_signals文档中的代码适合我的情况?
发布于 2021-01-27 23:57:36
免责声明:总的来说,在我看来,实时信号的例子有点不靠谱。它“作弊”地生成了尽可能多的图,但最终的结果是快速的。
这段着色器代码所做的就是尝试获取一系列的线顶点,并找出它们应该进入哪个“子图”。所有线的所有顶点将作为一个数组进入着色器。着色器代码试图说“这个顶点在数组中是第23位的,这意味着它必须属于子图5,并且它是该图中的第三个点,因为我们知道每个图有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/主线程会保持响应,而不会挂起等待数据进入。
https://stackoverflow.com/questions/65911898
复制相似问题