# 视野后端配置 server: address: ":18080" openapiPath: "/api.json" # 请求体上限(默认 8MB):APK 上传(管理端下发新版本)可能数百 MB,必须放大 clientMaxBodySize: "512mb" database: default: type: sqlite # SQLite 数据文件(运行时数据,不提交 git) link: "sqlite::@file(./data/observer.db)" cache: # 查询缓存 TTL(秒) ttl: 30 auth: # 登录 token 签名密钥(HMAC-SHA256,必填):换值即全员下线 secret: "changeme-auth-secret" # 登录 token 有效期(秒),默认 30 天 tokenTtl: 2592000 admin: # 后台管理端静态 token:请求头 X-Admin-Token 必须匹配;为空时管理接口全部拒绝 token: "Tongli686^*^" app: # Android APK 上传目录(运行时数据,与 ./data 平级、docker-compose 挂载持久化; # 目录下永远只保留最新一个文件 observer-latest.apk,下载地址固定 /download/observer-latest.apk) apkDir: "./workspace" # 模型训练体系运行时数据根目录(图片/标注/模型/训练产物;与 apkDir 同目录时共用 workspace) datasetDir: "./workspace" # 图像生成(管理端「AI 生成图片」):provider 必填项缺失时生成接口返回「图像生成服务未配置」 imageGen: provider: localai # dashscope | localai baseUrl: "http://192.168.3.210:18080" # localai 必填:本机 local-ai 地址(/v1/images/generations) model: z-image-turbo # localai 上加载的模型名;dashscope 用 qwen-image-3.0 apiKey: "" # dashscope 必填:DashScope API Key poolSize: 1 # 生成任务并发度(z-image 显存独占,默认 1) # 通用提示词模板:{species} 取数据集 gen_species(单值,空回退数据集名);{count} 为数量词 # (固定 "1只",每张仅 1 个目标;原表单数量选择已取消);{scene}/{action}/{light} 池内随机 # (scene/action/occlusion 从数据集表 gen_scenes/gen_actions/gen_occlusions 池随机,无 config 兜底; # gen_scenes 条目兼容捆绑组对象 {scene,weight,actions,occlusions,position}——选中时动作/遮挡组内联动, # position 覆盖默认位置锚(树栖场景须把目标锚到枝头,默认地平线锚只适合地面/水面), # 见技术设计.md「场景捆绑组+权重」,斑鸠 80% 树上场景即此机制); # {tone} 取数据集表 gen_tone(默认深色,白化个体配浅色);{sizeHint} 按数据集表 gen_heights 站高与 # 表单距离算出像素高,按画面占比分级尺寸词(像素数与固定物体类比词均不通用,2026-08-31)+ 占比。物种只写名字 # 不写羽毛细节(细节会把镜头拉近,详见技术设计)。引导语「手机原相机主摄不变焦」(2026-08-31 v4 定案): # 与 13px 像素数学完全自洽(公式即按主摄 vfov 52° 计算);手机拍远处目标天然显小(先验同向); # 顺带对齐推理端域(App 就是手机拍摄)。演进:广角(前景大主体先验)→ 长焦(物理放大)→ 全删 → 手机主摄不变焦。 # 拍摄角度描述(仰/俯/平)仍不带;位置锚 {position}(默认「在{distanceWord}的地平线附近」,地平线=远景场景锚点, # 适合地面/水面;捆绑组 position 可覆盖为枝头/树冠锚——2026-09-04,树栖目标锚在地平线上语义落地); # 动作/遮挡在普通模板中位于目标之后(动作=叙事焦点=视觉焦点,降级处理) promptTemplate: "野外大远景实拍:手机原相机主摄不变焦,{scene},{position}有且仅有{count}{species}{sizeHint},{occlusion},{species}在{action},只是很小的{tone}轮廓,隐约可辨,画面绝大部分是{scene},没有任何靠近镜头的动物,{light},写实野外摄影,真实照片质感" # 远景小目标模板(代码按尺寸占比 <2% 切换):目标小到动作/遮挡/性别细节无法呈现,保留细节描写会 # 迫使模型把目标画大(2026-08-31 实测两次失败后定案)——只留「远到看不清任何细节的小点」 promptTemplateTiny: "野外大远景实拍:手机原相机主摄不变焦,{scene},{position}有且仅有{count}{species}{sizeHint},远到看不清任何细节,只是隐约可辨的{tone}小点,画面绝大部分是{scene},没有任何靠近镜头的动物,{light},写实野外摄影,真实照片质感" lights: [清晨柔和阳光, 正午明亮天光, 傍晚金色斜阳, 阴天柔和散射光] # 负样本生成(技术设计.md「负样本库」):场景池按「三物种各自易被误认的形状/机理」系统穷举 # (细长轮廓→雉鸡 / 圆拱形→野兔 / 高处停息悬挂→斑鸠 + 室内/户外空镜),模板与正样本同相机词 # 同质感词、不含任何目标词;{scene} 按序循环取池(保证每个短语均匀覆盖)。 # 混淆动物单独模板——否定句只排除目标物种(图里的猫狗麻雀正是要教的"非目标")。 # 生成后自动过 RF-DETR 全图检测,有检出即剔除不入库(空检合格才进负样本库) negativePromptTemplate: "写实摄影:手机原相机主摄不变焦,{scene},光线自然,画面中没有任何动物、飞鸟和人物,真实照片质感" negativeScenes: [ 靠墙立着的扫把和拖把, 门口的门框与墙角线, 桌角与椅腿的斜线构图, 靠墙的雨伞和登山杖, 花园里斜插的竹竿和植物支架, 生锈的铁栅栏立柱, 盘绕在地面上的花园水管, 靠墙放着的铝梯, 晾衣杆上挂着一排衣架的剪影, 斜靠在树干上的长树枝, 篮球架的立柱与篮筐, 路边的电线杆与斜拉钢缆, 路边圆圆的石头和土块, 老树桩与盘结的树根, 装满东西的麻袋编织袋, 门口系好的垃圾袋, 成排摆放的花盆与陶罐, 沙发上摞着的靠垫抱枕, 草地上的干草垛, 屋顶堆放的旧瓦片, 地上放着的双肩包, 花园角落的堆肥堆, 电线上的绝缘子和一串小灯泡, 缠挂在电线上的旧风筝, 屋檐下挂着的灯笼和玉米串, 挂满柿子的果树梢, 建筑外墙的空调外机与管线, 树枝上成簇的松果, 悬在树杈间的大草团, 路灯灯头与监控探头, 阳台上悬挂的衣物, 树杈间卡着的塑料袋, 客厅一角布艺沙发与茶几, 卧室床铺与床头柜, 厨房台面灶具与悬挂的锅铲, 卫生间瓷砖与毛巾架, 书桌电脑键盘与零散杂物, 车库里堆放的纸箱工具和靠墙的自行车, 楼道里的消防栓与管道, 阳台栏杆与洗衣盆, 餐厅餐桌餐椅与吊灯, 空旷的开阔草地, 茂密的灌丛坡地, 林地边缘的树木与草地交界, 积雪覆盖的开阔旷野, 河边大片卵石滩, 收割后的农田田埂, 茂密的芦苇丛, 城市人行道与斑马线, 露天停车场的车辆间距, 公园步道与路边长椅 ] negativeHumanPromptTemplate: "写实摄影:手机原相机主摄不变焦,{scene},光线自然,画面中没有雉鸡、野兔和斑鸠,真实照片质感" # 人物/衣物池(2026-09-07 用户定案):人与服饰永远不会是识别目标,可安全进统一负样本; # 动物(猫/狗/鸽子等)不进统一负样本——它们将来可能训练成正式识别目标,公共负样本会压杀自家模型。 # 此类带主体图不参与空检剔除(RF-DETR 检出人是预期,无法按物种甄别),靠提示词保证不含目标物种 negativeHumans: [ 远处田间弯腰劳作的农民, 公园小路上散步的行人, 石子路上迎面走来的游客, 蹲在田埂上休息的人, 骑电动车驶过乡间道路的人, 树丛边缘经过的穿迷彩服的人, 挂在晾衣绳上的外套和花色衣物, 阳台栏杆上晾晒的被褥和衣物, 坐在长椅上穿深色外套的人的背影, 草地远处玩耍的孩童身影 ] # 目标尺寸提示(sizeHint):按「体高 × focalPx ÷ 表单距离」算出目标在图中的像素高与占比, # 注入模板 {sizeHint}——把「厘米」换算成「米」再参与投影公式;focalPx = 图高/2 ÷ tan(vfov/2); # 站高取数据集表 gen_heights(创建数据集时 VLM 生成,界面不维护,代码/配置不硬编码) assumedVfovDeg: 52 # 镜头垂直视场角(度),固定为手机默认主摄(2026-08-28 用户定案; # 曾随机 [15,90]、曾试超广角 90,均已否决) # 训练通道(并发度 1:GPU 独占,同时仅一个 running 任务——2026-09-03 起忙时新任务落 queued 排队, # 轮询在 running 结束后自动按创建顺序晋级启动,一次一个): # mode=subprocess 训练机与服务器同机;mode=ssh 异机(训练脚本/数据集经 ssh 通道同步) training: mode: subprocess # subprocess | ssh(2026-09-09 本机 M1 训练:subprocess;正式训练机切回 ssh) ssh: # host: "192.168.3.210" # 训练机地址(mode=ssh 必填) host: "192.168.3.210" user: "root" port: 22 privateKeyPath: "" # 私钥路径与 password 二选一 password: "123" workdir: /Users/zhangbin/yolo-workdir # 训练机工作目录(train_server.py / yolov8s.pt / yolov8n.pt 所在) venvPython: /Users/zhangbin/yolo-venv/bin/python datasetDir: datasets # 训练机数据集根目录(相对 workdir,yolo/ 为子目录) timeoutMinutes: 0 # 训练超时判死(started_at 起算;0 = 不限时,2026-09-10 用户定案训练时长不受限。存活探测失败仍按进程退出判死) # 双档位(2026-09-03 用户定案):s 档(高识别,默认)与 n 档(高性能)各自基座/分辨率; # 一次「开始训练」按档位各建一条任务(请求 variants 可限定单档);epochs/batch/device 双档共用。 # n 档未配置(modelN/imgszN 缺一)时发起含 n 档的请求报错 model: yolov8s.pt # s 档训练基座权重(须已同步到训练机 workdir;s 容量适配小目标+遮挡,详见技术设计) imgsz: 1280 # s 档训练/导出分辨率(须与 App 端推理输入对齐;1280 提升远距离小目标保留像素) modelN: yolov8n.pt # n 档(高性能)训练基座权重(须已同步到训练机 workdir) imgszN: 704 # n 档训练/导出分辨率(704² 端侧计算量约 s@1280 的 1/8,速度优先) epochs: 150 # 训练轮数(patience 30 早停,设大可自动停) batch: 16 # 批大小(按训练机显存调整) device: "mps" # GPU 编号(cpu 用 cpu;M1 本机用 mps,训练机切回时改回 "0") # RF-DETR 预标注服务(管理端标注工作台):需从服务器可达,未配置时预标注接口返回「标注服务未配置」 # 2026-09-10 本机架构:检测=local-ai(18082, rfdetr-xlarge);VLM=omlx(18081, 多模态 MLX 模型) localAi: baseUrl: "http://127.0.0.1:18082" # local-ai(/v1/detection,LaunchAgent 自启 ~/localai/start.sh) model: rfdetr-xlarge vlmBaseUrl: "http://127.0.0.1:18081" # omlx(OpenAI 兼容 /v1/chat/completions;空则回落 baseUrl) vlmApiKey: "wenwu901" # omlx api_key(~/.omlx/settings.json auth 节点) vlmModel: Qwen3.5-9B-MLX-4bit # VLM 模型(omlx 多模态 MLX 4bit;备选 gemma-4-E4B-mlx-4bit,空闲 10 分钟自动卸载) threshold: 0.2 # 候选置信度下限:0.2 以下直接丢弃(不入库);0.08 实测过低——COCO 通用 rfdetr 对类鸟纹理在 0.08~0.2 区间输出大量噪声候选 confConfirmed: 0.5 # 置信度 ≥ 0.5 视为确认(class 0 真目标);0.2~0.49 落疑似(class 1)待人工复核(2026-09-02 RNPHE_257 实测调定:误检可达 0.46~0.5 且与真目标仅差 ~0.1,误检落 class 0 会污染训练正样本) overlapThreshold: 0.3 # 重叠去重阈值(交叠/较小框面积,NMS 风格;RF-DETR 同目标常输出一大一小两框,minIoU 比 IoU 更能命中) inputSize: 700 # 提交前整图等比缩放最长边;RF-DETR 模型包固定 700x700 输入(stretch 拉伸、不支持动态尺寸,见模型包 inference_config.json),提交更大最终也拉伸到 700 无增益,2026-09-01 实测回退 # 预标注四级漏斗(技术设计.md「预标注四级漏斗」):全图扫描空检 → 切片扫描 → VLM 提议候选区精修,键见下 tileThreshold: 0.12 # 切片级候选置信度下限:小目标(切片后 ~40px)置信度天然偏低,量级取 RF-DETR-S 实测经验;低于全图 threshold 的候选仍入库落疑似(class 1)待人工复核,宁多勿漏 tileSize: 1024 # 切片块长边(原图像素):缩至 inputSize 后约 0.68 倍,画面高 2% 的目标(~64px)保住 ~44px 可检,全图直缩只剩 ~15px;块过小则滑窗块数爆炸,1024 为精度/块数平衡 tileOverlap: 0.25 # 相邻块重叠比:跨块目标不因切缝丢失;跨块重复检出由 overlapThreshold(minIoU)去重 # 标注类别名(写入 data.yaml,随 result.json 存模型 labels)从数据集表取: # 第一类 = gen_species(空回退数据集名),第二类 = gen_classes(空回退 class0/class1) labelTask: # 预标注并发度(逐张调 RF-DETR,缺失或非法时回退默认值) poolSize: 4 annotateReward: # 标注众包时长激励(技术设计.md「App 标注众包与时长激励」;缺失/非法回退 consts 默认值) claimSize: 10 # 每次领取图片数 claimTimeoutHour: 2 # 领取锁超时(pending 超时未提交视为放弃,领取时惰性释放) rewardPerImages: 10 # 每累计提交 N 张发一次时长 rewardMinutes: 30 # 每次发放分钟数(license 到期时间分钟级顺延) dailyCapMinutes: 120 # 每日发放上限(自然日) freezeRatio: 0.8 # 通过比例(approved/(approved+rejected))低于该值触发冻结 freezeMinReviewed: 5 # 触发冻结判定的最小已审核样本数(防小样本误冻) freezeHours: 24 # 冻结时长(license.annotate_frozen_until 时间戳对比自动解冻,基线重置后统计重新累计) # 套餐定价(静态配置,改价 = 改本节点后重启服务;金额单位为分,展示名由 days 派生"N天") plans: - id: day days: 1 price_cents: 1000 - id: week days: 7 price_cents: 5600 - id: month days: 30 price_cents: 18000 payment: # 回调验签/IO 处理并发度(缺失或非法时回退默认值) poolSize: 16 wechat: # 微信支付 APP 支付(APIv3),商户号未配置时接口返回「支付未配置」 appid: "" # 开放平台 AppID mchid: "" # 商户号 apiV3Key: "" # APIv3 密钥 serialNo: "" # 商户证书序列号 privateKey: "" # 商户 API 私钥文件路径(PEM) notifyUrl: "" # 回调地址,如 https://api.example.com/api/v1/payment/wechat/notify alipay: # 支付宝 APP 支付,未配置时接口返回「支付未配置」 appid: "" # 开放平台 AppID privateKey: "" # 应用私钥文件路径(PEM) alipayPublicKey: "" # 支付宝公钥文件路径(PEM) sellerId: "" # 收款方 PID(回调校验 app_id/seller_id 用) notifyUrl: "" # 回调地址,如 https://api.example.com/api/v1/payment/alipay/notify