{ Yutong Zhu }

developer • student • robot
photo

SeeuSeeu

能够感知空间状态变化,理解主人意图的监控系统
能够感知空间状态变化,理解主人意图的监控系统

这是一个包含端侧运行视觉理解模型,并且能通过ARS/TTS和Agent技术与视障人士进行理解交互的AI应用项目。整个项目的检测流程核心方案和模型选型部署大概完成了,后续还应该优化数据库结构,ASR模块等。我准备通过4C模型来讲解整个项目的实现,包括Context、Container、Component、Code。

Comtext
Comtext

Seeu系统接收语音输入和监控视频流进行理解,结果输出给外部web系统或者语音系统循环更新当前空间感知信息,控制端侧设备,以及回复用户的问题。

Container
Container

感知服务作为系统核心服务控制端侧感知模型进行视觉理解和入库记录,将ASR结果转发给Agent,并提供Agent的工具调用接口。这里涉及很多通信,外部输入通过eufy官方SDK接入,感知服务收到图片输入后通过预编译好的.engine推理引擎执行感知流水线,直连PostgreSQL数据库写入时刻快照frames和空间物体objects两张表。ASR后的消息通过感知服务转发给Agent做理解,LLM answer输出通过SDK以及TTS处理后通过语音输出。

数据库架构 frames + objects

Table Schame
Table Schame

由于要记录时空状态,这涉及时间和空间两个维度,在时间轴上应该按照时间顺序记录每个检测时刻的空间快照,而物体信息应该包含检测时刻的信息,即该物体是在哪次检测中出现的。因此应该将frames作为objects的外键嵌入表中,当该时刻的快照被删除,对应时刻的物体也一并删除。

写入数据库
写入数据库

具体是怎么写入的呢。由于对于家庭监控场景来说同一时间只会运行一个场景流水线,在业务代码中用 with _connet() as conn: 来建立数据库连接并插入,with .. 能够保证事务一致性,完成后commit并清理数据库连接。

建立数据库连接
建立数据库连接

建立数据库连接是通过数据库的URL建立TCP连接,新建表或返回连接。

端侧部署优化 Pytorch -> ONNX -> TensorRT

端侧模型部署,最应该理解的技术是Pytorch -> ONNX -> TensorRT这条链路,模型当然可以通过Pytorch自带的框架计算,但是要逐层运行速度慢,占用显存多。ONNX提供了规范的节点类型,从而将不同推理框架转换成统一的计算图表示,具体而言ONNX中是:节点类型 + 输入输出名 + 属性(如 kernel size)+ 权重。 而TensorRT会从.onnx文件将其转换成.engine文件,将ONNX中的算子映射成tensorRT自己的Layer,将 ONNX 编译成、只能在特定 GPU + TensorRT 上加载运行的推理二进制;加载后就是 GPU 上可直接执行的那套优化过的网络。所以这是针对特定GPU和TensorRT编译过的,换了GPU换了TensorRT版本都可能出错。在运行的时候将.engine反序列化一下,然后pytorch将张量显存指针喂给tensorRT,GPU直接算,算完拷回CPU。

PyTorch → ONNX
PyTorch → ONNX

在代码中首先定义一个Wrapper包一下,这是因为torch.onnx.export要求必须是nn.Module,forward的参数就是输入张量,返回值就是输出张量。sample是一张样本输出图,所以这里的张量尺寸是固定的,也只有静态尺寸的张量用tensorRT优化的效果才更好。options是一个字典,确定输入输出名字,ONNX算子版本,和关闭新导出器使用tracing。然后就是跑一次病创建计算图,torch.no_grad() 关闭梯度但是还允许tracing,之后就能看到生成的ONNX图了。

ONNX -> TensorRT
ONNX -> TensorRT

这一步就只是调用NVIDIA的官方工具,总体来说就是tensorRT Runtime将ONNX编译成能在GPU上执行的二进制,针对这块GPU做了优化。 shutil.which("trtexec"):PATH 里找。找不到就用 Jetson 系统镜像的默认路径 /usr/src/tensorrt/bin/trtexec。电脑上没装 TensorRT 的话,这里会失败,这是正常的——引擎必须在目标 GPU 上编。

推理进程启动 Runtime load engine
推理进程启动 Runtime load engine

推理进程启动时不会再走ONNX,它只读.engine。

运行推理
运行推理

形状校验,数据搬到GPU,引擎转成需要的精度,确保内存连续;在 Pytorch CUDA stream上异步启动,必须使用同一个stream,等GPU算完后 .cpu() 拷回张量。

业务代码接到引擎上的时候根本不加载大模型,存在引擎的时候直接使用引擎进行推理,不存在引擎的时候还是用pytorch。

A monitoring system that can perceive changes in spatial state and understand its owner's intentions
A monitoring system that can perceive changes in spatial state and understand its owner's intentions

This is an AI application project that includes an on-device visual understanding model and can interact with visually impaired people through ASR/TTS and Agent technologies. The core detection pipeline solution and model selection/deployment for the project are roughly complete; going forward, the database structure, ASR module, and other parts should still be optimized. I plan to explain the implementation of the whole project through the 4C model, including Context, Container, Component, and Code.

Context
Context

The Seeu system receives voice input and surveillance video streams for understanding, and outputs the results to an external web system or voice system to continuously update the current spatial perception information, control on-device devices, and answer users' questions.

Container
Container

The perception service, as the system's core service, controls the on-device perception model to perform visual understanding and record storage, forwards ASR results to the Agent, and provides a tool-calling interface for the Agent. A lot of communication is involved here. External input is connected through the official eufy SDK. After the perception service receives image input, it executes the perception pipeline through a precompiled .engine inference engine and writes directly to the PostgreSQL database, inserting into the two tables frames and objects—moment snapshots and spatial objects, respectively. Messages after ASR are forwarded by the perception service to the Agent for understanding, and the LLM answer output is processed through the SDK and TTS and then output as voice.

Database Architecture: frames + objects

Table Schema
Table Schema

Since spatiotemporal states need to be recorded, this involves two dimensions, time and space. On the time axis, the spatial snapshot at each detection moment should be recorded in chronological order, and object information should include information about the detection moment—that is, in which detection the object appeared. Therefore, frames should be embedded in the objects table as a foreign key, so that when the snapshot for that moment is deleted, the objects for the corresponding moment are also deleted.

Writing to the database
Writing to the database

How exactly is it written? For a home surveillance scenario, only one scenario pipeline runs at a time, so the business code uses with _connet() as conn: to establish a database connection and insert. with ... guarantees transaction consistency; after completion, it commits and cleans up the database connection.

Establishing a database connection
Establishing a database connection

Establishing a database connection means creating a TCP connection using the database URL, creating tables, or returning the connection.

On-Device Deployment Optimization: PyTorch -> ONNX -> TensorRT

For on-device model deployment, the technology you should understand most is the PyTorch -> ONNX -> TensorRT chain. Of course, a model can be computed through PyTorch's built-in framework, but running it layer by layer is slow and consumes a lot of GPU memory. ONNX provides standardized node types, thereby converting different inference frameworks into a unified computational graph representation. Specifically, ONNX is: node type + input/output names + attributes (such as kernel size) + weights. TensorRT then converts the .onnx file into an .engine file, mapping ONNX operators to TensorRT's own layers and compiling ONNX into an inference binary that can only be loaded and run on a specific GPU + TensorRT; once loaded, it is the optimized network that can be directly executed on the GPU. So it is compiled for a specific GPU and TensorRT version; changing the GPU or TensorRT version may cause errors. At runtime, the .engine is deserialized, and then PyTorch feeds the tensor's GPU memory pointer to TensorRT; the GPU computes directly, and when done, the result is copied back to the CPU.

PyTorch → ONNX
PyTorch → ONNX

In the code, first define a Wrapper to wrap it. This is because torch.onnx.export requires it to be an nn.Module; the parameters of forward are the input tensors, and the return value is the output tensor. sample is a sample input image, so the tensor size here is fixed; only static-size tensors get better optimization from TensorRT. options is a dictionary that determines the input/output names, the ONNX operator version, and disables the new exporter to use tracing. Then run it once and create the computational graph. torch.no_grad() disables gradients but still allows tracing, after which you can see the generated ONNX graph.

ONNX → TensorRT
ONNX → TensorRT

This step just calls NVIDIA's official tool. Overall, TensorRT Runtime compiles ONNX into a binary that can execute on the GPU, optimized for this particular GPU. shutil.which("trtexec"): looks in PATH. If not found, use the default path from the Jetson system image, /usr/src/tensorrt/bin/trtexec. If TensorRT is not installed on the computer, this will fail, which is normal—the engine must be compiled on the target GPU.

Inference process starts: Runtime loads engine
Inference process starts: Runtime loads engine

When the inference process starts, it no longer goes through ONNX; it only reads the .engine.

Running inference
Running inference

Shape validation, moving data to the GPU, converting it to the precision required by the engine, and ensuring memory is contiguous; launch asynchronously on a PyTorch CUDA stream, and the same stream must be used. After the GPU finishes computation, .cpu() copies the tensor back.

When the business code connects to the engine, it does not load the large model at all. If the engine exists, it directly uses the engine for inference; if the engine does not exist, it still uses PyTorch.