--- url: /aipod/package/aiapp-skill.md --- # AI App Skill `lzc-aipod-aiapp` 是用于 AI Pod AI App 开发的 Agent Skill。它把 AI App 的资源目录、`aipod.yml`、`agxorin`/`thor` 服务、浏览器扩展、启动 loading、依赖声明和旧包迁移规则整理成可复用的开发上下文。 Skill 仓库地址: ```text https://github.com/lazycatcloud/lzc-aipod-aiapp-skill/tree/master/lzc-aipod-aiapp ``` ## 适用场景 在下面这些任务中建议启用这个 skill: * 创建新的 AI Pod AI App。 * 将旧版 `pkg/ai-pod-service/docker-compose.yml` 迁移到 resource export 布局。 * 为同一个 AI App 补充 `agxorin/docker-compose.yml` 和 `thor/docker-compose.yml`。 * 调整 `config/aipod.yml` 中的 shortcut、依赖和本地化配置。 * 接入 `/startup/gate` 或启动 loading。 * 排查 AI App 服务 host、依赖解析、浏览器扩展和 resource 目录问题。 ## Codex 安装 Codex 的 skill 默认安装到: ```text ~/.codex/skills//SKILL.md ``` 最简单的方式是在 Codex 中直接请求安装 GitHub 仓库中的 skill: ```text 请安装这个 Codex skill: https://github.com/lazycatcloud/lzc-aipod-aiapp-skill/tree/master/lzc-aipod-aiapp ``` 如果需要手动安装,可以执行: ```bash tmpdir="$(mktemp -d)" git clone --depth 1 https://github.com/lazycatcloud/lzc-aipod-aiapp-skill.git "$tmpdir" mkdir -p ~/.codex/skills rm -rf ~/.codex/skills/lzc-aipod-aiapp cp -R "$tmpdir/lzc-aipod-aiapp" ~/.codex/skills/ rm -rf "$tmpdir" ``` 安装后,新开的 Codex 对话会自动发现这个 skill。当问题命中 AI Pod AI App 创建、迁移或调试场景时,Codex 会自动加载它;也可以在提示词中明确写上 `使用 lzc-aipod-aiapp skill`。 ### Codex 示例 ```text 使用 lzc-aipod-aiapp skill,帮我把当前仓库里的旧 AI App 迁移到 resource export 布局。 要求: 1. 将 pkg/manifest.yml 里的 aipod 配置拆到 config/aipod.yml 2. 将 pkg/ai-pod-service/docker-compose.yml 拆成 agxorin/docker-compose.yml 3. 如果已有 thor 镜像,补充 thor/docker-compose.yml 4. 检查 Traefik Host、depends_on_hosts 和启动 loading 5. 最后运行仓库现有验证命令 ``` ## Claude Code 安装 Claude Code 的个人 skill 默认安装到: ```text ~/.claude/skills//SKILL.md ``` 安装命令: ```bash tmpdir="$(mktemp -d)" git clone --depth 1 https://github.com/lazycatcloud/lzc-aipod-aiapp-skill.git "$tmpdir" mkdir -p ~/.claude/skills rm -rf ~/.claude/skills/lzc-aipod-aiapp cp -R "$tmpdir/lzc-aipod-aiapp" ~/.claude/skills/ rm -rf "$tmpdir" ``` 如果只想让当前项目使用这个 skill,也可以把目录放到项目内: ```bash mkdir -p .claude/skills cp -R ~/.claude/skills/lzc-aipod-aiapp .claude/skills/ ``` Claude Code 中可以直接输入 `/lzc-aipod-aiapp` 调用,也可以描述 AI Pod AI App 相关任务,让 Claude 根据 skill 的 description 自动加载。 ### Claude 示例 ```text /lzc-aipod-aiapp 请检查这个 AI App 的 resource 导出是否正确: 1. aipod-resources/config/aipod.yml 是否只包含 AI Pod 专属配置 2. agxorin 和 thor compose 是否暴露相同的 Host 3. lzc-build.yml 是否正确声明 resource_exports 4. lzc-manifest.yml 是否可以直接使用 application.upstreams 转到服务 UI ``` ## 更新 后续 skill 仓库有更新时,重新执行对应安装命令即可。安装命令会先删除本地旧目录,再复制最新的 `lzc-aipod-aiapp` 目录。 更新后建议开启一个新的 Codex 或 Claude Code 会话,确保工具重新读取最新的 `SKILL.md` 和 references。 ## 参考 * [lzc-aipod-aiapp skill 仓库](https://github.com/lazycatcloud/lzc-aipod-aiapp-skill/tree/master/lzc-aipod-aiapp) * [Claude Code Skills 文档](https://code.claude.com/docs/en/skills) --- --- url: /aipod/package/install-progress-api.md --- # AI App 安装与服务部署进度 API 本文说明 AI App 手动安装、设备侧服务部署、流式进度,以及按 `serviceId` 查询最近部署状态的接口。 如需联调测试流程,可参考项目仓库中的部署进度测试文档。 ## 1. 基础约定 * AI Pod 对外域名:`https://.` * `subdomain` 从应用的 `lzc-manifest.yml` 中读取,当前默认是 `ai` * `box-domain` 优先通过 `lzc-cli box default` 获取;拿不到时默认使用 `13gxg.heiyu.space` 平台侧接口基准路径: * `https://./backend/aiapps` * `https://./backend/services` 设备侧接口基准路径: * `http://:/v1/service` ## 2. AI App 手动安装 ### 2.1 `POST /backend/aiapps/install/stream` 用途: * 上传一个 AI App ZIP * 完成注册 * 如果包内带可部署服务,继续触发设备部署。新 resource export 服务路径为 `agxorin/docker-compose.yml`、`thor/docker-compose.yml`,缺失时 fallback 到 legacy `ai-pod-service/docker-compose.yml` * 通过 SSE 返回安装进度 请求: * Method: `POST` * Content-Type: `multipart/form-data` * 必填字段:`file` 返回: * Content-Type: `text/event-stream` * 事件名:`progress` 事件结构: ```json { "stage": "pulling", "status": "downloading", "progress": 37, "message": "registry.lazycat.cloud/demo/app:latest: Downloading", "pkgId": "demo-app", "serviceId": "demo-app", "currentImage": "registry.lazycat.cloud/demo/app:latest", "currentLayer": "sha256:abcd", "current": 5242880, "total": 10485760, "error": "" } ``` 说明: * 本地包处理阶段仍会发送 `upload_saved`、`unzipping`、`validating`、`registering` 等状态消息。 * 这些阶段的 `progress` 固定为 `0`,不计入安装进度条。 * 真正推进安装进度的是服务部署阶段: * `pulling`: `0-80` * `starting`: `85` * `waiting`: `95` * `completed`: `100` * `failed`: 保持失败前进度 * 如果 AI App 没有可部署服务,则直接返回: * `stage=completed` * `progress=100` * `message=installed without deployable service` 典型返回: ```text event:progress data:{"stage":"registering","progress":0,"message":"registering ai app","pkgId":"demo-app"} event:progress data:{"stage":"pulling","status":"downloading","progress":31,"message":"registry.lazycat.cloud/demo/app:latest: Downloading","pkgId":"demo-app","serviceId":"demo-app","currentImage":"registry.lazycat.cloud/demo/app:latest","currentLayer":"sha256:abcd","current":5242880,"total":10485760} event:progress data:{"stage":"starting","status":"starting","progress":85,"message":"starting service","pkgId":"demo-app","serviceId":"demo-app"} event:progress data:{"stage":"waiting","status":"waiting","progress":95,"message":"waiting for service health","pkgId":"demo-app","serviceId":"demo-app"} event:progress data:{"stage":"completed","status":"complete","progress":100,"message":"service deployed","pkgId":"demo-app","serviceId":"demo-app"} ``` 失败示例: ```text event:progress data:{"stage":"failed","status":"error","progress":42,"message":"pull images failed","pkgId":"demo-app","serviceId":"demo-app","error":"pull access denied"} ``` ### 2.2 `DELETE /backend/aiapps/apps/:pkgId` 用途: * 删除手动安装的 AI App * 同步删除对应服务目录、缓存目录和镜像 返回: ```json {"removed":true,"pkgId":"demo-app"} ``` ### 2.3 `GET /backend/aiapps/apps` 用途: * 查看当前 AI App 列表 ### 2.4 `GET /backend/aiapps/installed/:pkgId` 用途: * 查看指定 `pkgId` 是否已经注册到 AI App store 返回: ```json {"installed":true} ``` ## 3. 平台侧服务接口 ### 3.1 `GET /backend/services/progress?serialNumber=&serviceId=` 用途: * 查询某台设备上指定服务最近一次部署进度 * 适用于自动触发部署场景,或手动安装过程中断线后恢复查看 返回结构: ```json { "serviceId": "demo-app", "stage": "pulling", "status": "extracting", "progress": 56, "message": "registry.lazycat.cloud/demo/app:latest: Extracting", "currentImage": "registry.lazycat.cloud/demo/app:latest", "currentLayer": "sha256:abcd", "current": 7340032, "total": 10485760, "startedAt": 1742880000, "updatedAt": 1742880012, "finishedAt": 0, "error": "" } ``` 空闲态返回: ```json { "serviceId": "demo-app", "stage": "idle", "progress": 0, "message": "deployment not started" } ``` ### 3.2 现有平台侧接口 * `GET /backend/services/list?serialNumber=` * `GET /backend/services/start?serialNumber=&serviceId=` * `GET /backend/services/stop?serialNumber=&serviceId=` * `GET /backend/services/delete?serialNumber=&serviceId=` * `GET /backend/services/log?serialNumber=&serviceId=` 说明: * `start` 是同步触发,不返回流式进度 * 自动触发部署和 `start` 都会持续更新 `serviceId` 对应的最近部署状态 * 若需要实时进度,优先使用 AI App 安装流或设备侧 `/service/start/stream` ## 4. 设备侧服务接口 ### 4.1 `POST /v1/service/start` 用途: * 触发一次同步部署 * 内部仍会更新最近部署状态 请求字段: * `file`: service zip * `serviceId` * `dirOverWrite`: `true|false` * `forceRecreate`: `true|false` * `extraEnv[KEY]=VALUE`: 可选 ### 4.2 `POST /v1/service/start/stream` 用途: * 触发一次流式部署 * 与 `/v1/service/start` 使用同一套底层部署逻辑 * 只是额外把进度通过 SSE 返回 返回: * Content-Type: `text/event-stream` * 事件名:`progress` * 数据结构与 `backend.ServiceDeployStatus` 一致 ### 4.3 `GET /v1/service/progress?serviceId=` 用途: * 查询该服务最近一次部署状态 ### 4.4 `GET /v1/service/log?serviceId=` 用途: * 持续查看服务日志 ### 4.5 `GET /v1/service/installed` 用途: * 查看设备上当前已安装服务及 `docker-compose.yml` 摘要信息 ## 5. 部署进度语义 统一状态字段: * `stage`: 粗粒度阶段 * `status`: 细粒度状态 * `progress`: 0-100 * `message`: 人类可读消息 当前阶段定义: | stage | progress | 含义 | | --- | --- | --- | | `idle` | `0` | 当前没有部署记录 | | `prepare` | `0` | 开始部署,准备目录、解压、写 `.env` | | `pulling` | `0-80` | 拉取 compose 镜像 | | `starting` | `85` | 执行 `docker-compose up -d --wait` | | `waiting` | `95` | 容器已拉起,等待健康检查或 `--wait` 完成 | | `completed` | `100` | 部署成功 | | `failed` | 保持当前值 | 部署失败,错误见 `error` | `pulling.status` 目前可能出现: * `prepare` * `preparing` * `waiting` * `downloading` * `download_complete` * `extracting` * `verifying` * `already_exists` * `complete` 说明: * `pulling` 的百分比来自 Docker Engine `ImagePull` 的结构化字节进度,不再依赖 `docker-compose pull` PTY 文本。 * 多镜像场景下,按镜像平均权重汇总成整体 `0-80` 进度。 * `starting` 和 `waiting` 目前是阶段型进度,不是容器级精确百分比。 * 系统只保留每个 `serviceId` 最近一次部署状态。 ## 6. 调用示例 ### 6.1 手动安装 AI App 并查看流式进度 ```bash curl --no-buffer \ -F "file=@./demo-app.zip" \ "https://ai.13gxg.heiyu.space/backend/aiapps/install/stream" ``` ### 6.2 查询某个服务最近部署状态 ```bash curl \ "https://ai.13gxg.heiyu.space/backend/services/progress?serialNumber=&serviceId=demo-app" ``` ### 6.3 直接调用设备侧流式部署接口 ```bash curl --no-buffer \ -F "file=@./demo-app-service.zip" \ -F "serviceId=demo-app" \ -F "dirOverWrite=true" \ "http://:/v1/service/start/stream" ``` ### 6.4 直接调用设备侧部署状态查询接口 ```bash curl "http://:/v1/service/progress?serviceId=demo-app" ``` ## 7. ZIP 结构要求 AI App ZIP 支持 resource export 结构和 legacy 手动安装结构。 ### 7.1 Resource export 结构 推荐结构: ```text demo-app/ └── resources/ └── aipod/ └── demo-app/ ├── package.yml ├── lzc-manifest.yml ├── icon.png ├── config/ │ └── aipod.yml ├── agxorin/ │ └── docker-compose.yml ├── thor/ │ └── docker-compose.yml └── extensions/ └── extension.zip ``` 平台安装后会把 AIApp resource 展开到: ```text /lzcapp/run/resources/aipod// ``` 服务选择规则: * Thor 设备优先使用 `thor/docker-compose.yml` * 其他设备默认使用 `agxorin/docker-compose.yml` * 型号专用 compose 缺失时,fallback 到 legacy `ai-pod-service/docker-compose.yml` 插件选择规则: * 优先读取 `extensions/*.zip|*.crx` * 缺失时,fallback 到 legacy `browser-extensions/extension.zip` ### 7.2 Legacy 手动安装结构 旧版手动安装 ZIP 仍保持兼容: ```text demo-app/ └── pkg/ ├── manifest.yml ├── icon.png ├── extension.zip └── ai-pod-service/ └── docker-compose.yml ``` 设备侧 service zip 结构: ```text demo-app/ ├── docker-compose.yml └── ... ``` AI App 规则: * resource export ZIP 内必须存在且只能解析出一份 AIApp resource 根目录 * legacy 手动安装 ZIP 内必须存在且只能解析出一份 `pkg/manifest.yml` * legacy 手动安装服务路径为 `pkg/ai-pod-service/docker-compose.yml` * 新 resource export 服务路径为 `agxorin/docker-compose.yml`、`thor/docker-compose.yml` 或 fallback `ai-pod-service/docker-compose.yml` * compose 必须包含可解析的 Traefik `Host(...)` label,供平台识别 AI host 和 startup loading 状态 ## 8. 兼容性说明 * 自动触发部署、手动安装流式部署、平台侧 `start` 接口,共用同一套底层部署逻辑和同一份状态仓库。 * 手动安装时,AI App SSE 直接透传设备侧部署进度。 * 自动触发部署时,不返回 SSE;调用方应通过 `serviceId` 查询最近部署状态。 * legacy ZIP 中的 `pkg/ai-pod-service/docker-compose.yml` 只作为手动安装兼容路径,不是新 resource export 的唯一服务格式。 --- --- url: /aipod/package/spec.md --- # AI 应用 首先,我们先理解下下面几个概念: 1. `lzc-cli` 是懒猫微服中用来开发微服应用的脚手架,通过 `lzc-cli` 可以快速打包应用、安装应用、调试应用、发布应用等。 2. 通过 `lzc-cli` 打包后产生的包为 `lpk` 格式,这是微服中的应用格式。 3. 懒猫 AI 浏览器中可以安装插件,支持 `PC` 和 `Android` 平台。 `AI 应用` 是指在微服应用的基础上,导入 `AIApp resource` 的应用。这一类应用的下载和安装都和正常的微服应用一样,区别在于: * **支持添加 AI 服务**: 应用安装后,会自动将匹配设备型号的服务部署到算力舱上。 * **支持添加 AI 浏览器插件**: 应用安装后,AI 浏览器会自动加载对应插件。 * **支持定义 AI 浏览器快捷方式和依赖关系**: 快捷方式、上游服务、存储别名等配置由 resource 中的 `config/aipod.yml` 描述。 ## Resource 导入声明 微服应用通过 `lzc-manifest.yml` 的 `import_resources` 声明导入 AIApp resource: ```yml import_resources: - kind: aipod ``` 平台安装应用后,会把 AIApp resource 展开到下面的运行目录: ```text /lzcapp/run/resources/aipod// ``` 其中 `` 为应用包 ID。平台从该目录读取 AI Pod 配置、服务 compose、浏览器插件和兜底 metadata。 ## Resource 目录结构 推荐的 AIApp resource 结构如下: ```text aipod/ ├── package.yml ├── lzc-manifest.yml ├── icon.png ├── config/ │ └── aipod.yml ├── agxorin/ │ └── docker-compose.yml ├── thor/ │ └── docker-compose.yml └── extensions/ ├── desktop.zip └── android.crx ``` 字段说明: * `config/aipod.yml`: AI Pod 配置文件,不需要外层 `aipod:`。 * `agxorin/docker-compose.yml`: AGX Orin 及默认 Jetson 设备使用的服务定义。 * `thor/docker-compose.yml`: Thor 设备使用的服务定义。 * `extensions/*.zip|*.crx`: AI 浏览器插件文件,可放置一个或多个。 * `package.yml`、`lzc-manifest.yml`、`icon.png`: metadata 和图标兜底来源。 兼容旧包时,平台仍会识别以下 legacy 路径: * `ai-pod-service/docker-compose.yml`: legacy 服务定义。仅当缺少设备型号目录下的 compose 时作为 fallback。 * `browser-extensions/extension.zip`: legacy 浏览器插件。仅当缺少 `extensions/` 下插件时作为 fallback。 ## `config/aipod.yml` `config/aipod.yml` 只写 AI Pod 配置本身,不带外层 `aipod:`。例如: ```yml shortcut: disable: false title: ComfyUI url: /_lzc/aipod_backend/startup/gate/cloud.lazycat.aipod.comfyui?redirect=https%3A%2F%2Fcomfyui-ai favicon: /icon.png locales: en: shortcut: title: ComfyUI depends_on_others: - ollama-ai depends_on_hosts: - comfyui-ai storage_alias: models: /ssd/models ``` ### `shortcut` 安装到微服中的 AI 应用默认会显示在 AI 浏览器快捷方式上。可以通过 `shortcut.disable` 禁用,也可以覆盖标题、入口地址和图标: * `shortcut.disable`: 是否隐藏快捷方式,默认为 `false`。 * `shortcut.title`: 快捷方式标题。 * `shortcut.url`: 快捷方式打开地址。长初始化服务推荐使用平台 startup gate。 * `shortcut.favicon`: 快捷方式图标地址。 ### `locales` `locales` 用于配置多语言 metadata。平台会按浏览器语言选择对应标题、描述或快捷方式字段,缺失时回退到默认配置。 ### `depends_on_others` `depends_on_others` 声明当前 AI 应用依赖的其他 AI host。平台会在入口和启动检查中考虑这些依赖的可用性。 ### `depends_on_hosts` `depends_on_hosts` 声明当前 AI 应用自身需要检查的 AI host。通常填写 compose 中通过 Traefik 暴露的 `Host(...)` 值。 ### `storage_alias` `storage_alias` 声明服务中使用的存储别名,便于平台将模型、缓存或用户数据挂载到稳定路径。 ## 添加 AI 服务 AI 服务通过 resource 中的 `docker-compose.yml` 描述。平台根据设备型号选择服务目录: 1. Thor 设备优先使用 `thor/docker-compose.yml`。 2. 其他设备默认使用 `agxorin/docker-compose.yml`。 3. 如果型号目录缺失,再 fallback 到 legacy `ai-pod-service/docker-compose.yml`。 `agxorin`、`thor` 和 legacy 服务如果同时存在,暴露给 AI 浏览器的 AI host 必须一致,避免同一个应用在不同设备上生成不同访问地址。 ### 服务访问规则 算力舱中的服务使用 `traefik` 结合 `Host` 规则转发。可被平台识别和部署的 compose 必须包含可解析的 Traefik `Host(...)` label,并且 host 值必须以 `-ai` 结尾。 ```yml services: comfyui: image: registry.lazycat.cloud/demo/comfyui:latest labels: - "traefik.enable=true" - "traefik.http.routers.${LZC_SERVICE_ID}-comfyui.rule=Host(`comfyui-ai`)" networks: default: external: true name: traefik-shared-network ``` 规则说明: * `Host(...)` 中的值是微服和 AI 浏览器访问该服务的 AI host。 * 只有加入 `traefik-shared-network` 的服务才能被 Traefik 接管。 * 多个设备目录中的 compose 必须暴露相同的 AI host。 * 长初始化服务必须配置 `healthcheck`,通过容器健康状态控制 ready 状态。平台的安装进度和 startup loading 会复用该状态。 关于 `traefik` 的更多使用方法可以查看 [Host Rule](https://doc.traefik.io/traefik/routing/routers/#host-and-hostregexp)。 :::info 算力舱中的 `docker` 默认使用 `nvidia-runtime`,容器中能直接使用 `gpus`,不需要显式指定 `gpus` 相关配置。更详细的说明,[点击连接](https://forums.developer.nvidia.com/t/whats-difference-between-gpus-and-runtime-nvidia-for-the-docker-container/283468/4) ::: ### 服务环境变量 服务运行时,算力舱中会提供以下环境变量: 1. 数据持久化路径 * 定义: `/ssd/lzc-ai-agent/data/` * 环境变量: `LZC_AGENT_DATA_DIR` * 示例: `/ssd/lzc-ai-agent/data/cloud.lazycat.aipod.ai` ```yml services: ollama: volumes: - ${LZC_AGENT_DATA_DIR}/data:/root/.ollama ``` 2. 数据缓存路径 * 定义: `/ssd/lzc-ai-agent/cache/` * 环境变量: `LZC_AGENT_CACHE_DIR` * 示例: `/ssd/lzc-ai-agent/cache/cloud.lazycat.aipod.ai` ```yml services: ollama: volumes: - ${LZC_AGENT_CACHE_DIR}/cache:/root/.cache ``` 3. 服务 ID * 定义: `` 去掉 `.` 后的值 * 环境变量: `LZC_SERVICE_ID` * 示例: `cloudlazycataipodfishspeech` ```yml services: ollama: labels: - "traefik.http.routers.${LZC_SERVICE_ID}-ollama.rule=Host(`ollama-ai`)" ``` ## 添加 AI 浏览器插件 AI 浏览器插件放在 resource 的 `extensions/` 目录中,支持 `zip` 和 `crx`: ```text extensions/ ├── desktop.zip └── android.crx ``` `zip` 为浏览器兼容的插件格式。如果插件是 [wxt](https://wxt.dev/) 开发的,可以直接打包为 zip;也可以手动把插件目录打包成 zip。 兼容旧包时,平台仍会读取 `browser-extensions/extension.zip` 作为 legacy fallback。 ## Metadata 和图标兜底 平台优先读取 resource 中的 `config/aipod.yml`。当其中缺少标题、描述、图标等 metadata 时,会按下面顺序兜底: 1. `package.yml` 2. `lzc-manifest.yml` 3. `icon.png` 如果 metadata 同时存在于微服应用 manifest 和 AIApp resource 中,AI 浏览器相关展示以 resource 配置为准。 ## 启动入口和 loading 长初始化 AI 服务不再推荐每个 AI 应用内置前置 `caddy` 做健康判断。推荐复用平台提供的 startup gate: ```text /_lzc/aipod_backend/startup/gate/:appid?redirect= ``` ### 快捷方式直接进入 gate 如果应用入口就是一个 AI host,可以在快捷方式里直接进入 gate: ```yml shortcut: title: ComfyUI url: /_lzc/aipod_backend/startup/gate/cloud.lazycat.aipod.comfyui?redirect=https%3A%2F%2Fcomfyui-ai ``` 规则说明: * `:appid` 为 AI 应用包 ID。 * `redirect` 为服务 ready 后跳转的目标地址,需要 URL encode,通常是 compose 暴露的 AI host 或微服页面地址。 * 静态前端可以直接走微服平台路由。 * API 或模型服务访问走 Traefik upstream。 * loading、健康检查、失败提示复用平台能力,不需要每个应用重复实现。 ### 静态前端接入 startup gate 如果 AI 应用本身有微服静态前端,更推荐参考 `cloud.lazycat.aipod.imgtrans` 的做法,在 `lzc-manifest.yml` 中使用 `application.injects` 拦截页面请求。这样用户访问任意前端页面时,都会先进入平台 startup loading;服务 ready 后再回到原始页面,并且可以保留深链接和 query。 ```yml application: subdomain: imgtrans injects: - id: aipod-startup-gate 'on': request auth_required: false when: - /* unless: - /api/* - /debug/backend/* - /assets/* - /favicon.ico - /icon.png - /robots.txt - /manifest.json - /_lzc/* do: - src: > const appid = "cloud.lazycat.aipod.imgtrans"; const cookieName = "aipod_startup_ready_" + appid.replace(/[^A-Za-z0-9]/g, "_"); const cookies = String(ctx.headers.get("Cookie") || ""); if (cookies.split(";").map((item) => item.trim()).some((item) => item === cookieName + "=1")) { return; } const boxDomain = String(ctx.request.host || "").replace(/^[^.]+\./, ""); const query = ctx.request.raw_query ? "?" + ctx.request.raw_query : ""; const redirect = "https://" + ctx.request.host + ctx.request.path + query; const location = "https://" + boxDomain + "/_lzc/aipod_backend/startup/gate/" + encodeURIComponent(appid) + "?redirect=" + encodeURIComponent(redirect); ctx.response.send(307, "", { location }); routes: - /=file:///lzcapp/pkg/content/dist upstreams: - location: /api/ backend: http://backend:3000 disable_trim_location: true public_path: - /debug/backend/ - /api/ ``` 这个例子的关键点: * `when: /*` 拦截所有页面入口,`unless` 放行 API、调试接口、静态资源、图标和 `/_lzc/*` 平台内部路径,避免资源请求和 gate 自身被重复重定向。 * `appid` 必须填写当前 AI 应用包 ID,startup gate 会用它查询安装和服务 ready 状态。 * gate ready 后会写入 `aipod_startup_ready_` cookie,inject 先检查这个 cookie,已 ready 的请求直接放行,避免每次打开页面都进入 loading。 * `boxDomain` 通过当前 host 去掉第一段 subdomain 得到,例如从 `imgtrans.13gxg.heiyu.space` 得到 `13gxg.heiyu.space`。 * `redirect` 保留用户原始访问地址和 query,并用 `encodeURIComponent` 传给 gate。服务 ready 后,平台会跳回这个地址。 * 前端静态文件继续使用 `routes` 暴露,应用自己的 API 使用 `upstreams` 转发。startup gate 只负责 ready/loading,不替代业务路由。 对应的 AIApp resource 中可以声明依赖,平台会把这些依赖纳入 ready 判断: ```yml shortcut: disable: false depends_on_others: - trans - ocr ``` ## 指定某一个算力舱上的服务 在具有多个算力舱的情况下,可以通过 `f-{算力舱的序列号}-{服务名称}-ai.{微服名称}.heiyu.space` 来访问。例如: ```text https://f-1420225016421-dozzle-ai.catdog.heiyu.space ``` --- --- url: /aipod/issues/lzc-app-dependon-pod-service-health.md --- # AI 应用依赖算力舱服务的状态检测 在微服系统 `1.3.8` 之后,可以使用 `upstreams` 的特性去访问算力舱中的 `health check` 接口。如下所示 ```yml name: ComfyUI package: cloud.lazycat.aipod.comfyui version: 0.2.0 description: ComfyUI is a web UI for AI Art. license: https://choosealicense.com/licenses/mit/ homepage: author: application: subdomain: comfyui gpu_accel: true routes: - /=file:///lzcapp/pkg/content/dist health_check: test_url: http://127.0.0.1/version start_period: 5m upstreams: - location: /version backend: https://comfyui-ai.{{ .S.BoxDomain }}/api/manager/version trim_url_suffix: / use_backend_host: true dump_http_headers_when_5xx: true ``` --- --- url: /aipod/olddocs/todo.md --- # AI 算力评测 1. 文档翻译类 1. 总结 2. 翻译图书 3. 翻译字幕 4. 电话纪要 2. 摄像头 1. 语义理解 2. 3. 智能音箱 1. 语音合成 2. 多合一模型 4. 视频搜索 1. 结合上下文的语义理解 5. 跨国交流 1. 实时翻译 2. 音色合成(将翻译后的文字使用自己的音色合成语音) 6. 网页转播客 1. 内容识别 2. 文字转语音 7. RAG 全文搜索 1. 可以先支持相似搜索 2. 在相似搜索的基础上做文档召回、文档总结、文档理解 * \[x] 2024-09-18 11:35:35 过一遍 jetson 的文档,看有那些东西有用 * \[] 2024-09-24 16:12:43 在模型多个 engine 同时使用的情况下,是否会有功率的限制从而导致性能下降 * \[] fish-speech 模型 --- --- url: /aipod/aipod/api.md --- # API 本文档整理 LC-X3 相关的后端接口。 ## 判断当前是否有算力舱 ### `GET /backend/device/hasDefault` 用途: * 判断当前微服下是否存在一个可用的默认算力舱 * 可用于 AI 应用启动前预检,或页面加载时判断是否需要引导用户先添加算力舱 请求: * Method: `GET` * 路径:`/backend/device/hasDefault` * 完整地址示例:`https://ai./backend/device/hasDefault` * 无需请求体 返回: * `200 OK`:当前存在可用的默认算力舱 * `503 Service Unavailable`:当前没有可用的默认算力舱 说明: * 该接口不返回 JSON,直接通过 HTTP 状态码表达结果 * 后端会先查找默认算力舱;如果当前还没有显式默认值,会自动从已授权设备中挑选一台活跃设备作为默认算力舱 * 即使存在默认算力舱,如果该设备超过 `1` 分钟没有活跃心跳,也会返回 `503` 示例: ```bash curl -i "https://ai./backend/device/hasDefault" ``` 返回 `200` 时示例: ```http HTTP/1.1 200 OK ``` 返回 `503` 时示例: ```http HTTP/1.1 503 Service Unavailable ``` --- --- url: /aipod/benchmark.md --- # Benchmark * [Whisper.cpp](./whispercpp.md) * [Qwen3.5](./qwen3.5.md) * [SD.CPP](./sdcpp.md) * [Qwen3 Coder Next](./qwen3-coder-next.md) * [Qwen3 8B Embedding](./qwen3-8b-embedding.md) * [Qwen3 Embedding 0.6B on Orin](./qwen3-embedding-0.6b-vllm-orin.md) * [Qwen3 Embedding 0.6B on Thor](./qwen3-embedding-0.6b-vllm-thor.md) * [Qwen3 VL Embedding 2B on Orin](./qwen3-vl-embedding-2b-vllm-orin.md) * [Qwen3 VL Embedding 2B on Thor](./qwen3-vl-embedding-2b-vllm-thor.md) * [PaddleOCR-VL-1.5](./paddleocr-vl-1.5.md) * [BGE-M3](./bgem3.md) * [Llama 3.1 70B](./llama-3.1-70b.md) * [DeepSeek R1](./deepseek-r1.md) * [GPT-OSS 120B](./gpt-oss-120b.md) ## Embedding Model ![embedding model](/assets/embedding-model.CO-xauOF.png) --- --- url: /aipod/ollama/benchmark.md --- # Benchmark | Model Alias | Actual Model | Tokens/s | Notes | |:----------------|:--------------------|:---------|:------| | qwen3-coder:30b | - | 31.84 | - | | qwen3.5:9b | qwen3.5:9b-q4\_K\_M | 15 | 工具调用准确度相比 qwen3 有所增强 | | qwen3:35b-a3b (262k) | qwen3.5:35b-a3b-q4\_K\_M | 25 | 手动将上下文从 8k 调整到 262k;工具调用能力尚可;VRAM 占用稳定在 24.4G-30G | --- --- url: /aipod/benchmark/bgem3.md --- # bge-m3 ### vllm ``` bge-m3-vllm-1 | INFO: Application startup complete. bge-m3-vllm-1 | INFO 08-20 03:40:32 [metrics.py:481] Avg prompt throughput: 3698.0 tokens/s, Avg generation throughput: 5.9 tokens/s, Running: 0 reqs, Swapped: 0 reqs, Pending: 953 reqs, GPU KV cache usage: 0.0%, CPU KV cache usage: 0.0%. bge-m3-vllm-1 | INFO 08-20 03:40:38 [metrics.py:481] Avg prompt throughput: 28348.0 tokens/s, Avg generation throughput: 44.4 tokens/s, Running: 0 reqs, Swapped: 0 reqs, Pending: 698 reqs, GPU KV cache usage: 0.0%, CPU KV cache usage: 0.0%. bge-m3-vllm-1 | INFO 08-20 03:40:43 [metrics.py:481] Avg prompt throughput: 29275.2 tokens/s, Avg generation throughput: 46.4 tokens/s, Running: 0 reqs, Swapped: 0 reqs, Pending: 441 reqs, GPU KV cache usage: 0.0%, CPU KV cache usage: 0.0%. bge-m3-vllm-1 | INFO 08-20 03:40:49 [metrics.py:481] Avg prompt throughput: 29540.3 tokens/s, Avg generation throughput: 46.1 tokens/s, Running: 0 reqs, Swapped: 0 reqs, Pending: 188 reqs, GPU KV cache usage: 0.0%, CPU KV cache usage: 0.0%. bge-m3-vllm-1 | INFO: 172.20.0.4:45788 - "POST /v1/embeddings HTTP/1.1" 200 OK ``` 并发250多个请求,输出的 token 可以达到 29540 多 token/s ``` python3 ollama_perf.py --file GuiMiZhiZhuu.txt --url https://bgem3vllm-ai.13gxg.heiyu.space/v1/embeddings --model bge-m3 --- 测试参数 --- 文件路径: GuiMiZhiZhuu.txt 模型名称: bge-m3 API URL: https://bgem3vllm-ai.13gxg.heiyu.space/v1/embeddings 切片长度: 1024 字符 总字数: 4775359 总切片数: 4664 注意: 所有切片将在一个 API 请求中发送。 ------------------ 开始发送单个大请求,请稍候... 请求完成! --- 性能测试结果 --- 总耗时: 132.80 秒 总字数: 4775359 总处理 Tokens: 3384888 请求状态: 成功 (HTTP 200) --- 核心指标 --- 平均 Token 速度: 25489.00 tokens/秒 平均吞吐量: 35959.57 字/秒 -------------------- ``` ### ollama ``` $ python3 ollama_perf.py --file GuiMiZhiZhuu.txt --url https://ollama-ai.13gxg.heiyu.space/v1/embeddings --model bge-m3:latest --- 测试参数 --- 文件路径: GuiMiZhiZhuu.txt 模型名称: bge-m3:latest API URL: https://ollama-ai.13gxg.heiyu.space/v1/embeddings 切片长度: 1024 字符 总字数: 4775359 总切片数: 4664 注意: 所有切片将在一个 API 请求中发送。 ------------------ 开始发送单个大请求,请稍候... 请求完成! --- 性能测试结果 --- 总耗时: 529.10 秒 总字数: 4775359 总处理 Tokens: 3375250 请求状态: 成功 (HTTP 200) --- 核心指标 --- 平均 Token 速度: 6379.19 tokens/秒 平均吞吐量: 9025.38 字/秒 -------------------- ``` 小文件 ``` python3 ollama_perf.py --file xce --url https://ollama-ai.13gxg.heiyu.space/v1/embeddings --model bge-m3:latest --- 测试参数 --- 文件路径: xce 模型名称: bge-m3:latest API URL: https://ollama-ai.13gxg.heiyu.space/v1/embeddings 切片长度: 1024 字符 总字数: 41648 总切片数: 41 注意: 所有切片将在一个 API 请求中发送。 ------------------ 开始发送单个大请求,请稍候... 请求完成! --- 性能测试结果 --- 总耗时: 4.95 秒 总字数: 41648 总处理 Tokens: 29539 请求状态: 成功 (HTTP 200) --- 核心指标 --- 平均 Token 速度: 5968.35 tokens/秒 平均吞吐量: 8414.97 字/秒 -------------------- ``` --- --- url: /aipod/vllm/bge-m3.md --- # BGE-M3 嵌入模型 `BGE-M3` 是算力舱内置的向量嵌入服务,适合语义检索、RAG 召回、文本相似度等场景。 ## 服务信息 * 服务地址: * Embeddings 地址: * 默认模型名:`bge-m3` * 镜像:`registry.lazycat.cloud/x/lzc-aipod-bge-m3:a7d1e2b` ## 单独部署使用 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: bgem3vllm: image: registry.lazycat.cloud/x/lzc-aipod-bge-m3:a7d1e2b ports: - 8000:8000 environment: - LANGUAGE=en_US:en - LANG=en_US.UTF-8 - LC_ALL=en_US.UTF-8 - VLLM_CONFIGURE_LOGGING=0 - VLLM_DO_NOT_TRACK=1 - VLLM_NO_USAGE_STATS=1 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 10s timeout: 30s retries: 100 start_period: 360s ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 启动后可通过 `http://127.0.0.1:8000/v1/embeddings` 调用 ## 调用示例 ```bash curl http://127.0.0.1:8000/v1/embeddings \ -H "Content-Type: application/json" \ -d '{"model":"bge-m3","input":"算力舱文档检索测试"}' ``` --- --- url: /aipod/vllm/bge-reranker-v2-m3.md --- # BGE-Reranker-V2-M3 重排模型 `BGE-Reranker-V2-M3` 是算力舱内置的重排服务,通常用于在向量召回后对候选结果进行二次排序,提升检索精度。 ## 服务信息 * 服务地址: * 健康检查地址: * 镜像:`registry.lazycat.cloud/x/lzc-aipod-bge-rerank:a7d1e2b` ## 单独部署使用 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: bgererankerv2m3vllm: image: registry.lazycat.cloud/x/lzc-aipod-bge-rerank:a7d1e2b ports: - 8000:8000 environment: - LANGUAGE=en_US:en - LANG=en_US.UTF-8 - LC_ALL=en_US.UTF-8 - VLLM_CONFIGURE_LOGGING=0 - VLLM_DO_NOT_TRACK=1 - VLLM_NO_USAGE_STATS=1 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 10s timeout: 30s retries: 100 start_period: 360s ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 启动后先访问 `http://127.0.0.1:8000/health` 确认服务健康 --- --- url: /aipod/blip.md --- # BLIP 图像理解服务 `BLIP` 服务用于图像内容理解与描述生成,适合图像摘要、图文检索预处理等场景。 ## 服务信息 * 服务地址: * 健康检查地址: * 镜像:`registry.lazycat.cloud/x/videorag/blip:c218d42` ## 单独部署使用 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: blip: image: registry.lazycat.cloud/x/videorag/blip:c218d42 ports: - 3000:3000 environment: - AUTO_UNLOAD_INTERVAL=600 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 30s retries: 30 start_period: 30s ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 启动后先访问 `http://127.0.0.1:3000/health` 确认服务健康 --- --- url: /aipod/clip.md --- # CLIP 图文编码服务 `CLIP` 服务用于文本与图像的统一向量编码,适合跨模态检索、图文匹配和相似度计算。 ## 服务信息 * 服务地址: * 健康检查地址: * 镜像:`registry.lazycat.cloud/x/videorag/clip:e5202e3` ## 单独部署使用 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: clip: image: registry.lazycat.cloud/x/videorag/clip:e5202e3 ports: - 3000:3000 environment: - AUTO_UNLOAD_INTERVAL=300 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 30s retries: 30 start_period: 30s ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 启动后先访问 `http://127.0.0.1:3000/health` 确认服务健康 --- --- url: /aipod/comfyui.md --- # comfyui 在 `comfyui` 2.0 的版本后,添加了 overlay 做环境的持久化,避免升级后或者安装一些节点后导致环境坏掉的情况下可以回退到之前的版本上. ## 使用 Docker Compose 单独启动 以下示例适用于 Jetson AGX Orin(算力舱 X3),使用 `catdogai/comfyui:2.0.3-agxorin` 镜像直接启动 ComfyUI。请先在设备上安装 Docker 和 Docker Compose。 新建一个目录,在其中创建 `docker-compose.yml`,写入以下配置: ```yaml services: app: image: catdogai/comfyui:2.0.3-agxorin privileged: true volumes: - /etc/timezone:/etc/timezone:ro - /run/jtop.sock:/run/jtop.sock - /sys/firmware/devicetree:/sys/firmware/devicetree:ro - /proc/device-tree:/proc/device-tree:ro - ./lzc-comfy-data:/lzc-comfy-data ports: - 8188:8188 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8188/api/system_stats"] interval: 5s timeout: 5s retries: 100 start_period: 30s ``` 在 `docker-compose.yml` 所在目录执行: ```bash docker compose up -d ``` 启动后,在设备本机浏览器访问 。如果通过局域网内的其他电脑访问,请打开 `http://算力舱IP:8188`。 模型、输入输出文件和运行环境会持久化到当前目录下的 `lzc-comfy-data`,请保留该目录。首次启动需要准备运行环境,可以通过以下命令查看启动日志和服务状态: ```bash docker compose logs -f app docker compose ps ``` ## Overlay 运行层用法 镜像默认通过 `lzc-comfy-overlay run` 启动 ComfyUI。它会把镜像内的只读代码和 venv 同步到持久化运行层,再从持久化运行层启动服务: * 镜像代码目录:`/opt/ComfyUI` * 镜像 venv:`/opt/venv` * 运行时代码链接:`/opt/ComfyUI-user` * 运行时 venv 链接:`/opt/venv-user` * 默认数据根目录:`/lzc-comfy-data` * overlay 状态目录:`/lzc-comfy-data/overlay` 启动时会优先使用 OverlayFS,并挂载: ```text /lzc-comfy-data/overlay/merged/app /lzc-comfy-data/overlay/merged/venv ``` 如果当前容器或数据盘不支持 OverlayFS,脚本会自动降级为普通持久化 rsync 运行模式。降级模式可以继续启动 ComfyUI,但不提供快照和回滚能力。 用户数据目录会迁移并链接到: ```text /lzc-comfy-data/overlay/userdata/user /lzc-comfy-data/overlay/userdata/output /lzc-comfy-data/overlay/userdata/input /lzc-comfy-data/overlay/userdata/models ``` 这些目录不会被镜像升级覆盖。`lzc-comfyui.ignore` 只应排除根目录的 `/user/***`、`/output/***`、`/input/***`、`/models/***`、`/temp/***`,不要使用未锚定的 `input/`、`models/` 这类规则,否则会误排除 ComfyUI 源码里的同名子目录。 常用命令: ```bash # 准备 overlay 挂载和升级同步,不启动 ComfyUI lzc-comfy-overlay boot # 完整启动流程,镜像 CMD 默认使用这个命令 lzc-comfy-overlay run # 查看数据根目录、当前镜像版本、挂载状态和待处理回滚 lzc-comfy-overlay status # 手动创建快照 lzc-comfy-overlay snapshot --reason before-manager-update # 查看快照列表 lzc-comfy-overlay list # 标记回滚目标,重启 app 容器后生效 lzc-comfy-overlay rollback # 清理过期自动快照 lzc-comfy-overlay cleanup ``` 升级流程由 `lzc-version` 控制。设备端记录的上次镜像版本在: ```text /lzc-comfy-data/overlay/control/last-image-version ``` 当新镜像内的 `/opt/ComfyUI/lzc-version` 和记录值不一致,或设置了 `COMFYUI_FORCE_UPDATE=1`,启动时会同步镜像内的 ComfyUI 与 venv。同步前会创建 `pre-image-upgrade-*` 快照;升级后服务需要在 300 秒内通过 `http://localhost:8188/api/manager/version` 健康检查。健康检查失败时,脚本会自动回滚到升级前快照并再次启动。 可用环境变量: * `LZC_COMFY_DATA_DIR`:覆盖默认数据根目录 `/lzc-comfy-data`。 * `COMFYUI_FORCE_UPDATE=1`:强制重新从镜像同步运行层。 * `COMFYUI_DISABLE_UPDATE=1`:已有运行层和 venv 可用时跳过镜像同步。 自动快照包括启动快照、每日快照和升级前快照。自动快照最多保留最近 5 个;超过 7 天的旧自动快照会在 `cleanup` 时清理。手动快照不会被自动清理。 Q:如果遇到 ksampler 不支持 sm87 cuda 指令? A:这个其实是 segeatention 库中没有适配 sm87 指令,通过更改 `/opt/venv/lib/python3.12/site-packages/sageattention/core.py` 和 sm89 同样的逻辑即可。 # 添加代理 在算力舱的 `/ssd/lzc-ai-agent/services/cloud.lazycat.aipod.comfyui` 目录下创建一个 `compose.override.yml` ```yml services: app: environment: - https_proxy=http://代理ip:代理端口 ``` 注意: 如果不要将代理 ip 写成 `127.0.0.1`,在容器内部是访问不到这个地址的. --- --- url: /aipod/olddocs/mlc.md --- 可以使用 jetson 官方封装好的 mlc 来测试 mlc llm 的速度 ```sh jetson-containers run dustynv/mlc:0.1.1-r36.3.0 ``` 或者直接使用 docker 命令,将对应的位置绑定进去 ``` cd /ssd/lnks/jetson-containers docker run -d --runtime nvidia -it --rm --network host --shm-size=8g -v `pwd`:/workspace --volume /tmp/argus_socket:/tmp/argus_socket --volume /etc/enctune.conf:/etc/enctune.conf --volume /etc/nv_tegra_release:/etc/nv_tegra_release --volume /tmp/nv_jetson_model:/tmp/nv_jetson_model --volume /var/run/dbus:/var/run/dbus --volume /var/run/avahi-daemon/socket:/var/run/avahi-daemon/socket --volume /var/run/docker.sock:/var/run/docker.sock --volume /ssd/lnks/jetson-containers/data:/data --device /dev/snd --device /dev/bus/usb --device /dev/i2c-0 --device /dev/i2c-1 --device /dev/i2c-2 --device /dev/i2c-3 --device /dev/i2c-4 --device /dev/i2c-5 --device /dev/i2c-6 --device /dev/i2c-7 --device /dev/i2c-8 dustynv/mlc:0.1.1-r36.3.0 cd /workspace/mymodels/mlc-ai mlc chat Qwen2.5-72B-Instruct-q4f16_1-MLC ``` # config 问题 从 huggingface 中下载的 `mlc Qwen2.5-XB` 在加载的时候会出现 `mean_gen_len` 或者 `isArray` 等错误,这个需要将 `mlc-chat-config.json` 添加缺失的字段。 ``` "shift_fill_factor": 0.3, "mean_gen_len": 128, "max_gen_len": 512, ... "system_prefix_token_ids": [], ``` # 使用官方的 benchmark 脚本测试 ``` root@nvidia-desktop:/workspace/packages/llm/mlc# python3 benchmark.py --model /workspace/mymodels/mlc-ai/Qwen2.5-32B-Instruct-q4f16_1-MLC/ --max-new-tokens 128 --max-num-prompts 4 --prompt /data/prompts/completion_16.json --save /workspace/myresults/mlc.csv ``` # mlc 中的 mlc\_llm 版本比较低,运行 Qwen2-1.5B 的时候会出现 lm\_head.weight https://github.com/mlc-ai/mlc-llm/issues/2458 ``` ln -s /usr/bin/python3 /usr/bin/python cd /opt/mlc-llm git pull origin main # create build directory mkdir -p build && cd build # generate build configuration python ../cmake/gen_cmake_config.py # build mlc_llm libraries cmake .. && cmake --build . --parallel $(nproc) && cd .. ``` 貌似也不能解决这个问题 --- --- url: /aipod/deepseed.md --- # deepseed ``` docker run --gpus all --network=host --shm-size 10.24g --name deepseed -d -ti -v `pwd`:/models -v `pwd`/cache:/root/.cache/huggingface dustynv/pytorch:2.7-r36.4.0-cu128-24.04 bash docker exec -ti deepseed bash ``` --- --- url: /aipod/benchmark/deepseek-r1.md --- # DeepSeek R1 ## Ollama deepseek-r1:70b ollama 中的 `deepseek-r1:70b` 模型镜像为 `43GB`,加载到内存后的占用约为 `46GB`。 ```text deepseek-r1:70b d37b54d01a76 46 GB 100% GPU 8192 4 minutes from now total duration: 7m10.425334424s load duration: 56.463007ms prompt eval count: 25 token(s) prompt eval duration: 44.639105ms prompt eval rate: 560.05 tokens/s eval count: 1480 token(s) eval duration: 7m10.322503313s eval rate: 3.44 tokens/s ``` ## llama.cpp Benchmark 以下数据来自 `~/lzc-aipod-benchmark/deepseek-r1-distill-70b/cumulative-report.md`,对应模型为 `DeepSeek-R1-Distill-Llama-70B-Q4_K_M.gguf`,runtime 为 `llama.cpp`。这一节是 `Distill 70B` 的 `GGUF` benchmark,不是上面的 `ollama deepseek-r1:70b` 拉取记录。 | Device | Runtime | Weight | Single Stream | Peak Aggregate | Power | GPU Temp | |:-------|:--------|:-------|:--------------|:---------------|:------|:---------| | Jetson AGX Orin | `llama.cpp` | `GGUF Q4_K_M` | `3.346 tok/s` (`g64 / c1`) | `33.851 tok/s` (`g64 / c16`) | `50.32~68.787 W` | `89.5 C` | | Thor T4000 | `llama.cpp` | `GGUF Q4_K_M` | `2.909~2.912 tok/s` (`g64~g1024 / c1`) | `46.165 tok/s` (`g64 / c48`) | `22.066~22.295 W` for `c1`; `37.798 W` at peak | `52~53 C` for `c1`; `48.562 C` at peak | | Thor | `llama.cpp` | `GGUF Q4_K_M` | `4.392~4.415 tok/s` (`g64~g1024 / c1`) | `-` | `37.443~39.254 W` | `68~75 C` | | Thor T5000 | `llama.cpp` | `GGUF Q4_K_M` | `4.458~4.552 tok/s` (`g64~g1024 / c1`) | `-` | `37.699~38.937 W` | `61~63 C` | ## Notes * `Jetson AGX Orin` 这一行对应 benchmark 仓库中的测试机 `lzc-pod-juyIZt`。 * `THOR` 与 `T5000` 本轮都完成了 `g64,128,256,512,1024 / c1 / 300s` 的单路稳态复测。 * 当前高并发峰值只在 `T4000` 与 `Jetson AGX Orin` 上有完整结果,所以 `THOR` 和 `T5000` 的峰值聚合吞吐先留空。 * 数据来源:`/home/catdog/lzc-aipod-benchmark/deepseek-r1-distill-70b/cumulative-report.md` --- --- url: /aipod/issues/dhcp.md --- # DHCP 表现就是有线网络一直没有获取到 `ip` 地址,从经常 connecting 到 no connected 状态切换 1. 排查当前是否接收到 dhcp 的广播 ``` sudo apt install nmap sudo nmap --script broadcast-dhcp-discover ``` 如果可以接收到 dhcp 包,说明路由器和设备是通的,只是算力舱中系统配置出了问题,可以往 NetworkManager 方向排查。 * 查看 NetworkManager 的日志 ``` sudo journalctl -u NetworkManager -b 0 -r # -r 反序查看, 最新的在最上面 ``` * 也可以试下手动分配ip地址,然后使用 ping 检测这个网卡是否已经连通 2. 查看特定的网卡上是否接收到发过来的数据包 ``` sudo apt install arp-scan sudo arp-scan -I eno1 -l # sudo /usr/sbin/arp-scan -l ``` 这里要 `-I` 执行对应的有线网络网卡接口,要不然收到的可能是wifi发过来的。 如果可以收到包,说明不是网线和设备的问题,可能是路由器上的问题,最方便的方法就是 * 先拔掉网线,然后重启下路由器 * 重新插上网线 3. ping 指定网卡 ``` ping -I eno1 192.168.1.1 ping -I eno1 www.baidu.com ``` --- --- url: /aipod/issues/docker.md --- # docker 启动 traefik 错误? 详细的报错信息如下: ``` May 16 06:06:57 lzc-pod-juyIZt dockerd[1391]: time="2025-05-16T06:06:57.617540160Z" level=error msg="failed to start container" container=675c06f1d672ee0c36441d4e0fcd69d48e38355a40a4e85b7a42d0019467ffc4 error="failed to set up container networking: driver failed programming external connectivity on endpoint monitoring-grafana (02c852b6417b0a591b3b22838f3fd71452fd32bf386bf1c186cb0ba5f256b398): Unable to enable DIRECT ACCESS FILTERING - DROP rule: (iptables failed: iptables --wait -t raw -A PREROUTING -p tcp -d 172.20.0.6 --dport 3000 ! -i br-8321df9162d9 -j DROP: iptables v1.8.7 (legacy): can't initialize iptables table `raw': Table does not exist (do you need to insmod?)\nPerhaps iptables or your kernel needs to be upgraded.\n (exit status 3))" ``` 解决: https://forums.developer.nvidia.com/t/problems-with-docker-version-28-0-1-on-jetson-orin-nx/325541/26 在 `/etc/systemd/system/docker.service.d/` 添加一个 `override.conf` 文件,里面是的内容是 ```override.conf [Service] Environment="DOCKER_INSECURE_NO_IPTABLES_RAW=1" ``` 添加后,执行下面的命令重启相关的服务 ``` sudo systemctl daemon-reload sudo systemctl restart docker sudo systemctl restart lzc-ai-agent ``` docker 重启后,可以使用 `docker info` 查看 ``` > docker info | grep DOCKER_INSECURE_NO_IPTABLES_RAW WARNING: DOCKER_INSECURE_NO_IPTABLES_RAW is set ``` --- --- url: /aipod/ffmpeg/compile.md --- # FFmpeg ## jetson 官方提供的 ffmpeg 4.4 ```sh echo "deb https://repo.download.nvidia.com/jetson/ffmpeg main main" | sudo tee -a /etc/apt/sources.list echo "deb-src https://repo.download.nvidia.com/jetson/ffmpeg main main" | sudo tee -a /etc/apt/sources.list sudo apt update sudo apt install ffmpeg ``` ## compile ffmpeg ``` LD_LIBRARY_PATH=$HOME/ffmpeg_build/lib ./ffmpeg --version LD_LIBRARY_PATH=$HOME/ffmpeg_build/lib ./ffmpeg -decoders LD_LIBRARY_PATH=$HOME/ffmpeg_build/lib ./ffmpeg -decoders | grep av1 LD_LIBRARY_PATH=$HOME/ffmpeg_build/lib ./ffmpeg -decoders | grep h264_nvv4l2dec ``` * https://github.com/HandBrake/HandBrake/commit/75f9c84c140c8841cfe1324ef59452025899ad8b.patch?full\_index=1 --- --- url: /aipod/olddocs/fish-speech.md --- # fish-speech * 从现有的wav声音中采集声色 * fish-speech 要求 3.10 的 python 版本, 而 jetson pytorch 中只有在 jetpack 6.0 后 才有 3.10 pytorch 的版本,否则需要自己构建。所以这个模型尽量在 ubuntu 22.40 jetpack 6.0 cuda 12.x 以上运行。 1. 系统安装 libopenblas-dev ``` sudo apt-get -y update; sudo apt-get -y install libopenblas-dev; ``` 2. 下载 jetpack 6 对应的 pytorch 2.3 的版本 whl [link](https://forums.developer.nvidia.com/t/pytorch-for-jetson/72048/1414) 下载 torchaudio, torchvision, torch 进行安装即可 ``` conda create -n fish-speech python=3.10 gcc=12.1.0 pip3 install ./torch-2.3.0-cp310-cp310-linux_aarch64.whl ./torchaudio-2.3.0+952ea74-cp310-cp310-linux_aarch64.whl ./torchvision-0.18.0a0+6043bc2-cp310-cp310-linux_aarch64.whl ``` 3. 根据 fish-speech 的[教程](https://github.com/fishaudio/fish-speech/blob/main/docs/zh/index.md)安装 ``` pip install -e . ``` 没有加速的情况下,在生成语义 token 的阶段中 5.5 tokens/s 的速度 4. 编译 fish-speech 中的 模型进行加速。这个是通过将模型使用 torch.compile 进行编 译成类似 jit 加速一样. nvidia pytorch 中有一个 `Torch-TensorRt` 的 torch compile backend,但这个没有提 供 aarch64 的预编译的包,需要自己构建。 jetson agx orin 有一个 jetpack 5.x 构建好的容器,没有提供 jetpack 6 的版本(这个 容器在 jetpack 6 上使用会加载报 libc 等错误) ``` root@tegra-ubuntu:/workspace# python3 -c 'import torch_tensorrt; print(torch_tensorrt.__version__)' Traceback (most recent call last): File "/usr/local/lib/python3.8/dist-packages/torch_tensorrt/__init__.py", line 39, in import tensorrt File "/usr/lib/python3.8/dist-packages/tensorrt/__init__.py", line 68, in from .tensorrt import * ImportError: /lib/aarch64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by /usr/lib/aarch64-linux-gnu/nvidia/libnvdla_compiler.so) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "", line 1, in File "/usr/local/lib/python3.8/dist-packages/torch_tensorrt/__init__.py", line 82, in ctypes.CDLL(_find_lib(lib, LINUX_PATHS)) File "/usr/lib/python3.8/ctypes/__init__.py", line 373, in __init__ self._handle = _dlopen(self._name, mode) OSError: /lib/aarch64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by /usr/lib/aarch64-linux-gnu/nvidia/libnvdla_compiler.so) ``` 5. 在 ubuntu 20.0 jetpack 5.x 系统上使用构建好的容器(但由于 fish-speech 使用 python3.10 ,而容器中提供的包只有 3.8,也还是需要自己构建 3.8 的版本 torch\_tensorrt,而且像 pytorch 这些包也需要自己构建,因为没有 pytorch aarch64 3.10 jetson 的提前编好的) ``` sudo docker run -d --runtime nvidia -it --rm --network host --env HF_ENDPOINT=https://hf-mirror.com -v `pwd`:/workspace --shm-size=8g --volume /tmp/argus_socket:/tmp/argus_socket --volume /etc/enctune.conf:/etc/enctune.conf --volume /etc/nv_tegra_release:/etc/nv_tegra_release --volume /tmp/nv_jetson_model:/tmp/nv_jetson_model --volume /var/run/dbus:/var/run/dbus --volume /var/run/avahi-daemon/socket:/var/run/avahi-daemon/socket --volume /var/run/docker.sock:/var/run/docker.sock --volume /home/nvidia/lnks/jetson-containers/data:/data --device /dev/snd --device /dev/bus/usb --device /dev/i2c-0 --device /dev/i2c-1 --device /dev/i2c-2 --device /dev/i2c-3 --device /dev/i2c-4 --device /dev/i2c-5 --device /dev/i2c-6 --device /dev/i2c-7 --device /dev/i2c-8 --device /dev/i2c-9 -v /run/jtop.sock:/run/jtop.sock dustynv/torch_tensorrt:r35.3.1 ``` 6. 在 ubuntu 22.04 jetpack 6.0 conda python 3.10 环境下构建 torch tensorrt 模块 * dpkg -l | grep tensorrt -i 查看当前系统的 Tensorrt 版本,不要切换到一个不一样的版本上 * 根据当前的各个系统组件版本, 使用 git checkout 到一个指定的支持的 commit 中去 * git fetch --depth=1 origin commit-xxx * git checkout commit-xxx * 根据错误更改 BUILD 或者 WORKSPACE 中的 cuda 版本 * 需要指定 LD 的目录,否则会出现ld连接不了,导致构建失败。(bazel 中指定环境变量需要显示指定参数) * bazel build --action\_env=LD\_LIBRARY\_PATH=/path/to/lib your\_target ``` git clone --branch=main --depth=1 https://github.com/pytorch/TensorRT torch_tensorrt git fetch --depth=1 origin 4c3d026ea64bd646a840bf174ef9d1818f10c20f git checkout 4c3d026ea64bd646a840bf174ef9d1818f10c20f python3 setup.py bdist_wheel --use-cxx11-abi ``` 7. 重新构建 triton 包(需要在conda环境中指定 gcc 的版本,否则会出现 libstdc++的版 本低问题)。也可以通过下面的方式手动临时更改下 ``` git clone https://github.com/triton-lang/triton.git; cd triton; pip install ninja cmake wheel pybind11; # build-time dependencies pip install -e python --verbose mv /ssd/miniconda3/envs/fish-speech/lib/libstdc++.so.6 /ssd/miniconda3/envs/fish-speech/lib/libstdc++.so.6.old ln -s /usr/lib/aarch64-linux-gnu/libstdc++.so.6 /ssd/miniconda3/envs/fish-speech/lib/libstdc++.so.6 ``` 如果遇到 `triton.common` not found, 需要切换到 `2.3.x` 分支 ``` git switch release/2.3.x ``` 8. triton 3.0 需要在 torch 2.5 版本才能使用, 而 jetson agx orin 目前官方给出的只 有 2.4 的版本. 在 torch 2.3 中使用 triton 3.0 会出现 triton.common 缺失的问题。torch 2.3 版本需 要使用 triton 2.3.x 的分支版本,但 triton 2.3.x 分支中没有 aarch64 的适配. 9. torch 构建成功后,需要重新构建 torchaudio 库 [link](https://pytorch.org/audio/stable/build.jetson.html) ``` USE_CUDA=1 python setup.py bdist_wheel ``` 10. torch 构建成功后,需要重新构建 torchvision 库 ``` git clone https://github.com/pytorch/vision.git python setup.py bdist_wheel ``` 11. https://github.com/triton-lang/triton/issues/3505 --- --- url: /aipod/olddocs/tts.md --- # fish-speech | 模型 | 文字 | 输出声音时间 | 推理时间 | 比率(1s的推理时间,可以生成多少声音) | |:------------|:---------------------|:-------------|-----------|--------------------------------------| | fish-speech | 大白鹅故事(2522个字) | 9分10秒 | 37m8.903s | 0.24 | 需要从引用的音频中采集声色,这个采集完成后,就可以保存起来,后面可以继续使用。 # f5-tts 内存要求: 4GB (所以像内存 8GB 的卡也可以运行这个模型) 模型代码本身是 MIT 的开源协议,但模型权重数据是CC-BY-NC的 支持通过标点符合或者空白来添加停顿,这个在 fish-speech 中没有支持的。可以指定一 个示例音频,从音频中克隆音色。 | 模型 | 文字 | 输出声音时间 | 推理时间 | 比率(1s的推理时间,可以生成多少声音) | |:-------|:---------------------|:-------------|----------|--------------------------------------| | E2-TTS | 大白鹅故事(2522个字) | 8分44秒 | 23分50 | 0.36 | | F5-TTS | 大白鹅故事 | 8分44秒 | 17分3 | 0.51 | ``` conda activate f5-tts cd /ssd/lnks/tts/F5-TTS bash example/run2.sh ``` `E2-TTS` 比 `F5-TTS` 更有情感些, `F5-TTS` 的语音质量和 `fish-speech` 的效果差不 多。 # chattts # 测试文件: 大白鹅故事 ```txt 一 从前,有一只胖胖的母鹅。她已经到了做妈妈的年龄,可是她一直没做成妈妈。因为,这只鹅从来没有下过一只蛋,没有孵出过一只鹅来。 可怜的胖鹅,她是一只孤单单的鹅。 她多么希望能孵出一只胖胖的小鹅,听小鹅脆生生地叫她一声妈妈。她多么希望街坊邻居也能尊敬地叫她一声鹅妈妈。 可这一切离开她是多么的遥远,简直不可能。每当夜深人静的时候,胖鹅常常会独自流下伤心的泪,孤独的泪。 一天,胖鹅告别了乡亲们独个儿出外旅行去了。 胖鹅在蜿蜒的小路上,步履蹒跚地走着。她在晶亮的小河上,划动着一双红红的脚掌,体态悠闲地游着。 她看看山,看看水,看看变幻莫测的白云,看看盛开在原野、田梗上的小花。美丽的风景慰藉了她孤独的心灵,胖鹅渐渐变得开朗和愉快起来。 一天傍晚的时候,胖鹅爬上了河岸,她在田埂上一摇一摆地走着。田埂边上,是一片已经收获过的瓜田。瓜田里除了爪藤外,已经看不到西瓜了。 胖鹅走着走着,忽然她看见在几张瓜叶前,有半个看来是被刺猬啃破的西瓜,就在这只被啃破的西瓜对面,居然有个十分完整的圆西瓜。胖鹅高兴极了。她想正好用这只西瓜来解旅途的饥渴。 胖鹅抱住这只西瓜,不知为什么,她有一种感觉,觉得这只西瓜不同于一般的西瓜,它像一只蛋,一只圆圆的、硬硬的有生命的蛋。 也许,胖鹅太想有一个蛋了,她说什么也摆脱不了这个奇怪的想法,她认定这只西瓜是一只蛋。 胖鹅小心翼翼地,把这只“西瓜蛋”搬到了不远处,那儿有看瓜人留下的破草棚。胖鹅在草棚里,她围着“西瓜蛋”转了几圈,她毫不怀疑自己的眼光,这一定是个“西瓜蛋”。胖鹅细心地伏在“西瓜蛋”上,她满怀着热情和信心,在这个破草棚里孵起蛋来了。 胖鹅的体温,渐渐传到了“西瓜蛋”里。 二 一天、两天、三天、五天。 西瓜并没有被胖鹅孵烂。 恰恰相反,“西瓜蛋”的壳硬得很,只是变得很暖和很暖和了。 八天、九天、十天,胖鹅似乎感觉到“西瓜蛋”有了动静,她已经能听到嚓嚓的声响了。 “天哪,我的宝宝快出壳了,我好像听到它在啄蛋壳了!”胖鹅喃喃自语地说着,“我要当妈妈了,我要让宝宝自己啄破蛋壳,当它的可爱的小脑袋从破蛋壳里伸出来时,我一定要亲亲它。” 又过了几天,只听到格拉一声响,“西瓜蛋”的壳破了一个洞——只见一只小秃脑袋,从这个“窗户”里伸了出来,他长着一只尖尖的嘴,他张开一对小小的眼睛,惊奇地注视着这个有趣的世界。 胖鹅激动地伸长脖子,去亲了亲这个小秃脑袋一下,小东西吓得把头缩进了蛋壳。 由于小东西一惊慌,“西瓜蛋”格拉格拉一阵响,整个儿破了。这只被胖鹅从“西瓜蛋”里孵出来的,不知道姓名的小怪物,降临到这个世界上了。 胖鹅当妈妈了,她终于有了自己的孩子。 三 鹅妈妈给自己的孩子取了个名字,叫“小尖嘴”。小尖嘴出壳不久,就会站立,就会走路,还会发出呱呱叽叽的声音。这真是个讨人喜欢的小宝宝。 鹅妈妈是这样的疼爱自己的孩子。 她给小尖嘴找东西吃,她帮小尖嘴梳理几根刚出来的羽毛,还给他讲好听的故事。 鹅妈妈带着她的孩子回到了家乡,街坊邻居不再叫她胖鹅,而改口叫她鹅妈妈了。你说,鹅妈妈心里有多甜蜜——她终于有了孩子,她是个十足的妈妈了。 可惜的是她的孩子,长得越来越不像她。 小尖嘴的腿越长越长,以至于他看自己妈妈的时候,必须低下头来。他的脖子也越长越长,小小的脑袋、尖尖的嘴儿,显得有几分滑稽。 街坊们议论鹅妈妈的儿子,说他不像鸡,不像鸭,也不像鹅。 每当听到这样的话,鹅妈妈就有点不高兴,她会大声地说:“可他像我的儿子。不,就是我的儿子,他多有男子汉的气概!” 鹅妈妈几次想教他的宝贝游泳,不过都没有成功,小家伙站在小河里,河水刚浸到他的肚皮,可是当他蜷起腿来,像他的鹅妈妈那样游泳,就几乎被水呛死。 这是鹅妈妈最伤心的——她的儿子没法学会游泳。 四 有一天,鹅妈妈闲来无聊,因为儿子出门玩去了,她买来一本刚出版的杂志读着。它刊登来自世界各地的新闻和趣事。其中有一篇旅行家爱尔莎太大的回忆文章,吸引了她。 有一次爱尔莎太太和丈夫画家雷诺先生出门旅行。他们在一片异国的瓜田里走着。爱尔莎太太文章里有这样一段: “走着,走着,我的肚子忽然剧痛起来,我当时怀着孕,我就在瓜田里下了我这辈子的第一个,也是唯一的一个蛋,那是一个圆滚滚的,非常惹人喜爱的大蛋。” “可是,我的丈夫雷诺,他看见瓜田里有个可爱的小西瓜,突然画兴大发,他要让我们的孩子知道,他是降生在瓜田里的,雷诺要在蛋上留下一张有意义的写生画。于是,他就挤出了颜料,把对面西瓜的色彩画在了我刚下的那只可爱的蛋上,我的蛋就变成了一只活灵活现的小西瓜了。” “就在这时,”爱尔莎太太伤心地说,“突然,从远处跑来了一个黑点儿,根据我们在草原上生活多年的经验,可以断定,那是一只狼。我们不得不暂时躲一躲,撒腿离开了瓜田。” “过了好久,当我们回来时,瓜田里只剩下半只被啃过的西瓜,我们被画成西瓜的蛋,无影无踪了。” 爱尔莎太太在文章最后说:“我抬头四望,田野上除了一个看瓜人留下的破草棚,什么也没有,我伤心透了——我的唯一的孩子,就这样失去了⋯⋯” 看到这里,鹅妈妈眼前一阵黑。这场面她太熟悉了,鹅妈妈想说:“爱尔莎太太,你当时为什么不到草棚里来看看呢?可怜的人。” 鹅妈妈再看看文章后面,那里印着爱尔莎太太的照片,原来她和雷诺先生是一对驼鸟夫妻,他们的家乡在澳大利亚。 瞧,她那高高的腿,她那长长的脖子和小脑袋,这不和小尖嘴一模一样吗? “天哪。”鹅妈妈说,“我的小尖嘴是只小驼鸟。” 五 接下来的日子,是鹅妈妈一生最难忘的日子。她翻来覆去地想这件事。 鹅妈妈想把小尖嘴留在身边,她完全可以隐瞒这件事。是她从“西瓜蛋”里孵出了小尖嘴,是她喂养大了小尖嘴。也是小尖嘴给了她妈妈的称呼,赶走了她的孤单和寂寞。她怎么可以让小尖嘴离开自己,去到一个遥远而又陌生的地方,管一对陌生的夫妻,叫做爸爸妈妈呢⋯⋯ 可她再一想,小尖嘴毕竟是一只小驼鸟,他是个学不会游泳的小家伙,他的天地是在草原上,他会成为鸟类中的长跑冠军。 鹅妈妈想象着,小尖嘴张开翅膀,迈开长腿,像一匹骏马似地在草原上奔驰着,这多威风,多有男子汉气概⋯⋯ “唉,这才像个驼鸟。”鹅妈妈叹口气说,“要是老在我这儿,准会变得鹅不像鹅,驼鸟不像驼鸟的。” 鹅妈妈的心里乱极了。 六 电报毕竟还是发出去了。 “孤独”,这难忘的滋味,让鹅妈妈下定了决心。鹅妈妈想起,小尖嘴没来到她身边以前,她是多么的孤独,多么的寂寞啊。是小尖嘴给她带来了希望、愉快和热热闹闹的生活。 可是如今呢,鹅妈妈说:“我把‘孤独’送给了爱尔莎太太和她的丈夫了,这多么自私。” 是的,鹅妈妈早就从爱尔莎文章的字里行间,读到了孤独,读到了寂寞,读到了懊恨和思念⋯⋯反正,读到了一颗妈妈的破碎的心。 鹅妈妈在给爱尔莎太太的电报中写着:“我再也不会孤独了,因为我有过小尖嘴这个宝贝,他让我成了妈妈,他会永远记住我的。他在我心中,我也在他的心中,他应该成为我们共同的儿子。” 写到这里,鹅妈妈好像看见了爱尔莎太太和雷诺先生喜出望外的模样。 不知怎么的,鹅妈妈突然在电报的最后,想出了一句只有诗人才会想出来的话: “让孤独永远从我们中间离开吧!” 七 如今,当人们敲敲鹅妈妈的门,而一直没有动静的时候,你就会知道—— 鹅妈妈是在给大草原上的小尖嘴写信,而忘了周围的一切。 要么,就是她出远门,去探望那只当年她从“西瓜蛋”里孵出的,如今奔驰在澳大利亚大草原上的小尖嘴了⋯⋯ ``` --- --- url: /aipod/os/gpio.md --- # GPIO 键 1. 可编程按键 (开机键旁边的功能键) ``` sudo su gpioget $(gpiofind PM.07) # 安装后会输出 0, 松开后会输出 1 ``` 2. LED 灯 ``` sudo su gpioset -m signal $(gpiofind PP.04)=1 # 点亮红色 gpioset -m signal $(gpiofind PP.04)=0 # 关闭红色 gpioset -m signal $(gpiofind PR.00)=1 # 点亮白色 gpioset -m signal $(gpiofind PR.00)=0 # 关闭白色 ``` --- --- url: /aipod/benchmark/gpt-oss-120b.md --- # GPT-OSS 120B ## Benchmark 以下数据来自 `~/lzc-aipod-benchmark/gpt-oss-120b-moe/cumulative-report.md`,对应模型为 `gpt-oss-120b-mxfp4`,runtime 为 `llama.cpp`,权重格式为 `GGUF`。 | Device | Runtime | Weight | Single Stream | Peak Aggregate | Power | GPU Temp | |:-------|:--------|:-------|:--------------|:---------------|:------|:---------| | Thor | `llama.cpp` | `GGUF mxfp4` | `34.615 tok/s` (`g512 / c1`) | `178.536 tok/s` (`g64 / c16`) | `31.793 W` | `67 C` | | Thor T5000 | `llama.cpp` | `GGUF mxfp4` | `35.088 tok/s` (`g1024 / c1`) | `330.602 tok/s` (`g64 / c48`) | `31.261 W` for latest `c1`; `54.419 W` at `c48` | `56 C` for latest `c1`; `57.593 C` at `c48` | | Jetson AGX Orin | `-` | `-` | `no data` | `no data` | `-` | `-` | | Thor T4000 | `-` | `-` | `no data` | `no data` | `-` | `-` | ## Notes * `THOR` 当前单路最佳结果来自 `g128 / g256 / g512 / g1024` 的生成长度矩阵,其中 `g512 / c1` 为 `34.615 tok/s`。 * `T5000` 当前默认单路口径固定为 `g1024 / c1 / server_parallel=1 / 300s`,最新复测结果为 `35.088 tok/s`。 * `T5000` 的高并发峰值来自 `c40 / c48` 扩展扫描,其中 `g64 / c48` 达到 `330.602 tok/s`。 * 当前 benchmark 仓库里 `Orin` 与 `T4000` 还没有 `gpt-oss-120b` 的正式结果,因此这里只保留 `no data`。 * 数据来源:`/home/catdog/lzc-aipod-benchmark/gpt-oss-120b-moe/cumulative-report.md` --- --- url: /aipod/olddocs/jetson.md --- # Jetson AGX orin `LLM` 模型速度的关键在内存的带宽速度。假设推断的时候,每个token都会被查询一次。 像 NousResearch/Llama-2-7b-chat-hf 这个模型,官方发布的是 fp16 (模型大小为 14G), 量化成 int8 为7G。最后在 200/7 ~ 30 tokens/s 的速度. jetson 中的 PCIE 有两个 PCIE 4.0 8通道,根据网上的数据 PCIE 4.0x8 通道的速度大约为 (16 GB/s) --- --- url: /aipod/issues/jetson-suspend-hdmi-black-screen.md --- # Jetson suspend 后 HDMI 黑屏 ## 现象 算力舱执行 suspend 后可以从网络上恢复,SSH 和系统服务正常,但 HDMI 显示器上的 Ubuntu 桌面没有恢复,屏幕保持黑屏。 典型表现: * 可以通过 `ssh nvidia@<设备 IP>` 登录设备。 * `gdm` 或 `display-manager` 仍然是 `active`。 * X11 会话仍然存在。 * HDMI 显示器可以被识别,例如 `xrandr` 仍显示 `HDMI-0 connected`。 * 本地 HDMI 画面黑屏,GNOME 桌面没有正常渲染。 ## 排查命令 通过 SSH 登录设备: ```bash ssh nvidia@192.168.1.221 ``` 查看系统版本: ```bash cat /etc/nv_tegra_release uname -a ``` 查看图形服务状态: ```bash systemctl status gdm --no-pager systemctl status display-manager --no-pager loginctl list-sessions ``` 查看 HDMI 输出状态: ```bash export DISPLAY=:0 export XAUTHORITY=/run/user/1000/gdm/Xauthority xrandr --current ``` 查看 suspend/resume 和图形栈相关日志: ```bash journalctl -b --no-pager | egrep -i "suspend|resume|PM:|gnome-shell|mutter|Xorg|nvgpu|semaphore|error notifier" | tail -n 200 ``` 如果日志中出现类似内容,通常说明系统已经恢复,但 NVIDIA 图形栈或 GNOME Shell 没有正常恢复: ```text nvgpu ... semaphore acquire timeout nvgpu ... error notifier set to 24 ... owned by gnome-shell gnome-shell / mutter DisplayConfig timeout ``` ## 原因 这个问题不是整机没有从 suspend 中醒来,而是图形栈恢复异常。 在 Jetson Linux R36.4.3 / JetPack 6.2 环境中,suspend/resume 后可能出现 NVIDIA `nvgpu`、Xorg、GNOME Shell / Mutter 状态不一致。系统、网络和服务已经恢复,但 HDMI 桌面没有重新渲染,最终表现为黑屏。 NVIDIA 论坛中也有 R36.4.3 / JetPack 6.2 上 HDMI resume 黑屏的已知问题记录,长期建议升级到 Jetson Linux R36.5 / JetPack 6.2.2 或更新版本。 参考: * https://forums.developer.nvidia.com/t/orin-nano-8g-hdmi-blank-when-resume/337317 * https://forums.developer.nvidia.com/t/after-suspend-wakeup-hdmi-can-not-display/353817 * https://developer.nvidia.com/embedded/jetson-linux-r365 ## 临时方案一:手动重启 gdm 黑屏后,如果 SSH 还能登录,可以手动重启图形登录服务: ```bash sudo systemctl restart gdm ``` 也可以使用通用服务名: ```bash sudo systemctl restart display-manager ``` 这个操作会重启 HDMI 上的本地图形会话,当前桌面里的应用会被中断。 ## 临时方案二:resume 后自动重启 gdm 如果暂时不能升级系统,但希望每次从 suspend 恢复后自动把桌面拉起来,可以添加 systemd sleep hook: ```bash sudo tee /lib/systemd/system-sleep/restart-gdm-after-resume >/dev/null <<'EOF' #!/bin/sh case "$1" in post) systemctl restart gdm ;; esac EOF sudo chmod +x /lib/systemd/system-sleep/restart-gdm-after-resume ``` 删除这个临时方案: ```bash sudo rm -f /lib/systemd/system-sleep/restart-gdm-after-resume ``` 注意:这个方案只是绕过问题。每次 resume 后都会重启本地图形会话。 ## 临时方案三:禁用休眠 如果设备主要作为服务设备运行,不依赖本地 HDMI 桌面,最稳的临时处理是禁用 sleep / suspend,避免再次触发黑屏: ```bash sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target ``` 验证: ```bash systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target ``` 恢复休眠能力: ```bash sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target ``` ## 长期方案 升级到 Jetson Linux R36.5 / JetPack 6.2.2 或更新版本,并重新验证 suspend/resume 后 HDMI 桌面是否恢复。 --- --- url: /aipod/thor/2026-03-17-validation.md --- # Jetson Thor Validation Report * Test date: `2026-03-17` * Thor reference doc: `/home/compiler/lzc-aipod-docs/thor/benchmark.md` * Controller: `192.168.1.104` * Machine inventory and credentials: `/home/compiler/lzc_thor/inventory/hosts.private.yaml` * pip cache endpoint: `http://192.168.1.104:5000/index/` * Docker proxy template: `http://192.168.1.128:1085` ## Test Objects `T4000` is `192.168.1.184`, reports as `NVIDIA Jetson Thor Developer Kit`, runs `Ubuntu 24.04.3 LTS`, `L4T R38.4.0`, kernel `6.8.12-tegra`, `12` CPU cores, and about `59.9 GiB` system memory. `T5000` is `192.168.1.213`, reports as `NVIDIA Jetson AGX Thor Developer Kit`, runs `Ubuntu 24.04.3 LTS`, `L4T R38.4.0`, kernel `6.8.12-tegra`, `14` CPU cores, and about `122.8 GiB` system memory. ## Environment and Setup The controller-side proxpi-compatible pip cache was deployed and used through `http://192.168.1.104:5000/index/`. Remote Docker daemon proxy configuration was aligned to the user-specified upstream proxy `http://192.168.1.128:1085`, with `*.lazycat.cloud` and `*.heiyu.space` excluded through `no_proxy`. Both Thor machines passed network, container, CUDA, and multimedia baseline checks. The host-side framework check still reports `torch=False` and `vllm=False`, which is expected for this run because the framework validation was performed inside containers rather than by installing those packages directly on the host. Containerized PyTorch validation passed on both machines, and `vLLM 0.16.0` import validation also passed on both machines. The main framework exception is `T5000` `vllm_container_serve`: the container import succeeded, but the HTTP endpoint on `127.0.0.1:18000` never became reachable during the validation window. This should be treated as a deployment issue rather than a CUDA or TensorRT issue. ## Model Inference Results The image and embedding workloads completed successfully on both machines. On `T4000`, the text embedding workload `clip_text_best_b8` sustained about `197.292 qps`, stayed below `61C`, and peaked at `33.15W`. The vision embedding workload `clip_vision_fp16_b8` sustained about `45.0585 qps`, reached `78C`, and peaked at `58.38W`. `dinov2_fp16_b8` was the slowest sustained image embedding run on `T4000` at `7.124 qps`, with `78C` and `58.36W` peak telemetry. `resnet50_best_b8` reached about `468.935 qps` and `yolov8s_best_b1` reached about `912.803 qps`, both with notably lower thermals than the embedding models. On `T5000`, isolated image and embedding workloads were faster but also hotter. `clip_text_best_b8` sustained about `253.238 qps`, peaked at `79C`, and hit `52.53W`. `clip_vision_fp16_b8` reached about `61.1175 qps`, but it also pushed the board to `105C` and `95.42W`, making it one of the hottest non-synthetic workloads in this test set. `dinov2_fp16_b8` sustained about `4.308 qps`, peaked at `80C`, and hit `62.51W`. `resnet50_best_b8` reached about `616.558 qps` and `yolov8s_best_b1` about `531.15 qps`. The aggregate pattern is straightforward: `T5000` offers higher headroom for large-model and vision workloads, but it also reaches thermal limits much faster than `T4000`. `T4000` is slower, yet materially easier to cool under sustained load. ## llama.cpp Results `llama.cpp` was built in Docker on `T5000` with image `thor-llamacpp:cuda130`. The smaller baseline model `Qwen2.5-1.5B-Instruct-Q4_K_M` completed the full `2,4,6,8,16,32` concurrent batched benchmark without tuning drama. Under the baseline setting of `1024` prompt tokens and `256` generated tokens, total throughput climbed to `3248.14 tok/s` at `32` parallel requests while staying relatively cool at `51C`, `35.82W`, and fan PWM `94`. The supplementary model requested by the user, `Jackrong/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled-GGUF`, was added and tested on `T5000`. Because both Thor machines only had `14G` to `17G` free before cleanup, the larger `Q4` quantizations were not safe to stage. The selected file was `Qwen3.5-27B.Q3_K_S.gguf`, which is about `11.23 GiB`. `T5000` needed image cleanup before the model could be copied safely. The first 27B attempt used `ctx=32768` and `batch=256`; it failed during `llama_decode()` with `failed to find a memory slot for batch of size 256`. The stable configuration was `2048` prompt tokens, `512` generated tokens, `ctx=131072`, `batch=128`, and `ubatch=64`. Under that configuration, the six requested concurrency levels all completed. Total throughput was `38.30 tok/s` at parallel `2`, `58.81 tok/s` at `4`, `56.30 tok/s` at `6`, `40.39 tok/s` at `8`, `99.62 tok/s` at `16`, and `125.75 tok/s` at `32`. The peak telemetry for this 27B run was `80C`, `62.19W`, CPU `69.94C`, and fan PWM `255`. The 27B result is important for planning. It proves that `T5000` can run a meaningful long-context, long-generation `27B` GGUF workload entirely through `llama.cpp`, but only after model quantization, root-disk headroom, and KV-cache sizing are handled deliberately. It is not a drop-in replacement for the small-model path. ## TTS Result The planned TTS target was `registry.lazycat.cloud/x/lzc-aipod-tts:763ebf7` using the `Kokoro` service path. This scenario did not pass on either machine. On `T5000`, the image pulled successfully, but the container failed during model warmup with `CUDA error: no kernel image is available for execution on the device`. This means the current TTS image is not built with kernels that can execute on the tested Thor GPU architecture. The failure is not a transient network issue. On `T4000`, the same image could not be unpacked because overlayfs ran out of space during layer extraction. Registry connectivity itself was normal, so the blocker there is disk headroom, not registry access. The practical conclusion is that the current TTS image cannot be marked Thor-ready. It needs a Thor-compatible rebuild, and `T4000` also needs either more free root space or a smaller image before the scenario can be rerun. ## Full Load Result The combined CPU plus GPU stress scenario was executed for `600` seconds on both machines. The workload used full-core Python floating-point loops on the CPU side and simultaneous `dinov2_fp16_b8` plus `clip_vision_fp16_b8` TensorRT runs on the GPU side. `T4000` completed the full-load run with average GPU utilization around `89.923%`. It peaked at `71C` GPU temperature, `61.406C` CPU temperature, `54.88W` GPU power, fan PWM `255`, `54.879W` `VDD_GPU`, and `11.105W` `VIN_SYS_5V0`. This is a heavy but still controlled thermal profile. `T5000` also completed the full-load run, but it reached a far more aggressive thermal envelope. Average GPU utilization was about `91.009%`, while peak GPU temperature reached `110C`, CPU temperature reached `97.406C`, and peak GPU power reached `95.33W`. Fan PWM hit the ceiling at `255`. This run is the clearest indication that `T5000` has the performance headroom for aggressive workloads, but it also needs much stricter cooling and thermal policy if similar mixed CPU plus GPU loads are expected in production. ## Overall Conclusion The validation confirms that both Thor machines are usable for containerized CUDA, TensorRT, PyTorch, image embedding, text embedding, and `llama.cpp` workloads. `T4000` is easier to keep within a reasonable thermal envelope and is a safer default for medium workloads. `T5000` is the right target for larger LLM inference, but it regularly drives the fan to maximum and can exceed `100C` under the heaviest vision or mixed-load scenarios. The main blockers that remain are operational rather than foundational. `vLLM` serving still needs a separate startup fix on `T5000`. The current `Kokoro` TTS image is not compatible with Thor GPUs, and the `T4000` root filesystem is too tight for large image extraction. For the supplementary 27B `llama.cpp` path, the validated combination is `Q3_K_S` quantization on `T5000` with explicit disk cleanup and enlarged KV-cache settings. --- --- url: /aipod/thor/2026-03-18-validation.md --- # Jetson Thor Validation Report 测试日期为 `2026-03-18`,controller 为 `192.168.1.104`。pip 缓存入口使用 `http://192.168.1.104:5000/index/`,Docker 代理模板为 `http://192.168.1.128:1085`。 本次验证覆盖基础联通性、Docker 与 pip 代理、CUDA 与 TensorRT、图像与文本 embedding、llama.cpp 并发压测、Kokoro TTS 容器以及 CPU+GPU 满载热稳定性场景。详细原始数据仍保存在 CSV/JSON 中,下面只保留叙述性结论。 ## T4000 T4000 位于 `tegra-ubuntu-t4000.lan`,识别到的机型是 `NVIDIA Jetson Thor Developer Kit`,系统版本为 `Ubuntu 24.04.3 LTS`,L4T/JetPack 信息是 `# R38 (release), REVISION: 4.0, GCID: 43443517, BOARD: generic, EABI: aarch64, DATE: Wed Dec 31 00:15:19 UTC 2025`,内核 `6.8.12-tegra`,CPU 核心数 `12`,内存总量 `59.9GiB`。 基础能力方面,pip 代理 `PASS`,Docker 拉取 `PASS`,CUDA `PASS`,PyTorch 容器 `PASS`,TensorRT `PASS`,多媒体 `PASS`,vLLM/LLM 推理 `PASS`。 模型推理结果包括:clip\_text\_best\_b8 吞吐 195.747 qps,均值时延 5.107 ms;clip\_vision\_fp16\_b8 吞吐 45.477 qps,均值时延 21.988 ms;dinov2\_fp16\_b8 吞吐 7.033 qps,均值时延 142.176 ms;resnet50\_best\_b8 吞吐 470.882 qps,均值时延 2.122 ms;yolov8s\_best\_b1 吞吐 907.689 qps,均值时延 1.1 ms。 qwen25\_1p5b\_q4km 覆盖并发 2/4/6/8/16/32,最佳总吞吐 2560.92 tokens/s (parallel=32), 峰值温度 37C,峰值功耗 22.04W。 qwen35\_27b\_claude46\_q3ks 覆盖并发 2/4/6/8/16/32,最佳总吞吐 83.53 tokens/s (parallel=32), 峰值温度 45C,峰值功耗 22.06W。 kokoro\_tts 完成 次请求,平均时延 s,峰值温度 C,峰值功耗 W。 持续 600s 的 CPU+GPU 满载测试已执行,GPU 峰值温度 75C,CPU 峰值温度 67.718C,tj 峰值 76.031C,GPU 峰值功耗 57.21W,VIN\_SYS\_5V0 峰值 12.014W,风扇 PWM 峰值 。 当前聚合到的整机峰值温度为 `75`C,峰值功耗为 `57.26`W,风扇 PWM 峰值为 `124`。 补充说明:host torch not installed; host vllm not installed; vLLM verified by import; serve test pending or failed; TTS container test pending or failed ## T5000 T5000 位于 `tegra-ubuntu-t5000.lan`,识别到的机型是 `NVIDIA Jetson AGX Thor Developer Kit`,系统版本为 `Ubuntu 24.04.3 LTS`,L4T/JetPack 信息是 `# R38 (release), REVISION: 4.0, GCID: 43443517, BOARD: generic, EABI: aarch64, DATE: Wed Dec 31 00:15:19 UTC 2025`,内核 `6.8.12-tegra`,CPU 核心数 `14`,内存总量 `122.8GiB`。 基础能力方面,pip 代理 `PASS`,Docker 拉取 `PASS`,CUDA `PASS`,PyTorch 容器 `PASS`,TensorRT `PASS`,多媒体 `PASS`,vLLM/LLM 推理 `PASS`。 模型推理结果包括:clip\_text\_best\_b8 吞吐 240.08 qps,均值时延 4.164 ms;clip\_vision\_fp16\_b8 吞吐 66.363 qps,均值时延 15.067 ms;dinov2\_fp16\_b8 吞吐 9.651 qps,均值时延 103.612 ms;resnet50\_best\_b8 吞吐 597.18 qps,均值时延 1.673 ms;yolov8s\_best\_b1 吞吐 1279.56 qps,均值时延 0.78 ms。 qwen25\_1p5b\_q4km 覆盖并发 2/4/6/8/16/32,最佳总吞吐 3249.66 tokens/s (parallel=32), 峰值温度 45C,峰值功耗 35.07W。 qwen35\_27b\_claude46\_q3ks 覆盖并发 2/4/6/8/16/32,最佳总吞吐 124.18 tokens/s (parallel=32), 峰值温度 60C,峰值功耗 37.69W。 kokoro\_tts 完成 次请求,平均时延 s,峰值温度 C,峰值功耗 W。 持续 600s 的 CPU+GPU 满载测试已执行,GPU 峰值温度 112C,CPU 峰值温度 98.843C,tj 峰值 111.906C,GPU 峰值功耗 95.5W,VIN\_SYS\_5V0 峰值 18.086W,风扇 PWM 峰值 。 当前聚合到的整机峰值温度为 `112`C,峰值功耗为 `95.59`W,风扇 PWM 峰值为 `73`。 补充说明:host torch not installed; host vllm not installed; vLLM verified by import; serve test pending or failed; TTS container test pending or failed ## lzc-pod-QlqOCy lzc-pod-QlqOCy 位于 `lzc-pod-QlqOCy.lan`,识别到的机型是 `NVIDIA Jetson AGX Thor Developer Kit`,系统版本为 `Ubuntu 24.04.3 LTS`,L4T/JetPack 信息是 `# R38 (release), REVISION: 2.2, GCID: 42205042, BOARD: generic, EABI: aarch64, DATE: Thu Sep 25 22:47:11 UTC 2025`,内核 `6.8.12-tegra`,CPU 核心数 `14`,内存总量 `122.8GiB`。 基础能力方面,pip 代理 `PASS`,Docker 拉取 `PASS`,CUDA `PASS`,PyTorch 容器 ``,TensorRT `PASS`,多媒体 `PASS`,vLLM/LLM 推理 ``。 模型推理结果包括:clip\_text\_best\_b8 吞吐 257.857 qps,均值时延 3.877 ms;clip\_vision\_fp16\_b8 吞吐 65.38 qps,均值时延 15.294 ms;dinov2\_fp16\_b8 吞吐 10.608 qps,均值时延 94.269 ms;resnet50\_best\_b8 吞吐 636.926 qps,均值时延 1.569 ms;yolov8s\_best\_b1 吞吐 1393.67 qps,均值时延 0.716 ms。 未执行 llama.cpp 压测。 未执行 TTS 容器测试。 未执行 CPU+GPU 满载测试。 当前聚合到的整机峰值温度为 `C,峰值功耗为 `W,风扇 PWM 峰值为 `83`。 补充说明:无额外异常说明。 ## lzc-pod-juyIZt lzc-pod-juyIZt 位于 `lzc-pod-juyIZt.lan`,识别到的机型是 `NVIDIA Jetson AGX Orin Developer Kit`,系统版本为 `Ubuntu 22.04.5 LTS`,L4T/JetPack 信息是 `# R36 (release), REVISION: 4.3, GCID: 38968081, BOARD: generic, EABI: aarch64, DATE: Wed Jan 8 01:49:37 UTC 2025`,内核 `5.15.148-tegra`,CPU 核心数 `12`,内存总量 `61.4GiB`。 基础能力方面,pip 代理 `FAIL`,Docker 拉取 `FAIL`,CUDA `PASS`,PyTorch 容器 `FAIL`,TensorRT `PASS`,多媒体 `PASS`,vLLM/LLM 推理 `FAIL`。 模型推理结果包括:clip\_text\_best\_b8 吞吐 71.087 qps,均值时延 14.066 ms;clip\_vision\_fp16\_b8 吞吐 19.281 qps,均值时延 51.864 ms;dinov2\_fp16\_b8 吞吐 3.244 qps,均值时延 308.234 ms;resnet50\_best\_b8 吞吐 501.002 qps,均值时延 1.995 ms;yolov8s\_best\_b1 吞吐 566.905 qps,均值时延 1.763 ms。 未执行 llama.cpp 压测。 未执行 TTS 容器测试。 未执行 CPU+GPU 满载测试。 当前聚合到的整机峰值温度为 `C,峰值功耗为 `W,风扇 PWM 峰值为 `130`。 补充说明:Docker Hub pull timeout; used preloaded/synced images; host torch not installed; host vllm not installed; vLLM verified by import; serve test pending or failed ## Raw Outputs * Comparison table: `/home/compiler/lzc_thor/results/summary/comparison.csv` * Workload table: `/home/compiler/lzc_thor/results/summary/workloads.csv` * Raw artifacts root: `/home/compiler/lzc_thor/results/raw` --- --- url: /aipod/lc-x5.md --- # LC-X5 LC-X5 是算力舱设备系列中的一款设备,可以和微服配合使用,为 AI 应用和模型服务提供算力。 更多 LC-X5 的系统使用、刷机和服务部署文档会在后续补充。 --- --- url: /aipod/lc-x5/flash.md --- # LC-X5 刷机指南(NVIDIA Jetson Thor) 本文介绍如何使用 `catdogai/lzc-x5-flash:39.2` 为 LC-X5 刷入系统,包括默认模式、为 LVM 准备磁盘布局、仅重刷 NVMe,以及仅更新 QSPI 启动固件。 命令以当前 LC-X5 刷机流程为基础,官方参数说明以 **Jetson Linux R39.2** 为准。R39.2 对应 JetPack 7.2,目标系统基于 Ubuntu 24.04、Linux 6.8,参见 [NVIDIA JetPack 7.2 下载与版本说明](https://developer.nvidia.com/embedded/jetpack/downloads/archive-7.2)。不要混用其他版本的 BSP、rootfs、QSPI 固件或其他机型的镜像。 > 刷机会重写所选存储上的分区和系统,可能丢失已有数据。执行前备份需要保留的文件,并确认模块型号、目标 SSD 和分区配置。双盘 LVM 的初始化、扩容和恢复也涉及磁盘写入。 ## 1. 选择刷机方式 | 需求 | 使用方式 | 主要影响 | | --- | --- | --- | | 首次安装或完整重装 LC-X5 | 默认刷机模式 | 按板级配置更新启动固件和系统盘 | | 建立 APP 恢复系统,并准备 LVM 运行系统 | LC-X5 自定义 NVMe 分区模式 | 按自定义 XML 刷盘;启动后继续完成 LVM 初始化 | | 启动固件正常,只需要重装 SSD 系统 | `--external-only` | 重刷指定 NVMe;不更新 QSPI | | 修复或更新启动固件 | `--qspi-only` | 更新 QSPI 启动固件;不重装 NVMe rootfs | ## 2. 刷机前准备 ### 2.1 主机与设备 准备以下设备和环境: * 一台 Linux 刷机主机。建议使用 x86\_64 Ubuntu 22.04 或 24.04;R39.2 官方刷机主机发行版要求见 [Release Notes](https://docs.nvidia.com/jetson/archives/r39.2/ReleaseNotes/Jetson_Linux_Release_Notes_r39.2.pdf)。 * 可正常运行的 Docker,以及运行带 USB 设备访问权限的容器的权限。 * 已安装目标 NVMe SSD 的 LC-X5,以及稳定的电源。 * 一根支持数据传输的 USB 线,连接主机与设备的 Recovery 接口。建议直接连接主机。 * 显示器和键盘,或能够查看设备启动日志的串口控制台。 * 足够的主机磁盘空间,存放容器镜像、BSP、生成的镜像和日志。先检查 `df -h`,空间需求随 rootfs 和分区大小变化。 以下容器命令按原生 Linux 主机编写。容器仍依赖主机的 USB 和内核环境,不能把 Windows/macOS 上的 Docker Desktop 直接视为同等刷机环境。 首次操作时只连接一台待刷机设备。刷机主机上的 `lsblk` 显示的是**主机磁盘**,不能据此判断通过 Recovery USB 连接的 Jetson 内部 SSD。 ### 2.2 选择模块配置 | Jetson Thor 模块 | NVIDIA 模块编号 | 板级配置 `` | Force Recovery USB ID | | --- | --- | --- | --- | | T5000,128 GB 内存 | P3834-0008 | `jetson-agx-thor-devkit` | `0955:7026` | | T4000,64 GB 内存 | P3834-0000 | `jetson-agx-thor-t4000` | `0955:7226` | 配置名和 USB ID 来自 [NVIDIA R39.2 Quick Start](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/IN/QuickStart.html)。结合模块标签与配套 BSP 确认型号;自定义载板还需要设备厂商提供的适配,不能仅凭相同模块就套用其他载板的 BSP。 ## 3. 进入 Force Recovery 模式 ### 3.1 使用实体按键 实体按键需要拆开底部的盖子, 设备已经上电时: 1. 按住 **Force Recovery / RECOVERY** 按键。 2. 保持按住 RECOVERY,短按并松开 **RESET** 按键。 3. 松开 RECOVERY。 关机状态下,也可以按住 RECOVERY,按下并松开 POWER,再松开 RECOVERY。 ### 3.3 使用 UEFI 菜单 设备还能进入 UEFI 时,可尝试当前 LC-X5 流程中的菜单入口: 1. 开机看到 NVIDIA 标志时,反复按 **Esc**。 2. 进入 **Device Manager → NVIDIA Configuration → Boot Configuration**。 3. 启用 **Boot Into Recovery**,保存设置并继续或重启。 菜单名称和位置可能随固件变化。操作后必须在主机确认 Recovery USB ID;若未出现,使用实体按键进入 Force Recovery。不要把 Linux recovery shell 或 LC-X5 的 `APP recovery` 当成已经进入 USB 刷机模式。 ### 3.4 确认主机识别到设备 在**刷机主机**上执行: ```bash lsusb -d 0955: ``` T5000 应能看到 `0955:7026`,T4000 应能看到 `0955:7226`,设备名称可能带有 `NVIDIA Corp.` 或 `APX`。没有对应设备时,先检查数据线、Recovery 接口、供电和按键操作,再执行刷机命令。 ## 4. 启动容器并准备 BSP ### 4.1 创建工作目录 在**刷机主机**上创建用于保存日志和文件的目录: ```bash mkdir -p ~/lc-x5-flash/workspace cd ~/lc-x5-flash ``` 这里的 `workspace` 是主机目录,进入容器后映射为 `/workspace`。放在 `/workspace` 中的文件会保留在主机上。 ### 4.2 启动刷机容器 ```bash docker run -it --rm \ --privileged \ --network host \ -v /dev/bus/usb:/dev/bus/usb \ -v /dev:/dev \ -v "${PWD}/workspace:/workspace" \ catdogai/lzc-x5-flash:39.2 \ bash ``` 若当前用户没有 Docker 权限,在 `docker run` 前加 `sudo`。 | 参数 | 用途 | | --- | --- | | `--privileged` | 允许刷机工具访问 USB、设备节点并执行所需系统操作 | | `--network host` | 使用主机网络环境 | | `/dev/bus/usb`、`/dev` 挂载 | 访问 Recovery USB,以及设备重新枚举后的节点 | | `workspace` 挂载 | 保留日志和手动保存的产物 | | `--rm` | 退出时删除该容器 | > `/workspace` 挂载不会自动保存镜像中的 `/Linux_for_Tegra`。容器退出后,未挂载目录中的修改和新文件会随容器删除;需要保留的日志或产物应在退出前复制到 `/workspace`。 ### 4.3 准备 LC-X5 刷机环境 以下命令在**容器内**执行;每一步成功后再执行下一步: ```bash cd /Linux_for_Tegra sudo ./tools/l4t_flash_prerequisites.sh sudo ./apply_binaries.sh --openrm sudo ./twsetup/install.sh ``` * `l4t_flash_prerequisites.sh` 安装 NVIDIA 刷机依赖。 * `apply_binaries.sh` 将 NVIDIA 二进制组件应用到 rootfs。R39.2 的 Thor 流程仍接受 `--openrm`,但该参数不再改变结果,见 [官方 BSP 准备步骤](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/IN/QuickStart.html)。 * `twsetup/install.sh` 是配套安装步骤,按镜像中的提示完成;它不是 NVIDIA 标准 BSP 命令。 检查关键路径和当前脚本支持的参数: ```bash pwd ls -ld bootloader kernel rootfs nv_tegra ls -l l4t_initrd_flash.sh jetson-agx-thor-*.conf sudo ./l4t_initrd_flash.sh -h lsusb -d 0955: ``` 本文沿用 LC-X5 BSP 顶层的 `./l4t_initrd_flash.sh` 入口。官方工具目录是 `tools/kernel_flash/`;如果使用原版 BSP,按实际文件位置和该版本教程调用脚本。配套脚本缺失时,先核对镜像,不要直接跳过安装步骤。 ::: details 确认 SSD、容量与分区布局 ### 1 确认目标盘 `--external-device nvme0n1` 指的是 **Jetson 端**的设备名,不是刷机主机的磁盘名。安装多块 NVMe 时,不能仅凭插槽位置认定某块盘一定是 `nvme0n1`。 如果设备还能正常启动,在 **LC-X5 系统内**查看: ```bash lsblk -d -o NAME,PATH,SIZE,MODEL,SERIAL lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,PARTLABEL,MOUNTPOINTS cat /sys/block/nvme0n1/size sudo blockdev --getsize64 /dev/nvme0n1 ``` 记录目标 SSD 的型号、序列号和实际容量。正常系统与刷机 initrd 中的枚举顺序也应核对,特别是双盘场景。 ### 2 在刷机 initrd 中查询容量 如果设备已经进入配套刷机工具启动的 initrd,且该阶段已提供 ADB,可使用配套 ADB 工具。在**容器内 `/Linux_for_Tegra`** 执行: ```bash ADB=./unified_flash/out/bsp_images/tools/flashtools/flash/adb "${ADB}" devices ``` 将下面的占位符替换为上一步显示的设备序列号: ```bash THOR_SERIAL='<设备序列号>' "${ADB}" -s "${THOR_SERIAL}" shell \ /tmp/wr_sh.sh cat /sys/block/nvme0n1/size "${ADB}" -s "${THOR_SERIAL}" shell \ /tmp/wr_sh.sh cat /sys/block/nvme1n1/size ``` 第二条容量查询只适用于装有第二块 NVMe 的情况。ADB 路径和 `/tmp/wr_sh.sh` 属于配套刷机环境;文件不存在时不能直接套用。 **Force Recovery 的 BootROM 阶段不会提供上述 Linux sysfs 查询接口。** 只有刷机工具已启动 initrd,并且 `adb devices` 能识别设备时,才能使用这些命令。 ### 3 设置 `EXT_NUM_SECTORS` `EXT_NUM_SECTORS` 指定外部存储布局使用的扇区数量。对于本文使用的 512 字节扇区布局: ```text 字节容量 = EXT_NUM_SECTORS × 512 GiB 容量 = EXT_NUM_SECTORS × 512 ÷ 1024³ ``` Linux 的 `/sys/block/nvme0n1/size` 按 512 字节单位返回容量。核对 XML 的 `sector_size` 后,可将该读数用于对应布局,不要使用 SSD 包装上的标称 GB 容量估算。 | 扇区数示例 | 对应容量 | 使用条件 | | --- | --- | --- | | `167772160` | 80 GiB | 明确使用 80 GiB 布局,且目标 SSD 容量不小于该值 | | `488397168` | 约 232.89 GiB,即 250.06 GB | 目标盘实际读数相符,或明确使用不超过实际容量的该布局 | 这些数字不适用于所有 LC-X5。比 SSD 更大的值可能导致分区越界和刷机失败;更小的值也不会自动使用全部剩余空间。 R39.2 默认 Thor 布局假定 NVMe 大于约 234 GiB;更小的盘需要设置实际容量,详见 [R39.2 Release Notes 中的 NVMe 容量限制](https://docs.nvidia.com/jetson/archives/r39.2/ReleaseNotes/Jetson_Linux_Release_Notes_r39.2.pdf)。 `-S ` 控制 APP 分区大小,与整盘扇区数量不同。调整容量时,要保证 APP、其他分区和 GPT 都能放入目标盘,不能把 APP 设置为整盘大小。布局字段见 [NVIDIA 分区配置说明](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/AR/BootArchitecture/PartitionConfiguration.html)。 ### 4 区分 LC-X5 自定义 XML 与 NVIDIA 原版 XML 本文的 LC-X5 自定义分区命令使用: ```text /Linux_for_Tegra/flash_l4t_t264_nvme.xml ``` NVIDIA 原版 NVMe 配置通常位于: ```text /Linux_for_Tegra/tools/kernel_flash/flash_l4t_t264_nvme.xml ``` 两者即使文件名相同,内容和分区用途也可能不同。建立 LC-X5 的 APP recovery 与 LVM runtime 时,必须使用配套自定义布局,不能用原版 XML 替代。 在容器中检查自定义文件: ```bash test -f flash_l4t_t264_nvme.xml grep -nE 'sector_size|num_sectors|partition name=|APP|DATA_PV' \ flash_l4t_t264_nvme.xml ``` 后续运行 `lzc-thor provision` 时,布局必须包含该工具要求的 `APP` 和 `DATA_PV` 分区。XML 缺失或布局不符时,先取得正确的配套文件。 ::: ## 5. 执行刷机 本节各方案按需求选择,**不要依次全部执行**。命令都在**容器内 `/Linux_for_Tegra`** 执行,设备应处于 Force Recovery 模式。更换方案、容量、模块或 XML 后,应重新生成对应镜像,不要直接复用上一次的产物。 官方参数见 [Flashing Support for Jetson Thor(R39.2)](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/SD/FlashingSupportJetsonThor.html)。 ### 5.1 自定义 NVMe 分区模式:为 LVM 准备布局 此模式用于准备 LC-X5 的 LVM 存储布局。命令首先按 LC-X5 自定义 XML 刷写指定 NVMe;**`--external-device` 本身不会自动把两块 SSD 合并为 LVM**。 T5000: ```bash sudo ./l4t_initrd_flash.sh \ --external-device nvme0n1 \ -c flash_l4t_t264_nvme.xml \ jetson-agx-thor-devkit external ``` T4000: ```bash sudo ./l4t_initrd_flash.sh \ --external-device nvme0n1 \ -c flash_l4t_t264_nvme.xml \ jetson-agx-thor-t4000 external ``` 需要覆盖容量时,在 `sudo` 后加入 `EXT_NUM_SECTORS=<已核对的扇区数>`。 刷机并启动后,在 **LC-X5 上**检查布局和初始化状态: ```bash lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,PARTLABEL,MOUNTPOINTS findmnt / sudo lzc-thor provision status ``` 如果配套流程尚未建立 LVM runtime,按 [LC-X5 配置文档](./config.md) 中的 `provision` 流程完成 APP recovery 和 LVM 初始化。该流程分两个阶段,每阶段都会重启,不要跳过恢复环境检查。 根文件系统已经使用 `/dev/aipod_vg/lv_root` 后,才可按同一文档中的 `expand-lvm` 流程加入第二块盘。先核对其型号和序列号,再执行只读预演: ```bash # /dev/nvme1n1 仅为示例,请替换为已核对的第二块盘 sudo lzc-thor expand-lvm --disk /dev/nvme1n1 --dry-run ``` LVM 扩容不等于磁盘镜像或冗余保护。成员盘加入根文件系统后,缺失任意成员盘都可能导致无法启动。 ### 5.2 仅重刷 NVMe,不更新 QSPI 适用于 QSPI 正常且与目标 BSP 兼容,只需要重装 SSD 系统的情况。以下是 T4000 的示例: ```bash sudo ./l4t_initrd_flash.sh \ --external-only \ --external-device nvme0n1 \ -c flash_l4t_t264_nvme.xml \ jetson-agx-thor-t4000 external ``` T5000 将配置名替换为 `jetson-agx-thor-devkit`。 `--external-only` 仍会重写外部磁盘分区和系统,不是保留数据的增量更新。若 QSPI 损坏或版本不兼容,此方案不能修复启动固件。 生成外部镜像后,可辅助检查本次记录的目标设备: ```bash grep -n '^external_device=' tools/kernel_flash/images/external/flash.cfg ``` 该文件可能在生成镜像后才存在,也可能来自旧任务。它记录的是刷机配置,不能代替 SSD 型号、序列号和 initrd 枚举结果的核对。 ### 5.3 仅更新 QSPI 启动固件 QSPI 保存启动链固件,包括 bootloader 和 UEFI。此操作不重装 NVMe 系统,也不等同于更新 Linux 内核、GPU 驱动或 CUDA。 T5000: ```bash sudo ./l4t_initrd_flash.sh \ --qspi-only jetson-agx-thor-devkit mmcblk0p1 ``` T4000: ```bash sudo ./l4t_initrd_flash.sh \ --qspi-only jetson-agx-thor-t4000 mmcblk0p1 ``` `mmcblk0p1` 沿用官方 QSPI-only 示例中的位置参数;在此模式下,不应理解为要刷写一块 eMMC 系统盘。 仅在型号正确、固件与系统版本配套的前提下更新 QSPI。NVIDIA 标志后黑屏可能与启动固件有关,也可能是启动顺序、SSD、rootfs 或显示服务问题,应先按故障排查章节定位。 ### 5.4 保存刷机日志 将选定的命令接到 `tee` 可留存日志。以下以 T5000 默认模式为例,**它本身就会执行刷机**: ```bash set -o pipefail sudo ./l4t_initrd_flash.sh jetson-agx-thor-devkit internal \ 2>&1 | tee "/workspace/flash-$(date +%Y%m%d-%H%M%S).log" ``` `pipefail` 让管道保留刷机命令的失败状态,避免日志保存成功掩盖刷机失败。选择其他模式时替换为对应刷机命令,并保留容量和 XML 参数。 ## 6. 刷机后的启动与验证 ### 6.1 等待完成后重启 等工具明确报告成功并退出后,再重启设备。不要在写盘或更新 QSPI 时断电。 Thor 刷机后可能停留在 initrd 提示符,并不会自动进入安装好的 Linux。按 RESET,或在确认刷机结束后重新上电。参见 [官方刷机完成后的重启步骤](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/SD/FlashingSupportJetsonThor.html#flashing-the-target-device)。 若进入 UEFI Boot Manager,选择刚刷写的 NVMe 启动项。移除不再需要的 ISO 安装 U 盘,以免再次进入安装程序。 ### 6.2 完成首次启动配置 如果出现 NVIDIA/Ubuntu 首次启动向导,按提示创建用户、设置密码、时区和网络。如果镜像已预建用户,按交付说明登录,不要假定所有镜像使用相同的默认密码。 ### 6.3 验证版本、磁盘和 GPU 以下命令在**刷好后的 LC-X5** 上执行: ```bash cat /etc/nv_tegra_release uname -r dpkg-query -W nvidia-l4t-core findmnt / lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,PARTLABEL,MOUNTPOINTS df -h / nvidia-smi sudo nvpmodel -q ``` 检查要点: * `nvidia-l4t-core` 与目标 BSP 的 R39.2 版本一致;不能仅根据容器标签判断设备版本。 * 根文件系统来自预期磁盘或逻辑卷,容量与所选布局相符。 * GPU 能被驱动识别;`nvidia-smi` 失败时继续检查软件包和内核日志。 * LVM 模式进一步执行 `sudo pvs`、`sudo vgs`、`sudo lvs`,核对成员盘与 `/dev/aipod_vg/lv_root`。 常规初始化、JetPack 软件包、Docker、算力舱代理、VNC 和存储维护见 [LC-X5 配置与 `lzc-thor` 使用](./config.md)。先确认配套首次启动流程已完成,再按需初始化。 ## 7. 常见问题 ### 7.1 主机看不到 Recovery 设备 1. 确认 USB 线支持数据传输,连接到 Recovery 接口。 2. 保持供电,重新按 RECOVERY + RESET。 3. 在主机检查 `lsusb -d 0955:`;必要时用 `sudo dmesg -w` 观察 USB 插拔日志。 4. 主机已识别但容器看不到时,检查 `/dev` 挂载和容器权限,再在容器内执行 `lsusb`。 Recovery 模式下没有正常桌面显示,并不代表设备损坏;以 USB 枚举结果为准。 ### 7.2 等待目标启动、USB/ADB 超时 * 检查供电、数据线和设备重新枚举后的 USB 状态。 * 核对 T5000/T4000 配置是否选对,是否完成 BSP 准备。 * 断开不参与刷机的 USB 外设后重试,并重新进入 Force Recovery。 * 保存主机刷机日志与设备串口日志,定位是在生成镜像、启动 initrd、识别磁盘还是写入阶段失败。 ADB 查询仅适用于 initrd 已启动的阶段;`adb devices` 为空时,反复执行容量查询不能解决问题。 ### 7.3 分区容量超出磁盘、GPT 或写盘失败 重新读取目标盘 `/sys/block/nvme0n1/size`,核对 `EXT_NUM_SECTORS`、XML 的 `sector_size`/`num_sectors`,以及 APP 和其他分区总大小。即使标称 256 GB 的 SSD,也应使用实际读数检查是否满足默认布局。 参数或布局改变后重新生成镜像。不要只修改 `flash.cfg`,也不要复用不同容量磁盘生成的产物。 ### 7.4 NVIDIA 标志后黑屏或无法进入系统 按以下顺序排查: 1. 确认刷机成功,并在结束后手动重启过设备。 2. 按 Esc 查看 UEFI 能否识别 SSD,检查 Boot Manager 与启动顺序。 3. 如果仍可 SSH 登录,先检查显示与系统服务: ```bash systemctl --failed systemctl status display-manager sudo journalctl -b -p err --no-pager sudo dmesg -T | grep -Ei 'nvme|I/O error|ext4|nvidia|drm' ``` 4. 如果无法进入系统,用串口日志判断是否缺失 NVMe、找不到 rootfs,或停在恢复环境。双盘 LVM 还要检查所有成员盘是否连接。 5. 如果此前只刷过 NVMe,且证据指向 QSPI 损坏或固件与 BSP 不配套,再使用 QSPI-only 模式更新启动固件。 仅重刷 QSPI 无法修复损坏的 rootfs 或缺失的 LVM 成员盘。 ### 7.5 容器退出后找不到日志或产物 `--rm` 会删除容器;只有挂载目录中的文件仍在主机上。日志应保存到 `/workspace`,对应主机的 `~/lc-x5-flash/workspace`。退出前保存所需 XML、日志和本次参数记录。 ## 8. 官方教程与进一步阅读 与本文版本匹配的官方开发文档: | 官方资料 | 适合查看的内容 | | --- | --- | | [JetPack 7.2 下载与版本说明](https://developer.nvidia.com/embedded/jetpack/downloads/archive-7.2) | R39.2 对应关系、BSP 和 sample rootfs 下载 | | [Jetson Linux R39.2 Quick Start](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/IN/QuickStart.html) | BSP 准备、模块配置、Recovery 与 USB ID | | [Flashing Support for Jetson Thor](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/SD/FlashingSupportJetsonThor.html) | initrd、NVMe、QSPI-only、单分区与批量刷机 | | [Partition Configuration](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/AR/BootArchitecture/PartitionConfiguration.html) | XML、分区、扇区大小和容量设置 | | [UEFI Adaptation](https://docs.nvidia.com/jetson/archives/r39.2/DeveloperGuide/SD/Bootloader/UEFI.html) | 启动顺序、启动模式和恢复启动设置 | | [R39.2 Release Notes(PDF)](https://docs.nvidia.com/jetson/archives/r39.2/ReleaseNotes/Jetson_Linux_Release_Notes_r39.2.pdf) | 主机支持范围、容量限制和已知问题 | | [NVIDIA Jetson 文档归档](https://docs.nvidia.com/jetson/archives/) | 按实际 BSP 版本查找开发指南与发行说明 | 开发套件操作与其他安装方式: | 官方教程 | 适用场景 | | --- | --- | | [Thor 开发套件硬件布局](https://docs.nvidia.com/jetson/agx-thor-devkit/user-guide/latest/hardware_layout.html) | 参考开发套件的 Recovery、供电与调试接口 | | [Thor Quick Start:制作 ISO 安装 U 盘](https://docs.nvidia.com/jetson/agx-thor-devkit/user-guide/latest/quick_start.html) | 通过至少 16 GB 的 U 盘安装官方系统 | | [Thor BSP Setup:ISO、SDK Manager 与脚本](https://docs.nvidia.com/jetson/agx-thor-devkit/user-guide/latest/setup_bsp.html) | 比较安装路径及升级范围 | | [使用 SDK Manager 安装 Jetson 软件](https://docs.nvidia.com/sdk-manager/install-with-sdkm-jetson/index.html) | 图形化刷机和 SDK 安装 | | [SDK Manager 系统要求](https://docs.nvidia.com/sdk-manager/system-requirements/) | 主机资源和系统兼容要求 | | [Thor UEFI 与 Jetson ISO 兼容说明](https://docs.nvidia.com/jetson/agx-thor-devkit/user-guide/latest/twa_uefi_iso_compatibility.html) | 确认固件与 ISO 是否配套 | 带 `/latest/` 的开发套件教程会随 NVIDIA 新版本更新,可能已使用 R39.2.1 或后续版本。本文命令仍基于 `39.2` 配套镜像;下载或刷写前应核对版本,不要直接将新版文件替换到当前 BSP。 随包说明同样应保留参考:`Linux_for_Tegra/tools/kernel_flash/README_initrd_flash.txt`;备份与恢复操作说明位于 `Linux_for_Tegra/tools/backup-restore/README_backup_restore.txt`,使用前确认该版本支持当前设备和存储布局。 --- --- url: /aipod/lc-x5/config.md --- # LC-X5 配置与 `lzc-thor` 使用 LC-X5 刷机后,可以使用 `lzc-thor` 完成系统初始化、MGBE 网络测试、VNC 配置和磁盘管理。 除查看帮助外,本文命令均建议使用 `sudo` 执行。先用下面的命令确认当前系统支持的命令: ```bash sudo lzc-thor help ``` ## 命令速查 | 使用场景 | 命令 | | --- | --- | | 完成新设备的常规初始化 | `sudo lzc-thor all` | | 单独安装 JetPack 和监控工具 | `sudo lzc-thor packages` | | 修复克隆系统的机器和网卡身份 | `sudo lzc-thor clone-identity` | | 安装 Docker 和 NVIDIA 容器运行时 | `sudo lzc-thor docker` | | 安装独立的 `docker-compose` 命令 | `sudo lzc-thor docker-compose` | | 关闭 Ubuntu 自动升级 | `sudo lzc-thor disable-upgrades` | | 配置 MGBE 性能参数 | `sudo lzc-thor mgbe` | | 配置或执行 MGBE 带宽测试 | `sudo lzc-thor mgbe serve ...`、`sudo lzc-thor mgbe client ...` | | 配置时区、时间同步、最大频率和 Firefox | `sudo lzc-thor startup` | | 安装并启动 `lzc-ai-agent` | `sudo lzc-thor agent` | | 配置桌面 VNC | `sudo lzc-thor vnc ...` | | 扩大当前系统盘上的根文件系统 | `sudo lzc-thor resizefs` | | 建立只读 APP 恢复系统和 LVM 运行系统 | `sudo lzc-thor provision ...` | | 把一块新 NVMe 整盘加入根文件系统 | `sudo lzc-thor expand-lvm ...` | | 从 APP recovery 恢复出厂运行系统 | `sudo lzc-thor factory-reset ...` | `vnc`、MGBE 测试、`resizefs`、`provision`、`expand-lvm` 和 `factory-reset` 不包含在 `all` 中,必须按需单独执行。 ## 新设备快速初始化 ### 使用默认用户 ```bash sudo lzc-thor all ``` 该命令按下面的顺序执行: 1. 安装 JetPack、编译工具和 GPU 监控工具。 2. 重新生成克隆系统的机器身份,并配置稳定且每台设备不同的以太网 MAC。 3. 安装 Docker CE、Buildx、Compose 插件和 NVIDIA Container Toolkit。 4. 安装独立的 `/usr/bin/docker-compose`。 5. 关闭 Ubuntu 自动升级。 6. 配置全部已发现 MGBE 接口的 threaded NAPI 和 IOMMU DMA-FQ,并运行 `jetson_clocks`。 7. 配置时区、网络时间同步、开机运行 `jetson_clocks` 和 Firefox。 8. 安装并启动 `lzc-ai-agent`。 默认将 `nvidia` 用户加入 `docker` 组。完整初始化后建议重启设备,使机器身份、网卡身份和用户组等配置全部生效;重新登录后可以执行 `docker ps` 验证 Docker 权限。 ### 指定 Docker 用户 如果系统的日常登录用户不是 `nvidia`,使用 `--docker-user` 指定用户: ```bash sudo lzc-thor all --docker-user ubuntu ``` 也支持下面的等价写法: ```bash sudo lzc-thor all --user ubuntu sudo lzc-thor all ubuntu ``` `lzc-thor-init-all` 是 `lzc-thor all` 的等价入口: ```bash sudo lzc-thor-init-all --docker-user ubuntu ``` ## 常规配置命令 ### `help`:查看命令帮助 ```bash sudo lzc-thor help sudo lzc-thor --help ``` 查看带参数命令的详细用法时,将 `--help` 放在对应命令后: ```bash sudo lzc-thor clone-identity --help sudo lzc-thor mgbe serve --help sudo lzc-thor provision --help sudo lzc-thor expand-lvm --help sudo lzc-thor factory-reset --help ``` ### `packages`:安装 Jetson 软件包 ```bash sudo lzc-thor packages ``` 该命令通过 apt 安装以下软件包: * `nvidia-jetpack`:Jetson 平台软件栈。 * `build-essential`:常用编译工具。 * `nvitop`、`gpustat`、`nvtop`:GPU 状态监控工具。 适用于只需要补装或修复这些软件包,而不想执行完整初始化的场景。该命令没有额外参数。 ### `clone-identity`:修复克隆设备身份 从同一个母盘克隆多台 LC-X5 后,设备可能带有相同的 `/etc/machine-id`、NetworkManager 密钥或有线网络身份,导致 DHCP 和局域网设备识别异常。 先预览将执行的操作: ```bash sudo lzc-thor clone-identity --dry-run ``` 确认后执行修复,并在完成后重启: ```bash sudo lzc-thor clone-identity sudo reboot ``` 该命令会重新生成 `/etc/machine-id`,删除克隆带来的 NetworkManager 主机密钥,并将现有以太网连接配置为按设备生成稳定的本地管理 MAC。默认只重新加载 NetworkManager 配置,不会重启正在使用的网络连接。 如果正在本机控制台操作,并且可以接受网络立即中断,可以直接应用: ```bash sudo lzc-thor clone-identity --apply-now ``` > `--apply-now` 会重启 NetworkManager,SSH 会话可能立即断开。通过 SSH 操作时,优先使用不带该参数的命令,然后重启设备。 可用参数: | 参数 | 说明 | | --- | --- | | `--apply-now` | 写入配置后立即重启 NetworkManager。 | | `--no-machine-id` | 不重新生成 `/etc/machine-id`。 | | `--no-profile-update` | 不修改现有 NetworkManager 连接配置。 | | `--dry-run` | 只显示计划,不修改系统。 | 维护已挂载但未启动的克隆系统时,可以使用 `LZC_THOR_ROOT` 指定其根目录。离线模式只修改该目录下的文件,不操作当前系统的 NetworkManager: ```bash sudo LZC_THOR_ROOT=/mnt/thor-root lzc-thor clone-identity ``` ### `docker`:安装 Docker 和 NVIDIA 运行时 只安装 Docker,不修改任何用户组: ```bash sudo lzc-thor docker ``` 同时允许指定用户不加 `sudo` 使用 Docker: ```bash sudo lzc-thor docker --docker-user ubuntu ``` `--user ubuntu` 和直接写 `ubuntu` 也是等价写法。该命令会: * 配置 Docker 官方 apt 软件源。 * 安装 Docker CE、Docker CLI、containerd、Buildx 和 Compose 插件。 * 安装 NVIDIA Container Toolkit,并将 NVIDIA runtime 配置为 Docker 默认运行时。 * 启用并重启 `docker.service`。 * 在指定用户时,将该用户加入 `docker` 组。 修改用户组后,已有登录会话通常需要退出并重新登录才能获得新权限。 ### `docker-compose`:安装独立 Compose 命令 ```bash sudo lzc-thor docker-compose docker-compose version ``` 该命令从 Docker Compose 的 GitHub 最新 release 下载 Linux aarch64 二进制文件,校验 `.sha256` 后安装到 `/usr/bin/docker-compose`。它与 Docker 软件包提供的 `docker compose` 插件并存。 设备需要能够访问 GitHub。该命令总是获取执行时的最新版本,没有版本选择参数。 ### `disable-upgrades`:关闭自动升级 ```bash sudo lzc-thor disable-upgrades ``` 该命令关闭并屏蔽 apt 定时更新、`unattended-upgrades` 和 update-notifier 相关服务,同时卸载相关软件包。适用于需要固定 JetPack、内核和驱动环境的设备。 关闭自动升级后,系统不会自动安装安全更新;需要由管理员安排兼容性验证和手动更新。 ### `startup`:配置启动环境 ```bash sudo lzc-thor startup ``` 该命令会: * 根据当前公网 IP 查询时区;查询失败时使用 `Asia/Shanghai`。 * 启用 `systemd-timesyncd`,并配置 Cloudflare、阿里云等 NTP 服务。 * 创建并启用 `lzc-jetson-clocks.service`,开机时锁定 Jetson 最大频率。 * 通过 Snap 安装 Firefox。 执行时需要访问时区查询服务、Ubuntu 软件源和 Snap 服务。该命令没有额外参数。 ### `agent`:安装算力舱代理 ```bash sudo lzc-thor agent sudo systemctl status lzc-ai-agent.service ``` 该命令使用安装包内附带的 ARM64 客户端安装 `lzc-ai-agent`,然后启用并重启 `lzc-ai-agent.service`。适用于单独重装或修复算力舱代理。该命令没有额外参数。 ## MGBE 配置与带宽测试 ### `mgbe`:配置 MGBE 性能参数 ```bash sudo lzc-thor mgbe ``` 该命令自动发现名称类似 `mgbe0_0` 的全部物理 MGBE 接口,并执行以下配置: * 开启 threaded NAPI。 * 运行 `jetson_clocks`。 * 将各接口所在的 IOMMU group 设置为 `DMA-FQ`。 该命令只负责性能参数,不分配测试 IP,也不启动 `iperf3`。它包含在 `lzc-thor all` 中。 ### MGBE 测试的工作方式 MGBE 测试需要两台设备:一台运行服务端命令,另一台运行客户端命令。测试前,两端都应执行: ```bash sudo lzc-thor mgbe sudo apt install -y iperf3 ``` 测试辅助命令分为三个步骤: 1. `serve config` 和 `client config` 分配各链路的静态 IP,将配置写入 `/etc/netplan/99-mgbe-test.yaml`,并把链路分配保存到 `/var/lib/lzc-thor/mgbe-test`。 2. `serve run` 从已保存的服务端配置启动 `iperf3`;`client run` 从已保存的客户端配置发起测试。 3. 两端执行 `clean`,停止测试进程并删除临时网络配置和状态。 `config` 与 `run` 必须在同一端按角色配套使用。修改 IP 或测试链路数后,需要重新执行对应的 `config`。 各测试命令的参数和用途如下: | 命令 | 参数 | 用法 | | --- | --- | --- | | `mgbe serve config` | 必须提供 `--base-ip IP`;可选 `--mode 3x\|4x`、`--dry-run` | 为服务端接口分配 IP、应用 netplan 并保存服务端状态。 | | `mgbe serve run` | 可选 `--dry-run` | 读取服务端状态,并为每条链路启动 8 个不同端口的 `iperf3` 服务。 | | `mgbe client config` | 必须提供 `--server-base-ip IP` 和 `--local-suffix 1-254`;可选 `--mode 3x\|4x`、`--dry-run` | 根据服务端基准 IP 计算两端地址、应用客户端 netplan 并保存状态。 | | `mgbe client run` | 可选 `--duration SEC`、`--parallel N`、`--udp`、`--dry-run` | 检查每条链路是否可达,同时运行所有 `iperf3` 客户端并汇总发送端带宽。 | | `mgbe clean` | 可选 `--dry-run` | 停止已记录的服务进程,删除测试 netplan 和两端状态。 | ### 三链路测试示例 以下示例适用于 T4000 与 T5000 连接并使用 3 条 25GbE 链路的场景。 服务端执行: ```bash sudo lzc-thor mgbe clean sudo lzc-thor mgbe serve config --base-ip 192.168.100.10 --mode 3x sudo lzc-thor mgbe serve run ``` 客户端执行: ```bash sudo lzc-thor mgbe clean sudo lzc-thor mgbe client config \ --server-base-ip 192.168.100.10 \ --local-suffix 20 \ --mode 3x sudo lzc-thor mgbe client run ``` IP 会按链路递增第三段: | 链路 | 服务端 IP | 客户端 IP | | --- | --- | --- | | 第 1 条 | `192.168.100.10` | `192.168.100.20` | | 第 2 条 | `192.168.101.10` | `192.168.101.20` | | 第 3 条 | `192.168.102.10` | `192.168.102.20` | `--local-suffix` 是客户端 IP 的最后一段,范围为 `1` 到 `254`,并且不能与服务端 IP 的最后一段相同。 ### 选择 3x、4x 或自动模式 `--mode` 只用于 `serve config` 和 `client config`: | 写法 | 使用的接口 | | --- | --- | | 不写 `--mode` | `auto` 模式,使用全部自动发现的 MGBE 接口。 | | `--mode 3x` | 按接口名称排序后使用前 3 个接口。 | | `--mode 4x` | 按接口名称排序后使用前 4 个接口。 | 两端必须选择相同的模式。四链路测试示例: ```bash # 服务端 sudo lzc-thor mgbe serve config --base-ip 192.168.100.10 --mode 4x sudo lzc-thor mgbe serve run # 客户端 sudo lzc-thor mgbe client config \ --server-base-ip 192.168.100.10 \ --local-suffix 20 \ --mode 4x sudo lzc-thor mgbe client run ``` `run` 会读取 `config` 保存的模式,不接受 `--mode`。旧的 `lzc-thor mgbe test` 已被移除。 ### 调整测试参数 客户端默认测试 10 秒,每个 `iperf3` 客户端进程使用 24 条并行流: ```bash sudo lzc-thor mgbe client run --duration 30 --parallel 32 ``` 使用 UDP 测试: ```bash sudo lzc-thor mgbe client run --udp ``` 每条 MGBE 链路会使用 8 个 TOS 值启动 8 个客户端进程。因此 3x 模式会并发启动 24 个客户端进程,4x 模式会启动 32 个;`--parallel` 是每个进程内部的并行流数量,不是进程总数。 执行 `run` 时,工具会检查已保存接口的 MTU 9000、threaded NAPI、IOMMU DMA-FQ 和对应的 ODMDATA 25GbE 配置;无法确认 `jetson_clocks` 或 ODMDATA 时会给出警告。检查失败时,应先重新执行 `sudo lzc-thor mgbe` 并核对设备的链路模式,不要直接使用不完整的测试结果。 测试完成后会输出 `Concurrent aggregate sender bandwidth`。该数值只汇总同一个并发测试窗口内可解析的发送端结果,不应将多轮串行测试结果相加后当作链路带宽。 ### 预览和清理测试配置 测试辅助命令支持 `--dry-run`,可以在不修改主机的情况下检查计划: ```bash sudo lzc-thor mgbe serve config --base-ip 192.168.100.10 --mode 3x --dry-run sudo lzc-thor mgbe client run --dry-run sudo lzc-thor mgbe clean --dry-run ``` 正式测试结束后,在服务端和客户端都执行: ```bash sudo lzc-thor mgbe clean ``` `clean` 会停止命令记录的 `iperf3` 服务端进程,删除 `/etc/netplan/99-mgbe-test.yaml` 和 `/var/lib/lzc-thor/mgbe-test` 下的状态,并重新应用网络配置。 ## VNC 配置 `vnc` 不包含在 `all` 中。为默认的 `nvidia` 桌面用户配置 VNC: ```bash sudo lzc-thor vnc --password '' ``` 为其他桌面用户配置: ```bash sudo lzc-thor vnc --user ubuntu --password '' ``` 也可以用环境变量传入用户和密码: ```bash sudo LZC_THOR_VNC_USER=ubuntu \ LZC_THOR_VNC_PASSWORD='' \ lzc-thor vnc ``` | 参数或变量 | 说明 | | --- | --- | | `--user USER` | 目标桌面用户;默认读取 `LZC_THOR_VNC_USER`,未设置时为 `nvidia`。 | | `--password PASSWORD` | 必填的 VNC 密码,也可以通过 `LZC_THOR_VNC_PASSWORD` 提供。 | 该命令按需安装 Vino、启用其桌面启动项并写入用户的 VNC 设置。完成后应重启设备。没有配置桌面自动登录时,通常需要目标用户先在本地登录图形桌面,VNC 才能连接。 ## 存储命令 `resizefs`、`provision`、`expand-lvm` 和 `factory-reset` 会操作分区、文件系统、LVM 或启动配置。执行前应备份重要数据,并先确认设备名和当前根文件系统。 ```bash findmnt / lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,PARTLABEL,MOUNTPOINTS sudo pvs sudo vgs sudo lvs ``` ### `resizefs`:使用当前系统盘的剩余空间 将较小的系统盘克隆到较大的磁盘后,可以执行: ```bash sudo lzc-thor resizefs ``` 该命令根据当前 `/` 的布局自动选择处理方式: * 普通 ext4 分区根目录:将该分区扩展到磁盘剩余空间,再在线扩展 ext4。 * 标准 `aipod_vg/lv_root` 且只有一个分区型 PV:扩展 PV 所在分区和 PV,将 VG 的全部空闲空间分配给 `lv_root`,再在线扩展 ext4。 该命令不支持多 PV、加密存储、整盘文件系统、非 ext4 根文件系统或非标准 LVM 布局。多盘 LVM 运行系统需要增加新磁盘时,应使用 `expand-lvm`,而不是 `resizefs`。 ### `provision`:建立 APP recovery 和 LVM runtime `provision` 用于将带有 `APP` 和 `DATA_PV` 分区的 LC-X5 转换为以下布局: * `APP`:只读的出厂系统和恢复环境,同时保存 L4TLauncher 启动文件。 * `aipod_vg/lv_root`:日常使用的可写 ext4 根文件系统。 这是一个分两阶段执行、每阶段都会自动重启的流程。 #### 第 1 阶段:在可写 APP 中准备 先检查当前状态和计划: ```bash sudo lzc-thor provision status sudo lzc-thor provision --dry-run ``` 确认无误后开始准备: ```bash sudo lzc-thor provision --yes ``` 第 1 阶段会安装存储和 initramfs 工具、扩展 `DATA_PV`、检查 initrd 的 LVM 支持、写入受管理的启动项、将 APP 配置为只读,并把下次启动目标设为 `LZC Thor APP recovery`,然后重启。 #### 第 2 阶段:在 APP recovery 中创建运行系统 设备重启进入 `LZC Thor APP recovery` 后登录,再执行: ```bash sudo lzc-thor provision --maintenance --yes ``` 第 2 阶段会确认当前根目录确实是只读 APP,创建 `aipod_vg/lv_root`,以块级方式把 APP 克隆到逻辑卷,扩展 ext4、生成新的文件系统 UUID 和机器身份,最后切换到 LVM runtime 并重启。 进入运行系统后,可以检查: ```bash sudo lzc-thor provision status findmnt / findmnt /factory ``` 正常情况下,`/` 来自 `/dev/aipod_vg/lv_root`,APP 以只读方式挂载在 `/factory`。当前启动文件对应的软件包会被 hold,因此不支持在线更新内核、DTB 和 initrd。 > `provision` 会修改分区和启动配置。不要跳过 `--dry-run`,也不要在不符合 `APP`、`DATA_PV` 布局的设备上强行执行。 ### `expand-lvm`:增加一块 NVMe 到根文件系统 该命令只适用于根目录已经是可写的 `/dev/aipod_vg/lv_root`,用于把一块完整 NVMe 磁盘加入根文件系统。 先根据容量、型号和序列号确认目标盘: ```bash lsblk -d -o NAME,PATH,SIZE,MODEL,SERIAL ``` 对空盘先执行只读检查和预览: ```bash sudo lzc-thor expand-lvm --disk /dev/nvme1n1 --dry-run ``` 确认目标正确后正式扩容: ```bash sudo lzc-thor expand-lvm --disk /dev/nvme1n1 ``` 空盘会被创建 GPT,并建立一个标记为 `LZC_THOR_PV` 的分区。随后命令会执行 `pvcreate`、`vgextend`、`lvextend` 和 `resize2fs`,在线扩大根文件系统。 如果磁盘上已有分区或文件系统,命令默认拒绝操作。明确确认其中数据可以全部删除后,使用: ```bash # 交互式确认时,按提示完整输入目标磁盘路径 sudo lzc-thor expand-lvm --disk /dev/nvme1n1 --force-wipe # 无人值守确认;会直接清空目标盘 sudo lzc-thor expand-lvm --disk /dev/nvme1n1 --force-wipe --yes ``` | 参数 | 说明 | | --- | --- | | `--disk /dev/nvmeXnY` | 必填;必须是完整 NVMe 设备,不能填写分区。 | | `--dry-run` | 执行运行环境和磁盘安全检查,并显示计划,不写磁盘。 | | `--force-wipe` | 授权清空非空目标盘;未带 `--yes` 时要求交互确认。 | | `--yes` | 只可与 `--force-wipe` 同时使用,跳过交互确认。 | > 扩容后,每块成员盘都永久成为根文件系统的一部分。拔掉任意成员盘都可能导致系统无法启动。后续恢复出厂设置时,所有 `DATA_PV` 和 `LZC_THOR_PV` 成员盘都会被清空并重建。 命令支持从已完成的 `pvcreate`、`vgextend` 或扩容步骤继续执行;已经完整加入并分配空间的磁盘再次执行时不会重复扩容。 ### `factory-reset`:恢复 LVM 运行系统 `factory-reset` 只能在手动选择的只读 `LZC Thor APP recovery` 中执行,不能在日常 LVM runtime 中执行。 1. 重启设备,在 extlinux 菜单中手动选择 `LZC Thor APP recovery`。 2. 如果系统使用多块 LVM 成员盘,确认所有扩容盘都已连接。 3. 进入 recovery 后先查看状态并预演: ```bash sudo lzc-thor factory-reset status sudo lzc-thor factory-reset --dry-run ``` 4. 确认后执行恢复: ```bash sudo lzc-thor factory-reset --yes ``` 该命令会检查当前系统是否从只读 APP 启动、LVM 拓扑是否符合预期、所有成员盘是否在线,以及每个 PV 是否支持 discard。随后会清空所有 `DATA_PV` 和 `LZC_THOR_PV`,按原总容量重建 `aipod_vg/lv_root`,从 APP 克隆全新的运行系统并重启。APP、APP 中的 `/etc/fstab` 和 extlinux 默认启动项保持不变。 > `factory-reset --yes` 会永久删除运行系统、Docker 数据、模型、应用和用户文件。`--dry-run` 不会写磁盘,但仍会执行恢复环境、成员盘和 discard 能力检查。 ## 常见问题 ### 为什么执行 Docker 后仍提示权限不足? 用户加入 `docker` 组后,当前登录会话不会自动刷新组信息。退出 SSH 或桌面会话并重新登录,再执行: ```bash docker ps ``` ### 为什么 `mgbe serve run` 或 `client run` 提示没有状态? `run` 不接收 IP 和 `--mode`,只读取同一角色最近一次 `config` 保存的状态。先重新执行 `serve config` 或 `client config`,再执行对应的 `run`。 ### 什么时候用 `resizefs`,什么时候用 `expand-lvm`? * 原系统盘本身变大,想使用同一块盘尾部的剩余空间:使用 `resizefs`。 * 已经运行在 `aipod_vg/lv_root`,想把另一块完整 NVMe 加入根文件系统:使用 `expand-lvm`。 ### 如何只查看当前存储布局? ```bash sudo lzc-thor provision status ``` `provision status` 和 `factory-reset status` 都只输出当前根目录、`/factory`、`/boot`、块设备、LVM 和 extlinux 默认启动项,不修改系统。 --- --- url: /aipod/benchmark/llama-3.1-70b.md --- # llama.cpp build: 716301d1 (5757) ## Meta-Llama-3.1-70B-Instruct-IQ4\_XS.gguf ``` ./build/bin/llama-batched-bench -ngl 999 -m ../Meta-Llama-3.1-70B-Instruct-IQ4_XS.gguf -fa -npl 1,2,4,6,8,10,12 -npp 512 -ntg 128 -c 16384 ggml_cuda_init: GGML_CUDA_FORCE_MMQ: no ggml_cuda_init: GGML_CUDA_FORCE_CUBLAS: no ggml_cuda_init: found 1 CUDA devices: Device 0: Orin, compute capability 8.7, VMM: yes build: 5757 (716301d1) with cc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 for aarch64-linux-gnu llama_model_load_from_file_impl: using device CUDA0 (Orin) - 58814 MiB free llama_model_loader: loaded meta data with 33 key-value pairs and 724 tensors from ../Meta-Llama-3.1-70B-Instruct-IQ4_XS.gguf (version GGUF V3 (latest)) llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output. llama_model_loader: - kv 0: general.architecture str = llama llama_model_loader: - kv 1: general.type str = model llama_model_loader: - kv 2: general.name str = Meta Llama 3.1 70B Instruct llama_model_loader: - kv 3: general.finetune str = Instruct llama_model_loader: - kv 4: general.basename str = Meta-Llama-3.1 llama_model_loader: - kv 5: general.size_label str = 70B llama_model_loader: - kv 6: general.license str = llama3.1 llama_model_loader: - kv 7: general.tags arr[str,6] = ["facebook", "meta", "pytorch", "llam... llama_model_loader: - kv 8: general.languages arr[str,8] = ["en", "de", "fr", "it", "pt", "hi", ... llama_model_loader: - kv 9: llama.block_count u32 = 80 llama_model_loader: - kv 10: llama.context_length u32 = 131072 llama_model_loader: - kv 11: llama.embedding_length u32 = 8192 llama_model_loader: - kv 12: llama.feed_forward_length u32 = 28672 llama_model_loader: - kv 13: llama.attention.head_count u32 = 64 llama_model_loader: - kv 14: llama.attention.head_count_kv u32 = 8 llama_model_loader: - kv 15: llama.rope.freq_base f32 = 500000.000000 llama_model_loader: - kv 16: llama.attention.layer_norm_rms_epsilon f32 = 0.000010 llama_model_loader: - kv 17: general.file_type u32 = 30 llama_model_loader: - kv 18: llama.vocab_size u32 = 128256 llama_model_loader: - kv 19: llama.rope.dimension_count u32 = 128 llama_model_loader: - kv 20: tokenizer.ggml.model str = gpt2 llama_model_loader: - kv 21: tokenizer.ggml.pre str = llama-bpe llama_model_loader: - kv 22: tokenizer.ggml.tokens arr[str,128256] = ["!", "\"", "#", "$", "%", "&", "'", ... llama_model_loader: - kv 23: tokenizer.ggml.token_type arr[i32,128256] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... llama_model_loader: - kv 24: tokenizer.ggml.merges arr[str,280147] = ["Ġ Ġ", "Ġ ĠĠĠ", "ĠĠ ĠĠ", "... llama_model_loader: - kv 25: tokenizer.ggml.bos_token_id u32 = 128000 llama_model_loader: - kv 26: tokenizer.ggml.eos_token_id u32 = 128009 llama_model_loader: - kv 27: tokenizer.chat_template str = {{- bos_token }}\n{%- if custom_tools ... llama_model_loader: - kv 28: general.quantization_version u32 = 2 llama_model_loader: - kv 29: quantize.imatrix.file str = /models_out/Meta-Llama-3.1-70B-Instru... llama_model_loader: - kv 30: quantize.imatrix.dataset str = /training_dir/calibration_datav3.txt llama_model_loader: - kv 31: quantize.imatrix.entries_count i32 = 560 llama_model_loader: - kv 32: quantize.imatrix.chunks_count i32 = 125 llama_model_loader: - type f32: 162 tensors llama_model_loader: - type q5_K: 80 tensors llama_model_loader: - type q6_K: 1 tensors llama_model_loader: - type iq4_xs: 481 tensors print_info: file format = GGUF V3 (latest) print_info: file type = IQ4_XS - 4.25 bpw print_info: file size = 35.29 GiB (4.30 BPW) load: special tokens cache size = 256 load: token to piece cache size = 0.7999 MB print_info: arch = llama print_info: vocab_only = 0 print_info: n_ctx_train = 131072 print_info: n_embd = 8192 print_info: n_layer = 80 print_info: n_head = 64 print_info: n_head_kv = 8 print_info: n_rot = 128 print_info: n_swa = 0 print_info: is_swa_any = 0 print_info: n_embd_head_k = 128 print_info: n_embd_head_v = 128 print_info: n_gqa = 8 print_info: n_embd_k_gqa = 1024 print_info: n_embd_v_gqa = 1024 print_info: f_norm_eps = 0.0e+00 print_info: f_norm_rms_eps = 1.0e-05 print_info: f_clamp_kqv = 0.0e+00 print_info: f_max_alibi_bias = 0.0e+00 print_info: f_logit_scale = 0.0e+00 print_info: f_attn_scale = 0.0e+00 print_info: n_ff = 28672 print_info: n_expert = 0 print_info: n_expert_used = 0 print_info: causal attn = 1 print_info: pooling type = 0 print_info: rope type = 0 print_info: rope scaling = linear print_info: freq_base_train = 500000.0 print_info: freq_scale_train = 1 print_info: n_ctx_orig_yarn = 131072 print_info: rope_finetuned = unknown print_info: ssm_d_conv = 0 print_info: ssm_d_inner = 0 print_info: ssm_d_state = 0 print_info: ssm_dt_rank = 0 print_info: ssm_dt_b_c_rms = 0 print_info: model type = 70B print_info: model params = 70.55 B print_info: general.name = Meta Llama 3.1 70B Instruct print_info: vocab type = BPE print_info: n_vocab = 128256 print_info: n_merges = 280147 print_info: BOS token = 128000 '<|begin_of_text|>' print_info: EOS token = 128009 '<|eot_id|>' print_info: EOT token = 128009 '<|eot_id|>' print_info: EOM token = 128008 '<|eom_id|>' print_info: LF token = 198 'Ċ' print_info: EOG token = 128001 '<|end_of_text|>' print_info: EOG token = 128008 '<|eom_id|>' print_info: EOG token = 128009 '<|eot_id|>' print_info: max token length = 256 load_tensors: loading model tensors, this can take a while... (mmap = true) load_tensors: offloading 80 repeating layers to GPU load_tensors: offloading output layer to GPU load_tensors: offloaded 81/81 layers to GPU load_tensors: CUDA0 model buffer size = 35606.98 MiB load_tensors: CPU_Mapped model buffer size = 532.31 MiB .................................................................................................. llama_context: constructing llama_context llama_context: n_seq_max = 12 llama_context: n_ctx = 16384 llama_context: n_ctx_per_seq = 1365 llama_context: n_batch = 2048 llama_context: n_ubatch = 512 llama_context: causal_attn = 1 llama_context: flash_attn = 1 llama_context: freq_base = 500000.0 llama_context: freq_scale = 1 llama_context: n_ctx_per_seq (1365) < n_ctx_train (131072) -- the full capacity of the model will not be utilized llama_context: CUDA_Host output buffer size = 5.87 MiB llama_kv_cache_unified: CUDA0 KV buffer size = 5120.00 MiB llama_kv_cache_unified: size = 5120.00 MiB ( 16384 cells, 80 layers, 12 seqs), K (f16): 2560.00 MiB, V (f16): 2560.00 MiB llama_context: CUDA0 compute buffer size = 266.50 MiB llama_context: CUDA_Host compute buffer size = 52.13 MiB llama_context: graph nodes = 2567 llama_context: graph splits = 2 main: n_kv_max = 16384, n_batch = 2048, n_ubatch = 512, flash_attn = 1, is_pp_shared = 0, n_gpu_layers = 999, n_threads = 12, n_threads_batch = 12 | PP | TG | B | N_KV | T_PP s | S_PP t/s | T_TG s | S_TG t/s | T s | S t/s | |-------|--------|------|--------|----------|----------|----------|----------|----------|----------| | 512 | 128 | 1 | 640 | 14.184 | 36.10 | 42.285 | 3.03 | 56.468 | 11.33 | | 512 | 128 | 2 | 1280 | 8.941 | 114.53 | 50.120 | 5.11 | 59.060 | 21.67 | | 512 | 128 | 4 | 2560 | 17.983 | 113.88 | 71.604 | 7.15 | 89.587 | 28.58 | | 512 | 128 | 6 | 3840 | 27.168 | 113.07 | 81.013 | 9.48 | 108.181 | 35.50 | | 512 | 128 | 8 | 5120 | 36.480 | 112.28 | 93.166 | 10.99 | 129.646 | 39.49 | | 512 | 128 | 10 | 6400 | 45.901 | 111.54 | 95.976 | 13.34 | 141.877 | 45.11 | | 512 | 128 | 12 | 7680 | 55.496 | 110.71 | 78.255 | 19.63 | 133.751 | 57.42 | llama_perf_context_print: load time = 148042.28 ms llama_perf_context_print: prompt eval time = 718462.74 ms / 27408 tokens ( 26.21 ms per token, 38.15 tokens per second) llama_perf_context_print: eval time = 42280.79 ms / 128 runs ( 330.32 ms per token, 3.03 tokens per second) llama_perf_context_print: total time = 866615.25 ms / 27536 tokens ``` ``` ./build/bin/llama-bench -m ../Meta-Llama-3.1-70B-Instruct-IQ4_XS.gguf -ngl 999 -fa 1 -n 0 -p 64,128,256,512 ggml_cuda_init: GGML_CUDA_FORCE_MMQ: no ggml_cuda_init: GGML_CUDA_FORCE_CUBLAS: no ggml_cuda_init: found 1 CUDA devices: Device 0: Orin, compute capability 8.7, VMM: yes | model | size | params | backend | ngl | fa | test | t/s | | ------------------------------ | ---------: | ---------: | ---------- | --: | -: | --------------: | -------------------: | | llama 70B IQ4_XS - 4.25 bpw | 35.29 GiB | 70.55 B | CUDA | 999 | 1 | pp64 | 71.54 ± 0.06 | | llama 70B IQ4_XS - 4.25 bpw | 35.29 GiB | 70.55 B | CUDA | 999 | 1 | pp128 | 109.25 ± 0.26 | | llama 70B IQ4_XS - 4.25 bpw | 35.29 GiB | 70.55 B | CUDA | 999 | 1 | pp256 | 91.70 ± 46.86 | | llama 70B IQ4_XS - 4.25 bpw | 35.29 GiB | 70.55 B | CUDA | 999 | 1 | pp512 | 113.74 ± 0.19 | build: 716301d1 (5757) ``` --- --- url: /aipod/olddocs/benchmark.md --- # LLM 随着上下文的变长,生成 tokens 的速度会稍微变慢。 ## llama.cpp 加载器 | 模型 | CPU | GPU (cuda) | |:----------------------------------------|:--------------|:--------------------------| | Meta-Llama-3.1-70B-Instruct-Q5\_K\_S.gguf | 0.29 tokens/s | 3.04 tokens/s (电源: 60W) | | llama-2-7b-chat.Q4\_0.gguf | | 28 tokens/s (电源: 43W) | | | | | ## 使用 MLC 加载器, 使用 MLC 加载会比 llama.cpp 和 直接用 python transformers 要快 这个是正常使用,提问问题的速度,随着问题的变长而有所变慢. | 模型 | CPU | GPU (cuda) | |:----------------------------------------|:----|:--------------------------| | mlc-ai/Qwen2.5-72B-Instruct-q4f16\_1-MLC | | 3.3 tokens/s (电源: 56W) | | mlc-ai/Qwen2.5-32B-Instruct-q4f16\_1-MLC | | 7.5 tokens/s (电源: 53W) | | mlc-ai/Qwen2.5-14B-Instruct-q4f16\_1-MLC | | 16.3 tokens/s (电源: 55w) | | mlc-ai/Llama-2-13b-chat-hf-q4f16\_1-MLC | | 16.4 tokens/s (电源: 58w) | 官方在给出的 [benchmark](https://www.jetson-ai-lab.com/benchmarks.html) 中,限制 了生成的 token 长度,和 prompt 的长度,并都是第一次加载,没有上下文的。下面是用 官方的测试出来的结果. | 模型 | CPU | GPU (cuda) | |:-----------------------------------------|:----|:------------------------------------| | mlc-ai/Qwen2.5-72B-Instruct-q4f16\_1-MLC | | 3.49 tokens/s | | mlc-ai/Llama-3-70B-Instruct-q4f16\_1-MLC | | 4.1 tokens/s (电源: 62W) | | mlc-ai/Qwen2.5-32B-Instruct-q4f16\_1-MLC | | 8.2 tokens/s | | mlc-ai/Qwen2.5-14B-Instruct-q4f16\_1-MLC | | 17.6 tokens/s | | mlc-ai/Llama-2-13b-chat-hf-q4f16\_1-MLC | | 20.6 tokens/s (官方: 25.1 tokens/s) | | mlc-ai/Llama-3.1-8B-Instruct-q4f16\_1-MLC | | 32.3 tokens/s (电源: 55W) | | mlc-ai/Llama-3-8B-Instruct-q4f16\_1-MLC | | 32.2 tokens/s (官方: 40 tokens/s) | | mlc-ai/Qwen2.5-7B-Instruct-q4f32\_1-MLC | | 25.73 tokens/s | # ASR 语言识别 测试声音: 一个17分钟晨会的录音(中文) | 模型 | 框架 | 推理时间 | 比率(处理长度/s) | 效果 | |:--------------|:--------|:---------|:-------------------|--------------------------------| | whisper turbo | whisper | 192.298s | 5.3125 | | | paraformer-zh | funasr | 81.551s | 12.59 | | | SenseVoice | funasr | 37.49 | 27.20 | | 测试声音: 一个22分32秒电影声音(中文) | 模型 | 框架 | 推理时间 | 比率(处理长度/s) | 效果 | |:--------------|:--------|:---------|:-------------------|----------------------------------------------------------| | whisper turbo | whisper | 230s | 5.878 | 部分中文错误,但大部分台词都能识别 | | whisper tiny | whisper | 252s | 5.36 | 大部分的中文错误,质量太差 | | paraformer-zh | funasr | 86.384 | 15.65 | 感觉生成的质量比较好 | | SenseVoice | funasr | 44.83 | 30.15 | 生成的速度最快,但感觉少了很多,只有比较突出的声音被识别 | 测试声音: 一个14分钟的英语up说话 | 模型 | 框架 | 推理时间 | 比率(处理长度/s) | 效果 | |:----------------|:--------|:---------|:-------------------|--------------------------------------------| | whisper turbo | whisper | 162.48s | 5.16 | 在英语上,turbo 的效果和 tiny 的差别也不大 | | whisper tiny | whisper | 127s | 6.614 | 对英文来说, tiny.en 和 tiny 没有太多的区别 | | whisper tiny.en | whisper | 123s | 6.82 | | | SenseVoice | funasr | 36.12s | 23.25 | | `whisper turbo` 和 `whisper tiny` 的差别在 `turbo` 模型的学习能力更强(更强的泛化 能力),可以面对更多的未知的情况,而 `tiny` 在英语上很好,但在其他语言上就不行了。 `whisper` 非常适合英语环境中 `up主说话` 或者电影这些数据,断句和句子,以及标点符 合都非常好. `paraformer-zh` 和 `SenseVoice` 在输出上,都会输出成一个长句,(这个估计是 funasr 相关的都会在识别后,做一次词的重新整理,重新组合文本). 但也会导致在长语 音中,把本来应该分开的两个放在一个句子中。 ```sh ssh nvidia2 cd lnks jetson-containers run -v `pwd`:/workspace funasr_whisper_test:latest cd /workspace/funasr python3 funasr_paraformer-zh_test.py python3 funasr_sense_voice-test.py ``` --- --- url: /aipod/issues/lzc-app-forward-to-ai-service.md --- # LZC路由中转发到AI服务 一个简单的AI应用,只有一个算力舱中部署的服务,一些服务把界面这些也包括进去了。 服务在算力舱中部署后,可以通过 `https://${服务名称}-ai.${微服名称}.heiyu.space` 访问。 而在 LPK 应用的路由中,您可能会像下面这样子写 ```yml routers: - /=https://${服务名称}-ai.${微服名称}.heiyu.space ``` ## 微服 1.3.8+ 系统 使用 微服系统的 1.3.8 [upstreams](https://developer.lazycat.cloud/spec/manifest.html#%E5%8D%81%E3%80%81upstreamconfig-%E9%85%8D%E7%BD%AE) 特性实现 ```yml application: upstreams: - location: / backend: https://comfyui-ai.{{ .S.BoxDomain }} use_backend_host: true dump_http_headers_when_5xx: true ``` 使用模板[manifest.yml](https://developer.lazycat.cloud/advanced-manifest-render.html#%E6%A8%A1%E6%9D%BF%E5%8F%82%E6%95%B0) ## 微服 1.3.8 之前的系统 这种情况下,您在路由中不能知道当前访问的 `${微服名称}`,所以需要一个转发工具来处理,比如 `nginx` 或者 `caddy` ```yml application: routes: - /=http://caddy:80 services: caddy: image: registry.lazycat.cloud/caddy:2.9.1-alpine setup_script: | cat <<'EOF' > /etc/caddy/Caddyfile { auto_https off http_port 80 https_port 0 } :80 { redir https://${服务名称}-ai.{$LAZYCAT_BOX_DOMAIN} permanent } EOF cat /etc/caddy/Caddyfile ``` --- --- url: /aipod/issues/mdns_notfound.md --- # MDNS 发现不了设备 场景: 算力舱在插着有线的情况下,微服不能通过 mdns 发现算力舱。 解决方法: 1. 在微服中使用 `hc ustc` 更改 `apt` 源(如果所处的环境能直接从 apt 更新,则跳过这一步). 2. 微服中终端中使用 `apt install avahi-utils` 3. 安装后,使用 `avahi-browser -a` 查询局域网中的广播(如果能接收到 `_ai_jetson` 的广播,则表示可以 mdns 正常,可以排除 mdns 因素) 4. 使用 `avahi-daemon -r` 尝试重启 `daemon`,成功后,再确认步骤3 --- --- url: /aipod/ocr.md --- # OCR 在算力舱中部署了一个基于 `paddleocr` 的 `ocr` 服务. ## 文档 `ocr` 服务的 api 可以通过 查看。 ## 本地使用 ```bash curl -X 'POST' \ 'https://ocr-ai.13gxg.heiyu.space/ocr' \ -H 'accept: application/json' \ -H 'Content-Type: multipart/form-data' \ -F 'file=@ocr-4.png;type=image/png' \ -F 'request={"max_size":1920,"overlap":100,"memory_limit_mb":1024,"det":true,"rec":true,"cls":true}' ``` ## 单独部署使用 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: ocr: image: registry.lazycat.cloud/x/lzc-aipod-ocr:d81102a ports: - 3000:3000 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 10s timeout: 30s retries: 100 start_period: 1s ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 启动后访问 `http://127.0.0.1:3000/docs` --- --- url: /aipod/ollama/app-use-ollama-api.md --- # Ollama API 算力舱中默认部署了 `Ollama`,对于微服中的应用可以通过直接使用算力舱暴露的 `Ollama API` 来使用。 * `ollama` 地址为 * `ollama` 兼容 `OpenAI API` 地址为 如果您本地也安装了微服客户端,您也可以使用上面的连接给您本地的应用调用。比如可以直接使用本地的 curl 访问。 ```sh curl https://ollama-ai.${微服名称}.heiyu.space/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model": "qwen3:4b", "messages": [{"role": "user", "content": "Hello!"}]}' ``` 算力舱中的 `ollama` 也将端口暴露在局域网中了,您可以通过 `http://算力舱ip:11434` 来访问 `ollama` 服务. 算力舱的局域网ip可以在设备列表中[查看](/os/introduction.html#ssh)。 ## python 示例 ```python import base64 import requests import json # 获取图片并进行 base64 编码 image_url = "https://i-blog.csdnimg.cn/direct/7184675ec6ad495e8b7469c17edd56d9.png" try: response = requests.get(image_url) response.raise_for_status() # 检查请求是否成功 image_data = response.content encoded_image = base64.b64encode(image_data).decode('utf-8') except requests.exceptions.RequestException as e: print(f"Error downloading image: {e}") exit(1) # 构建请求的 JSON 数据 request_data = { "model": "qwen3-vl:2b-instruct", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "提取文字为 Markdown 格式,要求格式正确。" }, { "type": "image_url", "image_url": { "url": f"data:image/jpeg;base64,{encoded_image}" } } ] } ] } ollama_api_url = "https://ollama-ai.${微服名称}.heiyu.space/v1/chat/completions" headers = { "Content-Type": "application/json" } try: response = requests.post(ollama_api_url, headers=headers, data=json.dumps(request_data)) response.raise_for_status() # 检查请求是否成功 # 打印响应 print(response.json()) except requests.exceptions.RequestException as e: print(f"Error sending request to Ollama API: {e}") ``` --- --- url: /aipod/ollama/delete-model.md --- # ollama 中删除模型 ## Open WebUI 中删除模型 ![open webui delete model](/assets/openwebui-delete-model.DUAKooED.png) 1. 点击右下角的图标,在弹出的菜单栏中选择 “管理员设置” 2. 点击管理员设置中的 “设置” 标签 3. 点击模型管理 4. 选择您选删除的模型 5. 点击删除模型按钮 ## webollama 中删除模型 ![webollama delete model](/assets/webollama-delete-model.Djuidkww.png) 1. 点击模型列表,查看当前的模型。 2. 选择需要删除的模型,点击右边的删除图标 ## ollama 命令行删除 1. ssh 到算力舱中,比如 `ssh nvidia@192.168.1.130` 2. 执行 `docker exec -ti cloudlazycataipodwebollama-ollama-1 bash` 3. 执行 `ollama ls` 列出当前的所有模型 ```bash NAME ID SIZE MODIFIED qwen3-vl:8b 901cae732162 6.1 GB 37 minutes ago qwen3:30b-a3b-instruct-2507-q4_K_M 19e422b02313 18 GB 5 hours ago qwen3-vl:30b eda0be100877 19 GB 25 hours ago phi3:14b cf611a26b048 7.9 GB 3 weeks ago qwen3:4b-instruct-2507-q4_K_M 088c6bc07f1d 2.5 GB 5 weeks ago qwen3:4b e55aed6fe643 2.5 GB 2 months ago qwen3:14b 7d7da67570e2 9.3 GB 6 months ago qwen3:8b e4b5fd7f8af0 5.2 GB 6 months ago qwen3:1.7b 458ce03a2187 1.4 GB 6 months ago ``` 4. 执行您需要删除的模型,比如 `ollama rm qwen3:1.7b` ![ollama cli delete model](/assets/ollama-cli-delete-model.DVyOOvN0.png) --- --- url: /aipod/ollama/download-model.md --- # ollama 中添加模型 打开 `https://ollama.com/search` 查看 `ollama` 支持的模型,选择一个您想拉取的模型。比如我现在想拉取 `deepseek-r1:70b` 模型 ## 使用 Open-WebUI 中拉取模型 * 安装 Open WebUI 应用,[点击跳转](https://appstore.lazycat.cloud/#/shop/detail/cloud.lazycat.aipod.openwebui) * 点击 `Open-WebUI` 左上角的模型名称,点击后会弹出一个下拉框,让您切换模型。 ![Open-WebUI切换模型下拉框](/assets/Open-WebUI-switch-model-dropdown.Dc7KeYiF.png) * 当下拉的列表中没有您需要的模型时,您可以手动填写一个 `deepseek-r1:70b` 到搜索框中,这个时候会提示您 "从 Ollama.com 中拉取 deepseek-r1:70b" ![Open-WebUI从ollama拉取模型](/assets/Open-WebUI-pull-model-from-ollama.BL_diF36.png) * 点击 "从 Ollama.com 中拉取 deepseek-r1:70b",就会到后台拉取模型,界面上也会显示进度 ![Open-WebUI拉取模型进度](/assets/Open-WebUI-pull-model-progress.BpV013X4.png) ## 使用 ollama 命令行下载模型 * 首先,您需要通过 ssh 或者 显示屏 进去到算力舱的系统中 * 执行 `docker exec -ti cloudlazycataipodwebollama-ollama-1 bash` 进去到 `ollama` 的容器中 * 执行 `ollama pull deepseek-r1:70b` 即可 ## 使用 webollama 拉取模型 1. 安装 `webollama` 应用,[点击跳转](https://appstore.lazycat.cloud/#/shop/detail/cloud.lazycat.aipod.webollama) 2. 打开后,您可以看到以下界面,根据提示拉取模型 ![webollama-pull-model.png](/assets/webollama-pull-model.DaN_sFWR.png) 使用 `webollama` 拉取模型中,目前没有进度,请耐心等待(建议使用上面两种方法拉取,可以查看进度) --- --- url: /aipod/thor/benchmark.md --- # onnx 查看onnx的输入和输出 # validation * 自动化验证报告补充见 `2026-03-18-validation.md` * 控制端工作目录为 `~/lzc_thor` * TensorRT 模型下载优先使用 `https://dl.corp.linakesi.cn/...` 的内网路径,必要时使用 `curl -k` * vLLM embedding 结果补充见 `../benchmark/qwen3-embedding-0.6b-vllm-thor.md` * vLLM Qwen3-VL embedding 失败记录见 `../benchmark/qwen3-vl-embedding-2b-vllm-thor.md` ``` python -c " import onnx model = onnx.load('zh-clip-vit-roberta-large-patch14-vision-openvino.onnx') for input in model.graph.input: print(f'Input name: {input.name}') for dim in input.type.tensor_type.shape.dim: print(f' Dimension: {dim.dim_value}') " ``` # ollama 1. 镜像使用: ghcr.io/nvidia-ai-iot/ollama:r38.2.arm64-sbsa-cu130-24.04 2. ```sh docker run --gpus all --runtime nvidia --rm -ti -v /ssd/lzc-ai-agent/data/ollama/data:/data/models/ollama ghcr.io/nvidia-ai-iot/ollama:r38.2.arm64-sbsa-cu130-24.04 ``` | Models | Thor Tokens/s | Orin Tokens/s | |:----------|:---------------|:---------------| | qwen3:14b | 20.44 tokens/s | 13.36 tokens/s | | | | | | | | | # Resnet50 1. 数据来源: /usr/src/tensorrt/data/resnet50 2. 执行命令: ```sh curl -L -O https://media.githubusercontent.com/media/onnx/models/refs/heads/main/validated/vision/classification/resnet/model/resnet50-v1-12.onnx /usr/src/tensorrt/bin/trtexec --onnx=resnet50-v1-12.onnx --shapes=data:4x3x224x224 --stronglyTyped --noDataTransfers --useCudaGraph --useSpinWait --duration=300 ``` | 设备 | 类型 | 批量 | 吞吐量 每秒/张 | 延迟性能 每张/秒 | 温度 | 电源 | 输入尺寸 | |------|--------|------|--------------------|------------------|------|-------|-------------| | Orin | 源类型 | 4 | 238.496 x 4 = 953 | 4.1875 ms | | 62.3w | 4×3×224×224 | | Thor | 源类型 | 4 | 588.473 x 4 = 2353 | 1.59375 ms | | - | 4×3×224×224 | | | | | | | | | | | Orin | 源类型 | 8 | 129.226 x 8 = 1033 | 7.73438 ms | 70 | 63.4w | 8×3×224×224 | | Thor | 源类型 | 8 | 317.383 x 8 = 2539 | 3.07031 ms | 78 | - | 8×3×224×224 | | | | | | | | | | | Orin | int4 | 8 | 129.157 x 8 = 1033 | 7.73438 ms | 72 | 63.4w | 8×3×224×224 | | Thor | int4 | 8 | 319.255 x 8 = 2554 | 3.07031 ms | 78 | 195w | 8×3×224×224 | | | | | | | | | | | Orin | int8 | 8 | 493.355 x 8 = 3946 | 2.02734 ms | 72 | 63.4w | 8×3×224×224 | | Thor | int8 | 8 | 486.597 x 8 = 3892 | 3.07031 ms | 78 | - | 8×3×224×224 | | | | | | | | | | | Orin | best | 8 | 129.797 x 8 = 1038 | 7.70264 ms | - | - | 8×3×224×224 | | Thor | best | 8 | 645.349 x 8 = 5162 | 1.51562 ms | - | - | 8×3×224×224 | # clip vision 1. 数据来源: curl -L -O http://dl.corp.linakesi.cn/lzc-image-search-model/onnx-export/vision/zh-clip-vit-roberta-large-patch14-vision-openvino.onnx 2. 执行命令 ```sh /usr/src/tensorrt/bin/trtexec --onnx=zh-clip-vit-roberta-large-patch14-vision-openvino.onnx --shapes=pixel_values:8x3x224x224 --int8 --noDataTransfers --useCudaGraph --useSpinWait --duration=300 ``` | 设备 | 类型 | 批量 | 吞吐量 每秒/张 | 延迟性能 每张/秒 | 温度 | 电源 | 输入尺寸 | |------|------|------|-------------------|------------------|------|-----------------------|-------------| | Orin | int8 | 8 | 8.72568 x 8 = 64 | 114.576 ms | - | - | 8×3×224×224 | | Thor | int8 | 8 | 54.6192 x 8 = 436 | 17.9922 ms | - | - | 8×3×224×224 | | | | | | | | | | | Orin | int4 | 8 | 7.25669 x 8 = 56 | 137.828 ms | 72 | 71W | 8×3×224×224 | | Thor | int4 | 8 | 16.6125 x 8 = 128 | 60.0859 ms | 82 | 平均 112W(瞬时到120W) | 8×3×224×224 | | | | | | | | | | | Orin | fp16 | 8 | 17.639 x 8 = 141 | 56.8906 ms | 75 | 74W | 8×3×224×224 | | Thor | fp16 | 8 | 60.4162 x 8 = 480 | 16.4219 ms | 100 | 平均 150W(瞬时到180W) | 8×3×224×224 | | | | | | | | | | | Orin | best | 8 | 18.39 x 8 = 147 | 54.1875 ms | - | - | 8×3×224×224 | | Thor | best | 8 | 87.641 x 8 = 701 | 11.0312 ms | - | - | 8×3×224×224 | thor 的温度下降的很快,从80度下载到60只用几秒, 但 thor 的功率会有时候达到 180 W多 # clip text 1. 数据来源: curl -L -O https://dl.corp.linakesi.cn/lzc-image-search-model/onnx-export/text/zh-clip-vit-roberta-large-patch14-text-openvino.onnx 2. 执行命令 ```sh /usr/src/tensorrt/bin/trtexec \ --onnx=zh-clip-vit-roberta-large-patch14-text-openvino.onnx \ --minShapes=input_ids:1x77,attention_mask:1x77 \ --optShapes=input_ids:8x77,attention_mask:8x77 \ --maxShapes=input_ids:32x77,attention_mask:32x77 \ --shapes=input_ids:8x77,attention_mask:8x77 \ --best \ --noDataTransfers \ --useCudaGraph \ --useSpinWait \ --duration=300 ``` | 设备 | 类型 | 批量 | 吞吐量 每秒/张 | 延迟性能 每张/秒 | 温度 | 电源 | 输入尺寸 | |------|------|------|--------------------|------------------|------|------|----------| | Orin | best | 8 | 70.645 x 8 = 565 | 14.1562 ms | - | - | 8×77 | | Thor | best | 8 | 277.171 x 8 = 2217 | 3.57812 ms | - | - | 8x77 | | | | | | | | | | | Orin | fp16 | 8 | 70.6059 x 8 = 565 | 14.1562 ms | - | - | 8x77 | | Thor | fp16 | 8 | 177.429 x 8 = 1419 | 5.625 ms | - | - | 8×77 | | | | | | | | | | | Orin | int8 | 8 | 38.5414 x 8 = 208 | 25.9375 ms | - | - | 8x77 | | Thor | int8 | 8 | 246.038 x 8 = 1968 | 4.06298 ms | - | - | 8×77 | # yolov8s 1. 数据来源: curl -L -O http://dl.corp.linakesi.cn/lzc-ai/onnx/yolo/yolov8s.onnx (静态类型的 onnx,不能指定输入形状,默认为 1x3x640x640) 2. 执行命令 ```sh /usr/src/tensorrt/bin/trtexec \ --onnx=yolov8s.onnx \ --best \ --noDataTransfers \ --useCudaGraph \ --useSpinWait \ --duration=300 ``` | 设备 | 类型 | 批量 | 吞吐量 每秒/张 | 延迟性能 每张/秒 | 温度 | 电源 | 输入尺寸 | |------|------|------|--------------------|------------------|------|------|-------------| | Orin | best | 1 | 559.474 x 1 = 559 | 1.78125 ms | - | - | 1x3x640x640 | | Thor | best | 1 | 1320.01 x 1 = 1320 | 0.720703 ms | - | - | 1x3x640x640 | | | | | | | | | | | Orin | int4 | 1 | 188.694 x 1 = 188 | 5.29688 ms | - | - | 1x3x640x640 | | Thor | int4 | 1 | 565.56 x 1 = 565 | 1.73438 ms | - | - | 1x3x640x640 | | | | | | | | | | | Orin | int8 | 1 | 542.293 x 1 = 542 | 1.84375 ms | - | - | 1x3x640x640 | | Thor | int8 | 1 | 1203.38 x 1 = 1203 | 0.820312 ms | - | - | 1x3x640x640 | | | | | | | | | | | Orin | fp16 | 1 | 372.599 x 1 = 372 | 2.68652 ms | - | - | 1x3x640x640 | | Thor | fp16 | 1 | 1116.32 x 1 = 1116 | 0.890625 ms | - | - | 1x3x640x640 | # dino-v2 1. 数据来源: curl -L -O http://dl.corp.linakesi.cn/lzc-ai/onnx/dinov2.onnx 2. 执行命令 ```sh /usr/src/tensorrt/bin/trtexec \ --onnx=dinov2.onnx \ --minShapes=pixel_values:1x3x518x518 \ --optShapes=pixel_values:8x3x518x518 \ --maxShapes=pixel_values:32x3x518x518 \ --shapes=pixel_values:8x3x518x518 \ --best \ --noDataTransfers \ --useCudaGraph \ --useSpinWait \ --duration=300 ``` | 设备 | 类型 | 批量 | 吞吐量 每秒/张 | 延迟性能 每张/秒 | 温度 | 电源 | 输入尺寸 | |------|------|------|------------------|------------------|------|------|-------------| | Orin | best | 8 | 3.01077 x 8 = 24 | 332.203 ms | - | - | 8x3x518x518 | | Thor | best | 8 | 3.48498 x 8 = 24 | 283.906 ms | - | - | 8x3x518x518 | | | | | | | | | | | Orin | int4 | 8 | 0.793952 x 8 = 6 | 1260.28 ms | - | - | 8x3x518x518 | | Thor | int4 | 8 | 1.60084 x 8 = 12 | 622.144 ms | - | - | 8x3x518x518 | | | | | | | | | | | Orin | int8 | 8 | 0.974057 x 8 = 8 | - ms | - | - | 8x3x518x518 | | Thor | int8 | 8 | 2.41794 x 8 = 16 | 397.922 ms | - | - | 8x3x518x518 | | | | | | | | | | | Orin | fp16 | 8 | 3.10437 x 8 = 18 | 322.125 ms | - | - | 8x3x518x518 | | Thor | fp16 | 8 | 9.98557 x 8 = 80 | 98.1719 ms | - | - | 8x3x518x518 | | | | | | | | | | | Orin | bf16 | 8 | 2.99196 x 8 = 24 | 334.617 ms | - | - | 8x3x518x518 | | Thor | bf16 | 8 | 10.9799 x 8 = 88 | 90.6172 ms | - | - | 8x3x518x518 | --- --- url: /aipod/benchmark/paddleocr-vl-1.5.md --- # PaddleOCR-VL-1.5 ## Benchmark | Model | Framework | Scene | TTFT | Token/s | Sample | Notes | |:------|:----------|:------|:-----|:--------|:-------|:------| | PaddleOCR-VL-1.5 (f16) | llama.cpp | OCR / ReadingGeneration | 310 ms | 94 | - | 能识别复杂排版,但大段重复文本会出现幻觉 | | PaddleOCR-VL-1.5-GGUF-Q4\_K\_M.gguf | llama.cpp | ReadingGeneration | - | 199.66 | 307 tokens / 1.5 s | 相比 f16 更容易出现幻觉 | | PaddleOCR-VL-1.5-GGUF-Q8\_0.gguf | llama.cpp | ReadingGeneration | - | 176.82 | 2012 tokens / 11 s | 速度与稳定性较均衡,建议使用该精度 | ## Notes * 该模型主要提供纯 OCR 能力。 * 若要同时实现版面分析和 Markdown 输出,需结合其他模型使用。 * Image: `registry.lazycat.cloud/x/lzc-aipod-paddleocr-vl:2017cd2` --- --- url: /aipod/olddocs/chattts.md --- # pynini 这个库在 jetson 下没有,需要自己构建 --- --- url: /aipod/quantum/cuda-q.md --- # Quantum Computing `jetson agx orin` 理论上是支持 `CUDA-Q`,但 `nvidia` 现在主推 `dgx`,在 `jetson` 上估计适配的概率不大。 ## Test 根据这个 [Quick Start](https://nvidia.github.io/cuda-quantum/latest/using/quick_start.html#quick-start) 中的例子,能编译,但运行的时候会出现错误。 https://forums.developer.nvidia.com/t/cuda-q-sample-code-initialization-error/340400 ## Links 1. [NVIDIA CUDA-Q](https://nvidia.github.io/cuda-quantum/latest/using/quick_start.html) 2. [quantum-computing](https://www.nvidia.com/en-us/glossary/quantum-computing/) 3. [Low-Cost, Low-Power, High-Capacity Quantum Simulation Device](https://www.ezchips.org/low-cost-low-power-high-capacity-quantum-simulation-device/) 4. [dependencies-and-compatibility](https://nvidia.github.io/cuda-quantum/latest/using/install/local_installation.html#dependencies-and-compatibility) 5. [NVIDIA CUDA Quantum](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/quantum/containers/cuda-quantum) --- --- url: /aipod/olddocs/rag.md --- # qwen-agent 1. 使用 ollama 加载 llm 模型(都是量化成 4bit 的模型) 2. 测试文档: 阿基米德的报复.pdf 3. 需要将 qwen2.5 在模型在 ollama 中更改上下文为 32k (qwen2.5 的支持上下文为 128k) pdf页数: 124页 解析pdf花费: 25.250 秒 ![qwen agent assistant](/assets/qwen-agent-assistant.I4RjEvL6.png) ![qwen agent assistant 3b](/assets/qwen-agent-assistant2.DrNnniHr.png) ![qwen agent assistant 3b](/assets/qwen-agent-assistant3.DvCdUSxL.png) qwen-agent assistant:解析较快 qwen-agent docqa:解析成本高,但准确率高,可用在超长文档问答 | 模型 | 方案 | 解析pdf总tokens数 | 推理时间 | 回答是否正确 | |:-----------------|:---------------------|-------------------|:---------|--------------| | qwen2.5:0.5b 32k | qwen-agent assistant | 128829(约13万) | 15s | No | | qwen2.5:0.5b 32k | qwen-agent docqa | 149482(约15万) | 150s | No | | qwen2.5:1.5b 32k | qwen-agent assistant | 128829 | 20s | Yes | | qwen2.5:1.5b 32k | qwen-agent docqa | 149482 | 160s | No | | qwen2.5:3b 32k | qwen-agent assistant | 128829 | 21s | Yes | | qwen2.5:3b 32k | qwen-agent docqa | 149482 | 230s | Yes | | qwen2.5:14b 32k | qwen-agent assistant | 128829 | 50s | Yes | | qwen2.5:14b 32k | qwen-agent docqa | 149482 | 750s | Yes | ## 使用llm从查询的信息部分推导出多语言关键词 qwen2.5:0.5b: {"keywords\_zh": \["伏尔泰", "vulgate"], "text": "伏尔泰\n在书中说了"} qwen2.5:1.5b: {"keywords\_zh": \["伏尔泰书", "伏尔泰的书籍"], "keywords\_en": \["Voltaire's book"], "text": "伏尔泰书中的内容是什么"} qwen2.5:3b: {"keywords\_zh": \["伏尔泰", "书", "话", "内容"], "keywords\_en": \["Voltaire", "book", "sentence", "content"], "text": "伏尔泰在书中说了什么话?"} qwen2.5:14b: {"keywords\_zh": \["伏尔泰", "书", "说话", "内容"], "keywords\_en": \["Voltaire", "book", "quote", "content"], "text": "伏尔泰在书中说了什么话"} 做 RAG 的时候,需要将模型的上下文长度调整,否则会不准确,在测试中,发现 qwen2.5:14b 在 ollama 模型的默认的2048上下文中,不能回答正确,但在 32k 的上下文中回答正确. llm 越大,其对关键词的描述(或者整理后的结果)就越接近实际的问题,匹配出来的结果也越精准。 [使用ollama本地服务时问答效果不佳](https://github.com/netease-youdao/QAnything/blob/qanything-v2/FAQ_zh.md#%E4%BD%BF%E7%94%A8ollama%E6%9C%AC%E5%9C%B0%E6%9C%8D%E5%8A%A1%E6%97%B6%E9%97%AE%E7%AD%94%E6%95%88%E6%9E%9C%E4%B8%8D%E4%BD%B3) --- --- url: /aipod/benchmark/qwen3-coder-next.md --- # Qwen3 Coder Next | Model | Name | Ctx | Memory | Token/s | |:----------------|:---------------------------------|:-----|--------|:--------| | Thor | Qwen3-Coder-Next-UD-Q4\_K\_XL.gguf | 256k | 55G | 22.28 | | Jetson agx orin | Qwen3-Coder-Next-UD-Q4\_K\_XL.gguf | 256k | 58.3G | 17.88 | | | | | | | ::: info jetson agx orin 64G 中跑需要先把 lzc-earlyoom 服务停止掉,否则会触发杀服务 `sudo systemctl stop lzc-earlyoom` ::: --- --- url: /aipod/benchmark/qwen3-embedding-0.6b-vllm-orin.md --- # Qwen3 Embedding 0.6B on Orin 使用 `vllm` 在 `orin` 机器上测试 `Qwen/Qwen3-Embedding-0.6B`。 ## 测试配置 * 机器:`orin` * 镜像:`registry.lazycat.cloud/x/lzc-aipod-vllm:bffa39b-orin` * 模型:`Qwen/Qwen3-Embedding-0.6B` * 运行方式:`vllm serve --runner pooling` * 显存参数:`--gpu-memory-utilization 0.65 --max-model-len 32768 --enforce-eager` * 数据集:`datasets/qwen3_embedding_texts.jsonl` * 并发:`1,2,4,6,8,16` * batch size:`1,8,32,128` ## 结论 * OpenAI Embeddings 兼容接口可用,`/v1/models` 与 `/v1/embeddings` 均通过。 * 输出向量维度为 `1024`。 * 小 batch 低延迟模式下,`batch=1 concurrency=16` 达到最高 `QPS=99.7079`,平均延迟 `0.1465s`,`p95=0.1781s`。 * 大 batch 吞吐模式下,`batch=128 concurrency=8` 达到最高 `items/s=706.4871`,`prompt tokens/s=11868.5`,平均延迟 `1.2431s`,`p95=1.5489s`。 ## 关键结果 | batch | concurrency | items/s | prompt tokens/s | avg latency (s) | p95 (s) | | --- | --- | ---: | ---: | ---: | ---: | | 1 | 16 | 99.7079 | 1723.0776 | 0.1465 | 0.1781 | | 8 | 8 | 170.4528 | 2883.0500 | 0.3544 | 0.3940 | | 32 | 8 | 522.1799 | 8784.2497 | 0.4447 | 0.5071 | | 128 | 8 | 706.4871 | 11868.5000 | 1.2431 | 1.5489 | ## Telemetry * 样本数:`73` * 最高温度:`58.937 C` * 最高功耗:`41189 mW` * 最高风扇 PWM:`20.7843` * 最高 GPU 利用率:`99.9%` ## 产物 远端结果目录: ```bash /home/nvidia/bench-results/qwen3-embedding-vllm ``` 关键文件: ```bash /home/nvidia/bench-results/qwen3-embedding-vllm/qwen3_embedding_bench.json /home/nvidia/bench-results/qwen3-embedding-vllm/models.json /home/nvidia/bench-results/qwen3-embedding-vllm/server.log /home/nvidia/bench-results/qwen3-embedding-vllm/telemetry.jsonl /home/nvidia/bench-results/qwen3-embedding-vllm/telemetry.summary.json ``` --- --- url: /aipod/benchmark/qwen3-embedding-0.6b-vllm-thor.md --- # Qwen3 Embedding 0.6B on Thor 使用官方 `vllm` 镜像在 `thor` 机器上测试 `Qwen/Qwen3-Embedding-0.6B`。 ## 测试配置 * 机器:`thor` * 镜像:`nvcr.io/nvidia/vllm:26.01-py3` * 容器内 `vllm` 版本:`0.13.0+faa43dbf.nv26.01` * 模型:`Qwen/Qwen3-Embedding-0.6B` * 运行方式:`vllm serve --runner pooling` * 启动参数:`--gpu-memory-utilization 0.65 --max-model-len 32768 --enforce-eager` * 数据集:`datasets/qwen3_embedding_texts.jsonl` * 并发:`1,2,4,6,8,16` * batch size:`1,8,32,128` ## 结论 * 官方镜像在 thor 上可以直接提供 OpenAI Embeddings 兼容接口。 * `models` 与 `embeddings` 请求均通过,输出向量维度为 `1024`。 * 低延迟模式下,`batch=1 concurrency=16` 达到最高 `QPS=207.9151`,平均延迟 `0.0688s`,`p95=0.0851s`。 * 吞吐模式下,`batch=128 concurrency=16` 达到最高 `items/s=1371.0304`,`prompt tokens/s=23019.6541`,平均延迟 `1.1980s`,`p95=1.5949s`。 ## 关键结果 | batch | concurrency | items/s | prompt tokens/s | avg latency (s) | p95 (s) | | --- | --- | ---: | ---: | ---: | ---: | | 1 | 16 | 207.9151 | 3593.0330 | 0.0688 | 0.0851 | | 8 | 16 | 584.2202 | 9794.8176 | 0.1909 | 0.2693 | | 32 | 16 | 934.1475 | 15679.8117 | 0.4482 | 0.5799 | | 128 | 16 | 1371.0304 | 23019.6541 | 1.1980 | 1.5949 | ## Telemetry * 样本数:`32` * 最高温度:`34.437 C` * 最高功耗:`8102 mW` * 最高风扇 PWM:`null` * 最高 GPU 利用率:`null` ## 产物 远端结果目录: ```bash /home/nvidia/bench-results/qwen3-embedding-vllm ``` 关键文件: ```bash /home/nvidia/bench-results/qwen3-embedding-vllm/qwen3_embedding_bench.json /home/nvidia/bench-results/qwen3-embedding-vllm/models.json /home/nvidia/bench-results/qwen3-embedding-vllm/server.log /home/nvidia/bench-results/qwen3-embedding-vllm/telemetry.jsonl /home/nvidia/bench-results/qwen3-embedding-vllm/telemetry.summary.json ``` --- --- url: /aipod/benchmark/qwen3-vl-embedding-2b-vllm-orin.md --- # Qwen3 VL Embedding 2B on Orin 使用 `vllm` 在 `orin` 机器上测试 `Qwen/Qwen3-VL-Embedding-2B`,同时验证离线 embedding 与 HTTP 服务拉起。 ## 测试配置 * 机器:`orin` * 镜像:`registry.lazycat.cloud/x/lzc-aipod-vllm:bffa39b-orin` * 模型:`Qwen/Qwen3-VL-Embedding-2B` * 离线模式:`LLM(..., runner="pooling")` * HTTP 模式:`vllm serve --runner pooling` * 显存参数:`--gpu-memory-utilization 0.65 --max-model-len 8192 --enforce-eager` * 数据集:`datasets/qwen3_vl_embedding.jsonl` * 合成资源:`scripts/prepare_qwen3_vl_assets.py` ## 结论 * 离线 `embed()` 路径通过,`16` 条图文样本完成 embedding。 * 输出向量维度为 `2048`。 * 离线吞吐为 `2.4299 items/s`,总耗时 `6.5845s`。 * 图文检索样例 `4/4` top1 命中正确。 * HTTP 服务拉起成功,`/v1/models` 就绪。 ## 离线验证结果 | case | top1 | | --- | --- | | `case-jtop` | `positive-doc` | | `case-openwebui` | `positive-doc` | | `case-network` | `positive-doc` | | `case-embedding` | `positive-doc` | ## Telemetry * 样本数:`150` * 最高温度:`67.156 C` * 最高功耗:`56761 mW` * 最高风扇 PWM:`35.2941` * 最高 GPU 利用率:`99.8%` ## 产物 远端结果目录: ```bash /home/nvidia/bench-results/qwen3-vl-embedding-vllm ``` 关键文件: ```bash /home/nvidia/bench-results/qwen3-vl-embedding-vllm/qwen3_vl_embedding_offline.json /home/nvidia/bench-results/qwen3-vl-embedding-vllm/qwen3_vl_http_probe.json /home/nvidia/bench-results/qwen3-vl-embedding-vllm/qwen3_vl_http.log /home/nvidia/bench-results/qwen3-vl-embedding-vllm/qwen3_vl_models.json /home/nvidia/bench-results/qwen3-vl-embedding-vllm/telemetry.jsonl /home/nvidia/bench-results/qwen3-vl-embedding-vllm/telemetry.summary.json ``` --- --- url: /aipod/benchmark/qwen3-vl-embedding-2b-vllm-thor.md --- # Qwen3 VL Embedding 2B on Thor 使用官方 `vllm` 镜像在 `thor` 机器上测试 `Qwen/Qwen3-VL-Embedding-2B`,结论是当前镜像组合无法跑通。 ## 测试配置 * 机器:`thor` * 镜像:`nvcr.io/nvidia/vllm:26.01-py3` * 容器内 `vllm` 版本:`0.13.0+faa43dbf.nv26.01` * 模型:`Qwen/Qwen3-VL-Embedding-2B` * 尝试路径: * 离线 `LLM(..., runner="pooling")` * HTTP `vllm serve --runner pooling` * 尝试参数: * `--gpu-memory-utilization 0.65 --max-model-len 8192 --enforce-eager` * `--gpu-memory-utilization 0.5 --max-model-len 8192 --enforce-eager` * `dtype=float16` 也做了额外验证 * 数据集:`datasets/qwen3_vl_embedding.jsonl` * 合成资源:`scripts/prepare_qwen3_vl_assets.py` ## 结论 * 官方镜像可以识别模型,并进入 `runner=pooling` 的初始化流程。 * 当 `gpu-memory-utilization=0.65` 时,启动前即因空闲显存不足失败。 * 将显存占用降低到 `0.5` 后,初始化继续推进到视觉编码 profile 阶段,但仍失败。 * 失败根因不在脚本参数,而在当前官方镜像的 `vllm + torch` 组合上:`Qwen3-VL-Embedding-2B` 的视觉路径在 `torch.nn.functional.conv3d` 上触发 `RuntimeError: GET was unable to find an engine to execute this computation`。 * `float16` 额外验证后结果相同,因此不是 `bfloat16` 单一路径问题。 ## 失败摘要 * `0.65` 配置报错: * `Free memory on device (32.52/59.9 GiB) on startup is less than desired GPU memory utilization (0.65, 38.94 GiB).` * `0.5` 与 `float16` 配置报错: * `RuntimeError: GET was unable to find an engine to execute this computation` * 触发位置在 `qwen3_vl.py` 视觉编码路径的 `F.conv3d(...)` ## Telemetry * 样本数:`22` * 最高温度:`32.593 C` * 最高功耗:`6887 mW` * 最高风扇 PWM:`null` * 最高 GPU 利用率:`null` ## 产物 远端结果目录: ```bash /home/nvidia/bench-results/qwen3-vl-embedding-vllm ``` 当前仅生成了遥测文件,离线结果与 HTTP probe 未生成: ```bash /home/nvidia/bench-results/qwen3-vl-embedding-vllm/telemetry.jsonl /home/nvidia/bench-results/qwen3-vl-embedding-vllm/telemetry.summary.json ``` --- --- url: /aipod/benchmark/qwen3-8b-embedding.md --- # qwen3-embedding-8b ### ollama ``` python3 ollama_perf.py --file GuiMiZhiZhuu.txt --url https://ollama-ai.13gxg.heiyu.space/v1/embeddings --model dengcao/Qwen3-Embedding-8B:Q8_0 --- 测试参数 --- 文件路径: GuiMiZhiZhuu.txt 模型名称: dengcao/Qwen3-Embedding-8B:Q8_0 API URL: https://ollama-ai.13gxg.heiyu.space/v1/embeddings 切片长度: 1024 字符 总字数: 4775359 总切片数: 4664 注意: 所有切片将在一个 API 请求中发送。 ------------------ 开始发送单个大请求,请稍候... 请求完成! --- 性能测试结果 --- 总耗时: 3763.11 秒 总字数: 4775359 总处理 Tokens: 3325333 请求状态: 成功 (HTTP 200) --- 核心指标 --- 平均 Token 速度: 883.67 tokens/秒 平均吞吐量: 1268.99 字/秒 -------------------- ``` 小文件 ``` $ python3 ollama_perf.py --file xce --url https://ollama-ai.13gxg.heiyu.space/v1/embeddings --model dengcao/Qwen3-Embedding-8B:Q5_K_M --- 测试参数 --- 文件路径: xce 模型名称: dengcao/Qwen3-Embedding-8B:Q5_K_M API URL: https://ollama-ai.13gxg.heiyu.space/v1/embeddings 切片长度: 1024 字符 总字数: 41648 总切片数: 41 注意: 所有切片将在一个 API 请求中发送。 ------------------ 开始发送单个大请求,请稍候... 请求完成! --- 性能测试结果 --- 总耗时: 34.52 秒 总字数: 41648 总处理 Tokens: 29006 请求状态: 成功 (HTTP 200) --- 核心指标 --- 平均 Token 速度: 840.33 tokens/秒 平均吞吐量: 1206.59 字/秒 -------------------- ``` ### vllm 默认为官方的 Qwen/Qwen3-Embedding-8B bfloat16 类型 ``` $ python3 ollama_perf.py --file GuiMiZhiZhuu.txt --url http://192.168.1.209:8999/v1/embeddings --model qwen3-embedding-8b --- 测试参数 --- 文件路径: GuiMiZhiZhuu.txt 模型名称: qwen3-embedding-8b API URL: http://192.168.1.209:8999/v1/embeddings 切片长度: 1024 字符 总字数: 4775359 总切片数: 4664 注意: 所有切片将在一个 API 请求中发送。 ------------------ 开始发送单个大请求,请稍候... 请求完成! --- 性能测试结果 --- 总耗时: 2312.80 秒 总字数: 4775359 总处理 Tokens: 3329997 请求状态: 成功 (HTTP 200) --- 核心指标 --- 平均 Token 速度: 1439.81 tokens/秒 平均吞吐量: 2064.75 字/秒 -------------------- ``` 占用内存需要 25 GB --- --- url: /aipod/benchmark/qwen3.5.md --- # Qwen3.5 ## Benchmark 以下三机对比采用 `lzc_thor` 在 `2026-03-20T05-47-33Z` 的完整正式矩阵,只保留正确用法下的完整结果,不采用 `p1`、`cublas`、`mmq` 之类试验性单请求数据。 | Model | Best Parallel | Orin | Thor T4000 | Thor T5000 | |:------|:--------------|:-----|:-----------|:-----------| | `Qwen3.5-0.8B-Q4_K_M` | `16` | `1326.86 tok/s` / `TTFT 1.94 s` / `42.54 W` | `1311.50 tok/s` / `TTFT 1.88 s` / `21.72 W` | `1783.26 tok/s` / `TTFT 1.37 s` / `30.35 W` | | `Qwen3.5-2B-Q4_K_M` | `16` | `989.57 tok/s` / `TTFT 2.91 s` / `46.43 W` | `960.84 tok/s` / `TTFT 3.01 s` / `22.46 W` | `1291.47 tok/s` / `TTFT 2.17 s` / `32.93 W` | | `Qwen3.5-4B-Q4_K_M` | `16` | `431.96 tok/s` / `TTFT 7.16 s` / `48.48 W` | `442.65 tok/s` / `TTFT 7.07 s` / `23.95 W` | `622.09 tok/s` / `TTFT 4.87 s` / `35.87 W` | | `Qwen3.5-9B-Q4_K_M` | `16` | `318.65 tok/s` / `TTFT 10.85 s` / `56.49 W` | `314.55 tok/s` / `TTFT 11.23 s` / `24.60 W` | `450.86 tok/s` / `TTFT 7.71 s` / `35.85 W` | | `Qwen3.5-27B-Q4_K_M` | `16` | `95.80 tok/s` / `TTFT 44.38 s` / `56.82 W` | `90.82 tok/s` / `TTFT 48.44 s` / `25.32 W` | `150.03 tok/s` / `TTFT 25.49 s` / `36.91 W` | | `Qwen3.5-35B-A3B-Q4_K_M` | `16` | `192.59 tok/s` / `TTFT 27.41 s` / `43.12 W` | `181.57 tok/s` / `TTFT 30.34 s` / `18.74 W` | `271.35 tok/s` / `TTFT 19.09 s` / `31.05 W` | | `Qwen3.5-122B-A10B-IQ2_S` | `16` | `65.14 tok/s` / `TTFT 46.02 s` / `42.48 W` | `60.92 tok/s` / `TTFT 50.95 s` / `19.49 W` | `76.55 tok/s` / `TTFT 43.03 s` / `26.25 W` | ## Latest T4000 SM110 Retest `2026-03-20T17-10-00Z` 的最新 `sm110 maxn retest` 目前先完成了 `Thor T4000`,因此单独列出,避免把未完成的 T5000 / Orin 数据混到同一张对比表里。 | Model | Best Parallel | Thor T4000 | |:------|:--------------|:-----------| | `Qwen3.5-0.8B-Q4_K_M` | `16` | `1313.47 tok/s` / `TTFT 1.88 s` / `21.73 W` | | `Qwen3.5-2B-Q4_K_M` | `16` | `960.84 tok/s` / `TTFT 3.01 s` / `22.84 W` | | `Qwen3.5-4B-Q4_K_M` | `16` | `442.74 tok/s` / `TTFT 7.07 s` / `23.95 W` | | `Qwen3.5-9B-Q4_K_M` | `16` | `314.46 tok/s` / `TTFT 11.23 s` / `24.60 W` | | `Qwen3.5-27B-Q4_K_M` | `16` | `91.00 tok/s` / `TTFT 48.40 s` / `25.33 W` | | `Qwen3.5-35B-A3B-Q4_K_M` | `16` | `181.36 tok/s` / `TTFT 30.35 s` / `18.74 W` | | `Qwen3.5-122B-A10B-IQ2_S` | `16` | `60.67 tok/s` / `TTFT 51.23 s` / `19.49 W` | ## Notes * 三机对比数据来源:`/home/catdog/lzc_thor/docs/runs/2026-03-20T05-47-33Z` * 最新 `sm110` 重测数据来源:`/home/catdog/lzc_thor/docs/runs/2026-03-20T17-10-00Z` * `Qwen3.5-397B-A17B-IQ1_S` 在最新测试里 `T4000` 与 `Orin` 均未形成可用 benchmark,因此不写入正式 benchmark 表。 * 功耗列统一记录测试中的最大功率。 --- --- url: /aipod/tts/qwen3tts_api.md --- # Qwen3TTS(OpenAI 兼容 TTS) `Qwen3TTS` 是基于 `faster-qwen3-tts` 封装的 OpenAI 兼容文本生成语音服务。算力舱中的推荐服务名为 `qwen3tts-ai`,适合接入 OpenWebUI、OpenAI-compatible SDK 或任意自定义 HTTP 客户端。 和 `Kokoro tts-ai` 相比,这个服务的特点是: * 兼容 `POST /v1/audio/speech` * 支持 `wav` / `pcm` 流式音频返回 * 启动时先完成模型 warmup,再对外标记 ready * 支持多音色查询接口 `/v1/audio/voices` * 支持请求级 `language`、`instruct`、`seed` * `clone` 模式下支持生成和复用 `speaker.pt` * 额外提供 `POST /v1/audio/voice-clone/pt` ## 服务入口 * 推荐服务域名:`qwen3tts-ai` * 若沿用当前示例 compose,也可使用 `qwen3-tts-ai` * 健康检查:`/health` * 音色列表:`/v1/audio/voices` * TTS 接口:`/v1/audio/speech` * PT 生成接口:`/v1/audio/voice-clone/pt` 部署到算力舱后的访问形式示例: * 服务地址:`https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}` * 健康检查:`https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/health` * 音色列表:`https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voices` * TTS 接口:`https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech` * PT 生成接口:`https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voice-clone/pt` ## 执行与启动行为 `qwen3tts-ai` 和传统“进程启动即 ready”的服务不一样,它在正式接收请求前还会执行一轮 startup warmup。 启动流程大致如下: 1. 加载模型与音色配置 2. 初始化 CUDA graphs 3. 执行一轮 warmup 请求 4. `/health` 中的 `ready` 变为 `true` 5. 才建议上游 AI 应用或脚本发起正式 TTS 请求 执行上的注意点: * 容器进程已经起来,不代表服务已经 ready * 应用接入时应轮询 `/health`,并以 `ready=true` 作为可用标准 * 文档中的 Docker `healthcheck` 主要用于检测 HTTP 服务存活;如果你需要严格 readiness,请额外检查 `/health` 返回体中的 `ready` 字段 ## 健康检查 ### `GET /health` 该接口用于判断服务是否完成模型加载和 startup warmup。 请求: ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/health ``` 响应示例: ```json { "status": "ok", "ready": true, "model_loaded": true, "mode": "custom", "voices": ["aiden", "dylan", "vivian"], "startup_warmup_enabled": true, "startup_warmup_completed": true, "startup_warmup_seconds": 15.11 } ``` 建议: * 只有在 `ready=true` 时再发起正式 TTS 请求 * 可以通过 `mode` 字段判断当前服务运行模式:`custom` 表示 CustomVoice 固定音色模式,`clone` 表示 Base 声纹克隆模式 ## 音色列表接口 ### `GET /v1/audio/voices` 请求: ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voices ``` 响应示例: ```json { "voices": [ "aiden", "dylan", "eric", "ono_anna", "ryan", "serena", "sohee", "uncle_fu", "vivian" ], "default_voice": "vivian", "mode": "custom" } ``` 说明: * `voice` 请求参数必须从这里获取 * 如果传入不存在的 `voice`,服务会优先回退到 `default_voice` ## TTS 接口 ### `POST /v1/audio/speech` 该接口兼容 OpenAI TTS API 的常见调用方式。 常规 JSON 请求头: ```http Content-Type: application/json Accept: audio/wav ``` JSON 请求体示例: ```json { "model": "tts-1", "input": "欢迎使用 Qwen3TTS 服务。", "voice": "vivian", "response_format": "wav", "language": "Chinese", "instruct": "请用温和、清晰、偏慢一点的语气朗读。", "seed": 1234 } ``` 参数说明: | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `model` | string | 否 | 兼容字段,建议固定传 `tts-1` | | `input` | string | 是 | 待合成文本,不能为空 | | `voice` | string | 否 | 音色名,建议先通过 `/v1/audio/voices` 获取 | | `response_format` | string | 否 | 支持 `wav` / `pcm` / `mp3` | | `language` | string | 否 | 请求级语言,传入时覆盖该 voice 的默认语言配置 | | `instruct` | string | 否 | 自定义指令,用于控制语气、风格、节奏或口音倾向 | | `seed` | integer | 否 | 请求级随机种子。相同文本、音色、语言、指令和模型版本下,固定同一个 `seed` 可以复现同一份音频输出 | `instruct` 的执行规则: * 当前服务运行在 `custom` 或 `clone` 模式时可用 * 请求里传入 `instruct` 时,优先级高于服务端静态 voice 配置中的默认 `instruct` * 请求里不传 `instruct` 时,沿用当前服务默认行为 * 如果你只关心 OpenAI 兼容调用方式,可以把它理解为一个可选扩展字段 当前请求级扩展参数: * 已暴露并建议使用的字段:`language`、`instruct`、`seed` * `language` 用于覆盖音色默认语言 * `instruct` 用于控制语气、风格、节奏或口音倾向 * `seed` 用于让同一请求在同一服务版本下可复现 * `voice_clone_pt` 仅在 `clone` 模式下可用,并且必须通过 `multipart/form-data` 文件上传 `seed` 的使用建议: * 建议传非负整数,例如 `1234` * 要复现结果时,应同时固定 `input`、`voice`、`language`、`instruct`、`response_format` * 复现结论以同一镜像版本、同一模型权重、同一音色配置为前提 * 不传 `seed` 时,请求会走默认随机路径,不保证多次结果一致 `language` 的执行规则: * 请求里传入 `language` 时,优先级高于服务端静态 voice 配置中的默认语言 * 请求里不传 `language` 时,沿用当前服务默认行为;Orin 自定义镜像默认一般为 `Auto` * 常见可用值可按模型能力使用,例如:`Chinese`、`English`、`Japanese`、`Korean`、`German`、`French`、`Russian`、`Portuguese`、`Spanish`、`Italian` `voice` 的执行规则: * `voice` 请求参数大小写不敏感,建议先通过 `/v1/audio/voices` 获取可用值 * 如果传入不存在的 `voice`,服务会优先回退到 `default_voice` 当前不支持的请求级参数: * `speaker` * `instructions` * `temperature` * `do_sample` * `top_k` * `top_p` * `repetition_penalty` 说明: * OpenAI 接口里应使用 `voice`,而不是 `speaker` * OpenAI 接口里应使用 `instruct`,而不是 `instructions` * 采样相关内部参数当前没有作为 OpenAI 请求字段对外暴露,文档中不建议继续传递 * `voice_clone_pt` 不是 JSON 字段;如需传入 `pt` 文件,请改用 `multipart/form-data` 响应说明: | `response_format` | Content-Type | 是否流式 | 说明 | |---|---|---|---| | `wav` | `audio/wav` | 是 | 先返回 WAV header,再持续返回 PCM 音频块 | | `pcm` | `audio/pcm` | 是 | 直接返回 16-bit little-endian PCM 音频流 | | `mp3` | `audio/mpeg` | 否 | 先生成完整音频,再整体编码为 mp3 | ## PT 声纹克隆接口 以下能力只在服务运行于 `clone` 模式时可用,典型镜像为: * `registry.lazycat.cloud/x/faster-qwen3-tts:0.6b-base-clone-openai-orin-v2` `clone` 模式下推荐把 `voice` 固定传成 `dynamic`,然后通过请求级 `voice_clone_pt` 或静态 `voices.json` 决定说话人。 ### `POST /v1/audio/voice-clone/pt` 该接口根据一段参考音频提取 speaker embedding,并直接把对应的 `pt` 文件返回给调用方。 请求方式: * `Content-Type: multipart/form-data` * 文件字段:`ref_audio` * 可选表单字段:`filename` 请求示例: ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voice-clone/pt \ -F "ref_audio=@ref_audio.wav;type=audio/wav" \ -F "filename=speaker.pt" \ --output speaker.pt ``` 说明: * 服务端不会持久化这个 `pt` 文件 * `pt` 文件应由调用方自行保存,例如放到对象存储、NAS、数据库或业务侧文件目录 * 同一个说话人的后续请求可以反复复用同一个 `pt` ### 在请求中直接使用 `pt` 当你已经拿到 `speaker.pt` 后,可以直接在 `POST /v1/audio/speech` 中上传。 这时请求必须使用 `multipart/form-data`,因为既要传文本字段,也要传文件字段。 请求示例: ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Accept: audio/wav" \ -F "model=tts-1" \ -F "input=今天的风从江面慢慢吹过来,像有人把一句话轻轻放在耳边。" \ -F "voice=dynamic" \ -F "response_format=wav" \ -F "language=Chinese" \ -F "seed=1234" \ -F "voice_clone_pt=@speaker.pt;type=application/octet-stream" \ --output speech.wav ``` 可用文件字段名: * `voice_clone_pt` * `speaker_pt` * `pt` 执行规则: * 只在 `clone` 模式下可用 * 请求里上传 `voice_clone_pt` 时,优先级高于服务端静态 `voices.json` 里的 `speaker_pt` * 同一个 `pt` 搭配不同文本时,应保持同一说话人特征;如需提升复现性,建议同时固定 `seed` ### 将 `pt` 静态挂到服务里 如果你不想每次请求都重复上传 `speaker.pt`,可以把 `pt` 文件和 `voices.json` 一起挂到容器里。 `voices.json` 示例: ```json { "alice": { "speaker_pt": "/data/voices/alice.pt", "language": "Chinese" }, "narrator": { "speaker_pt": "/data/voices/narrator.pt", "language": "Chinese", "instruct": "请用平稳、自然的旁白语气朗读。" } } ``` 启动后: * `GET /v1/audio/voices` 会返回 `alice`、`narrator` * 调用 `POST /v1/audio/speech` 时直接传 `voice=alice` 或 `voice=narrator` * 这时请求体可以继续使用普通 JSON,不需要每次再上传 `pt` * `speaker_pt` 和 `instruct` 可以同时使用;如果 `voices.json` 已配置默认 `instruct`,请求里再传 `instruct` 时会覆盖静态配置 * 但在 `speaker_pt` 场景下,`instruct` 仍属于实验性支持,效果不保证稳定;如果你强依赖指令跟随,优先考虑直接使用 `ref_audio + ref_text` 的 ICL 模式 ## 非流式调用示例 ### 生成 WAV ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Content-Type: application/json" \ -d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"wav"}' \ --output speech.wav ``` ### 生成 WAV 并传入 instruct ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Content-Type: application/json" \ -d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"wav","instruct":"请用温和、清晰、偏慢一点的语气朗读。"}' \ --output speech.wav ``` ### 生成 WAV,并固定 seed ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Content-Type: application/json" \ -d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"wav","seed":1234}' \ --output speech.wav ``` 说明: * 相同请求体重复调用时,固定同一个 `seed` 可以得到相同结果 * 如果你更换了文本、音色、语言、指令、模型版本或镜像版本,输出也会随之变化 ### 生成 WAV,并显式指定 language + instruct + seed ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Content-Type: application/json" \ -d '{"model":"tts-1","input":"其实我真的有发现,我是一个特别善于观察别人情绪的人。","voice":"vivian","response_format":"wav","language":"Chinese","instruct":"用特别愤怒的语气说","seed":1234}' \ --output speech.wav ``` ### 生成 MP3 ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Content-Type: application/json" \ -d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"mp3"}' \ --output speech.mp3 ``` 说明: * `mp3` 会在完整音频生成后再编码,不适合低延迟首包场景 * 边收边播建议优先用 `wav` 或 `pcm` ## 流式调用说明 这个服务的“流式”不是 SSE 文本事件流,而是直接返回音频字节流: * `wav` / `pcm` 会通过 HTTP chunked transfer 逐块返回 * `mp3` 不支持边生成边返回 ### curl 流式保存 ```bash curl -N https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Content-Type: application/json" \ -d '{"model":"tts-1","input":"今天我们测试流式音频返回。","voice":"vivian","response_format":"wav"}' \ --output stream.wav ``` ### Python 流式读取 ```python import requests url = f"https://qwen3tts-ai.{LAZYCAT_BOX_DOMAIN}/v1/audio/speech" payload = { "model": "tts-1", "input": "今天我们测试流式音频返回。", "voice": "vivian", "response_format": "wav", "language": "Chinese", "instruct": "请用自然停顿、比较平稳的语气朗读。", } with requests.post(url, json=payload, stream=True, timeout=600) as resp: resp.raise_for_status() with open("stream.wav", "wb") as f: for chunk in resp.iter_content(chunk_size=4096): if chunk: f.write(chunk) ``` ### JavaScript 流式读取 ```javascript const resp = await fetch(`https://qwen3tts-ai.${boxDomain}/v1/audio/speech`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "tts-1", input: "今天我们测试流式音频返回。", voice: "vivian", response_format: "wav", language: "Chinese", instruct: "请用自然、平稳的旁白语气朗读。" }) }); if (!resp.ok) { throw new Error(`HTTP ${resp.status}`); } const reader = resp.body.getReader(); const chunks = []; while (true) { const { value, done } = await reader.read(); if (done) break; chunks.push(value); } const blob = new Blob(chunks, { type: "audio/wav" }); ``` ## 常见错误 ### 400 参数错误 ```json { "detail": "'input' text is empty" } ``` 或: ```json { "detail": "response_format 'aac' not supported. Use: wav, pcm, mp3" } ``` ### 400 音色不存在 ```json { "detail": "Voice 'unknown' is not configured. Available voices: ['aiden', 'vivian']" } ``` ### 503 服务未就绪 ```json { "detail": "Model not loaded" } ``` ## 单独部署使用 ### 方案一:CustomVoice 固定音色 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: faster-qwen3-tts: image: registry.lazycat.cloud/x/faster-qwen3-tts:0.6b-custom-openai-orin-v11 environment: HF_HUB_OFFLINE: "1" TRANSFORMERS_OFFLINE: "1" HF_HOME: /root/.cache/huggingface XDG_CACHE_HOME: /root/.cache QWEN_TTS_MODEL: /opt/models/Qwen3-TTS-12Hz-0.6B-CustomVoice QWEN_TTS_MODE: custom QWEN_TTS_DEFAULT_VOICE: vivian QWEN_TTS_LANGUAGE: Auto QWEN_TTS_CHUNK_SIZE: "8" QWEN_TTS_WARMUP_MAX_NEW_TOKENS: "32" volumes: - /etc/timezone:/etc/timezone:ro - ${LZC_AGENT_CACHE_DIR}/cache:/root/.cache - ${LZC_AGENT_DATA_DIR}/data:/data labels: - "traefik.enable=true" - "traefik.http.routers.${LZC_SERVICE_ID}-qwen3tts.rule=Host(`qwen3tts-ai`)" expose: - 8000 healthcheck: test: [ "CMD", "curl", "-f", "http://localhost:8000/health" ] interval: 30s timeout: 5s retries: 100 start_period: 1s networks: default: external: true name: traefik-shared-network ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 先通过 `http://127.0.0.1:8000/health` 检查服务是否已经 `ready=true` 4. 再通过 `http://127.0.0.1:8000/v1/audio/voices` 获取可用音色 5. 最后再调用 `http://127.0.0.1:8000/v1/audio/speech` 推荐的启动后校验顺序: ```bash curl http://127.0.0.1:8000/health curl http://127.0.0.1:8000/v1/audio/voices curl http://127.0.0.1:8000/v1/audio/speech \ -H "Content-Type: application/json" \ -d '{"model":"tts-1","input":"启动校验。","voice":"vivian","response_format":"wav","instruct":"请用简短、清晰的方式朗读。"}' \ --output verify.wav ``` 如果你是本机单独调试而不是挂到算力舱网关后面,可以把上面的 `expose` 改成: ```yml ports: - "8000:8000" ``` 当前推荐部署说明: * 推荐使用 `0.6b-custom-openai-orin-v11` * OpenAI 接口当前建议使用的请求字段是:`voice`、`language`、`instruct`、`seed` * 如果你需要复现同一份音频,请在请求中显式传入固定 `seed` * `speed` 当前虽被兼容接受,但暂未实际生效,因此文档未列为推荐参数 ### 方案二:Base + Clone + PT 如果你希望调用方自己管理 `speaker.pt`,或者希望把同一个 `pt` 绑定成多个命名音色,推荐使用 `clone` 镜像。 `docker-compose.yml` 示例: ```yml services: faster-qwen3-tts: image: registry.lazycat.cloud/x/faster-qwen3-tts:0.6b-base-clone-openai-orin-v2 environment: HF_HUB_OFFLINE: "1" TRANSFORMERS_OFFLINE: "1" HF_HOME: /root/.cache/huggingface XDG_CACHE_HOME: /root/.cache QWEN_TTS_MODEL: /opt/models/Qwen3-TTS-12Hz-0.6B-Base QWEN_TTS_MODE: clone QWEN_TTS_DEFAULT_VOICE: dynamic QWEN_TTS_VOICES: /data/voices.json QWEN_TTS_LANGUAGE: Auto QWEN_TTS_CHUNK_SIZE: "8" QWEN_TTS_WARMUP_MAX_NEW_TOKENS: "32" volumes: - /etc/timezone:/etc/timezone:ro - ${LZC_AGENT_CACHE_DIR}/cache:/root/.cache - ${LZC_AGENT_DATA_DIR}/data:/data labels: - "traefik.enable=true" - "traefik.http.routers.${LZC_SERVICE_ID}-qwen3tts.rule=Host(`qwen3tts-ai`)" expose: - 8000 healthcheck: test: [ "CMD", "curl", "-f", "http://localhost:8000/health" ] interval: 30s timeout: 5s retries: 100 start_period: 1s networks: default: external: true name: traefik-shared-network ``` 推荐目录结构: ```text /data ├── voices.json └── voices ├── alice.pt └── narrator.pt ``` 推荐调用顺序: 1. 先调用 `POST /v1/audio/voice-clone/pt` 生成 `speaker.pt` 2. 把 `speaker.pt` 保存到 `/data/voices/*.pt` 3. 更新 `/data/voices.json` 4. 重启服务或重新部署,使服务加载新的 `voices.json` 5. 之后直接用 `voice=<配置名>` 调用 `POST /v1/audio/speech` 如果你不想重启服务,也可以继续走请求级上传: ```bash curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \ -H "Accept: audio/wav" \ -F "model=tts-1" \ -F "input=这是一次请求级 pt 调用。" \ -F "voice=dynamic" \ -F "response_format=wav" \ -F "voice_clone_pt=@speaker.pt;type=application/octet-stream" \ --output speech.wav ``` ## 与当前 `tts-ai` 服务的区别 * `tts-ai` 当前文档对应的是 `Kokoro` 服务 * `qwen3tts-ai` 是 OpenAI 兼容的 Qwen3TTS 服务 * `qwen3tts-ai` 适合多音色、OpenAI-compatible client 和流式音频返回场景 --- --- url: /aipod/benchmark/sdcpp.md --- # SD.CPP ## Benchmark 以下数据采用 `lzc_thor` 在 `2026-03-21T03-10-55Z` 的正式 `speed_matrix` 结果,只保留 `Orin / Thor T4000 / Thor T5000` 三机都有有效结果的模型;边界探测、单服务/双服务对比和 smoke case 不写入这张表。 | Model | Best Case | Orin | Thor T4000 | Thor T5000 | |:------|:----------|:-----|:-----------|:-----------| | `FLUX.1-dev` | 以各机最佳 case 为准 | `0.033 img/s` / `30.72 s` / `44.75 W` | `0.047 img/s` / `42.73 s` / `20.59 W` | `0.068 img/s` / `29.41 s` / `38.41 W` | | `SD2.1` | 以各机最佳 case 为准 | `0.158 img/s` / `28.58 s` / `48.54 W` | `0.183 img/s` / `46.49 s` / `20.16 W` | `0.252 img/s` / `9.94 s` / `32.42 W` | | `SD3 Medium` | 以各机最佳 case 为准 | `0.072 img/s` / `20.83 s` / `49.16 W` | `0.148 img/s` / `13.48 s` / `20.55 W` | `0.237 img/s` / `8.44 s` / `38.28 W` | | `SDXL` | 以各机最佳 case 为准 | `0.030 img/s` / `67.45 s` / `48.28 W` | `0.040 img/s` / `50.10 s` / `20.93 W` | `0.048 img/s` / `41.34 s` / `34.31 W` | | `SSD-1B` | 以各机最佳 case 为准 | `0.217 img/s` / `11.56 s` / `47.82 W` | `0.243 img/s` / `16.44 s` / `20.93 W` | `0.357 img/s` / `11.21 s` / `34.69 W` | ## Best Case | Model | Orin Best Case | Thor T4000 Best Case | Thor T5000 Best Case | |:------|:---------------|:---------------------|:---------------------| | `FLUX.1-dev` | `p1` | `batch_2` | `batch_2` | | `SD2.1` | `p8` | `p16` | `p4` | | `SD3 Medium` | `p2` | `batch_2` | `batch_2` | | `SDXL` | `batch_2` | `batch_2` | `batch_2` | | `SSD-1B` | `p4` | `batch_4` | `batch_4` | ## Notes * 数据来源:`/home/catdog/lzc_thor/docs/runs/2026-03-21T03-10-55Z` * 表中的第二个数字为该最佳 case 的平均时延。 * 功耗列统一记录测试中的最大功率。 * `FLUX.2-dev` 与 `FLUX.2-klein` 在最新测试中未形成稳定可用结果,因此不写入正式 benchmark 表。 --- --- url: /aipod/asr.md --- # SenseVoice ASR(语音转写) `SenseVoice ASR` 是基于 `SenseVoice.cpp` 封装的语音转文本服务,算力舱中的服务域名为 `asr-ai`。它提供一个 OpenAI Audio API 风格的转写入口,适合后端服务、前端应用或脚本通过 HTTP 上传音频文件并获得转写文本。 ## 服务入口 * API 文档: * 服务地址: * 健康检查: * 转写接口: 部署到算力舱后的访问形式: * 服务地址:`https://asr-ai.${LAZYCAT_BOX_DOMAIN}` * API 文档:`https://asr-ai.${LAZYCAT_BOX_DOMAIN}/docs` * 健康检查:`https://asr-ai.${LAZYCAT_BOX_DOMAIN}/health` * 转写接口:`https://asr-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/transcriptions` ## 健康检查 ### `GET /health` 该接口用于确认 ASR HTTP 服务可用。 请求: ```bash curl https://asr-ai.${LAZYCAT_BOX_DOMAIN}/health ``` 响应示例: ```json { "status": "ok" } ``` ## 转写接口 ### `POST /v1/audio/transcriptions` 该接口接收一个音频文件并返回转写结果。请求必须使用 `multipart/form-data`。 服务会先用 `ffmpeg` 将上传音频转换为 `16 kHz`、单声道 `wav`,再执行 SenseVoice 推理。因此实际可用格式取决于镜像内 `ffmpeg` 可解码的音频格式,常见的 `wav`、`mp3`、`m4a`、`mp4`、`webm`、`flac`、`ogg` 通常都可以直接上传。 ### 请求参数 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `file` | file | 是 | 待转写音频文件 | | `language` | string | 否 | 语音语言,默认 `auto`。支持 `auto` / `zh` / `en` / `ja` / `ko` / `yue`,非法值会按 `auto` 处理 | | `response_format` | string | 否 | 默认 `json`。传 `text` 返回纯文本;传其他值时返回 JSON | | `token_timestamps` | string | 否 | 传 `true` 时在分段结果中包含 `words` 时间戳信息 | | `split_on_word` | string | 否 | 传 `true` 时同样包含 `words` 时间戳信息 | 当前服务不提供 `srt` / `vtt` 字幕输出,也不提供 `progress` / `progress_format` 进度事件流。需要字幕时建议拿 JSON 中的 `segments` 自行生成。 ### 输出格式 | `response_format` | Content-Type | 说明 | |---|---|---| | `json` | `application/json` | 默认格式,包含 `text` 和 `segments` | | `verbose_json` | `application/json` | 兼容常见调用习惯,实际返回同一类 JSON 结构 | | `text` | `text/plain; charset=utf-8` | 只返回转写文本 | JSON 响应示例: ```json { "text": "欢迎使用算力舱语音转写服务。", "segments": [ { "id": 0, "start": 0.0, "end": 2.4, "text": "欢迎使用算力舱语音转写服务。" } ] } ``` 开启 `token_timestamps=true` 或 `split_on_word=true` 后,`segments` 中会额外包含 `words`: ```json { "id": 0, "start": 0.0, "end": 2.4, "text": "欢迎使用算力舱语音转写服务。", "words": [ { "word": "欢迎", "text": "欢迎", "start": 0.12, "end": 0.42, "probability": 0.98 } ] } ``` 错误响应示例: ```json { "error": "multipart field 'file' is required" } ``` 常见错误: | HTTP 状态码 | 场景 | |---|---| | `400` | 未使用 `multipart/form-data`、缺少 `file` 字段,或 `ffmpeg` 无法转换上传音频 | | `500` | 临时文件写入失败 | ## 调用示例 ### JSON 转写 ```bash curl -fsS -X POST "https://asr-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/transcriptions" \ -F "file=@/path/to/audio.wav" \ -F "language=auto" \ -F "response_format=json" ``` ### 纯文本转写 ```bash curl -fsS -X POST "https://asr-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/transcriptions" \ -F "file=@/path/to/audio.wav" \ -F "response_format=text" ``` ### 带时间戳转写 ```bash curl -fsS -X POST "https://asr-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/transcriptions" \ -F "file=@/path/to/audio.wav" \ -F "language=zh" \ -F "token_timestamps=true" ``` ## 推荐调用流程 1. 准备音频文件,视频文件建议先抽取音频再上传。 2. 以 `multipart/form-data` 上传到 `/v1/audio/transcriptions`。 3. `language` 不确定时传 `auto`;已知语言时传 `zh` / `en` / `ja` / `ko` / `yue`。 4. 需要程序处理分段时使用默认 JSON;只需要展示文本时使用 `response_format=text`。 5. 需要词级时间戳时传 `token_timestamps=true` 或 `split_on_word=true`。 ## 后端环境变量 在依赖 ASR 的业务服务中,可以将 ASR 基地址配置为: ```yaml services: convert: environment: - ASR_URL=https://asr-ai.${LAZYCAT_BOX_DOMAIN} - HOMEDIR=/lzcapp/run/mnt/home ``` * `ASR_URL`:ASR 服务基地址,业务代码再拼接 `/v1/audio/transcriptions`。 * `HOMEDIR`:业务文件读写根路径,按应用需要配置。 ## 前端匹配 `asr-ai` 域名 ```ts const asrUrl = (path = "/v1/audio/transcriptions") => { const currentHost = window.location.host; const protocol = window.location.protocol; const host = currentHost.replace(/^([^.]+)/, "asr-ai"); return `${protocol}//${host}${path}`; }; ``` ## 单独部署使用 根据设备选择对应镜像。AGX Orin 使用 `-agxorin` 镜像,Thor T5000 使用 `-thor` 镜像。 ### AGX Orin ```yml services: asr: image: registry.lazycat.cloud/catdogai/lzc-aipod-sensevoiceasr:0.2.0-sensevoice-small-q4k-agxorin ports: - 3000:3000 environment: - PORT=3000 - SENSEVOICE_MODEL=/opt/sensevoice-models/sense-voice-small-q4_k.gguf - SENSEVOICE_LANGUAGE=auto - SENSEVOICE_THREADS=4 - SENSEVOICE_USE_ITN=true - OMP_NUM_THREADS=1 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 10s timeout: 30s retries: 100 start_period: 30s ``` ### Thor T5000 ```yml services: asr: image: registry.lazycat.cloud/catdogai/lzc-aipod-sensevoiceasr:0.2.0-sensevoice-small-q4k-thor ports: - 3000:3000 environment: - PORT=3000 - SENSEVOICE_MODEL=/opt/sensevoice-models/sense-voice-small-q4_k.gguf - SENSEVOICE_LANGUAGE=auto - SENSEVOICE_THREADS=4 - SENSEVOICE_USE_ITN=true - OMP_NUM_THREADS=1 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 10s timeout: 30s retries: 100 start_period: 30s ``` 启动: ```bash docker compose up -d curl http://127.0.0.1:3000/health ``` 启动后可访问 `http://127.0.0.1:3000/docs` 查看服务内置 API 文档。 ## 服务环境变量 | 变量 | 默认值 | 说明 | |---|---|---| | `PORT` | `3000` | HTTP 监听端口 | | `SENSEVOICE_MODEL` | `/opt/sensevoice-models/sense-voice-small-q4_k.gguf` | SenseVoice GGUF 模型路径 | | `SENSEVOICE_LANGUAGE` | `auto` | 默认识别语言 | | `SENSEVOICE_THREADS` | `4` | 推理线程数 | | `SENSEVOICE_USE_GPU` | `true` | 是否启用 GPU | | `SENSEVOICE_USE_ITN` | `true` | 是否启用逆文本正则化,包含标点处理 | | `SENSEVOICE_USE_PREFIX` | `false` | 是否输出 SenseVoice 前缀信息 | | `OMP_NUM_THREADS` | `1` | OpenMP 线程数 | ## 相关服务 * SenseVoice ASR 默认启用 `SENSEVOICE_USE_ITN=true`,会进行文本规范化和标点处理。 --- --- url: /aipod/tts/tts_api.md --- # TTS (文本生成语音) 在算力舱中部署了一个 `Kokoro` 的 `tts` 服务,这个服务的速度比较快。 如果您需要 OpenAI 兼容的多音色 TTS 服务,可以参考 [Qwen3TTS API](/tts/qwen3tts_api.md)。 `qwen3tts-ai` 的服务入口可以通过 访问。 ## 在线演示 您可以通过 访问在线演示。 ## 文档 `tts` 服务的 api 可以通过 查看。 `Qwen3TTS` 服务接口说明见 [Qwen3TTS API](/tts/qwen3tts_api.md)。 如果你要接 OpenAI 风格的 `POST /v1/audio/speech`,或者需要查询 `/v1/audio/voices`、使用 `instruct` / `language` 扩展参数、按 `/health` 中的 `ready=true` 做启动校验,应使用 `qwen3tts-ai`,而不是当前这一页介绍的 `tts-ai`。 `qwen3tts-ai` 当前文档里也明确说明了: * OpenAI 接口已支持请求级 `language` * OpenAI 接口已支持请求级 `instruct` * OpenAI 接口已支持请求级 `seed` * 同一镜像版本、同一模型和同一请求参数下,固定 `seed` 可以复现相同音频输出 * 当前文档只保留实际对外建议使用的请求字段;像 `speed` 这类兼容但未生效的字段不再作为正式参数介绍 ## 单独部署使用 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: tts: image: registry.lazycat.cloud/x/lzc-aipod-tts:763ebf7 ports: - 8880:8880 environment: - PYTHONPATH=/app:/app/api - USE_GPU=true - PYTHONUNBUFFERED=1 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8880/health"] interval: 30s timeout: 5s retries: 100 start_period: 1s ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 启动后访问 `http://127.0.0.1:8880/docs` ::: details 示例 ```vue ``` ::: --- --- url: /aipod/issues/video-to-webm.md --- 1. 在搜索微服中,视频的格式需要为 webm (VP9 编码),验证的方法可以将视频拖到AI浏览器中,检测是否能正常播放。 2. 您可以在网络上下载webm的视频,也可以按照下面提供的教程将视频转变成webm格式。 3. 方法1使用在线的网站转换, https://cloudconvert.com/mp4-to-webm/, https://video.online-convert.com/convert-to-webm, https://convertio.co/mp4-webm/ 4. 方法2使用本地的工具转换, windows 上看 https://videoconvert.minitool.com/video-converter/mp4-to-webm.html 里面介绍几个工具。MacOS https://www.winxdvd.com/video-transcoder/convert-webm-to-mp4-vlc.htm 5. 方法3使用懒猫微服中的 vp9convert,在懒猫微服的应用商店中搜索 `vp9convert` 这个应用,然后安装。安装后可以在网盘中上传您的素材,上传成功后,在 vp9convert 中将可以看到对应的文件。点击 `转换` 即可等待视频转码完成。(转码成功后,记得把不是 webm 的视频从网盘中删除) --- --- url: /aipod/olddocs/llama.cpp.md --- 可以使用 jetson 官方封装好的 text-generation-webui 来测试 llm ```sh jetson-containers run dustynv/text-generation-webui:r36.2.0 ``` `r36.2.0` 是最新提供的版本,但里面的 `llama.cpp python` 这个是比较旧的,会在加载 `llama3.1` 模型和 `70b` 的模型的时候出现 `Attributte: model` 的错误。 需要在 `r36.2.0` 容器中重新构建 `llama.cpp python whl` ```sh pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple sed -i 's@//.*ports.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list apt update && apt install vim pip install poetry cd /opt/llama-cpp-python make update.vendor make build.cuda ``` 构建出来后,可以使用以下命令将 whl 包构建出来,方便其他地方直接安装。 ``` CMAKE_ARGS="-DGGML_CUDA=on" python3 -m pip wheel --verbose -e . ``` --- --- url: /aipod/olddocs/video-understanding.md --- 现在大部分的视频搜索是基于从视频中抽帧实现,比较简单的实现是直接把提取出来图片和 用户提问放在一个 vlm 模型中;稍微复杂一点则除了图片还微调把图片的时间戳信息带进 去,这样就可以在询问中要求模型返回相对应的视频时间点。 但这个时间不能处理太长的视频,因为机器的内存的限制,太长的视频,上下文太多内存会 爆掉(现在的大部分视觉模型都不能处理太长的视频也就是这个原因)。 [Vlog: Video as a Long Document](https://github.com/showlab/VLog?tab=readme-ov-file) 这个项目中,是把视 频的片段先识别成文字(视频的语音通过语音识别成文字,视频的帧图片通过图片描述模型 识别成文字)。最后整个长的视频都变成一个长的文档,这样在用户询问的时候就是类似 RAG 的技术,从数据库中获取数据,然后总结。 另外一个做法是,对于长视频,特别是监控这种视频,大部分场景内视图中是没有物体动的, 导致关键帧很少,可以大大节省模型的上下文。 [LVNet](https://github.com/showlab/VLog?tab=readme-ov-file)就是从解决信息的冗余 度来出发点解决上下文会爆的问题。 但可以根据特定的场景去优化,像下面的场景 ## 场景1: 家庭监控,下班回家后,问AI,今天家里发生了什么? 家庭监控的场景下,视频长度不可控,大部分情况下很长,如果通过把视频的内容都丢到视觉模型中(如 qwen2-vl, llava, 带vision的语言模型)去理解,会导致内存爆掉。 这个场景我觉得可以 1. 摄像头在实时监控的时候,就使用类似 yolo 物体探测 或者 类似监控人体活动 的比较成熟的小模型来记录有人在动(如回家,坐下,站起来等时间) 2. 在做活动监控的时候,也通过语言识别的模型,可以查看当前时间点的声音 3. 把识别到的数据统一记录到数据库 4. 在用户询问的时候就是从数据库中查找数据,然后在总结(这就又类似于 RAG 的实现了) 在这个情况下,像家庭监控这个需求,就可以不需要多高算力 [ChatVideo](https://www.wangjunke.info/ChatVideo/) 是一个类似的做法. ![ChatVideo](/assets/ChatVideo.Bw9QiBTk.png) ## 场景2: 视频搜索 视频搜索,类似图片搜索,它不需要将所有的帧都给到一个大语言模型中让大语言模型理解, 只需要从图片中获取特征,然后根据特征进行匹配,这种就可以通过抽帧的方式实现,然后 将每一帧都向量化后存在数据库中。 这个情况下,就可以使用 CLIP 或者 其他图片编码模型都可以 ## 场景3: 视频理解 类似鸿蒙小艺这种,就用户当前的屏幕(单一图片),或者某一段比较短的视频进行理解。这些就可以使用带视觉的大模型实现(如 qwen2-vl, 内部也是通过对视频抽帧,然后把图片给大模型来理解,大模型理解后,就可以根据图片的内容来问答) ## Demo: Multimodal-RAG-on-Jetson 1. 通过 ffmpeg 将声音从视频中提取出来 2. 将声音进行切片,然后使用 `whisper turbo` 识别出来文字 3. 将视频抽帧,然后使用 `ollama llava-llama3` 总结图片中的内容,输出文字保存 4. 然后通过 `bge-m3` 做向量化 5. 最后使用 `llama-index` 结合 `llava-llama3` 做 `RAG` 搜索 jetson 上的 ffmpeg 需要单独编译,才能使用 jetson 的硬件加速 [jetson-ffmpeg](https://github.com/Keylost/jetson-ffmpeg) ## Qwen2-VL 模型测试 一个 12s 的视频,根据 fps=1 的处理,在处理后,具有 12 帧,放到 Qwen2-VL-7B-Instruct 中处理,然后对这个这个视频进行总结 模型只能根据视频中的帧,然后总结(或者理解)帧中出现的内容,不能输出在那一秒,出现了什么物体或者别的特征 | 模型 | 帧数 | 编码时间 | 推理时间 | |:---------------------|:-----|----------|:---------| | Qwen2-VL-7B-Instruct | 12 | 1.35s | 13s | | Qwen2-VL-2B-Instruct | 12 | 1.15s | 6.92s | Qwen2-VL-7B-Instruct 测试输出: 这个视频展示了一个繁忙的城市街道场景。在画面中, 可以看到许多车辆在道路上行驶,包括汽车、摩托车和自行车。街道两旁有树木和建筑物, 显示出这是一个城市环境。视频中还出现了两个共享单车,它们的颜色是黄色和蓝色,停放 在路边。 Qwen2-VL-2B-Instruct 测试输出: 在视频中,有一秒中出现了两个共享单车。 --- --- url: /aipod/vllm.md --- # VLLM 在算力舱中默认使用 `ollama` 来提供大模型服务,这个是为了可以很方便的切换各种各样的模型。如果您的需求固定,没有频繁切换和释放模型的情况下,您可以使用 `vllm` 来部署您的服务。 推荐根据设备选择 vLLM 镜像。 Orin 可以使用算力舱封装的 vLLM 镜像(`vllm 0.16.0 + cu126`): `docker run --rm -it registry.lazycat.cloud/x/lzc-aipod-vllm:d59c2ca bash` Jetson Thor 可以优先验证 NVIDIA 官方 vLLM 镜像: `docker run --rm --runtime=nvidia --ipc=host --network=host nvcr.io/nvidia/vllm:26.04-py3 python3 -c "import vllm, torch; print(vllm.__version__, torch.__version__, torch.cuda.get_device_name(), torch.cuda.get_device_capability())"` 该镜像已经在 Thor 上验证可以识别 `NVIDIA Thor`,并可启动 OpenAI-compatible `/v1/models` 与 `/v1/chat/completions`。如果模型配置中带有多模态字段,但只需要文本推理,启动时建议加 `--language-model-only`。 Thor 也可以使用 Jetson AI-IOT 镜像: `docker pull ghcr.io/nvidia-ai-iot/vllm:latest-jetson-thor` 这个镜像已经跑通 `Qwen3.5-27B-Text-NVFP4-MTP`。但 `Qwen3.5/3.6 27B FP8` 当前在 Thor 上的 GDN linear-attention 路径仍不建议作为默认方案。 如果您希望使用 Jetson 官方镜像,可以参考 [dustynv/vllm](https://hub.docker.com/r/dustynv/vllm/tags),例如 `docker pull dustynv/vllm:r36.4-cu129-24.04`。 ## 额外的资源 1. https://hub.docker.com/r/mitakad/vllm 2. [资源列表](../aipod/resources.md) 3. https://docs.vllm.ai/en/latest/getting\_started/installation/gpu.html 4. https://docs.nvidia.com/deeplearning/frameworks/vllm-release-notes/ ## 例子 下面以算力舱中自带的翻译模型为例(Qwen/Qwen3-4B-Instruct-2507 + vllm) 1. 算力舱直接运行下面的命令 `docker run --rm -p 9999:3000 -ti registry.lazycat.cloud/x/lzc-aipod-trans:3480c9d` 2. 局域网中使用 ``` curl http://192.168.1.167:9999/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model": "aipod-trans", "messages": [{"role": "user", "content": "hello!"}]}' ``` 注意: 记得将上面的ip地址(`192.168.1.167`)更改成您本地的ip地址 --- --- url: /aipod/vllm/Distributed_Distributed.md --- # vLLM Distributed Inference on Jetson Orin AGX [官方文档](https://github.com/dusty-nv/jetson-containers/blob/master/docs/distributed-inference.md) ## 测试镜像 1. mitakad/vllm:0.11.2-r36.4.tegra-aarch64-cp310-cu126-22.04-truncated 2. mitakad/vllm:0.12.0-r36.4.tegra-aarch64-cp310-cu126-22.04-truncated 3. mitakad/vllm:0.13.0-r36.4.tegra-aarch64-cp312-cu129-24.04-pre-release ## 步骤 注意点: 模型需要自己下载,下载到当前的目录下。在主节点上执行 ``` pip install modelscope ~/.local/bin/modelscope download --model openai-mirror/gpt-oss-120b --local_dir ./openai-mirror/gpt-oss-120b ``` 将主节点上的 10G 网口和另外一台的算力舱 10G 通过网线连接起来。 1. 主节点 ``` sudo ip addr add 192.168.38.1/24 dev eno1 VLLM_HOST_IP=192.168.38.1 && \ VLLM_IFNAME=eno1 && \ docker run -d --rm --runtime nvidia -it \ --network host \ --shm-size=8g \ --privileged \ --ipc=host \ -v /dev/shm:/dev/shm \ -e NVIDIA_DRIVER_CAPABILITIES=all \ -e VLLM_HOST_IP=${VLLM_HOST_IP} \ -e UCX_NET_DEVICES=${VLLM_IFNAME} \ -e NCCL_SOCKET_IFNAME=${VLLM_IFNAME} \ -e GLOO_SOCKET_IFNAME=${VLLM_IFNAME} \ -e TP_SOCKET_IFNAME=${VLLM_IFNAME} \ -v `pwd`/:/models \ -v `pwd`/cache:/root/.cache/huggingface \ --name vllm_xray \ mitakad/vllm:0.11.2-r36.4.tegra-aarch64-cp310-cu126-22.04-truncated docker exec -ti vllm_xray bash ray start -v --head --port 6379 --node-ip-address 192.168.38.1 --num-gpus 1 sysctl vm.drop_caches=3 ``` 2. 其他节点 ``` sudo ip addr add 192.168.38.2/24 dev eno1 VLLM_HOST_IP=192.168.38.2 && \ VLLM_IFNAME=eno1 && \ docker run -d --rm --runtime nvidia -it \ --network host \ --shm-size=8g \ --privileged \ --ipc=host \ -v /dev/shm:/dev/shm \ -e NVIDIA_DRIVER_CAPABILITIES=all \ -e VLLM_HOST_IP=${VLLM_HOST_IP} \ -e UCX_NET_DEVICES=${VLLM_IFNAME} \ -e NCCL_SOCKET_IFNAME=${VLLM_IFNAME} \ -e GLOO_SOCKET_IFNAME=${VLLM_IFNAME} \ -e TP_SOCKET_IFNAME=${VLLM_IFNAME} \ -v `pwd`/:/models \ -v `pwd`/cache:/root/.cache/huggingface \ --name vllm_xray \ mitakad/vllm:0.11.2-r36.4.tegra-aarch64-cp310-cu126-22.04-truncated docker exec -ti vllm_xray bash ray start -v --address 192.168.38.1:6379 --node-ip-address 192.168.38.2 --num-gpus 1 sysctl vm.drop_caches=3 ``` 3. 拷贝模型到另外的worker节点上,同样的目录,我放置的目录是 `~` ``` rsync -aP ~/openai-mirror nvidia@192.168.38.2:~ ``` 4. 在主节点的机器上执行下面的命令 ``` docker exec -ti vllm_xray bash # http://192.168.1.128:1085 是我本地的代理,您可以去掉或者更改成您的 curl -x http://192.168.1.128:1085 -L https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken -o fb374d419588a4632f3f557e76b4b70aebbca790 # 测试是否正常 TIKTOKEN_RS_CACHE_DIR=$(pwd) python -c 'from openai_harmony import load_harmony_encoding; load_harmony_encoding("HarmonyGptOss")' # 启动模型 sysctl vm.drop_caches=3 TIKTOKEN_RS_CACHE_DIR=$(pwd) TRANSFORMERS_OFFLINE=1 vllm serve /models/openai-mirror/gpt-oss-120b --port 8000 \ --served-model-name gpt-oss-120b \ --max-model-len 2048 \ --max-num-batched-tokens 2048 \ --kv-cache-memory-bytes 3000M \ --max-num-seqs 10 \ --tensor-parallel-size 2 \ --enable-expert-parallel \ --gpu-memory-utilization 0.8 \ --distributed-executor-backend ray ``` 5. 访问 通过终端上的 curl 进行访问。`192.168.1.175` 是我局域网中的地址,您需要更改成您机器的ip。 ``` curl 'http://192.168.1.175:8000/v1/chat/completions' \ --header 'Content-Type: application/json' \ --data '{ "model": "gpt-oss-120b", "messages": [ { "role": "user", "content": "who are you?" } ], "stream": false }' ``` ## 测试数据 | 模型 | Tokens/s | |:---------------------------|:---------| | Qwen3-4B-Instruct-2507-FP8 | 10.5 | | openai/gpt-oss-120b | 10.1 | ## 问题 1. 如果您遇到加载本地的模型,vllm 报 repo\_id 不对之类的错误,最简单的方法就是手动把那个 huggingface\_hub 的校验脚本中的校验函数更改。 --- --- url: /aipod/vllm/xray.md --- # vllm-xray 1. 主节点 ``` sudo ip addr add 192.168.38.2/24 dev eno1 docker run --gpus all --network=host --shm-size 10.24g --name vllm-xray -d -ti -v `pwd`:/models -v `pwd`/cache:/root/.cache/huggingface -e VLLM_HOST_IP=192.168.38.2 dustynv/vllm:r36.4-cu129-24.04 bash docker exec -ti vllm-xray bash ray start --block --head --port=6379 --node-ip-address=192.168.38.2 ``` 2. 其他节点 ``` sudo ip addr add 192.168.38.1/24 dev eno1 docker run --gpus all --network=host --shm-size 10.24g --name vllm-xray -d -ti -v `pwd`:/models -v `pwd`/cache:/root/.cache/huggingface -e VLLM_HOST_IP=192.168.38.1 dustynv/vllm:r36.4-cu129-24.04 bash docker exec -ti vllm-xray bash ray start --block --address=192.168.38.2:6379 ``` 3. 主节点启动模型 ``` pip install pandas -i https://mirrors.ustc.edu.cn/pypi/simple # 下载最新版本的 cupy 才支持 aarch64 all_proxy=http://192.168.1.128:1085 curl -L https://github.com/cupy/cupy/releases/download/v13.5.0/cupy_cuda12x-13.5.0-cp312-cp312-manylinux2014_aarch64.whl -O VLLM_HOST_IP=192.168.38.2 vllm serve /models/models/Qwen/Qwen3-30B-A3B-GPTQ-Int4 --enable-auto-tool-choice --tool-call-parser hermes --reasoning_parser deepseek_r1 --tensor-parallel-size 1 --pipeline-parallel-size 2 ``` 4. 测试 ``` time curl http://192.168.38.2:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model": "/models/models/Qwen/Qwen3-30B-A3B-GPTQ-Int4", "stream": true, "messages": [{"role": "user", "content": "你觉得中国和美国的差异性在什么地方?"}]}' ``` --- --- url: /aipod/olddocs/onnx/whisper.md --- # whisper ``` providers = [ ('TensorrtExecutionProvider', { 'device_id': 0, # Select GPU to execute 'trt_max_workspace_size': 2147483648, # Set GPU memory usage limit 'trt_fp16_enable': True, # Enable FP16 precision for faster inference 'trt_dla_enable': True, 'trt_int8_enable': True, }), ('CUDAExecutionProvider', { 'device_id': 0, 'arena_extend_strategy': 'kNextPowerOfTwo', 'gpu_mem_limit': 2 * 1024 * 1024 * 1024, 'cudnn_conv_algo_search': 'EXHAUSTIVE', 'do_copy_in_default_stream': True, }) ] ``` ```python import torch import torchaudio import numpy as np import onnxruntime as ort from scipy.io import wavfile from transformers import WhisperProcessor, WhisperTokenizer import os # 加载 ONNX 模型 sess_encoder = ort.InferenceSession("/ssd/lnks/onnx/turbo-encoder.onnx", providers=['TensorrtExecutionProvider', 'CUDAExecutionProvider']) sess_decoder = ort.InferenceSession("/ssd/lnks/onnx/turbo-decoder.onnx", providers=['TensorrtExecutionProvider', 'CUDAExecutionProvider']) os.environ['all_proxy'] = 'http://127.0.0.1:1080' processor = WhisperProcessor.from_pretrained("openai/whisper-large-v3-turbo") tokenizer = WhisperTokenizer.from_pretrained("openai/whisper-large-v3-turbo") audio_path = "/ssd/lnks/assets/speech.wav" # 替换为您的音频文件路径 sample_rate, waveform = wavfile.read(audio_path) waveform = waveform.astype(np.float32) / 32768.0 # 归一化到 [-1, 1] # 确保音频是单声道的 if len(waveform.shape) > 1: waveform = np.mean(waveform, axis=1) # 重采样到16kHz if sample_rate != 16000: waveform = torchaudio.functional.resample(torch.from_numpy(waveform), sample_rate, 16000).numpy() # 使用处理器准备输入 input_features = processor(waveform, sampling_rate=16000, return_tensors="np").input_features out_encoder = sess_encoder.run(None, {"mel": input_features}) tokens = list(tokenizer.sot_sequence_including_notimestamps) session = ort.InferenceSession("/ssd/lnks/onnx/model.onnx", ) # 运行推理 output = session.run(None, {"x": input_features})[0] # 解码输出 predicted_ids = np.argmax(output, axis=-1) transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)[0] # 使用示例 result = transcribe_audio(audio_path) ``` --- --- url: /aipod/olddocs/asr.md --- # Whisper turbo ## onnx 1. 测试声音时长 14分05秒 [mp3](https://anchor.fm/s/f7cac464/podcast/play/92312327/https%3A%2F%2Fd3ctxlq1ktw2nl.cloudfront.net%2Fstaging%2F2024-8-28%2F387196719-44100-2-f0f9be974d84c.mp3) | 后端 | 模型 | 声音长度 | 推理时间 | 比率 | |--------------------------|:-----------|--------------------|:----------|-----------------------------------| | whisper (指官方原生实现) | large-v3 | 14分05秒 英文 | 349.49秒 | 2.42(大概1秒可以推理2.42秒的声音) | | onnx + CUDA | turbo | 同上 | 141.05秒 | 5.99 | | onnx + Tensorrt | turbo | 同上 | 测试中... | | | whisper | turbo | 同上 | 89.90秒 | 9.38 | | whisper\_trt | tiny.en | 同上 | 26.38 | 32.03 | | whisper | tiny.en | 同上 | 70.12 | 12.05 | | funasr框架 | SenseVoice | 同上 | 24.11 | 35.04 | | funasr框架 | SenseVoice | 20分 普通话和粤语 | 34.31s | 34.97 | 2. 测试声音时长 30秒 | 后端 | 模型 | 声音长度 | 推理时间 | |-----------------|:-----------|----------|:----------| | whisper | turbo | 30s | 3.51秒 | | onnx + CUDA | turbo | 30s | 5.54秒 | | onnx + Tensorrt | turbo | 30s | 测试中... | | whisper\_trt | tiny.en | 30秒 | 1.80秒 | | whisper | tiny.en | 30秒 | 3.00秒 | | funasr | SenseVoice | 30s | 1.08秒 | | | | | | --- --- url: /aipod/benchmark/whispercpp.md --- # Whisper.cpp ## Benchmark 以下对比采用 `lzc_thor` 在 `2026-03-21` 的最新正式矩阵,主表只保留同时具备 `Orin / Thor T4000 / Thor T5000` 三机可比结果的模型。 | Model | Scene | Orin | Thor T4000 | Thor T5000 | Notes | |:------|:------|:-----|:-----------|:-----------|:------| | `base.en` | `jfk`, concurrency=`16` | `3.39 req/s` / `P95 4.55 s` / `56.82 W` | `7.39 req/s` / `P95 2.10 s` / `11.65 W` | `9.44 req/s` / `P95 1.64 s` / `17.68 W` | 三机都能稳定完成压测,适合作为英语轻量模型基线 | | `small.en` | `jfk`, concurrency=`16` | `2.46 req/s` / `P95 6.28 s` / `26.88 W` | `4.07 req/s` / `P95 3.80 s` / `14.25 W` | `5.36 req/s` / `P95 2.89 s` / `21.76 W` | 速度和识别质量相比 `base.en` 更均衡 | | `medium.en` | `jfk`, concurrency=`16` | `1.34 req/s` / `P95 11.52 s` / `35.50 W` | `1.77 req/s` / `P95 8.71 s` / `16.85 W` | `2.53 req/s` / `P95 6.11 s` / `26.94 W` | 中型模型下 Thor T5000 优势更明显 | ## More | Model | Scene | Thor T4000 | Thor T5000 | Notes | |:------|:------|:-----------|:-----------|:------| | `large-v3-turbo` | `jfk`, concurrency=`16` | `1.59 req/s` / `P95 9.70 s` / `20.93 W` | `2.52 req/s` / `P95 6.13 s` / `36.15 W` | `2026-03-21` 最新矩阵中 Orin 结果仍未完整落盘,先只保留双 Thor 对比 | ## Notes * 数据来源:`/home/catdog/lzc_thor/docs/runs/2026-03-21T08-45-00Z` * 主表使用的是正式压力矩阵结果,不引用中断或未汇总 case。 * 功耗列统一记录测试中的最大功率,不写平均功率。 --- --- url: /aipod/os/introduction.md --- # 介绍 算力舱中系统是 `Ubuntu 22.04.5 LTS` 版本,您可以将算力舱接入键盘和鼠标,以及显示屏就可以作为一个独立的主机使用。和微服中联动是通过 `lzc-ai-agent` 这个服务,负责去下发和启动相关的AI应用服务。在算力舱中服务运行的环境为 `docker` 。 ```/etc/os-release PRETTY_NAME="Ubuntu 22.04.5 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.5 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammy ``` ::: warning 算力舱中的 `Linux` 系统不能换成其他的发行版,这个 `Ubuntu` 是 `nvidia jetson` 官方适配的,如果您更改 `Linux` 到其他发行版,不确定会遇到那些问题。 如果您先恢复到出厂前的环境,您可以通过刷系统的方式来将系统还原。 ::: ## SSH 算力舱默认的帐号密码为 `nvidia:nvidia`. 1. 您可以在算力舱的管理列表中查看您当前的ip地址 ![ip address](/assets/aipod-ip-address.GKlPcpdK.png) 2. 您可以通过 `ssh nvidia@ip` 进到 算力舱的系统中去。 3. 如果您和算力舱不在一个局域网中,您也可以通过微服作为跳板机器,比如 `ssh -J root@微服名称.heiyu.space nvidia@ip` 进行连接。 --- --- url: /aipod/os/use-microserver-network.md --- # 使用微服网络连接外网 在算力舱中需要连接外网来下载镜像,如果您的环境网络有限制,在同一个局域网中,您可以使用以下的方式来将算力舱中的流量都转发到微服中去,使用微服作为出口。 1. 微服中执行 ssh到微服后执行(192.168.31.0替换为实际的局域网网段) ```sh iptables -t nat -I POSTROUTING --source 192.168.31.0/24 -j MASQUERADE iptables -P FORWARD ACCEPT ``` 2. 算力仓执行(192.168.31.60替换为微服的IP) ```sh sudo ip route add default via 192.168.31.60 ``` 3. 测试下 ```sh curl -v https://registry-static.lazycat.cloud ``` --- --- url: /aipod/ollama/standalone.md --- # 单独使用 您可以使用 docker-compose 在算力舱中单独部署服务。请根据算力舱机型选择对应的配置。 ## AGX Orin ```yml services: ollama: image: registry.lazycat.cloud/catdogai/jetson-ollama:0.17.5 volumes: - /etc/timezone:/etc/timezone:ro - ./data:/root/.ollama environment: - LANGUAGE=en_US:en - LANG=en_US.UTF-8 - LC_ALL=en_US.UTF-8 - OLLAMA_DEBUG=1 - OLLAMA_ORIGINS=* - OLLAMA_HOST=0.0.0.0 - OLLAMA_FLASH_ATTENTION=1 - OLLAMA_NUM_PARALLEL=4 - OLLAMA_CONTEXT_LENGTH=8192 - OLLAMA_KV_CACHE_TYPE=q8_0 - OLLAMA_LLM_LIBRARY=cuda_jetpack6 - LD_LIBRARY_PATH=/usr/local/lib/ollama:/usr/local/lib/ollama/cuda_jetpack6:/usr/local/cuda/compat:/usr/local/cuda/lib64 ports: - 11434:11434 mem_limit: 60G memswap_limit: 60G ``` ## Thor ```yml services: ollama: image: registry.lazycat.cloud/catdogai/jetson-ollama:0.17.5 volumes: - /etc/timezone:/etc/timezone:ro - ./data:/root/.ollama environment: - LANGUAGE=en_US:en - LANG=en_US.UTF-8 - LC_ALL=en_US.UTF-8 - OLLAMA_DEBUG=1 - OLLAMA_ORIGINS=* - OLLAMA_HOST=0.0.0.0 - OLLAMA_FLASH_ATTENTION=1 - OLLAMA_NUM_PARALLEL=4 - OLLAMA_KV_CACHE_TYPE=q8_0 - LD_LIBRARY_PATH=/usr/local/lib/ollama:/usr/local/cuda/lib64:/usr/local/cuda-13.0/lib64:/usr/lib/aarch64-linux-gnu/tegra:/usr/lib/aarch64-linux-gnu ports: - 11434:11434 mem_limit: 60G memswap_limit: 60G ``` 将对应机型的配置保存为 `docker-compose.yml`,然后在同目录下使用 `docker-compose up -d` 启动。 在启动成功后,可以通过局域网中的 `11434` 端口进行访问. `ollama` 最新版本会根据内存大小自动决定上下文长度。AGX Orin 算力舱的内存为 64G,默认分配的上下文可能较大,示例通过 `OLLAMA_CONTEXT_LENGTH=8192` 将上下文长度限制为 `8k`。 :::info 如果您的算力舱上不能访问到 `registry.lazycat.cloud`,您可以切换到 `dockerhub` 上,可以通过 `docker pull catdogai/jetson-ollama:0.17.5` ollama 经常更新,可能文档上没有及时更新,新的 ollama 版本 tag 可以在 `https://hub.docker.com/r/catdogai/jetson-ollama/tags` 页面中查看. ::: --- --- url: /aipod/package/publish.md --- # 发布AI应用 1. [算力舱分类](https://appstore.lazycat.cloud/#/shop/category/27) 2. [懒猫微服发布应用](https://developer.lazycat.cloud/publish-app.html) 发布AI应用和发布懒猫微服应用一样,同样是通过 `lzc-cli` 或者在懒猫微服的开发者平台上提交。发布应用的时候,记得写上 `AI Pod` 或者 `算力舱` 或者相关的说明,方便审核的时候将应用添加到 `算力舱` 分类中去. --- --- url: /aipod/ai-browser/image-trans.md --- # 图片翻译 ![image result](/assets/aipod-image-trans-result.DGZcHmE0.png) AI插件助手中,支持对图片进行翻译,您可以在 AIPOD 的配置选项中选择您的常用语言。 ![image config](/assets/aipod-config-image-trans-options.BlspQvSF.png) 当您配置好了后,您可以在浏览网站时,将鼠标放置在图片上方,在图片的右下角会有一个翻译的图标浮现出来,您点击后就可以对当前的图片进行翻译。 ![image trans icon](/assets/image-trans-tips.Ch1LT4Oy.png) 当翻译处理完成后,结果会直接覆盖上原来的图片上,同样您可以点击右下角的还原图标,进行恢复之前的格式。 --- --- url: /aipod/issues/ubuntu-dont-entry.md --- # 如果遇到 ubuntu 系统不能进去,但是通过 hdmi 切换 tty 却能正常使用 ## 第1步:查看当前的系统信息 1. **查看Ubuntu版本**: 在TTY中,输入以下命令: ```bash $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.5 LTS Release: 22.04 Codename: jammy ``` 这会显示您的Ubuntu发行版号和代号(例如 Ubuntu 22.04.5 LTS)。 2. **查看Jetson L4T版本**: L4T是NVIDIA为Jetson定制的底层驱动和系统软件。运行以下命令: ```bash $ cat /etc/nv_tegra_release # R36 (release), REVISION: 4.3, GCID: 38968081, BOARD: generic, EABI: aarch64, DATE: Wed Jan 8 01:49:37 UTC 2025 # KERNEL_VARIANT: oot TARGET_USERSPACE_LIB_DIR=nvidia TARGET_USERSPACE_LIB_DIR_PATH=usr/lib/aarch64-linux-gnu/nvidia ``` 这会显示您的L4T版本信息,这对于确定兼容的软件包至关重要。 3. **查看JetPack版本 (如果已安装)**: JetPack是一个包含L4T和各种NVIDIA SDK(如CUDA, cuDNN)的捆绑包。可以通过以下命令查看: ```bash $ sudo apt-cache show nvidia-jetpack | grep "Version" Version: 6.2.1+b38 Version: 6.2+b77 Version: 6.1+b123 ``` ## 第2步:检查桌面环境依赖包是否完整 图形界面无法启动最常见的原因是桌面环境的元软件包(meta-package)或其依赖项损坏或未安装完整。Ubuntu的默认桌面环境是GNOME,其对应的元软件包通常是 `ubuntu-desktop`。 1. **检查 `ubuntu-desktop` 包的状态**: ```bash dpkg -l | grep ubuntu-desktop ``` 如果该软件包已安装,您会看到一行信息,开头是 `ii`。如果未安装或状态异常,这里可能会有提示。 2. **检查系统是否有损坏的依赖项**: 运行apt的检查工具来发现任何未满足的依赖关系。 ```bash sudo apt-get check ``` 如果这个命令有任何输出,说明存在依赖问题。您可以尝试用以下命令修复它: ```bash sudo apt-get -f install ``` 3. **尝试重新安装桌面环境**: 这是解决依赖包问题的最有效方法。它会重新安装所有缺失的桌面组件。 ```bash sudo apt-get update sudo apt-get install --reinstall ubuntu-desktop sudo systemctl status gdm3 sudo systemctl restart gdm3 ``` 安装过程可能会需要一些时间。完成后,重启您的Jetson设备: ```bash sudo reboot ``` --- --- url: /aipod/knowledge/install.md --- # 安装方式 ## 商店安装 在商店中搜索 "知识库" 即可搜索安装 当您安装完成后,在您的`AI POD`应用或者AI浏览器首页,您可以看到一个 `Knowledge` 的图标。 ![Knowledge Base Detail](/assets/knowledge-shortcuts-2025-11-04_11-42.neqGd81Q.png) 点击打开后,会检测算力舱中相关的服务是否在运行中,您可能会看到下面的启动进度页面,请您耐心等待下(模型需要下载和启动) ![prepare-running](/assets/prepare-running.C1b21RvL.png) 如果您需要在 `Codex`、`Claude Code`、`Cherry Studio` 里直接调用知识库能力,可以继续参考 [知识库 MCP](/knowledge/mcp.md)。 --- --- url: /aipod/os/install_software.md --- # 安装软件 ## lzc-earlyoom 算力舱是 `jetson agx orin` 属于 `iGPU` 的架构,默认版本的 `earlyoom` 不能检测到 `GPU` 的使用内存。我们修改后的 `lzc-earlyoom` 解决了这个问题, 您可以根据以下连接的文档进行安装 https://gitee.com/lazycatcloud/lzc-aipod-resources#lazycat-aipod-early-oom-killer ## lzc-thermal https://gitee.com/lazycatcloud/lzc-aipod-resources#lazycat-aipod-thermal-control-module ## 安装 firefox ``` sudo apt install snap snap download snapd --revision=24724 sudo snap ack snapd_24724.assert sudo snap install snapd_24724.snap sudo sudo snap refresh --hold snapd sudo snap install firefox ``` 相关问题见 https://elinux.org/Jetson/L4T/r36.4.x\_patches#Web\_Browser ## 安装输入法 ``` sudo apt install fcitx5 fcitx5-rime fcitx5-pinyin ``` ## todesk ``` curl 'https://dl.todesk.com/linux/todesk-v4.7.2.0-arm64.deb' \ -H 'DNT: 1' \ -H 'Referer: https://www.todesk.com/' \ -H 'Upgrade-Insecure-Requests: 1' \ -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36' \ -H 'sec-ch-ua: "Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'sec-ch-ua-platform: "Linux"' -O sudo dpkg -i todesk-v4.7.2.0-arm64.deb ``` --- --- url: /aipod/aipod/port-forward.md --- # 微服端口转发 算力舱中的部署的服务,您可以通过 `https://xxx-ai.$微服名称.heiyu.space` 的方式进行访问。但这个方式访问需要依赖懒猫微服客户端,为了解决您局域网中访问,或者其他不能装客户端的机器,或者有一些特定的环境就是不能访问到 `heiyu.space` 的网络。 `AIPOD` 应用中的端口转发只是将 `xxxx-ai` 的 `http` 请求暴露到 *微服* 的网络上,您可以通过 `http://微服名称.heiyu.space:$端口号` 进行访问,也可以直接使用 `http://微服ip:端口` 进行访问。 1. 打开 `AIPOD` 的设置界面,点击端口转发 ![port-forward-page](/assets/port-forward-page.CgaTfgca.jpg) 2. 添加您需要转发的服务,比如 `ollama` ![port-forward-create-ollama](/assets/port-forward-create-ollama.CWtXaXnp.jpg) 3. 测试 打开 `http://微服名称.heiyu.space:端口`,比如我上面的是 `ollama` 和 `8888` 端口. ![port-forward-ollama.jpg](/assets/port-forward-ollama.BddNiLGU.jpg) 或者直接在局域网中使用微服的ip进行访问. ``` > curl http://192.168.1.246:8888 Ollama is running⏎ ``` ::: info 如果您需要将服务暴露到其他微服的设备上,您可以使用 `局域网转发工具` [商店地址](https://appstore.lazycat.cloud/#/shop/detail/cloud.lazycat.app.forward) ![local-port-forward-tools.jpg](/assets/local-port-forward-tools.DJ-rVSfw.jpg) ::: # 算力舱端口转发 请查看 [算力舱端口转发](../os/port-forward.md) --- --- url: /aipod/trans/mtrans.md --- # 快速翻译(神经网络翻译) 算力舱中内置了一个高性能神经网络翻译模型,具有极快的翻译速度,适合对响应时间有严格要求的场景。虽然翻译质量相比大语言模型略有不足,但在实时翻译、批量处理等对速度敏感的应用中表现优异。微服中的应用可以通过算力舱暴露的 `mtrans` API 直接调用该服务。 内置 92 个双向翻译模型,支持 47 种语言之间的翻译。支持自动语言检测(源语言代码使用 `auto` 表示)。 ## 服务信息 * `mtrans` 地址为 * `mtrans` 详细说明可查看 `https://github.com/xxnuo/MTranServer` ## 支持的语言 ### 完全支持双向翻译的语言(42种) 阿拉伯语、阿塞拜疆语、保加利亚语、孟加拉语、加泰罗尼亚语、捷克语、丹麦语、德语、希腊语、西班牙语、爱沙尼亚语、波斯语、芬兰语、法语、古吉拉特语、希伯来语、印地语、克罗地亚语、匈牙利语、印尼语、意大利语、日语、卡纳达语、韩语、立陶宛语、拉脱维亚语、马拉雅拉姆语、马来语、荷兰语、波兰语、葡萄牙语、罗马尼亚语、俄语、斯洛伐克语、斯洛文尼亚语、阿尔巴尼亚语、瑞典语、泰米尔语、泰卢固语、土耳其语、乌克兰语、简体中文、英语 ### 仅支持翻译到英语的语言(8种) 白俄罗斯语、波斯尼亚语、冰岛语、马耳他语、挪威语(博克马尔)、挪威语(尼诺斯克)、塞尔维亚语、越南语 ::: tip 提示 这些语言只能作为源语言翻译成英语,不能作为要翻译到的目标语言。 ::: ## 语言代码对照表 代码规则遵循 BCP47 标准 [IETF Language Tag](https://en.wikipedia.org/wiki/IETF_language_tag) ### 支持双向翻译的语言(42种 + 英语) | 语言代码 | 语言名称(中文) | 语言名称(英文) | |---------|-----------------|-----------------| | ar | 阿拉伯语 | Arabic | | az | 阿塞拜疆语 | Azerbaijani | | bg | 保加利亚语 | Bulgarian | | bn | 孟加拉语 | Bengali | | ca | 加泰罗尼亚语 | Catalan | | cs | 捷克语 | Czech | | da | 丹麦语 | Danish | | de | 德语 | German | | el | 希腊语 | Greek | | en | 英语 | English | | es | 西班牙语 | Spanish | | et | 爱沙尼亚语 | Estonian | | fa | 波斯语 | Persian | | fi | 芬兰语 | Finnish | | fr | 法语 | French | | gu | 古吉拉特语 | Gujarati | | he | 希伯来语 | Hebrew | | hi | 印地语 | Hindi | | hr | 克罗地亚语 | Croatian | | hu | 匈牙利语 | Hungarian | | id | 印尼语 | Indonesian | | it | 意大利语 | Italian | | ja | 日语 | Japanese | | kn | 卡纳达语 | Kannada | | ko | 韩语 | Korean | | lt | 立陶宛语 | Lithuanian | | lv | 拉脱维亚语 | Latvian | | ml | 马拉雅拉姆语 | Malayalam | | ms | 马来语 | Malay | | nl | 荷兰语 | Dutch | | pl | 波兰语 | Polish | | pt | 葡萄牙语 | Portuguese | | ro | 罗马尼亚语 | Romanian | | ru | 俄语 | Russian | | sk | 斯洛伐克语 | Slovak | | sl | 斯洛文尼亚语 | Slovenian | | sq | 阿尔巴尼亚语 | Albanian | | sv | 瑞典语 | Swedish | | ta | 泰米尔语 | Tamil | | te | 泰卢固语 | Telugu | | tr | 土耳其语 | Turkish | | uk | 乌克兰语 | Ukrainian | | zh | 简体中文 | Simplified Chinese | ### 仅支持翻译到英语的语言(8种) | 语言代码 | 语言名称(中文) | 语言名称(英文) | |---------|-----------------|-----------------| | be | 白俄罗斯语 | Belarusian | | bs | 波斯尼亚语 | Bosnian | | is | 冰岛语 | Icelandic | | mt | 马耳他语 | Maltese | | nb | 挪威语(博克马尔) | Norwegian (Bokmål) | | nn | 挪威语(尼诺斯克) | Norwegian (Nynorsk) | | sr | 塞尔维亚语 | Serbian | | vi | 越南语 | Vietnamese | --- --- url: /aipod/aipod/extenstion-chrome.md --- # 懒猫AI助手插件 :::info 懒猫AI助手插件通过 `AI POD` 应用下发,所以需要确保懒猫微服客户端已经打开,并且微服在线。插件的版本会保持和当前的 `AI POD` 版本一致 ::: ## 插件更新 在各个端的 AI 浏览器中,当您重启浏览器就会自动拉取当前最新的浏览器插件,并进行安装。在 `chrome://extensions` 界面中您可以看到当前的插件版本。 ![](/assets/chrome-extension-version.DOQyb3dz.png) 您也可以在 `AI POD` 的设置界面上查看当前的插件版本 ![](/assets/ai-extenstion-version.B_my8yc6.png) ## 把 AI 插件安装到 Chrome 中 0. 打开 `AI POD` -> 插件 -> 选中 `cloud.lazycat.app.ai.zip` 点击下载 ![](/assets/extenstions-shortcut.DBNTZv5q.png) 1. 打开 `Chrome` 浏览器, 确保您使用的是最新版本的 `Chrome` 浏览器 2. 访问扩展程序页面, 在地址栏中输入 `chrome://extensions` 并按回车 3. 启用开发者模式, 在页面右上角找到 "开发者模式" 开关,确保它已启用 4. 拖拽安装插件, 将 `cloud.lazycat.app.ai.zip` 文件直接拖拽到 `Chrome` 扩展页面中 5. 打开一个新标签页,可以看到插件已经安装(首页变成了 `AI POD` 的页面) ## 懒猫助手插件新标签页 懒猫助手默认会接管浏览器的新标签页,您可以在插件的配置中切换成您之前的标签页 ![](/assets/chrome-extension-newtab.CvpjkJVp.jpg) --- --- url: /aipod/imagen/api.md --- # 文生图 下面的接口调用依赖于您已经安装了 `文生图` 应用,[点击跳转安装](https://appstore.lazycat.cloud/#/shop/detail/cloud.lazycat.aipod.imagen) 文生图的接口是兼容 OpenAI 的 API 格式。 1. 获取模型接口(目前仅有一个默认模型 sd-cpp-local 对应 z-image-turbo) `curl https://imagen-ai.$微服名称.heiyu.space/v1/models` 2. 生成图片接口(https://platform.openai.com/docs/api-reference/images/create) ``` curl https://imagen-ai.微服.heiyu.space/v1/images/generations \ -H "Content-Type: application/json" \ -d '{ "model": "sd-cpp-local", "prompt": "A lovely cat", "n": 1, "size": "512x512", "response_format": "b64_json" }' \ -o >(jq -r '.data[0].b64_json' | base64 --decode > output.png) ``` 参数用法同 OpenAI: * prompt 提示词 * n 生成图片数量 * size 图片尺寸,长和宽都必须是 16 的整数倍否则不合法 * response\_format 目前仅支持这个固定格式 模型特定参数:因为 OpenAI 格式未预留额外参数接口,因此当前版本可以将 Z-Image-Turbo 的一些高级参数放到提示词里 ``` "prompt": "A lovely cat{\"seed\": 357925}", ``` 当前主要有: * steps 迭代步数,当前版本后端设置的默认值是 7, 模型推荐值是 9,取值范围 0-9,步数越少生成越快,比如演示测试时可以指定为 4 步来快速生成,不用等太久。另外这个 turbo 是蒸馏过的模型,步数不能超过 20,否则会过渲染图像无法使用 * seed 生成种子,默认值是随机值,可以手动指定同一个种子以得到稳定一致的结果,特殊需求下需要这个参数 --- --- url: /aipod/issues/traefik-dashboard.md --- # 查看算力舱中运行的服务转发规则 算力舱中使用 `traefik` 做转发,匹配的规则通过 `Host` 来匹配,当您通过 访问的时候,会自动根据 `ollama-ai` 转发到 `ollama` 的容器中去,而端口则是您容器中填写的 `expose` 端口。 :::info 当您的服务如果具有多个 expose 端口的话,会导致转发不成功,可以通过 `docker inspect` 查看,也可以检查 `Dockerfile` 中是否已经定义了 `expose` 字段。 ::: 您可以打开 来查看算力舱上的 `traefik` 转发情况。 ![traefik http router screenshot](/assets/traefik-dashboard-http-router.Ct9i6PnB.png) --- --- url: /aipod/os/clean_data.md --- # 清理数据 在算力舱结合微服使用的过程中,`lzc-ai-agent` 服务启动的时候,会自动将旧版本的docker镜像清理掉。 在微服中将对应的 `AI应用` 卸载掉后,会自动将算力舱中的镜像已经对应模型数据清理。 ## 清理AI应用服务旧镜像 智能扫描您服务安装的位置,判断没有使用的旧版本镜像并删除。 ```sh /ssd/lzc-ai-agent/bin/client.arm64 -clean-lzc-images ``` ## 清理异常状态的容器 以下不影响系统正常运行 ```sh docker ps -a | grep '[Dead|Exited]' | awk '{print $2}' | xargs -i docker rm {} ``` ## 清理镜像 ::: warning 这个命令不影响系统当前运行, 执行以下命令可以清理未使用到的镜像,如果有镜像被容器占用,docker会自动跳过。但要注意,如果您本地构建或者拉取的镜像如果没有正在运行,也会被删除。 ::: ```sh docker images | awk '{print $2}' | xargs -i docker rmi {} ``` ## docker 清理 清理docker的缓存以及没有用到的数据 ```sh docker system prune ``` --- --- url: /aipod/aipod/resources.md --- # 相关资源 ## Nvidia Jetson 镜像 AI 领域常用的一些基础镜像,在 jetson 的官方都有支持,并且更新也是挺频繁的。 1. https://hub.docker.com/u/dustynv 2. https://github.com/dusty-nv/jetson-containers 3. https://pypi.jetson-ai-lab.io/ 4. https://pypi.jetson-ai-lab.io/jp6/cu129 5. https://pypi.jetson-ai-lab.io/jp6/cu128 6. https://pypi.jetson-ai-lab.io/jp6/cu126 7. Jetson Thor / CUDA 13.0 Python 包源: https://pypi.jetson-ai-lab.io/sbsa/cu130 8. Jetson Thor vLLM 镜像: `ghcr.io/nvidia-ai-iot/vllm:latest-jetson-thor` 额外的使用场景: 1. https://www.jetson-ai-lab.com/tutorial-intro.html ## NVIDIA vLLM 官方资源 1. NGC vLLM 镜像: `nvcr.io/nvidia/vllm:-py3` 2. 当前在 Jetson Thor 上验证可启动的版本: `nvcr.io/nvidia/vllm:26.04-py3` 3. vLLM 版本信息: `0.19.0+6bc3197f.nv26.04.48680843` 4. 容器 CUDA / PyTorch: `CUDA 13.2` / `torch 2.12.0a0` 5. Thor 运行状态: 可识别 `NVIDIA Thor`,compute capability `(11, 0)`,并可启动 OpenAI-compatible `/v1/models` 与 `/v1/chat/completions` 6. 注意: Thor 当前驱动支持 CUDA 13.0,运行 `26.04-py3` 时会进入 CUDA minor compatibility mode Thor 上最小验证命令: ``` docker run --rm --runtime=nvidia --ipc=host --network=host \ nvcr.io/nvidia/vllm:26.04-py3 \ python3 -c "import vllm, torch; print(vllm.__version__, torch.__version__, torch.cuda.get_device_name(), torch.cuda.get_device_capability())" ``` 如果模型配置带有多模态字段,但只需要文本推理,启动 `vllm serve` 时建议加上 `--language-model-only`,避免 vLLM 去加载不存在的 `preprocessor_config.json`。 ## 封装的镜像 官方打包的镜像一般会比较旧,版本没有及时更新,我们也会在这些基础的镜像上更新软件的版本。 1. ollama https://hub.docker.com/r/catdogai/jetson-ollama/tags 2. vLLM https://hub.docker.com/r/catdogai/lzc-aipod-vllm/tags 3. Thor vLLM 最新官方验证基线 `nvcr.io/nvidia/vllm:26.04-py3` 4. Thor vLLM Jetson AI-IOT 基线 `ghcr.io/nvidia-ai-iot/vllm:latest-jetson-thor` ## 常用镜像 1. CUDA 镜像 `dustynv/cuda:12.8-samples-r36.4.0-cu128-24.04` 2. PyTorch 镜像 `dustynv/pytorch:2.6-r36.4.0-cu128-24.04` --- --- url: /aipod/knowledge/mcp.md --- # 知识库 MCP `Knowledge` 应用提供了 MCP 接口,可以让 `Codex`、`Claude Code`、`Cherry Studio` 这类支持 MCP 的客户端直接访问知识库中的文件、网页和检索能力。 ## 使用前提 * 请先安装最新版知识库,安装方式见 [安装方式](/knowledge/install.md)。 * 本页内容基于当前知识库仓库版本 `1.3.40`。 * 如果您在桌面 MCP 客户端中使用,建议先为对应用户生成访问 token。 ## 服务入口 * Knowledge 应用地址: * MCP 地址: * 协议:`Streamable HTTP` ## 鉴权方式 1. `ssh` 登录微服。 2. 执行下面的命令,为需要访问知识库的用户生成 token: ```bash hc api_auth_token gen --uid ``` 3. 在 MCP 客户端配置中通过 `Lzc-Api-Auth-Token` 传入这个 token。 ## 工具说明 知识库 MCP 按 `rag`、`fs`、`ws` 三个命名空间组织工具。很多 MCP 客户端会把它们显示为 `rag_search`、`fs_ls`、`ws_ls` 这样的名字。 ### `rag` * `rag_search`:搜索知识库中的相关片段。 * `rag_summarize`:对指定文件或网页做摘要分析。 * `rag_read`:分页读取文件或网页内容。 ### `fs` * `fs_ls`:列出知识库文件目录。 * `fs_search`:按关键词搜索文件或目录。 * `fs_find`:按文件名查找文件。 * `fs_traverse`:递归遍历目录。 * `fs_get_info`:查看文件或目录详情。 * `fs_get_stats`:查看文件系统统计信息。 * `fs_sync`:同步知识库文件状态。 * `fs_mkdir`、`fs_rm`、`fs_mv`、`fs_update_meta`:创建、删除、移动、更新元数据。 ### `ws` * `ws_ls`:列出网页知识目录。 * `ws_search`:搜索保存的网页。 * `ws_get_stats`:查看网页知识统计信息。 * `ws_mkdir`、`ws_delete`、`ws_update_meta`:管理网页目录和元数据。 > `fs_mkdir`、`fs_rm`、`fs_mv`、`fs_update_meta`、`ws_mkdir`、`ws_delete`、`ws_update_meta` 这类写操作会修改知识库内容,建议仅在确认范围正确时使用。 ## MCP 样例 > 注意将下面配置中的 `xxx` 替换为您的微服名称。 ### Claude Code 配置 编辑 Claude Code 配置文件 `~/.claude.json`,添加如下 MCP 服务器配置: ```json { "mcpServers": { "knowledge": { "type": "http", "url": "https://knowledge.xxx.heiyu.space/mcp/", "headers": { "Lzc-Api-Auth-Token": "" } } } } ``` ### Codex 配置 编辑 Codex 配置文件 `~/.codex/config.toml`,添加如下 MCP 服务器配置: ```toml [mcp_servers] [mcp_servers.knowledge] type = "http" url = "https://knowledge.xxx.heiyu.space/mcp/" [mcp_servers.knowledge.http_headers] Lzc-Api-Auth-Token = "" ``` ### AI 对话客户端配置 以 Cherry Studio 配置为例,在 设置 -> MCP 服务器 中添加如下配置: * 名称:`knowledge` * 类型:可流式传输的 HTTP(`streamableHttp`) * URL:`https://knowledge.xxx.heiyu.space/mcp/` * Header:`Lzc-Api-Auth-Token: ` 保存后启用 MCP 服务器,即可在工具列表中看到知识库相关工具。 ## 常见使用场景 * 在 `Codex` 中查询“知识库里关于某个主题的文档结论”。 * 让客户端先列出 `/` 或某个目录下的文件,再按目录范围做检索。 * 对某个文档目录执行摘要,再继续深入读取命中的片段。 * 搜索已经保存到知识库中的网页内容,而不是重新联网抓取。 ## 参数说明 * ``:需要访问知识库的用户 ID。 * ``:通过 `hc api_auth_token gen` 生成的访问令牌。 * `<微服名称>`:当前微服名称,对应 `knowledge.<微服名称>.heiyu.space`。 --- --- url: /aipod/os/disk.md --- # 硬盘 算力舱默认携带的硬盘是 `1T` 版本的固态硬盘。但算力舱具有两个 `pcie4` 的接口,您可以按照您的需求来更换硬盘。 ## 换硬盘 1. 将换下来的硬盘的数据拷贝到新硬盘上(原来的硬盘上有一些模型的数据和算力舱的客户端) 2. 换上新的硬盘即可 ::: warning 换硬盘时需要确保 `/etc/fstab` 中的挂载路径是对应上的,要不然系统进不去。 下面这个是一个示例,里面将固态盘挂载到 `/ssd` 这个目录下。 ``` UUID=942a4d75-e509-4fa2-8d3a-23cb6925b68c /ssd ext4 defaults 0 2 /ssd/home /home none bind 0 0 ``` ::: ## 迁移系统到固态 在算力舱现在系统中,系统是在 64G 的 `emmc` 存储上的,而 `docker` 和 `home` 是在 `SSD` 存储上的,您可以按照下面的命令来将系统迁移到 `SSD` 上。 1. /ssd 目录 这个目录是现在 1T 硬盘的挂载路径,您可以在里面看到 `docker`, `home`, `lzc-ai-agent` 等目录。 2. 拷贝当前的系统到 nvme 硬盘中, 假设您的 nvme 为 nvme0n1p1 ``` sudo mount /dev/nvme0n1p1 /mnt sudo rsync -axHAWX --numeric-ids --info=progress2 --exclude={"/dev/","/proc/","/sys/","/tmp/","/run/","/mnt/","/media/*","/lost+found", "/ssd/", "/home/"} / /mnt sudo mkdir -p /mnt/ssd # 迁移之前的 /ssd 到新的目录 sudo systemctl stop lzc-ai-agent sudo systemctl stop docker systemctl stop nvzramconfig systemctl disable nvzramconfig mkdir -p /mnt/ssd mv /mnt/docker /mnt/ssd/ mv /mnt/lzc-ai-agent /mnt/ssd/lzc-ai-agent ``` 3. 更改 `/etc/fstab` 需要将 /ssd/ 相关的行注释掉,更新后的 `/etc/fstab` 看起来类似下面的文本 ``` # /etc/fstab: static file system information. # # These are the filesystems that are always mounted on boot, you can # override any of these by copying the appropriate line from this file into # /etc/fstab and tweaking it as you see fit. See fstab(5). # # /dev/root / ext4 defaults 0 1 UUID=A9EE-ABCF /boot/efi vfat defaults 0 1 ``` 4. 写入 `/etc/systemd/system/setssdroot.service` ``` [Unit] Description=Change rootfs to SSD in M.2 key M slot (nvme0n1p1) DefaultDependencies=no Conflicts=shutdown.target After=dev-nvme0n1p1.device Requires=dev-nvme0n1p1.device Before=shutdown.target ConditionPathExists=/dev/nvme0n1p1 ConditionPathExists=/etc/setssdroot.conf ConditionVirtualization=!container [Service] Type=oneshot RemainAfterExit=yes ExecStart=/sbin/setssdroot.sh [Install] WantedBy=default.target ``` 5. 写入 `/sbin/setssdroot.sh` ``` #!/bin/sh # Runs at startup, switches rootfs to the SSD on nvme0 (M.2 Key M slot) NVME_DRIVE="/dev/nvme0n1p1" CHROOT_PATH="/nvmeroot" INITBIN=/lib/systemd/systemd EXT4_OPT="-o defaults -o errors=remount-ro -o discard" modprobe ext4 mkdir -p ${CHROOT_PATH} mount -t ext4 ${EXT4_OPT} ${NVME_DRIVE} ${CHROOT_PATH} cd ${CHROOT_PATH} /bin/systemctl --no-block switch-root ${CHROOT_PATH} ``` 6. 执行命令 ``` sudo chmod 777 /sbin/setssdroot.sh systemctl daemon-reload sudo systemctl enable setssdroot.service sudo cp /etc/systemd/system/setssdroot.service /mnt/etc/systemd/system/setssdroot.service sudo cp /sbin/setssdroot.sh /mnt/sbin/setssdroot.sh sudo cp /etc/fstab /mnt/etc/fstab sudo touch /etc/setssdroot.conf ``` `/etc/setssdroot.conf` 为一个标识文件,这个文件存在才会使用 nvme 上的 rootfs 启动。所以如果您需要切换会 `emmc` 系统,您可以 * 将 nvme 硬盘拔掉 * 手动 mount emmc 硬盘,然后将里面的 `/etc/setssdroot.conf` 文件删除重启即可 7. 重启生效 ### 配置 Swapfile 在您迁移系统后,重启系统,lsblk 可以看到当前系统已经在 nvme 上,配置 swapfile ``` fallocate -l 32G /swapfile mkswap /swapfile ``` 创建一个 `/etc/systemd/system/swapfile.swap` 文件写入下面的内容 ``` [Swap] What=/swapfile [Install] WantedBy=swap.target ``` 执行下面的命令进行启动 ``` systemctl daemon-reload systemctl status swapfile.swap systemctl enable swapfile.swap systemctl start swapfile.swap ``` ## 修复 apt man 数据库错误 ``` # 修复权限 sudo chown -R man:root /var/cache/man sudo chmod -R 755 /var/cache/man # 重建数据库 sudo mandb ``` --- --- url: /aipod/os/flash-jetson.md --- # 算力舱刷机 > 如果您使用 `算力舱刷机工具(新版)`,请优先参考:[刷系统(新版)](./flash-jetson-rc2.md) 下面有两种方式进行刷机 1. 通过微服中安装 “算力舱刷机工具” 应用来刷机 2. 直接通过您本地的机器,启动算力舱刷机镜像来刷机 ::: warning 在构建系统镜像的时候,会需要较大的硬盘空间,请确保您的机器所剩余的空间大于 60GB ::: ## jetson刷机应用运行刷机系统 1. 在内测工具中的算力舱内测组中安装这个应用,由于这个应用中的包含了 `jetson` 系统镜像和 `cuda` 相关的驱动,比较大(20G)需要您耐心等候 2. 等待应用安装完成后,打开这个应用会出现一个 `terminal`,这个 `terminal` 就是用来刷机的系统 3. 以下步骤和直接使用本地机器运行 `docker` 镜像一样 (请看下面的 "在刷机系统中刷算力舱系统") ## 直接使用本地的机器,运行刷机镜像系统 1. 首先拉取镜像 `docker pull registry.cn-hangzhou.aliyuncs.com/catdogai/jetson-flash:t912-6.2-0.0.7` 2. 执行 `docker run -it --rm --privileged --network host -v /dev/bus/usb:/dev/bus/usb -v /dev:/dev registry.cn-hangzhou.aliyuncs.com/catdogai/jetson-flash:t912-6.2-0.0.7 bash` 3. 等待 `docker` 启动后,将会出现一个 `terminal`,这个 `terminal` 就是用来刷机的系统 4. 以下步骤和直接使用微服应用刷机一样(请看下面的 "在刷机系统中刷算力舱系统") ## 在刷机系统中刷算力舱系统 1. 确保算力舱和刷机系统通过 `typec` 连接起来 * 如果您是使用微服应用进行刷系统,您需要把算力舱上的 `typec` 口和微服上的 `usb` 连接起来。 * 如果您是使用本地的机器进行刷系统,您需要把算力舱上的 `typec` 口和您的机器`usb`连接起来。 2. 确保算力舱处于 `recovery mode` * 通过点击算力舱的管理列表中的 `recovery` 键来进入 `recovery` 模式。 * 在算力舱刷机工具的系统中,可以直接使用 `ssh nvidia@192.168.55.1` 进去到算力舱的系统,然后执行 `sudo reboot --force forced-recovery` 进去到 `recovery` 模式。 ::: info 在拆开算力舱的外壳后,靠近电源按钮的键为 `recovery` 另外一个为 `reset` 键。在这两个键的下发会有两个小字标识。分别为 `RECY` 和 `RST`。 1. 在关机的情况下,通过按住 `recovery` 按钮,然后再按开机键2秒,再松开开机键,然后才松开 `recovery` 键 2. 开机的情况下,先按住 `recovery` 键,再按压 `reset` 键,最后松开 `reset` 键,再松开 `recovery` 键 ::: 在做了上面的操作后,机器会进入 `recovery` 模式,这种情况下风扇不转。在刷机系统的 `terminal` 上的执行 `lsusb` 如果显示的结果中出现 `ID 0955:7023 Nvidia Corp.APX` (注意不是 `NVIDIA Linux for Tegra` 这个表示还在正常的运行系统 `Tegra`) 则表示算力舱已经成功的进入到 `recovery` 模式 3. 在刷机系统上执行 ``` cd /Linux_for_Tegra sudo ./flash.sh jetson-agx-orin-devkit internal # 重复可以刷可以使用 sudo ./flash.sh -r jetson-agx-orin-devkit internal ``` 等待运行结束后,会显示以下的内容 ``` [ 1100.2456 ] Coldbooting the device [ 1100.2459 ] tegrarcm_v2 --chip 0x23 0 --ismb2 [ 1100.2462 ] MB2 version 01.00.0000 [ 1100.3052 ] Coldbooting the device [ 1100.3056 ] tegrarcm_v2 --chip 0x23 0 --reboot coldboot [ 1100.3057 ] MB2 version 01.00.0000 *** The target generic has been flashed successfully. *** Reset the board to boot from internal eMMC. ``` 算力舱会自动重启,重启成功后,系统已经正常运行,下面是继续配置 `docker` 和 微服的AI客户端服务。 4. 配置微服AI服务客户端和 `docker` 存储目录, 有两种方式进去到算力舱中 * 使用 `Type-C` 连接 `Type-C` 口还连接的情况下,您可以使用 `ssh nvidia@192.168.55.1` (如果您 `ping 192.168.55.1` 不通的话,那需要您本地配置下,要将一个usb口的 `ip` 配置成 `192.168.55.x` 同一个网段的地址) 进入到算力舱中去,帐号密码为 `nvidia:nvidia` * 通过显示屏 直接在界面上登录用户 `nvidia`,密码为 `nvidia` * 插上网线,通过路由器查看 `ip` 地址 当您知道算力舱的 `ip` 地址后,您可以通过 `ssh nvidia@ip` 来进入到算力舱系统中。 进去后,打开终端,在 `eMMC` 系统的 `/home/nvidia` 目录下先解压安装脚本,再进入目录执行: ```bash cd /home/nvidia tar -xzf lzc-jetson-installer.tar.gz cd lzc-jetson-installer ls lzc-jetson-installer.sh ``` `lzc-jetson-installer.sh` 在 `lzc-jetson-installer` 目录中,不在 `/home/nvidia` 根目录。 1. 在安装 `docker` 的时候会需要连接网络,如果您有代理,速度将会快很多. `DOCKER_PROXY=http://127.0.0.1:1080` 2. 如果您不需要安装 `lzc-ai-agent` 您可以使用 `DISABLE_LZC_AIAGENT=1` 比如 `sudo DOCKER_PROXY=http://127.0.0.1:1080 ./lzc-jetson-installer.sh` 安装成功后,算力舱会自动重启,到此算力舱的系统部署好了! --- --- url: /aipod/os/flash-jetson-rc2.md --- # 算力舱刷系统(新版) > 适用应用:`算力舱刷机工具(新版)` > > lpk 下载: [点击下载](https://gitee.com/lazycatcloud/lzc-aipod-resources/releases/download/0.1.0-rc3/cloud.lazycat.aipod.flashtools-v0.1.0-rc3.lpk) 如果需要在刷机界面手动下载资源,可打开: * https://gitee.com/lazycatcloud/lzc-aipod-resources/releases 这份文档用于 `jetson-flash` 目录里的安装脚本,重点入口是: * `startup_scripts/setup_ssd_docker.sh` 脚本支持多个 `phase`,每个阶段只做一件明确的事。 ## 先进入刷机系统 有两种方式进入刷机系统: 1. 通过微服中安装“算力舱刷机工具”应用 2. 直接通过本地机器启动刷机镜像 ::: warning 在构建系统镜像的时候,会需要较大的硬盘空间,请确保机器剩余空间大于 60GB。 ::: ### 方式 1:使用算力舱刷机工具 1. 在内测工具中的算力舱内测组中安装这个应用。 这个应用包含 `jetson` 系统镜像和 `cuda` 相关驱动,体积较大(约 20G),需要等待下载完成。 2. 安装完成后,打开应用会出现一个 `terminal`,这个 `terminal` 就是刷机系统。 3. 如果需要手动下载刷机资源,可在刷机界面打开: * https://gitee.com/lazycatcloud/lzc-aipod-resources/releases ### 方式 2:在本地机器运行刷机镜像 1. 先拉取镜像,二选一即可: ```bash # 大陆地址 docker pull registry.cn-hangzhou.aliyuncs.com/catdogai/jetson-flash:t912-6.2-0.1.0-rc2 # Docker Hub 地址 docker pull catdogai/jetson-flash:t912-6.2-0.1.0-rc2 ``` 2. 启动容器,下面分别是大陆地址和 Docker Hub 地址的命令,任选其一: ```bash # 大陆地址 docker run -it --rm --privileged --network host -v /dev/bus/usb:/dev/bus/usb -v /dev:/dev registry.cn-hangzhou.aliyuncs.com/catdogai/jetson-flash:t912-6.2-0.1.0-rc2 bash # Docker Hub 地址 docker run -it --rm --privileged --network host -v /dev/bus/usb:/dev/bus/usb -v /dev:/dev catdogai/jetson-flash:t912-6.2-0.1.0-rc2 bash ``` 3. 等待 `docker` 启动完成后,会出现一个 `terminal`,这个 `terminal` 就是刷机系统。 ## 刷机前步骤 1. 确保算力舱和刷机系统通过 `typec` 连接起来。 * 如果使用微服应用进行刷系统,请把算力舱上的 `typec` 口和微服上的 `usb` 连接起来 * 如果使用本地机器进行刷系统,请把算力舱上的 `typec` 口和本地机器 `usb` 连接起来 2. 确保算力舱处于 `recovery mode`。 * 可通过管理列表中的 `recovery` 键进入 `recovery` 模式 * 可在算力舱刷机工具系统中执行 `ssh nvidia@192.168.55.1` 登录后,运行 `sudo reboot --force forced-recovery` ::: info 在拆开算力舱的外壳后,靠近电源按钮的键为 `recovery`,另外一个为 `reset` 键。在这两个键的下方会有两个小字标识,分别为 `RECY` 和 `RST`。 1. 在关机的情况下,通过按住 `recovery` 按钮,然后再按开机键 2 秒,再松开开机键,然后才松开 `recovery` 键 2. 在开机的情况下,先按住 `recovery` 键,再按压 `reset` 键,最后松开 `reset` 键,再松开 `recovery` 键 ::: 3. 进入 `recovery mode` 后,在刷机系统执行 `lsusb`。 如果结果中出现 `ID 0955:7023 Nvidia Corp.APX`,说明已经成功进入 `recovery` 模式。注意不要看到 `NVIDIA Linux for Tegra`,那个表示设备还在正常启动的 `Tegra` 系统里。 4. 在刷机系统执行刷机命令: ```bash cd /Linux_for_Tegra sudo ./flash.sh jetson-agx-orin-devkit internal # 重复刷写可用: sudo ./flash.sh -r jetson-agx-orin-devkit internal ``` 等待运行结束后,会显示类似下面的内容: ```text [ 1100.2456 ] Coldbooting the device [ 1100.2459 ] tegrarcm_v2 --chip 0x23 0 --ismb2 [ 1100.2462 ] MB2 version 01.00.0000 [ 1100.3052 ] Coldbooting the device [ 1100.3056 ] tegrarcm_v2 --chip 0x23 0 --reboot coldboot [ 1100.3057 ] MB2 version 01.00.0000 *** The target generic has been flashed successfully. *** Reset the board to boot from internal eMMC. ``` 算力舱会自动重启。重启成功后,按下面“刷机后先准备安装脚本”继续。 ## 刷机后先准备安装脚本(必做) 刷机后,在板载系统(eMMC)的 `/home/nvidia` 目录下会有 `lzc-jetson-installer.tar.gz`,先解压再执行后续步骤: ```bash cd /home/nvidia tar -xzf lzc-jetson-installer.tar.gz cd lzc-jetson-installer ls lzc-jetson-installer.sh ``` `lzc-jetson-installer.sh` 脚本在 `lzc-jetson-installer` 目录中,不在 `/home/nvidia` 根目录。 ## 1. 先理解两个系统 这份文档里会反复提到两个系统: * **板载系统(eMMC)**:系统运行在机器主板自带的内置存储上。可以把它理解成“机器自带的基础系统”。 * **SSD 系统(NVMe)**:系统运行在后续迁移到的 NVMe SSD 上。可以把它理解成“迁移后的工作系统”。 这两个说法描述的是“当前系统跑在哪块存储上”,不是两套完全独立的软件版本。 最容易判断的方法是: ```bash findmnt / ``` 判断规则: * 输出是 `/dev/mmcblk0p1` 一类:你当前在 **板载系统(eMMC)** * 输出是 `/dev/mapper/ssd_vg-ssd_lv`:你当前在 **SSD 系统(NVMe)** 可以这样理解它们的区别: * **板载系统(eMMC)**:刷机完成后先进入这里,主要负责初始化磁盘、迁移系统、修复或重建启动链路。 * **SSD 系统(NVMe)**:迁移完成后主要在这里长期运行,负责 Docker、`lzc-ai-agent` 和日常业务。 * **板载系统(eMMC)** 更接近“安装器 / 维护系统”。 * **SSD 系统(NVMe)** 更接近“最终工作系统”。 它们的优缺点可以这样理解: * **板载系统(eMMC)优点**:机器出厂就能用,刷机后可以直接进入,适合做初始化、修复和兜底恢复。 * **板载系统(eMMC)缺点**:存储空间和性能通常不如 NVMe SSD,更不适合长期承载镜像、模型和日常业务数据。 * **SSD 系统(NVMe)优点**:容量更大、读写更快,更适合长期运行 Docker、模型服务和业务数据。 * **SSD 系统(NVMe)缺点**:需要先完成初始化和迁移;如果启动链路或磁盘状态有问题,排查时通常还是要回到板载系统(eMMC)处理。 如果只写 `eMMC 系统`,有些人会误以为这是某个单独发布的系统版本。为了减少误解,下面统一写成: * **板载系统(eMMC)** * **SSD 系统(NVMe)** ## 2. 先理解目标 这套脚本的目标是: 1. 在 NVMe 上建立 LVM(`ssd_vg/ssd_lv`) 2. 把系统迁移到 NVMe 3. 开机自动从 NVMe 运行(switch-root) 4. 在 SSD 系统(NVMe)中配置 Docker 和 `lzc-ai-agent` ## 3. 常见场景怎么选 ### 3.1 执行环境速查(最重要) 下面所有 `./lzc-jetson-installer.sh` 命令都在 `/home/nvidia/lzc-jetson-installer` 目录执行。 各 phase 应该在哪个系统执行: * `configure-disk`:**板载系统(eMMC)** * `migrate-rootfs`:**板载系统(eMMC)** * `bootstrap-ssd`:**板载系统(eMMC)** * `configure-system`:**SSD 系统(NVMe)** * `reset`:从 **板载系统(eMMC)** 发起(脚本会自动重启并在 NVMe 首次启动继续执行) * `all`:通常从 **板载系统(eMMC)** 发起(通常执行到 step2 后需要重启) ### 场景 A:全新配置 ```bash # 当前环境:板载系统(eMMC) sudo ./lzc-jetson-installer.sh --phase configure-disk sudo ./lzc-jetson-installer.sh --phase migrate-rootfs sudo reboot # 机器起来后(当前环境:SSD 系统(NVMe)) sudo /opt/lzc-jetson-installer/startup_scripts/setup_ssd_docker.sh --phase configure-system ``` ### 场景 B:已经有克隆好的 SSD(批量装机) 只做板载系统(eMMC)上的引导配置,不复制 rootfs: ```bash # 当前环境:板载系统(eMMC) sudo ./lzc-jetson-installer.sh --phase bootstrap-ssd sudo reboot ``` 注意: * 批量装机前先做 1 台完整演练(含断电重启、回归检查) * 克隆盘不要和原盘同时在线启动同一机器,避免 LVM UUID 冲突 如果您的母盘容量比较小,而目标的盘比较大,您需要重新的执行下面的脚本进行扩容到硬盘实际的大小 ```sh #!/bin/bash set -euo pipefail DISK=/dev/nvme0n1 PART=/dev/nvme0n1p1 LV=/dev/ssd_vg/ssd_lv echo '[1/5] 修复 GPT 备份表到磁盘末尾' sgdisk -e "$DISK" echo '[2/5] 将第 1 分区扩到整盘末尾' parted -s "$DISK" resizepart 1 100% echo '[3/5] 通知内核重新读取分区表' partprobe "$DISK" || true udevadm settle || true sleep 2 DISK_SIZE=$(blockdev --getsize64 "$DISK") PART_SIZE=$(blockdev --getsize64 "$PART") echo "disk_size=$DISK_SIZE part_size=$PART_SIZE" if [ $((DISK_SIZE - PART_SIZE)) -gt $((20 * 1024 * 1024 * 1024)) ]; then echo '分区表已经改了,但内核还没刷新到新大小。先重启一次,再执行下面两条:' echo "pvresize $PART" echo "lvextend -l +100%FREE -r $LV" exit 2 fi echo '[4/5] 扩容 LVM PV' pvresize "$PART" echo '[5/5] 扩容 LV 和 ext4 文件系统' lvextend -l +100%FREE -r "$LV" echo lsblk echo pvs echo vgs echo lvs -o +devices echo df -h / ``` 最终 `lsblk` 应该看到:ssd\_vg/ssd\_lv 接近整盘大小 ### 场景 C:彻底重置(会清空所有 NVMe) ```bash # 当前环境:板载系统(eMMC) sudo ./lzc-jetson-installer.sh --phase reset ``` `reset` 会自动重建并在最后自动重启。 注意:`reset` 是基于当前板载系统(eMMC)重建,不是“完整出厂重刷”。 如果你要完全重置系统,请重新刷机。 ## 4. phase 详细说明(做什么、下一步做什么) ### 4.1 `--phase configure-disk`(步骤 1) 执行环境: * **板载系统(eMMC)** 作用: * 探测 NVMe * 初始化 LVM 根盘(`ssd_vg/ssd_lv`) * 如果当前插着多块空白 NVMe,会自动并入同一 VG * 安装后续手动扩容脚本到 `/opt/lzc-jetson-installer/bin/lzc-expand-lvm.sh` 成功后下一步: * 运行 `--phase migrate-rootfs` 如果需要指定基盘: ```bash sudo SETUP_SSD_BASE_DISK=/dev/nvme0n1 ./lzc-jetson-installer.sh --phase configure-disk ``` 如果目标 NVMe 不是空白盘,但你确认可以清理后重建: ```bash sudo SETUP_SSD_BASE_DISK=/dev/nvme0n1 SETUP_SSD_ALLOW_REINIT_NONBLANK=1 ./lzc-jetson-installer.sh --phase configure-disk ``` 注意: * `SETUP_SSD_ALLOW_REINIT_NONBLANK=1` 会清理目标盘上的现有数据 * 不确定盘符时先用 `lsblk` 确认,不要直接执行 ### 4.2 `--phase migrate-rootfs`(步骤 2) 执行环境: * **板载系统(eMMC)** 作用: * 把当前根系统复制到 `/ssd` * 写入目标系统 `fstab` * 安装并启用 `setssdroot.service`(下次启动会切到 NVMe root) * 在板载系统(eMMC)中禁用自动升级服务(在 setssdroot 配置完成之后执行) 成功后下一步: 1. `sudo reboot` 2. 启动后运行 `--phase configure-system` ### 4.3 `--phase bootstrap-ssd` 执行环境: * **板载系统(eMMC)** 作用: * 适用于“已经有可启动克隆 SSD”的机器 * 激活已有 LVM root(`/dev/ssd_vg/ssd_lv`) * 在板载系统(eMMC)上安装 `setssdroot.service` * 同步安装扩容脚本到 `/opt/lzc-jetson-installer/bin/lzc-expand-lvm.sh` * 在板载系统(eMMC)中禁用自动升级服务(同样在 setssdroot 配置后执行) 成功后下一步: * `sudo reboot` 注意: * 如果使用的是克隆盘,不要和原盘同时在线启动同一机器,避免 LVM UUID 冲突 ### 4.4 `--phase configure-system`(步骤 3) 前提: * 当前 `/` 已经是 NVMe LVM root(不是板载系统 eMMC) 执行环境: * **SSD 系统(NVMe)** 作用: * 禁用 SSD 系统(NVMe)当前系统自动升级服务 * 配置 swap * 配置 Docker * 配置 fan / bluetooth * 安装并启动 `lzc-ai-agent` 成功后下一步: * 正常使用即可 如果你要跳过 `lzc-ai-agent` 安装和启动: ```bash sudo DISABLE_LZC_AIAGENT=1 /opt/lzc-jetson-installer/startup_scripts/setup_ssd_docker.sh --phase configure-system ``` 如果你需要单独执行安装软件脚本并设置代理: ```bash sudo bash ./scripts/chroot_install_software.sh --http-proxy http://127.0.0.1:7890 --docker-proxy http://127.0.0.1:7890 ``` 也可以用环境变量写法: ```bash sudo HTTP_PROXY=http://127.0.0.1:7890 DOCKER_PROXY=http://127.0.0.1:7890 bash ./scripts/chroot_install_software.sh ``` ### 4.5 `--phase reset` 执行环境: * **从板载系统(eMMC)发起** 作用: * 停止相关服务 * 清空所有 NVMe(危险) * 重建步骤 1 和步骤 2 * 安排首次 NVMe 启动自动执行步骤 3 * 自动重启 * 基于当前板载系统(eMMC)重建(不是完整出厂重刷) 成功后下一步: * 脚本会自动重启 * 首次进入 NVMe 后自动执行步骤 3 * 完成后登录机器做验证 如果你确认要跳过交互确认: ```bash sudo ./lzc-jetson-installer.sh --phase reset --yes ``` 注意: * `reset` 会清空所有 NVMe 数据 * `reset` 是基于当前板载系统(eMMC)重建,不是完整系统重刷;如需完全重置请重新刷机 ### 4.6 `--phase all` 执行环境: * **通常从板载系统(eMMC)执行** 作用: * 顺序执行 `configure-disk + migrate-rootfs` * 如果当前已经是 NVMe root,继续执行 `configure-system` 注意: * 大部分首次安装场景仍然需要你在重启后执行一次 `configure-system`(或使用 `reset` 的自动链路) ## 5. 常见问题(按报错找解决) ### 5.1 报错:`No blank NVMe disk is available for initialization` 原因: * 所有 NVMe 都被识别为非空(有分区或签名) 处理: * 指定基盘并允许清理: ```bash sudo SETUP_SSD_BASE_DISK=/dev/nvme0n1 SETUP_SSD_ALLOW_REINIT_NONBLANK=1 ./lzc-jetson-installer.sh --phase configure-disk ``` ### 5.2 报错:`Current rootfs is not on NVMe LVM`(运行步骤 3 时) 原因: * 你还在板载系统(eMMC)里 处理: 1. `findmnt /` 看当前根是否 `ssd_vg/ssd_lv` 2. 不是的话先重启 3. 再执行 `configure-system` ### 5.3 插了第二块盘后空间没变 先确认: * 这块盘是否在初始化时已经在线且空白 * 不是的话手动扩容: ```bash sudo /opt/lzc-jetson-installer/bin/lzc-expand-lvm.sh --disk /dev/nvme1n1 ``` 如果目标盘不是空白盘,但你确认可以清理: ```bash sudo /opt/lzc-jetson-installer/bin/lzc-expand-lvm.sh --disk /dev/nvme1n1 --force-wipe sudo /opt/lzc-jetson-installer/bin/lzc-expand-lvm.sh --disk /dev/nvme1n1 --force-wipe --yes ``` 注意: * `--force-wipe` 会清理目标盘上的现有数据 * `--yes` 会跳过确认提示,适合无人值守场景,执行前请先确认盘符 ### 5.4 reset 后提示盘不是空白 原因: * 旧分区信息缓存未及时刷新(脚本已做刷新与重试) 处理: * 直接重试 `reset` * 或指定盘 + 允许重建 ### 5.5 UI 显示磁盘可用空间异常(多盘同一 LVM) 说明: * 后端已去重同一逻辑卷的 `fsavail` 统计,避免两块盘重复累计 ## 6. 验证系统 ```bash findmnt / lsblk sudo pvs sudo vgs sudo lvs systemctl status lzc-ai-agent --no-pager systemctl is-enabled setssdroot.service ``` 你应该看到: * `/` 在 `/dev/mapper/ssd_vg-ssd_lv` * `lzc-ai-agent` 正常启用并运行 * `setssdroot.service` 已启用 --- --- url: /aipod/aipod/firemare.md --- # 算力舱固件 在算力舱中有一个固件,负责和微服中进行通讯,固件的版本号和微服中的 `AI POD` 版本号保持一致。 固件更新有两种方式: * 在 AI 浏览器中会自动检测当前是否有可用的更新,如果有可用更新,将会弹出一个可以升级的弹窗。点击升级后也会将固件进行升级。 ![update tips](/assets/firemare-update.D0StsBzo.png) * 在算力舱的管理列表中,当有可用的更新后,也会有一个更新提示。 ![device list firemare update](/assets/device-list-firemare.CqlsinH-.png) --- --- url: /aipod/os/port-forward.md --- # 算力舱端口转发 在算力舱的系统中,默认具有一个 `lzc-ai-agent` 的 `systemd` 服务,这个服务负责拉取镜像、管理容器启动、还有和微服连接。 在一些情况下,您可以需要直接使用算力舱的 ip 进行返回一个里面 docker 部署的服务。但因为内部的服务都是没有暴露端口,所以您需要一个端口转发,将服务的端口暴露到算力舱的局域网地址中去。 0. 算力舱的 ip 地址可以在 `aipod` 设备管理列表中查看 ![copy-aipod-ip](/assets/copy-aipod-ip.CZxL-fUT.jpg) 1. 打开地址 `http://算力舱的ip:40793/ui` 地址。如果您是在算力舱上的浏览器访问,您可以 `http://127.0.0.1:40793/ui` 打开。并添加您需要转发的服务,比如 `bgem3vllm-ai` ![client-port-forward.jpg](/assets/client-port-forward.CLsbHSw5.jpg) 3. 测试 打开 `http://ip:端口`,比如我上面的是 `bgem3vllm-ai` 和 `5666` 端口. ![client-port-forward-test.jpg](/assets/client-port-forward-test.DM0Pk5TI.jpg) 或者直接在算力舱中进行访问. ``` > curl http://127.0.0.1:5666/v1/models ``` ::: info 注意: 目前不会自动拉起对应的服务,在处理中 ::: --- --- url: /aipod/trans/trans.md --- # 精准翻译(大语言模型) 算力舱中内置了一个优化过的高性能大语言模型,专门用于高质量翻译任务,能够提供准确、流畅、符合语境的翻译结果,特别适合对翻译质量要求较高的场景。微服中的应用可以通过算力舱暴露的 `trans` API 直接调用该服务。 ## 模型特点 * **高质量翻译**:基于大语言模型,提供准确且符合语境的翻译 * **多语言支持**:支持主流语言之间的相互翻译 * **灵活配置**:支持自定义翻译要求,如语言对、翻译风格等 * **兼容性强**:完全兼容 OpenAI API 格式,易于集成 * **高性能部署**:基于 vLLM 框架优化部署,响应速度快 * **优秀基础模型**:基于 Qwen3 模型,翻译任务效果优秀 ## 服务信息 * **API 地址**: * **OpenAI 兼容地址**: * **默认模型名**:`aipod-trans`(当前版本指向 Qwen3 模型,推荐使用此别名 `aipod-trans` 方便 API 调用和后续无感升级) * **响应模式**:支持流式和非流式响应 * **自定义配置**:支持通过 system prompt 指定翻译语言、风格等要求 ## 单独部署使用 1. 将下面内容保存到一个 `docker-compose.yml` 文件中 ```yml services: trans: image: registry.lazycat.cloud/x/lzc-aipod-trans:3480c9d ports: - 3000:3000 environment: - LANGUAGE=en_US:en - LANG=en_US.UTF-8 - LC_ALL=en_US.UTF-8 - VLLM_CONFIGURE_LOGGING=0 - VLLM_DO_NOT_TRACK=1 - VLLM_NO_USAGE_STATS=1 - MAX_MODEL_LEN=2048 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 10s timeout: 30s retries: 100 start_period: 1s ``` 2. 在当前目录运行 `docker-compose up -d` 启动 3. 启动后可以通过 `http://127.0.0.1:3000/v1/chat/completions` 调用 --- --- url: /aipod/os/network-config.md --- # 网络配置 算力舱的支持 wifi 和 有线连接,其中有线连接支持 `2.5G` 和 `10G` 网络。 ## 通过 `HDMI + 鼠标键盘` 配置 wifi 将显示屏连接到算力舱后,点击屏幕右上角的 wifi 图标进行连接。 ## 通过微服 `AI POD` 配置 wifi ::: warning 在算力舱中已经存在机器是不会被扫描到的,如果您想重新添加,您需要先在算力舱的管理列表中,将算力舱删除。 ::: 1. 打开 `AIPOD` 的配置界面,点击 `添加算力舱` 2. 确保手机打开蓝牙,并且已经授予 `懒猫微服` 客户端访问附近蓝牙设备的权限。 3. 查找设备,在找到的列表中,如果具有ip地址,则是通过网线找到的,否则就是蓝牙找到的。 ![scan device list](/assets/scan-devices-list.5EUJjVKd.jpg) 4. 选择您需要绑定的设备,点击 “开始绑定”。 5. 会弹出一个 wifi 选择列表,在这个列表中,您可以选择您的 wifi,填写 wifi 密码进行连接。 ![join wifi](/assets/join-wifi.CH4DrK92.jpg) --- --- url: /aipod/ai-browser/self-signed-cert.md --- # 自签名证书 在证书没有申请下来之前,可以先手动在浏览器中导入证书使用。 1. 在浏览器中打开 `chrome://certificate-manager/localcerts` ![./images/localcerts.jpg](/assets/localcerts.BLS1z_2S.jpg) 2. 下载证书 LazyCatCloud-CA.crt 并保存到本地 3. 回到 `chrome://certificate-manager/localcerts/usercerts`,在 `Trusted certificates` 中导入该证书 ![./images/usercerts.jpg](/assets/usercerts.BSbs2vY9.jpg) 4. 导入之后的结果 ![./images/usercerts-result.jpg](/assets/usercerts-result.CoxR13y6.jpg) --- --- url: /aipod/os/jtop.md --- # 资源占用 ## 在微服中查看算力舱的资源占用 1. 打开 AIPOD 应用 点击快速跳转 ![起动器](/assets/aipod-launcher.-kD8R7-R.png) 2. 点击右上角的设置按钮。 ![设置](/assets/aipod-home.DKlbcO9Y.png) 3. 点击算力舱管理列表中的查看详情。 ![查看详情](/assets/aipod-vram-detail.DKS7Ylzf.png) 4. 点击后会跳转到一个新的页面,里面展示您当前算力舱各个服务的占用显存。(注意:这里只会显示AI应用占用的,如果您手动部署的,将不会显示出来) ![显存列表](/assets/aipod-vram-list.DsIkTiEL.png) ## jtop 查看算力舱占用 1. 打开 jtop连接 ![jtop-ai](/assets/aipod-jtop.97yc_x6X.png) 其中底部的菜单栏是可以点击的或者按 `1, 2, 3, 4, 5, 6, 7` 进行切换。 ![jtop-ai-mem](/assets/aipod-jtop-mem.Bxm8G06C.png) 2. ssh 到算力舱后,执行 `jtop` 命令查看 ![jtop-ai-cpu](/assets/aipod-jtop-cpu.D5I9Y9LD.png) ## 算力舱中为什么不用使用 nvidia-smi? > It is not possible to use nvidia-smi on a Jetson. This program/utility requires a PCI bus. Jetsons have an integrated GPU (iGPU) which is wired directly to the memory controller. Only a discrete GPU (dGPU) on a PCI slot can use that utility. > 在Jetson上无法使用nvidia-smi。该程序/工具需要PCI总线。Jetson配备的集成显卡(iGPU)直接连接到内存控制器。只有安装在PCI插槽上的独立显卡(dGPU)才能使用该工具。 > An interesting proposal for NVIDIA: Make a surrogate nvidia-smi program which looks like the original nvidia-smi to programs performing a GPU query, but have it talk instead to the iGPU. Probably not practical, but if it worked, it would make the GPU much more flexible and interchangeable with other software. > 一个有趣的想法,针对NVIDIA:制作一个代理程序nvidia-smi,它在执行GPU查询的程序看来就像原始程序nvidia-smi,但实际上却与iGPU通信。这可能不切实际,但如果能实现,将使GPU在与其他软件的兼容性和灵活性上大大提升。 > EDIT (August, 2024): The above is still true, except that Orin is able to use L4T R36.x, and there is a partial surrogate nvidia-smi with this. This is an nvidia-smi with limited functionality which might suffice in some cases, but the iGPU is still an iGPU without any PCI bus function on Orin plus R36.x. > 编辑(2024年8月):上述内容仍然成立,只是Orin可以使用L4T R36.x,并且为此提供了一个部分替代方案nvidia-smi。这是一个nvidia-smi,功能有限,可能在某些情况下足够使用,但Orin加上R36.x的iGPU仍然没有PCI总线功能。 1. https://forums.developer.nvidia.com/t/nvidia-smi-not-present-in-jetson-linux/239757/2 --- --- url: /aipod/issues/traefik-gateway-timeout.md --- # 通过https访问服务超时 当您使用 `https://${服务名称}-ai.${微服名称}.heiyu.space` 提示 `Gateway timeout`,这个可能是您在 `docker-compose.yml` 中定义的服务,没有添加 `traefik` 的网卡,您可以在 `docker-compose.yml` 中添加默认的网卡,也可以只对其中暴露的服务指定网卡。 1. 默认网卡 ```yml networks: default: external: true name: traefik-shared-network ``` 2. 指定网卡 ```yml services: xxx: networks: - traefik-shared-network ``` --- --- url: /aipod/ai-browser/config-flags.md --- # 配置浏览器Flags ## 配置 Local Network Access Checks 在最新版的 `Chrome (142+)` 中可能会出现以下问题 * 网页总结的页面中,会出现 `Failed to fetch` 的错误 * 在开发者工具中控制台中可以看到 `...has been blocked by CORS policy: Permission was denied for this request to access the 'unknown' address space.` 1. 在 `Chrome` 中打开 `chrome://flags/#local-network-access-check` 2. 将配置项从 `Default` 更改成 `Disabled`,默认的选项为 `Blocking` ![local network access checks](/assets/config-local-access-network.DIKIIl8X.png) 相关连接: * https://chromestatus.com/feature/5152728072060928 * https://developer.chrome.com/blog/local-network-access ## 配置网络 1. 打开地址 `chrome://proxy` ![config network](/assets/config-network.Dwz38CV0.png) 2. 在底部的填写框中,将 `*.heiyu.space;*.lazycat.cloud;` 写进去,允许访问 `*.heiyu.space` 和 `*.lazycat.cloud` 地址。 3. 点击 `Apply` 按钮应用。 --- --- url: /aipod/os/firmware.md --- # 驱动 # Wifi网卡 AW-XB560NF ``` sudo modprobe cfg80211 sudo insmod 8852ce_20250707.ko mv rtl8852bu_config rtl8852bu_fw rtl8852cu_config rtl8852cu_fw /lib/firmware sudo rmmod rtk_btusb sudo insmod rtk_btusb.ko ```