13个重大发布

0.14.0 2024年5月5日
0.12.0 2024年4月11日
0.10.1 2023年12月7日
0.10.0 2023年11月25日
0.5.0 2023年7月7日

#95硬件支持

Download history 49/week @ 2024-04-26 146/week @ 2024-05-03 11/week @ 2024-05-10 49/week @ 2024-05-17 9/week @ 2024-05-24 1/week @ 2024-06-07 3/week @ 2024-06-28 75/week @ 2024-07-05

每月 1,280 下载量

自定义许可

200KB
4.5K SLoC

neuromorphic_drivers 是一个用于与USB神经形态设备交互的库。驱动程序从头开始编写,考虑到可移植性和性能。

支持设备和功能

名称 类型 分辨率 数据类型 掩码 同步 速率限制器 温度 照度
Prophesee EVK4 摄像头 1280 × 720 DVS, 触发
Prophesee EVK3 HD 摄像头 1280 × 720 DVS, 触发 - - -

此表列出了该库支持的特性。一些设备支持未列出的特性或标记为“未添加”的特性,这些特性尚未添加到 neuromorphic_drivers。

名称 链接
Prophesee EVK4 https://www.prophesee.ai/event-camera-evk4/
Prophesee EVK3 HD https://www.prophesee.ai/event-based-evk-3/

Python

入门指南

pip install neuromorphic_drivers

在Linux上,安装包后运行以下命令以安装UDEV规则。

neuromorphic-drivers-install-udev-rules

以下脚本从连接的设备读取数据。

import neuromorphic_drivers as nd

nd.print_device_list()  # print a table that lists connected devices

with nd.open() as device:
    for status, packet in device:
        if "dvs_events" in packet:
            # packet["dvs_events"] is a structured numpy array
            # with dtype [("t", "<u8"), ("x", "<u2"), ("y", "<u2"), ("on", "?")])
            pass
        if "trigger_events" in packet:
            # packet["trigger_events"] is a structured numpy array
            # with dtype [("t", "<u8"), ("id, "<u1"), ("rising", "?")])
            pass

数据包中的键取决于设备,并且仅在关联数组非空时存在。数据包包含可变数量的事件(通常为几千到几百万),覆盖可变的时间量(通常为几十微秒到几毫秒)。

设备配置

配置(针对每个设备特定)定义了所有设备参数,从偏置到感兴趣的区域。它可以在打开设备时指定,并且可以在任何时候更新。设备 d 的默认配置在 python/neuromorphic_drivers/generated/devices/d.py 中定义。

import neuromorphic_drivers as nd

configuration = nd.prophesee_evk4.Configuration(
    biases=nd.prophesee_evk4.Biases(
        diff_off=170,
        diff_on=130,
    )
)

with nd.open(configuration=configuration) as device:
    for status, packet in device:
        ...
        if not_sensitive_enough:
            configuration.biases.diff_on -= 10
            configuration.biases.diff_off -= 10
            device.update_configuration(configuration)

nd.open 可打开任何支持的设备。但是,如果指定了配置,则仅考虑匹配的设备。向 update_configuration 传递错误的配置类型会引发错误。

速率限制器

Prophesee的EVK4具有硬件事件速率限制器,当传感器每秒生成的数据超过指定数量时,会随机丢弃事件。限制器有两个参数。reference_period_us定义了计数周期(最大200µs)。maximum_events_per_period定义了每个周期内事件的数量,超过这个数量限制器会启动。实际限制是maximum_events_per_period / reference_period_us事件/µs。

import neuromorphic_drivers as nd

configuration = nd.prophesee_evk4.Configuration(
    rate_limiter=nd.prophesee_evk4.RateLimiter(
        reference_period_us=200,
        maximum_events_per_period=4000,
    )
)

with nd.open(configuration=configuration) as device:
    ...

原始模式

如果数据速率高,将原始USB数据转换为事件可能是一个昂贵的操作。原始模式跳过解析,如果只是想将数据存储到文件以供离线处理,则可能很有用。

import neuromorphic_drivers as nd

with nd.open(raw=True) as device:
    for status, packet in device:
        # in raw mode, packet is a "bytes" object

其他开放选项

def open(
    # initial device configuration
    configuration: typing.Optional[Configuration] = None,

    # timeout for each iteration (for status, packet in device)
    # By default, the iterator blocks until data is available.
    # In some cases (other time-driven calculations, graphics...),
    # it can be desirable to run an iteration of the loop even if no data is available.
    # If iterator_timeout is not None, packet may be None.
    iterator_timeout: typing.Optional[float] = None,

    # whether to skip data parsing
    raw: bool = False,

    # device serial number, None selects the first available device.
    # Use nd.list_devices() to get a list of connected devices and their serials.
    serial: typing.Optional[str] = None,

    # USB software ring configuration, None falls back to default.
    usb_configuration: typing.Optional[UsbConfiguration] = None,

    # maximum number of raw USB packets merged to create a packet returned by the iterator.
    # Under typical conditions, each iterator packet is built from one USB packet.
    # However, if more than one USB packet has already been received,
    # the library builds the iterator packet by merging the USB packets,
    # to reduce the number of GIL (Global Interpreter Lock) operations.
    # While merging speeds up processing in such cases, it can lead to large latency spikes if too many packets are merged at once.
    # iterator_maximum_raw_packets limits the number of packets that are merged, even if more USB packets have been received.
    # The remaning USB packets are not dropped but simply returned on the next iteration(s).
    iterator_maximum_raw_packets: int = 64,
): ...

USB配置的字段在设备间是相同的,但默认值取决于设备。以下值适用于Prophesee EVK4。

@dataclasses.dataclass
class UsbConfiguration:
    buffer_length: serde.type.uint64 = 131072 # size of each buffer in the ring, in bytes
    ring_length: serde.type.uint64 = 4096 # number of buffers in the ring, the total size is ring_length * buffer_length
    transfer_queue_length: serde.type.uint64 = 32 # number of libusb transfers submitted in parallel
    allow_dma: bool = False # whether to enable Direct Memory Access

更多示例

有关不同用法示例,请参阅python/examples

python/examples/any_display.py实现了一个使用GPU计算指数衰减的实时事件查看器。它需要vispy和glfw(pip install vispy glfw pyopengl)。

python/examples/evk4_plot_hot_pixels生成图表并需要Plotly(pip install plotly pandas kaleido)。

贡献

本地构建(首次运行)。

cd python
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install maturin numpy
maturin develop  # or maturin develop --release to build with optimizations

本地构建(后续运行)。

cd python
source .venv/bin/activate
maturin develop  # or maturin develop --release to build with optimizations

在推送新代码之前,运行以下命令以进行代码审查和格式化。

cd python
isort .; black .; pyright .

python/python/neuromorphic_drivers/generated中的文件由构建脚本(python/build.rs)生成,不应直接修改。这些文件包含在本存储库中,用于文档目的。

当同时修改Rust库和Python包装器时,将neuromorphic-drivers = "x.y"更改为neuromorphic-drivers = {path = "../drivers"}以使用最新的Rust代码版本。

Rust

文档

有关文档,请参阅https://docs.rs/neuromorphic-drivers/latest/neuromorphic_drivers/

UDEV规则

  1. 将以下内容写入/etc/udev/rules.d/65-neuromorphic-drivers.rules

    SUBSYSTEM=="usb", ATTRS{idVendor}=="152a",ATTRS{idProduct}=="84[0-1]?", MODE="0666"
    SUBSYSTEM=="usb", ATTRS{idVendor}=="04b4",ATTRS{idProduct}=="00f[4-5]", MODE="0666"
    
  2. 运行以下命令(或重新启动机器)。

    sudo udevadm control --reload-rules
    sudo udevadm trigger
    

贡献

运行特定的测试。

cd drivers
cargo test --release read -- --nocapture

在crates.io上发布。

cargo publish -p neuromorphic-types
cargo publish -p neuromorphic-drivers

性能

事件速率

在特定情况下,最近的事件相机(如Prophesee EVK4)可能产生的数据量超过了实时处理的能力。虽然数据可以始终实时移动到计算机内存中,但最简单的算法(包括将原始USB字节转换为{t, x, y, polarity}事件表示)在数据速率峰值期间难以跟上。该库使用单独的线程进行读取(USB到内存)和加工(内存到内存或磁盘),在接口处使用环形缓冲区(ring)。短数据突增被环形缓冲区无缝吸收,通常不会造成问题,尽管它们会暂时增加延迟。然而,持续的高数据速率会导致环形缓冲区逐渐填满,最终导致程序崩溃。根据使用情况,可以应用以下解决方案之一

  • 降低相机的灵敏度(通常通过更改diff_offdiff_on
  • 如果设备支持,启用事件速率限制器(限制器在将事件传送到计算机之前随机丢弃事件,减少了带宽问题,但会显著降低瞬态突增的质量)
  • 通过屏蔽行和列来降低相机的空间分辨率
  • 如果可能,请更改环境(避免闪烁的灯光、减少光流、去除背景杂乱,并将大型和快速移动的物体移出视野)
  • backlog变得过大时(最大backlog是环形缓冲区大小减去传输队列大小),请调用device.clear_backlog(until=0)(直到为0)
  • 使用nd.open(raw=true)跳过解析器,直接访问USB字节,通常用于将它们保存到文件

直接内存访问

此库依赖于libusb进行所有USB通信。libusb支持直接内存访问(目前仅限Linux,未来可能会添加其他平台),允许USB控制器直接将数据包写入内存,而不需要CPU(和操作系统内核)干预。虽然这可以提高性能并降低CPU使用率,但DMA没有我们默认禁用的警告。如果用户了解其USB控制器及其限制,可能希望启用它以在嵌入式系统中提高性能。

USB驱动程序有有限数量的DMA文件对象(128/256/512/1024)。这通常不足以容纳事件突发(EVK4默认使用4096个缓冲区,每个缓冲区131072字节)。如果所有DMA缓冲区都已被使用,代码将回退到非DMA缓冲区(例如,前128个缓冲区将是DMA,其余的是非DMA),这可能会导致性能随时间变化。

使用所有可用的DMA缓冲区可能导致其他USB传输失败(包括用于配置设备的控制传输)。

因此,使用DMA需要以下解决方案之一:

  • 使用少量DMA缓冲区,在处理之前将数据包复制到更大的环形缓冲区中(这多少有些违背了DMA的目的,并增加了内存复制)。
  • 使用更大的缓冲区(约1MB)来增加环形缓冲区的大小,而不增加缓冲区数量,但这会增加延迟。
  • 确保处理始终能够跟上数据速率(稀疏场景/低灵敏度偏差/事件速率限制/简单处理)。请注意,简单地将向量化的EVT3事件扩展到13字节的DVS事件在数据峰值期间并不是实时处理。

依赖关系

~1.8–2.7MB
~58K SLoC