对于人体平衡的实验测量,我想连接并读出多个I2C传感器(陀螺仪/磁力/加速度计,例如这个)。
我已经设法将它们连接起来(libftdi+python3.7),并且可以从单个寄存器中读取。
但是,我要求来自多个设备和寄存器的数据(大约)同步或时间记录。 我怎样才能做到这一点?
(编辑:一个不错的解决方案是固定长度的并行化缓冲区,每个I2C设备/寄存器一个,在后台填充并存储在触发后。
背景:
(1)我不明白I2C的ICL时钟是如何工作的。我知道可以设置一个阅读频率,比如100Hz。我可以在任意时间点读取,传感器值似乎在变化,但可能不是一直都在变化。如何控制读数的确切时间?
(2)还是时钟只是解决方案的一部分? 我一直在寻找缓冲解决方案,并找到了bytearray
s和io.BytesIO
。但我不知道如何只阅读新值。大多数指南都提到文件存储,但我宁愿不流式传输到文件(数据不大)。我还"读了一点"(字面意思)异步执行,使用 asyncio。 但是,我的尝试(如下)并不理想,因为它需要大量的开销来存储测量的精确时间点。还有其他限制,见下文。
(3)我的问题被放大了,因为到目前为止,我只能从I2C读取单个寄存器,即我必须循环读取9次才能获得9DOF数据。我了解到有多种解决方案可以通过python访问I2C,我正在使用FT232H突破[*],其中许多都支持。其中一些一次查询多个字节,可能涉及一些缓冲区。然而,我让它工作的唯一方法是使用 libftdi 并将 I2C 部分从 Adafruit_GPIO 移植到 python3。所以我还无法重现并行字节查询。(具体如何)libftdi 能够进行此类查询吗?也许这可以扩展?
起点:
我的第一个直觉是用multiprocessing
和队列做一些事情。但是,当有多个线程或进程时,如果我没有正确进行 ICL 同步,它们就会漂移(请参阅 1)。此外,队列似乎已填满 fifo,但如果以足够的速率阅读,我可以使用后进先出和刷新。
然后我发现了asyncio
,并进行了第一次尝试。 然后,我尝试使用基于io.BytesIO
的类似缓冲区的连接。但我从未学会如何真正填充、读取和刷新缓冲区,所以我的方法可能很笨拙。
下面是问题的模拟,创建随机字节并处理它们。
import asyncio as AIO
import io as IO
import numpy as NP
# a sender object that keeps spitting out data
class Sender(IO.BytesIO):
def __init__(self, label):
super(Sender, self).__init__()
self.label = label
async def GetGoing(self):
await self.PutStuffIn()
async def PutStuffIn(self):
while True:
rnd = NP.random.bytes(1)
print (self.label, 'sending', rnd)
self.write(rnd)
await AIO.sleep(0.01)
### a receiving object that collects data
class Receiver(object):
def __init__(self, senders):
self.streams = senders
async def GetReceiving(self):
await self.ReadStuffOut()
async def ReadStuffOut(self):
pointers = {stream.label: 0 for stream in self.streams}
while True:
await AIO.sleep(0.02)
for stream in self.streams:
data = stream.getvalue()
print (stream.label, 'received', data[pointers[stream.label]:])
pointers[stream.label] = stream.tell()
### main process
async def Main():
# create multiple senders and a receiver
senders = [Sender(1), Sender(2), Sender(3)]
read = Receiver(senders)
# execute sender and receiver in parallel
await AIO.wait( [
send.GetGoing()
for send in senders]
+[ read.GetReceiving()
] )
### mission control
if __name__ == "__main__":
loop = AIO.get_event_loop()
loop.run_until_complete(Main())
期望:
如上所述,这种工作,但是:
使用
io.BytesIO.getvalue()
似乎一直得到整个缓冲区,并且它没有被清空:我想我创建了一个列表,而不是一个真正的缓冲区?对于
asyncio
,执行是不确定的,所以我不能预先分配固定大小的缓冲区。我使用它只是因为它的速度,因为我想比 I2C 时钟更快地采样,所以它可能是错误的轨道。另一方面,让多个发送方准并行工作似乎是一种好方法。(未显示)我想更多地了解并行I2C寄存器访问的深层结构,以及它是否可以跨设备使用。
提前非常感谢您的提示和指导!
顺便说一句,我几乎是文盲C
,但(乐于学习)也会喜欢该语言的代码示例。
[*] 我使用的是FT232H分线,而不是我的机器人,因为在我介绍的内容之上,还将通过DAQ获取测力板数据。因此,这也必须并行化,这可以通过多处理来实现。
I2c 协议一次仅支持从一个设备进行串行数据传输。典型的多路复用器仅将主接口移至每个隔离通道,因此它们只能支持串行/顺序读取。为了并行读取多个 i2c 设备,每个相同的设备都应位于到处理器的物理隔离总线上。然后可以使用多线程并行读取每个设备,最后可以聚合所有数据。这将花费与仅从一个传感器读取数据所需的时间大致相同,但会产生更多数据。要在 pi 上做到这一点,可以使用额外的软件定义的 i2c 总线和多线程代码从中读取。否则,我不知道有任何硬件可以执行这样的功能。也许可以在硬件级别对FPGA进行编程以执行此操作。