Holoscan 传感器桥软件架构
Holoscan 传感器桥设备在传感器设备和 GPU 加速的 Holoscan 应用程序之间提供高速接口。连接到传感器桥设备的 периферия 控制通过传感器桥板上的 I2C、SPI 或本地总线接口提供。与这些 периферия 的交互使用称为控制平面的网络消息进行。从高速传感器采集的数据由 FPGA 收集,并通过 UDP 转发回主机,使用称为数据平面的消息。
Holoscan 传感器桥主机软件提供对象来管理用于控制 I2C、SPI 和本地总线事务的控制平面消息。其他传感器桥主机软件对象提供网络接收器操作器,用于配置和定向数据平面流量接收的数据。对于具有 ConnectX SmartNIC 设备的系统,接收到的数据平面流量可以使用 RDMA 透明地写入 GPU 内存。对于没有 ConnectX 设备的系统,有一个网络接收器对象使用 Linux Sockets API 提供相同的功能,但没有数据包接收卸载提供的高性能。
Holoscan 传感器桥软件还包括用于图像格式转换和图像信号处理的其他操作器。
作为传感器桥主机软件架构的介绍,我们将逐步介绍 examples/imx274_player.py 示例的主要方面。在此示例中,IMX274 立体相机单元为以 60FPS 运行的 4k 或 1080p 视频提供实时视频源。
应用程序与传感器对象上的 API 交互
应用程序通常实例化和使用传感器对象,这些对象提供特定于设备的 API。例如,相机对象可以提供用于设置其曝光的 API
camera.set_exposure(1000)
camera->set_exposure(1000);
要实例化相机对象,应用程序代码通常会
使用
Enumerator.find_channel
枚举本地系统可见的传感器桥设备。find_channel
接受过滤接收消息的参数;当找到与给定条件匹配的枚举消息时,将返回一个包含有关枚举设备的元数据的字典。channel_metadata = hololink_module.Enumerator.find_channel(channel_ip=args.hololink)
hololink::Metadata channel_metadata = hololink::Enumerator::find_channel(hololink_ip);
当从给定的 IP 地址观察到枚举数据时,有关找到的设备的信息将返回到
channel_metadata
变量中。使用
channel_metadata
构造DataChannel
对象。此对象将接收到的数据与 GPU 内存缓冲区连接起来。hololink_channel = hololink_module.DataChannel(channel_metadata)
hololink::DataChannel hololink_channel(channel_metadata);
使用
hololink_channel
构造我们的相机传感器对象camera = hololink_module.sensors.imx274.dual_imx274.Imx274Cam(hololink_channel, ...)
请注意,在本例中,IMX274 传感器是使用 Python 实现的,下面的代码展示了如何在 C++ 中创建 Python 对象。
py::module_ imx274 = py::module_::import("hololink.sensors.imx274"); py::object Imx274Cam = imx274.attr("dual_imx274").attr("Imx274Cam"); py::object camera = Imx274Cam("hololink_channel"_a = hololink_channel, ...);
构造相机实例实际上不与传感器桥设备交互——我们只是存储设备通信信息以供以后使用。相机实例是 HoloscanApplication 的构造函数运行所必需的;因此,我们有动力尽早创建此对象。
单个传感器桥设备通常有多个
DataChannel
实例;许多 API 会影响与特定传感器桥设备关联的所有数据通道实例。为了重置传感器桥设备(保证设备处于已知状态的唯一方法),我们将获取底层Hololink
实例的句柄。此板上的所有DataChannel
实例都将在此处返回相同的Hololink
实例。调用hololink.reset
将重置所有附加的数据通道实例。hololink = hololink_channel.hololink() hololink.reset()
std::shared_ptr<hololink::Hololink> hololink = hololink_channel.hololink(); hololink->reset();
在我们的示例应用程序中,我们需要初始化相机时钟并配置相机将传输的图像格式。请注意,在 IMX274 立体相机中,相同的时钟用于驱动两个相机设备,因此必须注意确保在另一个相机正在使用时不要初始化相机。
camera.setup_clock() camera_mode = imx274_mode.Imx274_Mode.IMX274_MODE_3840X2160_60FPS camera.configure(camera_mode)
camera.attr("setup_clock")(); py::object camera_mode = Imx274_Mode(0); camera.attr("configure")(camera_mode);
在我们的 IMX274 演示中,
camera.setup_clock
和camera.configure
调用传感器桥设备的 I2C 控制器对象来写入正确的设备寄存器集。现在我们准备好启动我们的应用程序管道了。
application.run()
application->run();
除非显式停止管道,否则对
application.run
的此调用永远不会返回。application.run
从调用每个操作器的start
方法开始。RoceReceiverOp
和LinuxReceiverOperator
的start
方法都将调用camera.start
(其中 camera 是传递到构造函数的device
参数中的设备对象)。相机在调用start
时,将被配置为开始发送视频数据。application.run
然后进入循环,执行管道,调用每个操作器的compute
方法。网络接收器操作器compute
方法会阻塞,直到将整个数据帧接收到它初始化的内存块中。
我们的相机对象通过在传感器桥设备中存在的 I2C 总线上读取和写入设备寄存器来工作。假设我们的相机连接到本地总线地址为 0x04000200 的传感器桥 I2C 控制器。(我们将该地址存储在常量 hololink_module.CAM_I2C_CTRL
中。)相机对象可以通过调用 hololink.get_i2c
来获取 Hololink.I2c
对象的句柄,该对象具有用于生成 I2C 事务的 API。如果相机本身响应 I2C 总线地址 0x34(我们将其称为 CAM_I2C_ADDRESS
),那么它可以支持 camera.set_register
方法,如下所示
class Imx274Cam:
def __init__(
self,
hololink_channel,
i2c_controller_address=hololink_module.CAM_I2C_CTRL,
...
):
self._hololink = hololink_channel.hololink()
self._i2c = self._hololink.get_i2c(i2c_controller_address)
...
def set_register(self, register, value):
...
self._i2c.i2c_transaction(
CAM_I2C_ADDRESS,
write_data,
read_byte_count,
)
def set_exposure(self, value):
self.set_register(..., value)
所有类型的传感器对象都可以通过这种方式得到支持:I2C、SPI 或传感器桥本地总线的接口都可以通过 Hololink
实例上存在的 API 进行控制。
DataChannel 枚举和 IP 地址配置
传感器桥设备中的每个数据平面实例每秒发送一次 UDP 枚举数据包;主机使用这些数据包来定位可访问的设备。Enumerator.find_channel
方法收集并解码这些消息,并使用它们生成作为 channel_metadata
传回的字典。Holoscan 传感器桥使用本地广播 MAC ID (FF:FF:FF:FF:FF:FF) 发送这些数据包。路由器不允许将这些消息转发到其他网络,因此只有本地连接的主机才会收到这些消息。您的主机必须连接到与传感器桥设备相同的网络才能进行通信。
Holoscan 传感器桥枚举消息基于 BOOTP 协议,并且像 BOOTP 一样,它们提供了一种重新配置 HSB 设备的 IP 地址的机制。如果主机希望重新配置设备的 IP 地址,它会发送一个回复消息,其中包含要分配给该数据平面控制器的新 IP 地址。传感器桥演示容器包含一个名为 hololink
的命令行工具,可用于为传感器桥设备分配新的 IP 地址
$ hololink set-ip b0:4f:13:e0:20:4c 192.168.100.250
您的 MAC-ID 和 IP 地址将有所不同;可以给出包含任意数量的 mac-id 和 ip-address 对的列表。默认情况下,这将启动一个永远运行的进程;收到任何 IP 地址不是配置值的枚举请求时,将发送回复,将配置的地址分配给该数据通道。当重置传感器桥设备时,以守护程序方式运行此程序非常重要
应用程序代码在新 IP 地址建立连接
应用程序执行
hololink.reset
设备重置并恢复为默认 IP 地址
应用程序代码看到带有默认 IP 地址的枚举——它忽略了它
当
hololink set-ip
看到枚举数据包的 IP 地址不是新 IP 地址时,它将发送一个回复,其中包含新的 IP 地址配置Holoscan 传感器桥更新其 IP 地址。现在将使用新地址发送枚举数据
应用程序代码随后在新 IP 地址看到枚举
应用程序重新连接并完成重置请求
枚举请求和回复数据包遵循 RFC951 中给出的规范,但例外情况是枚举请求由传感器桥设备在 UDP 端口 12267 上发送,回复由主机发送到 UDP 端口 12268。
Holoscan 传感器桥数据通道使用 RoCE v2 RDMA 写入和 RDMA 写入立即请求
ConnectX SmartNIC 固件支持处理经过身份验证的 RoCE v2 请求,而无需 CPU 干预。传感器桥设备通过生成 RDMA 写入和带有立即请求的 RDMA 写入请求来将数据平面内容发送到主机,从而利用了这一点。DataChannel 使用目标网络寻址、身份验证密钥以及单个数据包和整体数据帧大小来配置传感器桥设备。配置完成后,传感器桥设备将以 RDMA 写入请求发送接收到的传感器数据,有效负载大小由单个数据包大小值给出。这些请求在被 ConnectX 接收后,将直接写入 GPU 或系统内存——这些写入完全从 CPU 卸载。在接收到的总字节数达到数据帧大小后,将使用 RDMA 写入立即请求发送特殊的元数据数据包。此写入立即请求为 CPU 调度中断,该中断用于标记接收到的数据已准备好进行进一步处理——此中断是 RoceReceiverOp.compute
在调用 get_next_frame.
时等待的内容。
Holoscan SDK 元数据和 HSB
HSB 设备在每个接收到的数据帧之后发送一个元数据块。有关此数据的内容和用途的详细信息,请参阅 HSB 延迟的描述。