2026年3月

AI Agent 发展太快了,我非常焦虑,很想参与到这个历史性的时刻,于是我们做了一个用来管理 side project 的 side project 。它叫 Chorus ,是一条永不下班的产品开发管线,Agent 负责产品调研、管理和开发,人类负责提 idea 和审批。项目开展不到一个月,目前只有 85 个 star ,但我们乐在其中。

我们公司一共 3 个人,我,我的合伙人,以及一个跑在 OpenClaw 上的 AI Agent 。我负责调教 OpenClaw 做产品管理和市场营销,合伙人和 Claude Code 负责开发。我们都有本职工作,Chorus 不到一周写完。

OpenClaw 的职位是产品经理。它通过两条通道连接我们的协作平台 Chorus:MCP 工具用来执行操作,SSE 事件流用来实时感知。当有人在 Chorus 网页上分配任务或者 @mention 这个 Agent ,它几秒内自动唤醒开始工作。不需要打开终端,不需要手动输入指令。它协调一组 Claude Code Agent 来交付功能,一周工作七天,让我们有时间喝杯咖啡,头脑风暴更多创意。

每天早上,OpenClaw 自动爬取 HN 、Reddit 、Product Hunt 和 GitHub Trending ,寻找竞品动态和用户在多 Agent 编程领域的痛点。等我打开 Slack ,已经有一份带链接和观点的摘要在等我了。白天我和合伙人在频道里讨论产品方向,Agent 在旁边听。当我们聊到值得做的东西时,它直接把零散的对话整理成 Chorus 上的一条结构化 Idea 。没人让它做,它自己判断的。

接着它认领这条 Idea ,结合现有的 PRD 和代码库进行分析,在 Chorus 上生成一份 Proposal:包含范围和约束的产品需求文档,加上带依赖关系和 Agent 小时估算的任务 DAG 。我像审 PR 一样审它。"范围太大,先砍掉 GitHub 集成。"Agent 修改,我批准。

双向连接在这里发挥了作用。我在 Chorus 网页上点击批准的瞬间,任务自动创建,OpenClaw 通过 SSE 实时收到通知,立刻领取新任务并分配给 Dev Agent 。从这里开始有两条路。

第一种是 Bring Your Own Agent 。团队成员把自己的编程 Agent 通过 MCP 连接到 Chorus ,认领任务,在平台上协作开发。Claude Code Agent Teams 和 Chorus 的配合特别好,因为 Chorus 的任务 DAG 天然映射到并行执行:独立任务同时扇出给多个子 Agent ,有依赖的任务等前置任务验证通过后才启动。Team Lead 读取 DAG 分配工作,子 Agent 并发执行互不干扰。这是我们现在每天在用的方式。

第二种是接入像 OpenCode server mode 这样的自主 Agent 运行时,让它们端到端地领取任务并自动执行,不需要人类盯着。这条路我们还在设计。自主程度到哪?验证环节怎么设?我们也不确定这方面的需求大不大,想听听大家的意见。

上周我们交付了 4 个功能。两个人加一群 AI Agent 。人类始终在网页和 Slack 上操作,Agent 从不需要手动指令。

这套体系能跑起来,靠三样东西的汇合。OpenClaw 让持久化 Agent 成为可能:它有长期记忆、工具调用、定时执行,还有插件系统支持后台服务。Chorus 插件维持一条持久的 SSE 连接,让 Agent 始终在监听平台事件,通过 /hooks/wake 在事件到达时立即触发行动。Claude Code Agent Teams 让编码工作可以扇出到多个并行 Agent 。Chorus 把一切串起来,作为从 Idea 到 Proposal 到 Task 到 PR 的协作平台,所有参与者共享同一个信息源。

交互模式是区别于聊天机器人的关键。在 Chorus 上,你 @mention Agent 就像 @mention 同事一样。"@PM-Agent ,这个任务好像范围不够,能加上错误处理吗?"Agent 通过 SSE 收到 mention ,自动唤醒,读取上下文,回复评论或更新 Proposal 。感觉像在给队友发消息,不像在编程一个工具。

最终形态是一条不下班的产品开发管线。AI 覆盖从市场调研到代码交付的全流程,人类只在两个关卡介入:批准方案和审查产出。反馈周期是几小时而不是几周,我们在 Proposal 阶段就能发现错误。Agent 从每次驳回中学习,因为 OpenClaw 让它的记忆跨 session 持久化。

上个月 AI 开销不算少,但 ROI 很明显。我们不是在替代工程师,我们是两个人干出十个人的活,还有时间认真想下一步该做什么。

如果你是独立创始人或者小团队,这套架构值得一试。OpenClaw 做持久化 Agent 运行时,Chorus 做协作平台,Claude Code 做执行层,全部开源。

小型创业公司的瓶颈从来不是写代码,而是搞清楚该做什么、怎么拆解、怎么保持对齐。这就是产品管理。给 AI 一个合适的平台和正确的工作流连接方式,它能干大部分。

───

OpenClaw:github.com/openclaw/openclaw
Chorus:github.com/Chorus-AIDLC/Chorus

1. 前言

当前端到端智能驾驶技术发展迅速,SparseDrive 作为代表性模型受行业关注。工程化落地时,其模型导出与性能评测环节存在普遍技术挑战,涉及架构与环境兼容性、算子适配等多维度。为推动端到端智驾技术社区化发展,本文梳理 SparseDrive 从 ONNX 导出到硬件部署的技术链路,剖析算子替换、编译报错修复、量化策略优化等案例,构建含环境配置、数据集处理、权重管理、配置工程化的全流程技术指南,为社区提供可复用的端到端模型工程化方案,加速智驾模型从研究到车规级部署转化。

代码库:https://github.com/swc-17/SparseDrive

2. 环境部署

解压公版代码包,然后创建 python 虚拟环境:

conda create -n sparsedrive python=3.8 -y
conda activate sparsedrive
pip3 install --upgrade pip
#whl包获取:
curl -O -u 'openexplorer:c5R,2!pG' ftp://vrftp.horizon.ai/misc_j5/torch/torch-1.13.0+cu116-cp38-cp38-linux_x86_64.whl
curl -O -u 'openexplorer:c5R,2!pG' ftp://vrftp.horizon.ai/misc_j5/torch/torchvision-0.14.0+cu116-cp38-cp38-linux_x86_64.whl
pip3 install torch-1.13.0+cu116-cp38-cp38-linux_x86_64.whl
pip3 install torchvision-0.14.0+cu116-cp38-cp38-linux_x86_64.whl 
pip3 install torchaudio==0.13.0
cd ~/SparseDrive-main

直接 pip3 install -r requirement.txt 会报错,这里打算逐个安装 whl 包。

2.1 升级 gcc(for 安装 mmcv-full)

步骤 1:安装新版 GCC/G++

使用 conda 安装,不会破坏系统自带的 GCC 4.8.5:

安装 GCC 10

conda install  -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main  -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge  gcc_linux-64=10 gxx_linux-64=10

安装完成后,你会在 Conda 环境里有新 GCC,例如:

$ which gcc

/home/users/yue01.chen/anaconda3/envs/sparsedrive/bin/x86\_64-conda-linux-gnu-gcc

步骤 2:指定编译器环境变量

为了确保 pip 编译 mmcv-full 时使用 Conda 的新版 GCC,而不是系统 4.8.5,需要设置环境变量:

export CC=$(which x86\_64-conda-linux-gnu-gcc) export CXX=$(which x86\_64-conda-linux-gnu-g++)

可以把这两行添加到 。bashrc 或 。zshrc 中,保证每次激活环境自动生效。

步骤 3:卸载旧的 mmcv/mmcv-full

pip uninstall mmcv mmcv-full -y

步骤 4:从源码编译 mmcv-full

使用 --no-binary 强制从源码编译:

pip install mmcv-full==1.7.1 --force-reinstall --no-cache-dir --no-binary mmcv-full

说明:

  • --no-binary mmcv-full 表示不使用预编译 wheel,直接编译 C++/CUDA 扩展。
  • --force-reinstall + --no-cache-dir 可以避免 pip 缓存的旧版本干扰。

步骤 5:验证安装

Python 中验证 mmcv-full GPU 扩展是否可用:

import mmcv from mmcv.ops import nms\_match print("mmcv-full GPU extensions are ready!")

  1. 如果报错 ModuleNotFoundError: No module named 'mmcv。\_ext',说明编译仍有问题,需要检查:
  • GCC 版本 ≥ 7
  • CUDA 环境变量 CUDA\_HOME 是否指向 /home/users/yue01.chen/cuda-11.8
  • nvcc 可用 (nvcc --version)

后续在运行中缺乏什么库就直接 pip3 install 即可。

3. 创建数据集与权重下载

3.1 生成 pkl

  1. 从官网下载 nuscenes 数据集,解压后把 expansion 文件夹放到 maps 下,
  2. 然后运行:
sh scripts/create_data.sh

代码运行完成会在 data/info 目录下生成:

├── data
│   ├── infos
│   │   ├── mini
│   │   ├── nuscenes_infos_train.pkl
│   │   └── nuscenes_infos_val.pkl

报错的时候把这个注释了:

报错的时候把这个注释了:

img

3.2 生成 kmeans.py

sh scripts/kmeans.sh

3.3 权重下载

https://github.com/swc-17/SparseDrive/releases/download/v1.0/sparsedrive\_stage1.pth https://github.com/swc-17/SparseDrive/releases/download/v1.0/sparsedrive\_stage2.pth

https://download.pytorch.org/models/resnet50-19c8e357.pth

下载完成后放在 ckpt 文件夹。

4. config 文件修改

#单卡单batch
total_batch_size = 1
num_gpus = 1
#使用pytorch实现的dfa
use_deformable_func = False  # mmdet3d_plugin/ops/setup.py needs to be executed
#导出的onnx不要with_motion_plan,因为跑验证集的时候发现这部分跑不通
task_config = dict(
    with_det=True,
    with_map=True,
    with_motion_plan=False,

另外,还有非常重要的一点,config 文件中的 MultiheadFlashAttention 都替换为普通的 MultiheadAttention。

5. 导出脚本和适配修改

导出思路:为了不大幅侵入源码,在导出脚本里重写了 forward,并增加环境变量进行控制

5.1 去除后处理

使用环境变量 my\_var=="export\_to\_onnx"进行控制:

img

5.2 重写 forward

在 tools 文件夹下构建 forward\_export.py,重写 sparsedrive、det\_head 和 map\_head 的 orward 函数,如下所示:

from typing import List, Optional, Tuple, Union
import warnings
​
import numpy as np
import torch
import torch.nn as nn
#为了适配输入的形式和时序输入,重写了"SparseDrive" 类的forward
def simple_test_onnx_wrapper(self, img, T_global, T_global_inv, timestamp, projection_mat, image_wh, ego_status,cached_anchor,cached_feature,mask,cached_confidence,cached_map_anchor,cached_map_feature,cached_map_confidence):
    data = {
        "img_metas": [{

5.3 self.instance\_bank.get\_for\_export\_det\_onnx()函数

路径:SR/12yuanrong/SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py

def get_for_export_det_onnx(self, batch_size, metas=None, dn_metas=None):
        instance_feature = self.instance_feature.unsqueeze(0).repeat(batch_size, 1, 1)
        anchor = self.anchor.unsqueeze(0).repeat(batch_size, 1, 1)  
        #从上一帧的时序输出中获取输入 
        cached_anchor = metas["cached_anchor"]
        cached_feature = metas["cached_feature"]
        self.mask = metas["mask"]
        self.confidence = metas["cached_confidence"]
        time_interval=metas["img_metas"][0]["timestamp"]

5.4 self.instance\_bank.get\_for\_export\_map\_onnx()函数

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py

def get_for_export_map_onnx(self, batch_size, metas=None, dn_metas=None):
        instance_feature = self.instance_feature.unsqueeze(0).repeat(batch_size, 1, 1)
        anchor = self.anchor.unsqueeze(0).repeat(batch_size, 1, 1)   
        cached_anchor = metas["cached_map_anchor"]
        cached_feature = metas["cached_map_feature"]
        self.mask = metas["mask"]
        self.confidence = metas["cached_map_confidence"]
        time_interval=metas["img_metas"][0]["timestamp"]
​
        return (

5.5 修改导出会报错的代码

报错 1

img

将 instance\_inds 修改为 np.int32 类型。

报错 2

报错:

traceback : Traceback (most recent call last):
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/distributed/elastic/multiprocessing/errors/__init__.py", line 346, in wrapper
      return f(*args, **kwargs)
    File "./tools/export_onnx.py", line 314, in main
      torch.onnx.export(
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 504, in export
      _export(
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1529, in _export
      graph, params_dict, torch_out = _model_to_graph(
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1115, in _model_to_graph

报错原因:

PyTorch aten::tile 运算符在 ONNX opset 17 中没有对应的实现,所以导出失败。

解决办法

把 torch.tile 替换成等价的 repeat

在 PyTorch 里,torch.tile 其实就是 repeat 的一个封装,功能等价。 而 repeat ONNX 里是受支持的(映射到 Repeat 节点)

解决办法:

把 self.instance\_bank.get\_for\_export\_det\_onnx()和 self.instance\_bank.get\_for\_export\_map\_onnx()函数中的

instance_feature = torch.tile(
            self.instance_feature[None], (batch_size, 1, 1)
        )
anchor = torch.tile(self.anchor[None], (batch_size, 1, 1))

修改成 repeat 实现,如下:

instance_feature = self.instance_feature.unsqueeze(0).repeat(batch_size, 1, 1)
anchor = self.anchor.unsqueeze(0).repeat(batch_size, 1, 1)

报错 3(重要)

报错截图:

File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 504, in export     _export(   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1529, in _export     graph, params_dict, torch_out = _model_to_graph(   File "/home/users/naconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1115, in _model_to_graph     graph = _optimize_graph(   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 663, in _optimize_graph     graph = _C._jit_pass_onnx(graph, operator_export_type)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1867, in _run_symbolic_function     return symbolic_fn(graph_context, *inputs, **attrs)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_opset9.py", line 6664, in onnx_placeholder     return torch._C._jit_onnx_convert_pattern_from_subblock(block, node, env)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1867, in _run_symbolic_function     return symbolic_fn(graph_context, *inputs, **attrs)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_opset11.py", line 230, in index_put     if symbolic_helper._is_bool(indices_list[idx_]):   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_helper.py", line 736, in _is_bool     return _is_in_type_group(value, {_type_utils.JitScalarType.BOOL})   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_helper.py", line 708, in _is_in_type_group     scalar_type = value.type().scalarType() RuntimeError: r INTERNAL ASSERT FAILED at "../aten/src/ATen/core/jit_type_base.h":547, please report a bug to PyTorch.

报错原因:

ONNX 导出失败的根因是图里某处会把一个标量常量以没有 dtype(即 None) 的形式传给了 ONNX 导出器,导致 torch.onnx。\_type\_utils.JitScalarType.from\_name 收到 None 并抛出 ValueError: Scalar type name cannot be None。这类情况常在用高级索引/原地赋值(tensor[index] = other、index\_put、masked\_scatter 等)时出现,导出器有时会把标量常量漏掉 dtype。

优先级修复建议(按顺序尝试)

  1. 定位问题代码:查找模型中类似 x[:, idx] = y、x[index] = y、index\_put、masked\_scatter、masked\_fill 的用法。也可在 torch.onnx.export(..., verbose=True) 打印的导出图里查找 aten::index\_put、index\_put、prim::ListConstruct 等节点位置。
  2. 把原地/索引赋值改写为 ONNX 友好的算子:常用替代方法:

    1. 用 scatter:
    2. x = x.clone() x = x.scatter(dim, indices.unsqueeze(-1).expand(...), y)
    3. 用布尔 mask + torch.where:
    4. mask = torch.zeros\_like(x, dtype=torch.bool) mask[:, indices] = True x = torch.where(mask, y\_broadcasted, x)
  3. 这两种通常能被 ONNX 导出器更好地支持。
  4. 确保传入 torch.onnx.export 的示例输入都有明确 dtype(不要传 None 或 Python 原始标量),例如 tensor.float(). cuda()、indices.long().cuda()。
  5. 尝试不同的 opset 或更新 PyTorch:有些导出器 bug 在较新 opset 或 PyTorch 版本里被修复。可试 opset\_version=12、14 等;若可行,升级 PyTorch 往往能解决这类问题。
  6. 临时回退方案:如果短时间无法改模型,可使用 ATen fallback(operator\_export\_type=OperatorExportTypes.ONNX\_ATEN\_FALLBACK)导出,得到包含 ATen 节点的 ONNX(不适合生产但便于调试)。
  7. 不要修改 site-packages(除非非常了解风险):虽然可以在 \_type\_utils.from\_name 做防守性修改防止报错,但这不是长期或推荐的做法。

通过二分法定位到是 refine 模块的报错(即在 refine 模块前 return 导出 onnx 不报错,经过 refine 层以后 return 会报错),然后逐渐定位到其中的这个部分触发了上述 1 中的错误,如下:

output[..., self.refine_state] = (
#     output[..., self.refine_state] + anchor[..., self.refine_state]
# )
# if self.normalize_yaw:
#     output[..., [SIN_YAW, COS_YAW]] = torch.nn.functional.normalize(
#         output[..., [SIN_YAW, COS_YAW]], dim=-1
#     )
# if self.output_dim > 8:
#     if not isinstance(time_interval, torch.Tensor):
#         time_interval = instance_feature.new_tensor(time_interval)

修改后的代码:

@PLUGIN_LAYERS.register_module()
class SparseBox3DRefinementModule(BaseModule):
    def __init__(
        self,
        embed_dims=256,
        output_dim=11,
        num_cls=10,
        normalize_yaw=False,
        refine_yaw=False,
        with_cls_branch=True,

5.6 scatternd 消除

由于征程 6 工具链目前只支持 CPU 实现的 scatternd,所以在导出 onnx 的时候把这部分替换成 slice+concat 的实现。

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/detection3d/detection3d\_blocks.py

def forward(
        self,
        anchor,
        instance_feature=None,
        T_cur2temp_list=None,
        cur_timestamp=None,
        temp_timestamps=None,
    ):
        bs, num_anchor = anchor.shape[:2]
        size = anchor[..., None, [W, L, H]].exp()

5.7 导出代码

导出脚本 export\_onnx.py 基于 SparseDrive-main/tools/test.py 进行编写,其具体实现如下:

# Copyright (c) OpenMMLab. All rights reserved.
import argparse
import mmcv
import os
from os import path as osp
​
import torch
import warnings
from mmcv import Config, DictAction
from mmcv.cnn import fuse_conv_bn

另外,需要对 tools/dist\_test.sh 进行修改如下;

img

导出脚本运行:

bash scripts/test.sh

5.9 cache 过程的 scatternd 和 Cast 算子消除(如果模型中存在 cache 过程的话)

如果想要在模型中增加输出 cache 的功能,即在 forward\_export.py 的函数中增加以下代码:

img

但是公版的 self.instance\_bank.cache()函数的写法会引入工具链只能在 CPU 上支持的 ScatterND 算子和 Cast 算子,所以这里需要对代码做两处适配。

5.9.1 消除 scatternd 算子:

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py 中的 cache 函数:

if self.confidence is not None:
    # confidence[:, : self.num_temp_instances] = torch.maximum(
    #     self.confidence[0] * self.confidence_decay,
    #     confidence[:, : self.num_temp_instances],
    # )
    left = torch.maximum(
        self.confidence[0] * self.confidence_decay,
        confidence[:, :self.num_temp_instances],)
    right = confidence[:, self.num_temp_instances:]
    confidence = torch.cat([left, right], dim=1)

5.9.2 消除 cast 算子:

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py 中的 topk 函数:

def topk(confidence, k, *inputs):
    # bs, N = confidence.shape[:2]
    # confidence, indices = torch.topk(confidence, k, dim=1)
    # indices = (
    #     indices + torch.arange(bs, device=indices.device)[:, None] * N
    # ).reshape(-1)
    # outputs = []
    # for input in inputs:
    #     outputs.append(input.flatten(end_dim=1)[indices].reshape(bs, k, -1))
    bs, N = confidence.shape[:2]

6. 性能评测

6.1 算子支持情况

  1. nash-p 下可以编译成功
  2. 修改模型后,所有算子支持 BPU 实现
b30.binary_eltwise  : 2071
 b30.conv2d          : 503
 b30.gather2d        : 10
 b30.lut             : 314
 b30.pool2d          : 1
 b30.reduce          : 528
 b30.resize2d        : 3
 b30.warp            : 48
 b30vpu.dequantize   : 9
 b30vpu.quantize     : 8

6.2 静态 per 性能分析

6.2.1 确定性能瓶颈

获取到 perf.html 和 perf.json 后,使用【新版 perf 文件解读与性能分析】附录中的脚本对性能进行分析,输入为 perf.json,输出如下所示:

按照算子类型统计的耗时:

img

耗时排名 TOP20 的算子:

img

根据以上信息,可以得出优化目标:

  1. Mul 和 ReduceSum 算子的耗时最久,而且 mul 算子 ddr 耗时超过计算耗时的 65%,引发了带宽问题;
  2. ToP12 耗时的算子就是 Mul 和 ReduceSum,所以重点是优化 Mul 和 ReduceSum 算子。

6.2.2 性能优化策略

查看模型结构发现,模型中耗时的 Mul 和 ReduceSum 都处于这样的子结构中,所以我们主要是对这个结构进行性能优化。

img

此结构主要由 Mul、ReduceSum 和数据搬运算子组成,一方面 MulReduceSum 是运行在专门做向量计算的 VAE,加速效果不如张量,另一方面输入的 shape 非常大,也就解释了为何会引发带宽问题。、

所以这里考虑将 Mul+ReduceSum 计算替换为等价的 Mamtmul,从而使得这部分计算在 VAE 上加速。

性能优化效果验证

这里主要有以下步骤:

  1. 替换为 Matmul 计算:根据上述子图结构将其替换为 Matmul 计算,并导出 optimized.onnx;
  2. 替换等价性验证:在原始 onnx 中提取上述子图,和 optimized.onnx 进行输出一致性验证;
  3. 性能评测:同时对原始 onnx 子图和 optimized.onnx 进行 fast-perf,验证性能收益。

上述步骤可以参考:https://developer.horizon.auto/blog/13065

1. 前言

当前端到端智能驾驶技术发展迅速,SparseDrive 作为代表性模型受行业关注。工程化落地时,其模型导出与性能评测环节存在普遍技术挑战,涉及架构与环境兼容性、算子适配等多维度。为推动端到端智驾技术社区化发展,本文梳理 SparseDrive 从 ONNX 导出到硬件部署的技术链路,剖析算子替换、编译报错修复、量化策略优化等案例,构建含环境配置、数据集处理、权重管理、配置工程化的全流程技术指南,为社区提供可复用的端到端模型工程化方案,加速智驾模型从研究到车规级部署转化。

代码库:https://github.com/swc-17/SparseDrive

2. 环境部署

解压公版代码包,然后创建 python 虚拟环境:

conda create -n sparsedrive python=3.8 -y
conda activate sparsedrive
pip3 install --upgrade pip
#whl包获取:
curl -O -u 'openexplorer:c5R,2!pG' ftp://vrftp.horizon.ai/misc_j5/torch/torch-1.13.0+cu116-cp38-cp38-linux_x86_64.whl
curl -O -u 'openexplorer:c5R,2!pG' ftp://vrftp.horizon.ai/misc_j5/torch/torchvision-0.14.0+cu116-cp38-cp38-linux_x86_64.whl
pip3 install torch-1.13.0+cu116-cp38-cp38-linux_x86_64.whl
pip3 install torchvision-0.14.0+cu116-cp38-cp38-linux_x86_64.whl 
pip3 install torchaudio==0.13.0
cd ~/SparseDrive-main

直接 pip3 install -r requirement.txt 会报错,这里打算逐个安装 whl 包。

2.1 升级 gcc(for 安装 mmcv-full)

步骤 1:安装新版 GCC/G++

使用 conda 安装,不会破坏系统自带的 GCC 4.8.5:

安装 GCC 10

conda install  -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main  -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge  gcc_linux-64=10 gxx_linux-64=10

安装完成后,你会在 Conda 环境里有新 GCC,例如:

$ which gcc

/home/users/yue01.chen/anaconda3/envs/sparsedrive/bin/x86\_64-conda-linux-gnu-gcc

步骤 2:指定编译器环境变量

为了确保 pip 编译 mmcv-full 时使用 Conda 的新版 GCC,而不是系统 4.8.5,需要设置环境变量:

export CC=$(which x86\_64-conda-linux-gnu-gcc) export CXX=$(which x86\_64-conda-linux-gnu-g++)

可以把这两行添加到 。bashrc 或 。zshrc 中,保证每次激活环境自动生效。

步骤 3:卸载旧的 mmcv/mmcv-full

pip uninstall mmcv mmcv-full -y

步骤 4:从源码编译 mmcv-full

使用 --no-binary 强制从源码编译:

pip install mmcv-full==1.7.1 --force-reinstall --no-cache-dir --no-binary mmcv-full

说明:

  • --no-binary mmcv-full 表示不使用预编译 wheel,直接编译 C++/CUDA 扩展。
  • --force-reinstall + --no-cache-dir 可以避免 pip 缓存的旧版本干扰。

步骤 5:验证安装

Python 中验证 mmcv-full GPU 扩展是否可用:

import mmcv from mmcv.ops import nms\_match print("mmcv-full GPU extensions are ready!")

  1. 如果报错 ModuleNotFoundError: No module named 'mmcv。\_ext',说明编译仍有问题,需要检查:
  • GCC 版本 ≥ 7
  • CUDA 环境变量 CUDA\_HOME 是否指向 /home/users/yue01.chen/cuda-11.8
  • nvcc 可用 (nvcc --version)

后续在运行中缺乏什么库就直接 pip3 install 即可。

3. 创建数据集与权重下载

3.1 生成 pkl

  1. 从官网下载 nuscenes 数据集,解压后把 expansion 文件夹放到 maps 下,
  2. 然后运行:
sh scripts/create_data.sh

代码运行完成会在 data/info 目录下生成:

├── data
│   ├── infos
│   │   ├── mini
│   │   ├── nuscenes_infos_train.pkl
│   │   └── nuscenes_infos_val.pkl

报错的时候把这个注释了:

报错的时候把这个注释了:

img

3.2 生成 kmeans.py

sh scripts/kmeans.sh

3.3 权重下载

https://github.com/swc-17/SparseDrive/releases/download/v1.0/sparsedrive\_stage1.pth https://github.com/swc-17/SparseDrive/releases/download/v1.0/sparsedrive\_stage2.pth

https://download.pytorch.org/models/resnet50-19c8e357.pth

下载完成后放在 ckpt 文件夹。

4. config 文件修改

#单卡单batch
total_batch_size = 1
num_gpus = 1
#使用pytorch实现的dfa
use_deformable_func = False  # mmdet3d_plugin/ops/setup.py needs to be executed
#导出的onnx不要with_motion_plan,因为跑验证集的时候发现这部分跑不通
task_config = dict(
    with_det=True,
    with_map=True,
    with_motion_plan=False,

另外,还有非常重要的一点,config 文件中的 MultiheadFlashAttention 都替换为普通的 MultiheadAttention。

5. 导出脚本和适配修改

导出思路:为了不大幅侵入源码,在导出脚本里重写了 forward,并增加环境变量进行控制

5.1 去除后处理

使用环境变量 my\_var=="export\_to\_onnx"进行控制:

img

5.2 重写 forward

在 tools 文件夹下构建 forward\_export.py,重写 sparsedrive、det\_head 和 map\_head 的 orward 函数,如下所示:

from typing import List, Optional, Tuple, Union
import warnings
​
import numpy as np
import torch
import torch.nn as nn
#为了适配输入的形式和时序输入,重写了"SparseDrive" 类的forward
def simple_test_onnx_wrapper(self, img, T_global, T_global_inv, timestamp, projection_mat, image_wh, ego_status,cached_anchor,cached_feature,mask,cached_confidence,cached_map_anchor,cached_map_feature,cached_map_confidence):
    data = {
        "img_metas": [{

5.3 self.instance\_bank.get\_for\_export\_det\_onnx()函数

路径:SR/12yuanrong/SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py

def get_for_export_det_onnx(self, batch_size, metas=None, dn_metas=None):
        instance_feature = self.instance_feature.unsqueeze(0).repeat(batch_size, 1, 1)
        anchor = self.anchor.unsqueeze(0).repeat(batch_size, 1, 1)  
        #从上一帧的时序输出中获取输入 
        cached_anchor = metas["cached_anchor"]
        cached_feature = metas["cached_feature"]
        self.mask = metas["mask"]
        self.confidence = metas["cached_confidence"]
        time_interval=metas["img_metas"][0]["timestamp"]

5.4 self.instance\_bank.get\_for\_export\_map\_onnx()函数

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py

def get_for_export_map_onnx(self, batch_size, metas=None, dn_metas=None):
        instance_feature = self.instance_feature.unsqueeze(0).repeat(batch_size, 1, 1)
        anchor = self.anchor.unsqueeze(0).repeat(batch_size, 1, 1)   
        cached_anchor = metas["cached_map_anchor"]
        cached_feature = metas["cached_map_feature"]
        self.mask = metas["mask"]
        self.confidence = metas["cached_map_confidence"]
        time_interval=metas["img_metas"][0]["timestamp"]
​
        return (

5.5 修改导出会报错的代码

报错 1

img

将 instance\_inds 修改为 np.int32 类型。

报错 2

报错:

traceback : Traceback (most recent call last):
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/distributed/elastic/multiprocessing/errors/__init__.py", line 346, in wrapper
      return f(*args, **kwargs)
    File "./tools/export_onnx.py", line 314, in main
      torch.onnx.export(
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 504, in export
      _export(
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1529, in _export
      graph, params_dict, torch_out = _model_to_graph(
    File "/home/users/yue01.chen/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1115, in _model_to_graph

报错原因:

PyTorch aten::tile 运算符在 ONNX opset 17 中没有对应的实现,所以导出失败。

解决办法

把 torch.tile 替换成等价的 repeat

在 PyTorch 里,torch.tile 其实就是 repeat 的一个封装,功能等价。 而 repeat ONNX 里是受支持的(映射到 Repeat 节点)

解决办法:

把 self.instance\_bank.get\_for\_export\_det\_onnx()和 self.instance\_bank.get\_for\_export\_map\_onnx()函数中的

instance_feature = torch.tile(
            self.instance_feature[None], (batch_size, 1, 1)
        )
anchor = torch.tile(self.anchor[None], (batch_size, 1, 1))

修改成 repeat 实现,如下:

instance_feature = self.instance_feature.unsqueeze(0).repeat(batch_size, 1, 1)
anchor = self.anchor.unsqueeze(0).repeat(batch_size, 1, 1)

报错 3(重要)

报错截图:

File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 504, in export     _export(   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1529, in _export     graph, params_dict, torch_out = _model_to_graph(   File "/home/users/naconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1115, in _model_to_graph     graph = _optimize_graph(   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 663, in _optimize_graph     graph = _C._jit_pass_onnx(graph, operator_export_type)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1867, in _run_symbolic_function     return symbolic_fn(graph_context, *inputs, **attrs)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_opset9.py", line 6664, in onnx_placeholder     return torch._C._jit_onnx_convert_pattern_from_subblock(block, node, env)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/utils.py", line 1867, in _run_symbolic_function     return symbolic_fn(graph_context, *inputs, **attrs)   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_opset11.py", line 230, in index_put     if symbolic_helper._is_bool(indices_list[idx_]):   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_helper.py", line 736, in _is_bool     return _is_in_type_group(value, {_type_utils.JitScalarType.BOOL})   File "/home/users/anaconda3/envs/sparsedrive/lib/python3.8/site-packages/torch/onnx/symbolic_helper.py", line 708, in _is_in_type_group     scalar_type = value.type().scalarType() RuntimeError: r INTERNAL ASSERT FAILED at "../aten/src/ATen/core/jit_type_base.h":547, please report a bug to PyTorch.

报错原因:

ONNX 导出失败的根因是图里某处会把一个标量常量以没有 dtype(即 None) 的形式传给了 ONNX 导出器,导致 torch.onnx。\_type\_utils.JitScalarType.from\_name 收到 None 并抛出 ValueError: Scalar type name cannot be None。这类情况常在用高级索引/原地赋值(tensor[index] = other、index\_put、masked\_scatter 等)时出现,导出器有时会把标量常量漏掉 dtype。

优先级修复建议(按顺序尝试)

  1. 定位问题代码:查找模型中类似 x[:, idx] = y、x[index] = y、index\_put、masked\_scatter、masked\_fill 的用法。也可在 torch.onnx.export(..., verbose=True) 打印的导出图里查找 aten::index\_put、index\_put、prim::ListConstruct 等节点位置。
  2. 把原地/索引赋值改写为 ONNX 友好的算子:常用替代方法:

    1. 用 scatter:
    2. x = x.clone() x = x.scatter(dim, indices.unsqueeze(-1).expand(...), y)
    3. 用布尔 mask + torch.where:
    4. mask = torch.zeros\_like(x, dtype=torch.bool) mask[:, indices] = True x = torch.where(mask, y\_broadcasted, x)
  3. 这两种通常能被 ONNX 导出器更好地支持。
  4. 确保传入 torch.onnx.export 的示例输入都有明确 dtype(不要传 None 或 Python 原始标量),例如 tensor.float(). cuda()、indices.long().cuda()。
  5. 尝试不同的 opset 或更新 PyTorch:有些导出器 bug 在较新 opset 或 PyTorch 版本里被修复。可试 opset\_version=12、14 等;若可行,升级 PyTorch 往往能解决这类问题。
  6. 临时回退方案:如果短时间无法改模型,可使用 ATen fallback(operator\_export\_type=OperatorExportTypes.ONNX\_ATEN\_FALLBACK)导出,得到包含 ATen 节点的 ONNX(不适合生产但便于调试)。
  7. 不要修改 site-packages(除非非常了解风险):虽然可以在 \_type\_utils.from\_name 做防守性修改防止报错,但这不是长期或推荐的做法。

通过二分法定位到是 refine 模块的报错(即在 refine 模块前 return 导出 onnx 不报错,经过 refine 层以后 return 会报错),然后逐渐定位到其中的这个部分触发了上述 1 中的错误,如下:

output[..., self.refine_state] = (
#     output[..., self.refine_state] + anchor[..., self.refine_state]
# )
# if self.normalize_yaw:
#     output[..., [SIN_YAW, COS_YAW]] = torch.nn.functional.normalize(
#         output[..., [SIN_YAW, COS_YAW]], dim=-1
#     )
# if self.output_dim > 8:
#     if not isinstance(time_interval, torch.Tensor):
#         time_interval = instance_feature.new_tensor(time_interval)

修改后的代码:

@PLUGIN_LAYERS.register_module()
class SparseBox3DRefinementModule(BaseModule):
    def __init__(
        self,
        embed_dims=256,
        output_dim=11,
        num_cls=10,
        normalize_yaw=False,
        refine_yaw=False,
        with_cls_branch=True,

5.6 scatternd 消除

由于征程 6 工具链目前只支持 CPU 实现的 scatternd,所以在导出 onnx 的时候把这部分替换成 slice+concat 的实现。

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/detection3d/detection3d\_blocks.py

def forward(
        self,
        anchor,
        instance_feature=None,
        T_cur2temp_list=None,
        cur_timestamp=None,
        temp_timestamps=None,
    ):
        bs, num_anchor = anchor.shape[:2]
        size = anchor[..., None, [W, L, H]].exp()

5.7 导出代码

导出脚本 export\_onnx.py 基于 SparseDrive-main/tools/test.py 进行编写,其具体实现如下:

# Copyright (c) OpenMMLab. All rights reserved.
import argparse
import mmcv
import os
from os import path as osp
​
import torch
import warnings
from mmcv import Config, DictAction
from mmcv.cnn import fuse_conv_bn

另外,需要对 tools/dist\_test.sh 进行修改如下;

img

导出脚本运行:

bash scripts/test.sh

5.9 cache 过程的 scatternd 和 Cast 算子消除(如果模型中存在 cache 过程的话)

如果想要在模型中增加输出 cache 的功能,即在 forward\_export.py 的函数中增加以下代码:

img

但是公版的 self.instance\_bank.cache()函数的写法会引入工具链只能在 CPU 上支持的 ScatterND 算子和 Cast 算子,所以这里需要对代码做两处适配。

5.9.1 消除 scatternd 算子:

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py 中的 cache 函数:

if self.confidence is not None:
    # confidence[:, : self.num_temp_instances] = torch.maximum(
    #     self.confidence[0] * self.confidence_decay,
    #     confidence[:, : self.num_temp_instances],
    # )
    left = torch.maximum(
        self.confidence[0] * self.confidence_decay,
        confidence[:, :self.num_temp_instances],)
    right = confidence[:, self.num_temp_instances:]
    confidence = torch.cat([left, right], dim=1)

5.9.2 消除 cast 算子:

路径:SparseDrive-main/projects/mmdet3d\_plugin/models/instance\_bank.py 中的 topk 函数:

def topk(confidence, k, *inputs):
    # bs, N = confidence.shape[:2]
    # confidence, indices = torch.topk(confidence, k, dim=1)
    # indices = (
    #     indices + torch.arange(bs, device=indices.device)[:, None] * N
    # ).reshape(-1)
    # outputs = []
    # for input in inputs:
    #     outputs.append(input.flatten(end_dim=1)[indices].reshape(bs, k, -1))
    bs, N = confidence.shape[:2]

6. 性能评测

6.1 算子支持情况

  1. nash-p 下可以编译成功
  2. 修改模型后,所有算子支持 BPU 实现
b30.binary_eltwise  : 2071
 b30.conv2d          : 503
 b30.gather2d        : 10
 b30.lut             : 314
 b30.pool2d          : 1
 b30.reduce          : 528
 b30.resize2d        : 3
 b30.warp            : 48
 b30vpu.dequantize   : 9
 b30vpu.quantize     : 8

6.2 静态 per 性能分析

6.2.1 确定性能瓶颈

获取到 perf.html 和 perf.json 后,使用【新版 perf 文件解读与性能分析】附录中的脚本对性能进行分析,输入为 perf.json,输出如下所示:

按照算子类型统计的耗时:

img

耗时排名 TOP20 的算子:

img

根据以上信息,可以得出优化目标:

  1. Mul 和 ReduceSum 算子的耗时最久,而且 mul 算子 ddr 耗时超过计算耗时的 65%,引发了带宽问题;
  2. ToP12 耗时的算子就是 Mul 和 ReduceSum,所以重点是优化 Mul 和 ReduceSum 算子。

6.2.2 性能优化策略

查看模型结构发现,模型中耗时的 Mul 和 ReduceSum 都处于这样的子结构中,所以我们主要是对这个结构进行性能优化。

img

此结构主要由 Mul、ReduceSum 和数据搬运算子组成,一方面 MulReduceSum 是运行在专门做向量计算的 VAE,加速效果不如张量,另一方面输入的 shape 非常大,也就解释了为何会引发带宽问题。、

所以这里考虑将 Mul+ReduceSum 计算替换为等价的 Mamtmul,从而使得这部分计算在 VAE 上加速。

性能优化效果验证

这里主要有以下步骤:

  1. 替换为 Matmul 计算:根据上述子图结构将其替换为 Matmul 计算,并导出 optimized.onnx;
  2. 替换等价性验证:在原始 onnx 中提取上述子图,和 optimized.onnx 进行输出一致性验证;
  3. 性能评测:同时对原始 onnx 子图和 optimized.onnx 进行 fast-perf,验证性能收益。

上述步骤可以参考:https://developer.horizon.auto/blog/13065

这几天遇到一件比较糟心的事情, 想请各位大佬分析一下

是这样的, 因为本身是 IT 行业, 加上喜欢折腾一些软硬件, 所以家里有搭建一套完整的 HomeLab, Server 方面有几个 ProxmoxVE 节点组成的集群, 因为每个月电费/网费的成本确实比较高, 再加上 PVE Node 确实有不少闲置资源, 在几年前有开始挂小黄鱼上对外出租一部分网路/计算资源, 用来 cover 一部分 infra 的开销, 也没刻意推广过, 属于有一单做一单, 几年下来每月大概能有十几个用户这样子

在上个月 20 号的时候, 有一个新的用户购买了一台虚拟机资源, 从购买到交付一切正常, 使用了 4 天的时间, 期间每天都有流量进出, 24 号当天下午, 突然疯狂给我发消息, 大概意思就是服务器连不上, 要退款, 我检查了网路没有问题, server 也正常再跑, 虚拟机状态也正常, 也没有其他人反应出现问题, 当时没多想询问是否可以重启, 得到确定的答复后给他重启了一下 vm, 那天后面他也没再找我, 结果后面三四天, 每天都来找我, 各种各样的问题, 包括但不限于 RDP 远程桌面连不上, 网路不通连不上...

后面我让他测试 ping 到我这边的 domian, 看到 dns 能解析到正确的 ip, 但是 icmp 不可达, 我又让他测试 ping 到我的上级网关, 这个点就已经是中国联通的 ISP 了, 他还是不通, 最后我索性就让他 ping 测试到 221.6.4.66, 这个地址是江苏联通在南京的省级 DNS 伺服器, 绝对不可能不通的, 结果他截图还是不通, 这时候我就开始怀疑这家伙在骗我





后面我用 ITDog/BOCE/IPIP.net/ping.pe 这些测试站测试了到我这边连接全部正常的截图发给他, 表示这是他自己的问题, 我这边网路一切正常, 结果这人下面的操作给我看傻了, 他测出来的结果全是红的, 我到现在都不知道他是如何做到的, 但我可以肯定他一定在撒谎, 除非我俩在平行宇宙




这是我的测试图



这是他提供的测试图




按我的理解, 他肯定不可能控制对方的测试服务器让他们到我不通, 只有可能在前端做手脚, 但看对方的截屏感觉是手机/平板的截屏, 能用 F12 这种操作改前端页面吗? 真的想不明白他到底怎么做到的, 特意请教一下各位大佬们, 我实在是想不到还有什么阴招了

这几天遇到一件比较糟心的事情, 想请各位大佬分析一下

是这样的, 因为本身是 IT 行业, 加上喜欢折腾一些软硬件, 所以家里有搭建一套完整的 HomeLab, Server 方面有几个 ProxmoxVE 节点组成的集群, 因为每个月电费/网费的成本确实比较高, 再加上 PVE Node 确实有不少闲置资源, 在几年前有开始挂小黄鱼上对外出租一部分网路/计算资源, 用来 cover 一部分 infra 的开销, 也没刻意推广过, 属于有一单做一单, 几年下来每月大概能有十几个用户这样子

在上个月 20 号的时候, 有一个新的用户购买了一台虚拟机资源, 从购买到交付一切正常, 使用了 4 天的时间, 期间每天都有流量进出, 24 号当天下午, 突然疯狂给我发消息, 大概意思就是服务器连不上, 要退款, 我检查了网路没有问题, server 也正常再跑, 虚拟机状态也正常, 也没有其他人反应出现问题, 当时没多想询问是否可以重启, 得到确定的答复后给他重启了一下 vm, 那天后面他也没再找我, 结果后面三四天, 每天都来找我, 各种各样的问题, 包括但不限于 RDP 远程桌面连不上, 网路不通连不上...

后面我让他测试 ping 到我这边的 domian, 看到 dns 能解析到正确的 ip, 但是 icmp 不可达, 我又让他测试 ping 到我的上级网关, 这个点就已经是中国联通的 ISP 了, 他还是不通, 最后我索性就让他 ping 测试到 221.6.4.66, 这个地址是江苏联通在南京的省级 DNS 伺服器, 绝对不可能不通的, 结果他截图还是不通, 这时候我就开始怀疑这家伙在骗我





后面我用 ITDog/BOCE/IPIP.net/ping.pe 这些测试站测试了到我这边连接全部正常的截图发给他, 表示这是他自己的问题, 我这边网路一切正常, 结果这人下面的操作给我看傻了, 他测出来的结果全是红的, 我到现在都不知道他是如何做到的, 但我可以肯定他一定在撒谎, 除非我俩在平行宇宙




这是我的测试图



这是他提供的测试图




按我的理解, 他肯定不可能控制对方的测试服务器让他们到我不通, 只有可能在前端做手脚, 但看对方的截屏感觉是手机/平板的截屏, 能用 F12 这种操作改前端页面吗? 真的想不明白他到底怎么做到的, 特意请教一下各位大佬们, 我实在是想不到还有什么阴招了

目前在使用 s6 ,电池健康度 74%,日常使用没什么问题,考虑要不要换个 ultra2 或者 3 (主要是觉得钛金属原色比较帅,和目前用的 15 pro 原色比较搭...功能上最多用到通知和偶尔的游泳记录),有必要吗,如果要换,二手的靠谱吗,哪个渠道买好一点呢

🚀 OTFS/OCDM/AFDM 高机动 NTN 对比仿真平台

统一信道建模 · 三体制公平对比 · 双检测器性能评估 支持 OTFS / OCDM / AFDM 一键仿真,输出 BER 曲线与结构化结果文件

MATLAB Waveform Channel Detector

📌 为什么需要这个项目?

高机动场景下(高速终端、卫星链路)时延-多普勒扩散显著,传统单波形脚本很难回答“到底哪种体制在当前参数下更稳”。常见问题包括:

常见痛点(零散脚本)本项目解决方式
🔴 参数不统一,体制对比不公平✅ 统一 K/L/M/SNR/NTN 参数链,三体制同口径
🔴 信道模型拆散在多处,难验证正确性✅ 通用 NTN_channels+ 兼容封装 NTN_channels1/2
🔴 只有一种检测器,无法比较检测增益✅ 同时支持 LMMSEMMSE-SD
🔴 输出文件命名混乱、结果难复现✅ 固定文件名输出,单目录清晰落盘
🔴 演示与正式仿真混在一起demofull 双模式分离,场景明确

🎯 核心价值

🔬 研究价值

  • 公平比较框架:同一 NTN 参数下评估 OTFS/OCDM/AFDM。
  • 统一检测口径:在每个体制内同时给出 LMMSE 与 MMSE-SD。
  • 可解释链路:信道构造、域变换、等效信道、检测与 BER 统计全链路闭环。
  • 可复现实验:固定随机种子与固定输出行为,便于重复实验和论文复核。

💼 工程价值

  • 模块化结构清晰:入口脚本薄、算法模块独立、工具函数复用。
  • 输出可直接集成:图像 + MAT 结构化结果适合后处理与报告自动化。
  • 扩展路径明确:新增体制、新增检测器可低耦合接入。
  • 维护门槛低:全中文注释与文档,利于团队协作交接。

⚡ 技术亮点

🏗️ 完整工程架构

OTFS-OCDM-AFDM/
 ├── run_unified_comparison.m        # 正式仿真入口(full)
 ├── demo_waveform_showcase.m        # 演示入口(demo)
 ├── src/
 │   ├── default_params.m            # 参数工厂:full/demo 模式
 │   ├── prepare_output_dir.m        # 输出目录准备 + 旧结果清理
 │   ├── run_ocdm_simulation.m       # OCDM 仿真主循环
 │   ├── run_afdm_simulation.m       # AFDM 仿真主循环
 │   ├── run_otfs_simulation.m       # OTFS 仿真主循环
 │   ├── NTN_channels.m              # 通用 NTN 信道矩阵构造
 │   ├── NTN_channels1.m             # AFDM 兼容信道封装
 │   ├── NTN_channels2.m             # OCDM(含CP)兼容信道封装
 │   ├── mmse_sd_detector_unified.m  # MMSE-SD 统一检测器
 │   ├── normalize_channel_matrix.m  # 信道归一化
 │   ├── symbols_to_bits.m           # 符号转比特工具
 │   ├── plot_comparison_results.m   # 曲线绘图与保存
 │   └── print_performance_summary.m # 终端摘要与结构化统计
 └── docs/
     ├── 算法文档.md
     ├── 代码文档.md

🧠 统一仿真内核(关键设计)

  1. 统一参数入口default_params(mode) 生成全局参数,避免体制参数漂移。
  2. 统一信道构造思路NTN_channels 输出时变矩阵,AFDM/OCDM 通过兼容层适配。
  3. 统一检测对比:三体制均执行 LMMSE 与 MMSE-SD,结果结构一致。
  4. 统一输出策略:固定命名 + 单目录覆盖,利于快速比对“当前版本结果”。

📊 实测性能(full 模式结果)

数据来源:outputs/unified_comparison_results.mat(当前项目实测输出)

LMMSE BER
SNR (dB)OCDMAFDMOTFS最优体制
03.150e-013.073e-013.071e-01OTFS
42.299e-012.165e-012.171e-01AFDM
81.469e-011.254e-011.268e-01AFDM
127.530e-024.730e-024.740e-02AFDM
163.080e-028.200e-037.900e-03OTFS
201.370e-029.000e-049.000e-04AFDM/OTFS 并列
MMSE-SD BER
SNR (dB)OCDMAFDMOTFS最优体制
03.154e-013.076e-013.073e-01OTFS
42.312e-012.173e-012.179e-01AFDM
81.500e-011.272e-011.285e-01AFDM
127.880e-024.750e-024.770e-02AFDM
163.340e-025.900e-035.800e-03OTFS
201.800e-022.000e-043.000e-04AFDM
结论:在当前配置下,AFDM 与 OTFS 在中高 SNR 区间显著优于 OCDM;MMSE-SD 相对 LMMSE 的增益在高 SNR 更明显。

💻 核心实现展示

1) OCDM:Fresnel 域有效信道构造

% 发射端:QAM符号 -> OCDM调制 -> 加循环前缀
 s_qam = qammod(x, params.M, 'UnitAveragePower', true);
 s_ocdm = IFSnT * s_qam;
 s_cp = CP_mtx * s_ocdm;
 ​
 % 有效信道构造与归一化
 Heff = R_mtx * H * CP_mtx;
 D = normalize_channel_matrix(FSnT * Heff * IFSnT);

2) AFDM:DAFT 域等效信道

% DAFT 变换矩阵
 A = D2 * DFT * D1;
 AH = A';
 ​
 % 仿射域信道与检测输入
 Y = A * r_time;
 H_daft = normalize_channel_matrix(A * HT * AH);

3) OTFS:DD 域等效化

% OTFS 变换算子
 OP = kron(WH, eye(M));
 ​
 % 时域信道 -> DD域等效信道
 H_eff = normalize_channel_matrix(OP' * HT * OP);
 r_dd = H_eff * x + w;

4) MMSE-SD:增广系统 + QR 回代

A = [H; reg * eye(n_tx)];
 b = [y; zeros(n_tx, 1)];
 [Q, R] = qr(A, 0);
 z = Q' * b;
 ​
 for i = n_tx:-1:1
     inter = R(i, i+1:end) * x_hat(i+1:end);
     rhs = z(i) - inter;
     soft_val = rhs / R(i, i);
     x_hat(i) = nearest_constellation(soft_val, constellation);
 end

🔄 端到端调用链

run_unified_comparison / demo_waveform_showcase
         ↓
 default_params(mode)
         ↓
 prepare_output_dir(...)
         ↓
 run_ocdm_simulation
 run_afdm_simulation
 run_otfs_simulation
         ↓
 plot_comparison_results
 print_performance_summary
         ↓
 save(...results, params, summary, plot_paths)

🎬 一键运行

% 进入工程目录
 cd('D:/03_代码库/04.code4sale/OTFS-OCDM-AFDM')
 ​
 % 快速演示
 run('demo_waveform_showcase.m')
 ​
 % 正式仿真
 run('run_unified_comparison.m')

📂 输出预览

当前输出目录:outputs/

典型结果文件:

  • unified_comparison_results.mat
  • ber_comparison_full.png
  • ber_comparison_full.fig
  • demo_results.mat
  • ber_comparison_demo.png
  • detector_gain_demo.png

图像示例:
ber_comparison_full.png

🖥️ 运行环境

  • MATLAB:R2020a 或更高版本(建议 R2022a+)
  • 依赖函数:

    • qammod / qamdemod(通信相关工具箱)
    • physconst(物理常量函数)
  • 操作系统:Windows / Linux / macOS 均可(路径按系统调整)

🛒 获取方式

本文代码仅为核心片段,完整版工程已整理好。 关注公众号 【3GPP 仿真实验室】进行获取。

文件合并拆分 在线工具分享

平时整理资料、处理日志、拆分大文本时,很多人都会遇到一个问题:文件太大不方便传,或者内容分散不好汇总。为了解决这个高频小麻烦,我用 Vue 3(Nuxt 3) 做了一个「文件合并拆分」在线工具,打开网页就能直接用。

在线工具网址:https://see-tool.com/file-merge-split
工具截图:

这个工具主要有两种模式:

  1. 文件拆分:把一个大文件拆成多个小文件。支持按“行数”拆分,也支持按“文件大小(KB/MB)”拆分。
  2. 文件合并:把多个文本文件按顺序合并成一个文件,还可以设置分隔符,方便区分每段内容。

对普通用户来说,最实用的场景包括:

  • 大日志按大小拆分后再上传或发送
  • 长名单按行拆分给不同同事处理
  • 多个零散 TXT/CSV 合并成一个总文件
  • 批量整理导出的文本结果

使用也很简单。

如果你要拆分文件:选择“拆分”模式 -> 上传文件 -> 选择按行数或按大小 -> 点击开始拆分 -> 下载全部结果。

如果你要合并文件:选择“合并”模式 -> 一次上传多个文件 -> 选择分隔方式(可自定义) -> 点击开始合并 -> 下载合并后的文件。

我在交互上尽量做得直观:上传后会显示文件行数、体积和预计拆分数量,处理完成后可单独下载,也可一键下载全部,减少重复操作。

另外,这个工具更偏向日常实用,不需要安装软件,电脑和手机浏览器都能使用。对于经常处理文本文件的人来说,它可以明显节省整理时间。

如果你也经常碰到“文件太大不好发”或“文件太多不好管”的情况,可以试试这个工具。

终于可以在微信跟飞书上控制 AI Agent 们了。10 分钟就能配好。而且支持记忆跟定时任务。不用再去折腾 openclaw 。claude code 可以接国产模型,随时切换,性价比很高。而且对 skill 支持非常好,调度和 agent 能力也很强。微信需要公网 IP ,但通过企业微信可以很容易接入,并且可以直接在个人微信就可以聊。

控制他们干任何活,聊天均可。真正实现随时随地大小码。
睡觉前交代几个任务,醒来就可以看结果。

主要是 agent 能力很强,落地效果比 openclaw 更好。

使用下面的项目,10 分钟就可以接入啦。目前是在 beta 版本。安装非常简单。

github 地址:https://github.com/chenhg5/cc-connect

参考截图:

3e86f8becfb6d09bfc486f986bd75a74.JPG

15aeef6888984fa0eac05dddc83799c3.PNG

可以加用户群:

用户群

可穿戴设备的生理信号处理是健康科技与研究领域反复出现的工程难题。ECG 记录、心率变异性指标、加速度计数据,不管处理哪一类信号,从原始传感器输出到有意义的生物标志物,整条链路都是碎片化的:算法散落在论文、代码仓库和临时脚本里,接口各不相同,数据模型也无法共享。

PhysioDSP 是一个开源 Python 库,目标是改变这种局面。它给出了一套统一、可扩展的框架来处理和分析生理传感器数据,核心关注点放在算法一致性、类型安全与可复现性上。项目托管在 GitHub,pip 安装即可使用:

pip install physiodsp

核心架构

PhysioDSP 的算法架构简洁统一,各组件易于集成、扩展和测试。

每个算法遵循同一套模式:Settings 类(基于 Pydantic)定义可配置参数;Algorithm 类继承 BaseAlgorithm,对外暴露 run() 方法;结果以 Pandas DataFrame 返回,直接接入下游处理。传感器数据封装在 AccelerometerDataEcgDataHrvData 等类型化数据模型中,实例化时即执行 schema 校验。

切换算法或调参无需改动数据处理代码——把数组包进对应的数据类,就能接入 PhysioDSP 的处理流水线。

from physiodsp.ecg.peak_detector import EcgPeakDetector  
from physiodsp.sensors.ecg import EcgData  
  
ecg_data = EcgData(timestamps=timestamps, values=ecg_values, fs=250)  
detector = EcgPeakDetector()  
result = detector.run(ecg_data)  
  
# 包含 RR 间期和心率的 DataFrame
print(result.biomarker)  

已实现的算法

当前版本包含四个功能模块。

活动分析方面,activity/ 模块实现了多种体动记录算法,均经过单元测试且可投入生产:ENMO(欧几里得范数减一)、过零率、高于阈值时间(用于剧烈活动量化)、PIM(比例积分模式,处理多轴数据),以及一套个性化活动评分方案,输出归一化的 0–100 每日活动与恢复指数,支持基线个性化。

ECG 处理集中在 ecg/ 模块,核心是一个基于类 Pan-Tompkins 滤波和峰值检测流水线的 QRS 波群检测器。传入任意采样频率的 EcgData 对象后,该检测器在单个 DataFrame 中返回心跳时间戳、RR 间期与瞬时心率。hrv/ 模块负责 HRV 评分,算法将 RMSSD(连续差值均方根)经 sigmoid 归一化映射到 0–100 分,同时纳入趋势和稳定性分量,计算基于滑动窗口,面向纵向监测场景。

底层的 dsp/ 模块提供卷积和滤波工具,既是上述各模块的计算基础,也可独立用于构建自定义信号处理流水线。

传感器支持与数据模型

传感器层对三种 IMU 模态——加速度计、陀螺仪、磁力计——以及 ECG 和 HRV 时间序列做了统一抽象。每个数据类接收原始 NumPy 数组、时间戳和采样频率,经 Pydantic 校验输入后,向下游算法提供一致的接口。传感器抽象与算法逻辑的分离带来一个直接的好处:接入新硬件或新信号类型时无需改动已有算法实现。运行环境要求 Python ≥ 3.11,依赖 NumPy、Pandas、SciPy 和 Pydantic。

总结

PhysioDSP 虽然刚刚看是,但是已经是一个可用的生理信号处理基础框架。当前版本(0.1.0b0)覆盖活动分析、ECG 峰值检测和 HRV 评分三个方向,架构统一且易于测试。后续计划中的模块包括能量消耗估算、睡眠质量指标和基于机器学习的活动识别。

https://avoid.overfit.cn/post/f014d12b5f154279b969a1d7f5bedc1c

BY Matteo Serafino

一、写在前面:为什么要写这篇文章

在 BPU 推理链路中,VP(Vision Pipeline)承担了大量图像前处理工作,例如裁剪、缩放、颜色格式转换等。 相比普通的软件图像处理接口,VP 接口直接面向硬件,性能优势明显,但也引入了​严格且隐含的使用约束​。

在实际项目中,hbVPRoiResize 是一个非常常用的接口,同时也是一个​非常容易踩坑的接口​。 本文基于一次真实的客户问题排查过程,对 hbVPRoiResize 的:

  • 接口功能定位
  • ROI 与缩放比例的计算规则
  • YUV 场景下的关键约束
  • 典型报错的根因与修正方式

进行一次​工程化的系统整理​,希望为后续遇到类似问题的开发者提供可直接参考的经验。

二、hbVPRoiResize 接口是做什么的

从功能上看,hbVPRoiResize 用于:

  • 从输入图像(src)中指定一个 ROI 区域
  • 对该 ROI 进行 等比例缩放
  • 输出到目标图像(dst),必要时自动进行 padding

需要注意的是:

hbVPRoiResize 并不是一个“自由裁剪 + 任意 resize”的接口它是一个严格受硬件约束的 ​ROI​ 等比缩放接口

这也是后续很多问题产生的根源。

三、ROI 的定义与有效区域计算

接口中使用的 ROI 一般定义为:

hbVPRoi roi{left, top, right, bottom};

在实际处理时,VP 会先对 ROI 做一次 与原始图像 ​src​​​ 的交集裁剪​,保证:

  • ROI 不越界
  • ROI 的有效宽高由交集区域决定

因此,传入的 ​ROI​​​ 参数并不一定就是最终参与计算的 ROI​,但在不越界的情况下,两者通常一致。

四、等比例缩放的计算规则(核心逻辑)

hbVPRoiResize ​只支持等比例缩放​,其缩放比例的计算规则为:

  1. 分别计算宽、高方向的缩放比例:
sx = dst_width  / roi_width
sy = dst_height / roi_height
  1. 选择较小的比例作为最终 scale:
scale = min(sx, sy)
  1. 一个方向会被完整填满,另一个方向不足的部分通过 padding 补齐

这一点非常关键:

不能人为指定非等比缩放,也不能单独控制 padding 行为所有结果都由 scale 自动推导

五、YUV 场景下的关键使用约束

当输入或输出图像为 YUV(尤其是 NV12 / YUV420) 时,VP 接口会引入额外的硬件约束,这些约束往往是问题的根因:

5.1 尺寸偶数约束

  • src / dst 的 width 和 height 必须是 偶数
  • ROI 的宽高也需要满足对应的偶数要求

5.2 Padding 对齐约束(极易被忽略)

在 YUV 场景下:

dst​ 图像在 padding 方向(top+bottom 或 left+right)的总 padding 必须是 4 的倍数

这不是算法层面的要求,而是 VP 内部处理 Y/UV 子采样与硬件 block 对齐的结果。

六、一次典型问题的完整复盘

6.1 问题现象

客户在调用 hbVPRoiResize 时遇到如下报错:

[E][VP] dstImg padding height(include top and bottom) should be multiple of 4, given padding height: 75
[E][VP] roi scale is invalid
[E][UCP] taskHandle is null pointer

UCP 提交任务失败,返回错误码 -200004。

6.2 输入参数

hbVPRoi roi{0, 648, 3839, 2159};
int32_t resize_width  = 1024;
int32_t resize_height = 480;

假设原始图像宽度为 3840,高度足够覆盖该 ROI。

6.3 参数推导过程

  • ROI 宽度:
roi_w ≈ 3840
  • ROI 高度:
roi_h ≈ 2159 - 648 + 1 = 1512
  • 宽方向缩放比例:
sx = 1024 / 3840 ≈ 0.2667
  • 高方向缩放比例:
sy = 480 / 1512 ≈ 0.317
  • 最终 scale:
scale = 0.2667
  • 缩放后的 ROI 高度(内部取整后):
scaled_h ≈ 405
  • 需要的 padding 高度:
padding_h = 480 - 405 = 75

6.4 为什么会报错

关键在于这一点:

75 % 4 != 0

在 YUV 场景下,VP 要求 上下 padding 的总高度必须是 4 的倍数。当前参数组合下,padding_h = 75,不满足该约束,因此:

  1. VP 判定 ROI scale 非法
  2. 任务参数校验失败
  3. UCP 无法创建 taskHandle
  4. 后续访问 taskHandle 触发空指针报错

七、问题的修正思路(工程经验)

在不改变整体逻辑的前提下,通常有两类可行修正方式:

7.1 微调 ROI(最推荐)

通过调整 ROI 的 top 或 bottom,使得:

(dst_height - scaled_h) % 4 == 0

例如,将 ROI 高度从约 1512 调整为 1516:

hbVPRoi roi{0, 644, 3839, 2159}; // roi_h ≈ 1516

此时:

  • scaled\_h ≈ 404
  • padding\_h = 480 - 404 = 76
  • 76% 4 == 0,满足约束

7.2 调整 dst 尺寸

如果输出尺寸可控,也可以通过调整 resize\_height 使 padding 满足 4 的倍数约束(同时仍需满足偶数要求)。

八、示例工程说明

本文对应的示例工程包含:

  • ROI + Resize 调用示例
  • YUV 图像输入构造
  • VP 接口调用流程
  • 交叉编译并在 ARM 端运行

工程文件结构包括:

  • main.cc
  • CMakeLists.txt
  • build.sh

示例代码可作为 ​最小可运行参考​,用于验证 ROI、scale 与 padding 的关系。

(示例工程可通过链接获取:roi\_resize\_sample.tar.gz

九、小结

通过这次问题排查可以看到:

  • hbVPRoiResize 的报错往往不是接口 bug,而是参数组合违反硬件约束
  • ROI、scale、padding 三者是强相关的
  • 在 YUV 场景下,padding 为 4 的倍数是一个必须显式考虑的工程约束

理解这些规则后,类似的 error -200004 问题可以在参数层面快速定位和解决。

项目地址: https://github.com/ikanam/AutoClash

因为 Clash 一直没有基于 WLAN 的策略组,所以 Vibe 了这个 App 通过 API 实现了自动策略组功能。
目前支持 WLAN 匹配和 ISP 匹配,都是根据我自己的需求来加的,有其他规则需求可以自己 Fork 自己改,也欢迎提 PR 。

整个项目都是由 Claude Opus 4.6 和 MiniMax M2.5 生成,我也没有怎么检查代码,毕竟是个小众需求,能用就行。




🔗 https://luolei.org

我将过去 10 多年间的 300+ 篇博客、推文及 GitHub 内容全部向量化,构建了一个基于 RAG 的个人知识系统 🧠

今天下午又花了一小时,将「 AI 罗磊」接入了基于 Vinext 框架的新博客。

现在,可以直接在博客上与我的「数字分身」对话了 🤖

和「自己」聊天的感觉,还挺奇妙的。

面试精灵Offer蛙在求职圈里都有不少人用,但两款工具的设计思路不太一样。

面试精灵则更注重技术能力的提升,自动说话人识别、简历定制化、联网搜索这些功能做得比较深入;Offer蛙的定位很明确——让回复更像面试者自己说的话,沟通技巧是它的卖点。

面试精灵操作页面

功能特性对比

根据我们对AI面试助手的全面评测,以下是面试精灵和Offer蛙的功能特性对比表格:

功能特性面试精灵Offer蛙
面试助手
笔试助手
简历优化X
模拟面试XX
面试记录/分析
交流社群XX
界面美观度43
操作简单/可访问性44
功能强大43.5
价格(元/小时)1069
性价比4.53.5
免客户端下载
多语言支持X
语音识别优化X
自动说话人识别X
隐蔽模式(多机互联)X
简历输入
个人知识库X
大厂面经库X
联网搜索XX
多种回复模式X
回复结果显示增强

核心功能对比

语音识别能力

两款工具在常规语音识别上都能正常工作,但在面对英文术语和新词汇时表现不同。

Offer蛙对语音识别做了优化,特别针对面试场景中的常见关键词进行了调整。在标准面试问题上表现不错。

面试精灵在英文术语识别上更有优势。比如"Transformer"、"DeepSeek"这类词,很多语音识别工具会出错,但面试精灵能较好地纠正过来。这对技术面试很重要,因为技术问题经常涉及英文术语。

回复风格和沟通技巧

这是Offer蛙最突出的地方。

Offer蛙生成的回复非常符合面试者的口吻,第一人称叙述自然,语气贴近真实求职者。如果你不想自己整理回答思路,只想照着读,Offer蛙的这种风格很适合。

面试精灵的回复更偏技术专业,结构清晰,重点突出。它会把关键信息加粗显示,方便快速抓住重点。这种风格适合希望理解回答逻辑自己发挥的用户。

面试精灵提示词优化

简历定制化能力

两款工具都支持上传简历,但实际效果差异比较大。

Offer蛙在简历相关问题上的表现不错,能按照STAR结构(Situation-Task-Action-Result)组织回复,项目经历描述清晰。但在使用公司信息这类问题上,它的回复会出现占位符,没有真正利用提前填写的信息。

面试精灵在简历定制化上做得更全面。无论是自我介绍、项目描述,还是对公司的了解,它都能较好地利用简历和职位需求信息。实测中,面试精灵的回答能准确引用简历中的具体细节。

时效性问题处理

对于"DeepSeek最近很火爆"这类时效性问题,两款工具的差距比较明显。

Offer蛙内置的知识更新到2023年,无法回答2025年的新事物。实测中,它对这类问题的回复要么出错,要么泛泛而谈。

面试精灵支持联网搜索,而且英文术语识别准确。它能通过搜索找到最新信息,给出正确回答。这对关注技术趋势的求职者很重要。

隐蔽性和操作便捷性

两款工具都支持多设备协同,但实现方式不同。

Offer蛙需要用户手动选择窗口输入声道,然后点击触发回复。操作不算复杂,但需要用户主动干预。

面试精灵支持自动说话人识别,能自动区分面试官的话和用户的话,识别到问题后自动处理。这种跨设备隐蔽操作的方式更安全,面试时不用频繁操作另一台设备。

界面和显示效果

Offer蛙的界面比较简单,美观度一般。功能相对单一,但核心功能做得还算扎实。

面试精灵的界面更现代化一些,代码块、公式、图表等复杂内容的显示效果更好。前端支持LaTeX公式、流程图、泳道图,对技术岗位的面试更友好。

价格对比

Offer蛙约69元/小时,在同类产品中算中等偏上。

面试精灵基础版约10元/小时,精英版约25元/小时。就算用最高配置,价格也比Offer蛙低不少。

两款工具都有新用户免费额度,可以先试用再决定。

功能完善度

Offer蛙的功能相对单一,主要聚焦面试辅助本身,但在语音识别优化和回复口吻上做得比较用心。

面试精灵功能更全面,除了面试助手,还提供笔试助手功能。笔试助手纯网页操作,通过多设备互联实现远程截图,视觉大模型自动识别题目并生成答案。

回复效果实测对比

为了更直观地展示两款工具的回复效果差异,我们来看具体案例。

实测案例:自我介绍问题

问题:"请你先简短做个自我介绍吧。"

这个题目测试的是RAG检索增强生成的个性化回复效果。

两款工具在本题中都表现得不错。Offer蛙能按照STAR结构(Situation-Task-Action-Result)组织回复,项目经历描述清晰,回复口吻很贴近面试者。面试精灵的回答能够准确引用简历中的具体信息,按照"基础信息-技能-项目-动机与胜任"的结构组织,内容完整且贴切。

面试精灵自我介绍问题回复

实测案例:项目描述问题

问题:"请详细描述下你简历中的这个点云感知项目"

这个题目测试的是简历信息的利用能力。

两款工具在本题中都表现不错,都能准确贴合简历中的项目经历,回复遵从STAR结构。面试精灵在技术细节的描述上更深入一些。

Offer蛙项目描述问题回复

实测案例:时效性问题

问题:"DeepSeek最近很火爆,你了解他的技术么?知道他厉害在哪里么?"

这个题目测试的是英文术语识别和联网检索能力。

面试精灵在语音识别结果"Deep Seeker"虽然不正确,但是最接近真值"DeepSeek"的。大模型纠错后,回复内容正确,并结合了联网搜索对DeepSeek这一较新知识进行RAG检索。

面试精灵时效性问题回复

Offer蛙在语音识别这一步就翻车,对于"DeepSeek"识别错误,如识别成"Deeppse",导致后续的回复结果都不是很相关。

Offer蛙时效性问题回复

实测案例:公司了解问题

问题:"你对我们公司了解多少?"

这个题目测试的是提前填写的面试信息利用能力。

面试精灵能够根据提前填写的面试准备信息,逻辑清晰地介绍面试的目标公司和与面试者的切合度。

Offer蛙的回复没有输出任何有用信息,留了很多占位字符,明显没有理解所求职的目标公司。

评测数据对比

以下是两款工具在各评测维度的平均得分对比(满分5分):

评测维度面试精灵Offer蛙
帮助性4.784.22
语音识别准确率4.444.22
意图识别正确率54.56
内容深度及个性化4.784.11
沟通技巧4.675
准确性4.783.67
全面性4.784.33
直观性4.894.56

从评测数据可以看出,Offer蛙在沟通技巧上表现突出,但在准确性、内容深度和个性化上弱于面试精灵。面试精灵整体表现更均衡,特别是在利用简历信息和联网搜索的能力上更有优势。

总结和建议

两款工具各有特点,选择哪款要看你更看重什么。

如果你希望回复更像自己的语言,不想动脑筋整理思路,只想照着读,Offer蛙的沟通技巧和回复口吻可能适合你。但要注意它的时效性知识比较落后,对新技术问题不太擅长。

如果你更看重技术深度和准确性,面试精灵可能更合适。自动说话人识别让操作更隐蔽,简历定制化让回答更贴切,联网搜索能应对时效性问题,价格也更实惠。

从整体评测数据来看,面试精灵在准确性、内容深度和个性化上表现更均衡,特别是在利用简历信息和联网搜索的能力上更有优势。结合其高性价比,面试精灵可能是更符合大多数求职者需求的选择。


这篇文章对比了Offer蛙和面试精灵的主要差异。两款工具各有特点,关键是要根据自己实际的使用场景和需求来选择。

Claude will return soon
Claude is currently experiencing a temporary service disruption. We're working on it, please check back soon.

天塌了,Claude Code 是挂了吗?

目前 33 岁,碰代码没那么多了,但技术还是了解。主要是做管理(虚线),感觉现在自己精力越来越差,脑子反应越来越迟钝了(对比 2 年前,那会还是反应很快),运动比较少(真的累),现在别人说句话都要反应一阵,原来都是秒懂。多做一些数独,或者推理题会有帮助吗?