Files
ykcheungab 457918434a 移除无效拓扑字段并精简设计参数说明
删除未被读取的 expected 配置,避免误认为拓扑已受保护。配置检查、manifest 测试及文档检查通过。
2026-10-07 16:04:31 +08:00

22 KiB
Raw Permalink Blame History

design.yaml 参数说明

本文是 sensors/<sensor>/r01/design.yaml 的详细说明,主要面向传感器研发工程师。

本文重点解释两个命令:

  • texture_patch:在普通 UV 展开图上生成一整张标记图案;
  • texture_patch_views:把同一套图案拆成多个相机投影视图,并决定每个视图负责印刷哪一部分。

例子:orisys-design texture-views .\sensors\finger\r01 --grayscale

1. 总览

传感器设计工具依赖开源3维绘图软件blender以及自带的Python API 开发,需要传感器3维CAD模型、镜头FOV参数、cmos的尺寸。目前主要用于摄像头位置和标记点的设计,后续会加入光源优化。

摄像头位置的设计主要是确保在镜头FOV限制下,可以完全覆盖传感的表面,分为感知面积最大化和死区清除两部分。

标记点设计目的是:

  • 算法上要导出unwarp conformal的平面图,把3维变形映射到2维计算同时降低畸变。
  • 生产上要在不可展曲面上印刷/雕刻标记点,程序需要导出曲面在加工平面上投影的平面图,格式包括 PNG、SVG 或 PDF。

导出的图片类型一览:

Blender 三维传感表面 (sensing surface)
         ├─ `export` conformal UV 展开                           → 计算用
         │
         ├─ `texture` 单相机投影(projected from view) UV 展开   → 生产用,一张图覆盖整个表面
         │
         └─ `texture-views` 多个相机投影视图 + 分割               → 生产用,每张图只印自己负责的区域

设计参数放置在sensors/<sensor>/r01/design.yaml里面,具体设计资源参考现有传感器的文件树。

1.1 基础原理

叠动视触觉传感器基于单色光技术,标记图案要具备高密度纹理信息,用于光流追踪,目前采用随机彩色方格矩阵纹理设计和UV打印生产,对于平面传感器可以简单密铺,对于Finger曲面传感器需要考虑曲面畸变、镜头透视误差、可视区域、可生产性等等,要在3维层面进行设计,与三维绘图的呈现方式类似。考虑到代码兼容性、价格、功能等因素,采用开源的三维绘图软件Blender和配套的python进行程序开发,可以从相机、模型、图案、光源4个部分独立和耦合设计。

设计流程是:1. 结构传感器曲面设计;2.模型导入Blender摄像头位置摆放;3.曲面不可视区域去除;4.结构曲面修改;5.模型导入Blender和标记图案设计;6.生产摄像头位置摆放和图案生产文件导出。

曲面结构除了标品外,要根据客户灵巧手可释放的空间来设计,但一般有一个非触觉版本的模型,可以作为基准来修改,考虑塑料壳、硅胶层的厚度,得出一个初步的感知曲面形状和位置。然后导入Blender进入详细设计,在结构和摄像头FOV的约束下,选择合适的摄像头摆放位置来达到可视区域的最大化,再根据摄像头FOV去除曲面的不可视区域,然后反馈结构进行修改。最后根据修改后的摄像头位置、曲面结构,生成计算用和生产用标记点图案。

计算用标记点图案作用是把在三维空间的曲面投影到二维的平面上,方便通过过去用于平面的算法计算,投影方法目前采用conformal投影,未来可以尝试其他方法。三维到二维的投影通过Blender里面在实际摄像头位置的透视投影相机,和感知曲面的模型获取,并通过export指令导出图案和投影矩阵。生产用图案是通过Blender的正交投影相机模拟在传感器治具正上方,由加工设备(UV打印机或激光雕刻机)看到的形状/投影,从而把我们在曲面上的目标图案,从三维空间投影到生产的二维平面上,支持多角度生产和重复部分去除功能。

通过Bleder Python API,在具备视触觉传感器结构和材料特性下可以进行物理准确的仿真,是本设计和模拟工具的核心支撑。

1.2 Blender 使用

以下文档基于Blender 5.0 版本 (5.0.1)编写,如非必要不进行版本更新。

blender_layout

Blender的资源树在右上方,不同集合(Collection)分别代表不同的资源:

集合 种类 作用 导出
ASSEMBLY mesh、摄像头 外壳模型、生产视角摄像头 所有网格物体,cam_uv_x
EXCLUDE 非导出物体 文件暂存 无
EXPORT_COMPUTE mesh 感知曲面 sensing_surface
FRAMES empty 原点定义 origin_sensor
DESIGN_SIM 摄像头、光源 视场设计仿真 cam_sensor

下方文档是AI生成,待整理


1.1 初学者需要知道的几个词

术语 简单解释
UV 把三维表面的点映射到二维平面的坐标。U、V 类似图像中的 x、y
UV 展开(unwrap) 像展开纸盒一样,把弯曲表面摊到二维平面;允许人为安排接缝和布局
Project from View (Bounds) 从指定相机看向物体,把画面投影到 UV,并缩放到 [0,1]²
pattern camera 定义标记图案从哪个方向投到表面,即“图案来源”
region camera 判断一个区域由哪个印刷视图负责,即“区域分工”
ownership 某个三角形最终归哪一个视图打印
hole 没有任何视图负责的表面区域,会形成漏印或“秃斑”
seam Blender 中标记的接缝,用于把表面分区
page 输出 SVG/PDF 对应的实际物理尺寸,单位为毫米
raster PNG 的像素尺寸,例如 3700 × 3280 px

1.2 阅读参数时遵守三条规则

  1. 每一个测量值都必须说明失效条件。 例如相机移动后,以旧相机位置测出的覆盖率就不能 继续使用。
  2. 测量条件是结果的一部分。 涉及区域相机时,必须同时记录相机位姿,因为位姿就是算法 输入。
  3. 文件哈希只代表测量当时的文件。 geometry.blend 会持续修改。引用旧哈希前必须重新 计算,不能把历史哈希当成当前状态。

本文最重要的目的,是防止一个过期数值继续显得“很合理”。只把说明写在 YAML 注释里,没有 自动失败路径;参数失效后程序仍可能正常运行,却生成错误结果。因此本文会明确写出每个关键 数值的来源、失效条件和重测方法。

2. 两种纹理命令的区别

项目 texture texture-views
YAML 配置块 texture_patch texture_patch_views
输出定义 uv_outputs:名称到文件名的映射 pairs[]:视图 ID、相机、UV map
二维布局 普通 UV 展开,不是相机投影 由相机拟合到边界的投影视图
物理页面 fixed:读取 texture_patch.vector 中声明的毫米尺寸 auto:从相机图像平面推导,并验证它确实是投影视图
区域归属 不需要,一张图覆盖整个表面 必须,每个 pair 都包含完整表面,但只打印归自己负责的部分
任务格式 texture-job-v3 texture-job-multiview-v1
Blender 驱动 blender/scripts/random_patch_dd02c.py blender/scripts/random_patch_multiview.py

texture_patch_views 会从 texture_patch 继承以下“图案本身”的设置:

  • 栅格尺寸 width、height;
  • 网格数量 nx、ny;
  • 随机种子 seed;
  • 固定页面备用值 vector;
  • 颜色配置;
  • edge_sample_grid。

代码入口是 texture_views.resolve_views_settings(...),内部调用 resolve_texture_settings(...)。它不会继承 uv_outputs、merge、seam 或 print, 因为这些字段分别属于单图输出或多视图分区。

继承方向只有一个:texture_patch 是页面、颜色和图案随机性的基础配置; texture_patch_views 在此基础上增加投影视图和区域分工。

为什么不合并两个配置块?在 texture_patch 中,页面是人工声明的输入,例如 18.5 × 16.4 mm;在 texture_patch_views 中,页面是经过验证后从相机投影推导出的 结果。合并后,page.mode 的含义会依赖旁边是否出现某个键,还会削弱当前严格的未知键 检查,使拼错的配置可能被静默忽略。如果未来出现第三个消费者,或者 uv_1、uv_2 不再是边界投影,再重新评估公共配置结构。

3. texture_patch:在普通 UV 展开图上生成图案

命令会为 uv_outputs 中的每个 UV map 各生成一套 PNG、SVG 和 PDF。所有输出共用相同的 随机颜色和页面设置,仅 UV 布局不同。

UV 展开必须预先在 Blender GUI 中完成并保存在 source_blend。无界面运行只读取已有 UV map,不会自动创建或重新展开 UV。

3.1 uv_outputs 的顺序不能随意调整

程序通过下面的方式构造输出列表:

block["uv_outputs"].items()

因此 YAML mapping 的顺序会进入 texture-job-v3 的 outputs[]。目前四个测试固定要求顺序 为:

["produce", "compute"]

仅从生成像素来看,调换顺序不会改变颜色,因为 patch_colors 在遍历各 UV 前只计算一次。 但任务文件数组顺序会改变并导致测试失败,所以不要为了“整理”而重排。

3.2 为什么有的配置是 nx == ny,有的是 80 × 45

nx、ny 表示相机空间中的图案网格数量。finger-xynova/r01 的 cam_sensor 是正方形 画幅,所以 nx == ny 可以让相机空间中的单元接近正方形。

finger/r01 使用 80 × 45,来自 16:9 相机。这个比例对它自己的相机是合理的,不能因为 另一个型号采用方形网格就统一修改。

3.3 width、height 与 vector 的关系

  • width、height:PNG 栅格尺寸,单位是像素;
  • vector.width_mm、vector.height_mm:SVG/PDF 对应的物理页面尺寸,单位是毫米。

当前两个 revision 都使用 200 px/mm:

3280 / 16.4 = 200
3200 / 16.0 = 200
3700 / 18.5 = 200

维护时应满足:

raster pixels = px_per_mm × physical millimetres

_warn_if_page_aspect_differs 会比较栅格长宽比与物理页面长宽比。如果不一致,SVG/PDF 填满页面时会沿某一方向被拉伸。保持上述关系,警告就不会出现。

3.4 seed 是整个物理图案的随机种子

固定 seed 是为了让同一配置反复运行得到相同的图案。同一次生成中的 PNG、SVG、PDF 和 所有 UV map 共用一套颜色。

texture-views 里的每个 pair 也必须使用相同 seed,因为它们打印的是同一个物理表面的 不同部分。如果每个视图使用 seed + i,两个印刷文件会在同一个表面上产生不同图案。

已知文档缺陷:orisys-design texture-views --seed 的帮助文字声称“第 i 个 pair 使用 seed + i”,但实际代码让所有 pair 使用 settings.seed。实际代码行为是正确的,CLI 帮助文字需要另行修正。

4. texture_patch_views:多个投影视图和区域分工

本节说明每个键的语义。为什么这样设计——为什么所有权在生成之前决定、为什么单位是三角形、 以及两种 seam 模式各自的适用条件——见 D:\orisys_ws\docs\workflow\multiview-ownership-decision.md。测量数据(拓扑、相机、入射角、 cell pitch)见 geometry-measurements.md。

这个流程较容易混淆,因为它同时使用三类信息。

4.1 三种角色

  1. 图案相机 pattern_camera:决定标记图案从哪个方向投到传感表面。所有视图共用一个 图案来源,默认是 cam_sensor。
  2. 区域相机 pairs[].camera:决定该视图适合负责哪些三角形。它只参与区域分工,不是 图案来源。
  3. pairs[].uv_map:决定该视图最终写入哪个二维布局。

可以把它们理解成:pattern camera 决定“印什么”,region camera 决定“谁来印”,UV map 决定“印刷文件中的坐标”。区分这三者,也能解释为什么片段数量和三角形数量不是同一个概念。

4.2 merge.incidence_limit_deg

区域归属以 loop triangle 为单位。一个三角形只有同时满足以下条件,才可能归某个视图负责:

  • 区域相机能看到它;
  • 相机视线与表面法向的夹角不超过 incidence_limit_deg;
  • 投影面积达到要求。

没有任何视图能够负责的三角形就是 hole,会造成漏印。

在 finger-xynova/r01 上,使用真正的 assign_ownership 和本次运行生成的 ground truth, 测得:

incidence_limit_deg hole fraction 相对 max_hole_fraction: 0.02
62° 0.0480 失败
65° 0.0130 通过
70° 0.0001 通过

这里 min_triangle_coverage 不是限制因素:把它从 0.60 放宽到 0,在 62° 和 70° 下都不改变 结果。

这组测量对应以下区域相机位姿:

相机 方位角 仰角 距离
cam_uv_1 308.26° −24.46° 32.62 mm
cam_uv_2 231.74° −24.46° 32.62 mm

只要移动区域相机,这张表就立即失效。 incidence_limit_deg 描述的是“特定相机位姿与 特定表面”的组合,不是传感器型号自身永远不变的属性。重新调整相机后必须重跑扫描。

已撤回的旧结论:早期 design.yaml 曾写过“默认 62° 时 hole 面积为 0.100 mm²、比例 0.0002”,以及“4960/5007 个三角形有归属,输出 20454 + 21483 个片段”。这些数据在 相机移动前成立,现在已经失效。保留这段记录,是为了说明为什么相机相关数据必须写明 重测触发条件。

不要删除 YAML 中显式设置的 incidence_limit_deg: 70.0。代码中 texture_views.MERGE_DEFAULTS 和 blender/scripts/texture_merge.py 的默认值都是 62.0, 并有测试要求两处一致。但对当前 Xynova 表面,62° 会产生 4.8% hole;删除覆盖值会静默恢复 错误默认值。

4.3 max_hole_fraction 是阻断门槛

当 hole fraction 超过 max_hole_fraction 时,命令会失败并报告:

  • 实际 hole fraction;
  • 未覆盖的三角形数量;
  • 可能原因。

它不是普通警告。这项检查是在一次 4.8% 漏印被静默输出之后增加的,目的是让“表面有秃斑” 从肉眼发现的问题变成自动失败。

4.4 page.mode: auto 与 fixed

auto 只适合使用 Blender Project from View (Bounds) 生成的布局。这种布局把相机图像 平面映射到 [0,1]²,程序可通过最小二乘仿射拟合反推出页面的实际尺寸。

程序不会因为写了 auto 就盲目信任 UV。它会验证:

  • 最大拟合残差不超过 1e-5;
  • UV 边界位于 [0,1]²。

真实投影视图的拟合误差大约为 1.2e-7;普通 UV 展开的误差约为 0.17–0.70,因此会被拒绝。 auto 不会在失败时悄悄退回固定页面。

fixed 使用 texture_patch.vector 中的页面尺寸。produce 和 compute 是真正的普通 UV 展开,必须使用 fixed;从相机反推它们的页面没有物理意义。

自动页面最终按整数像素确定:

page size = rounded raster pixels / px_per_mm

这样页面和栅格的长宽比天然一致。200 px/mm 下,舍入导致的尺寸误差最多为 0.0025 mm, 远低于当前 0.1 mm 对位公差。

已经测得的页面尺寸:

revision view 对应测量位姿下的页面
finger/r01 uv_1 17.510 × 15.975 mm
finger/r01 uv_2 17.510 × 15.980 mm
finger-xynova/r01 uv_1 / uv_2 17.670 × 23.565 mm

这些数值会随相机位置改变,正是使用 auto 的原因。Xynova 相机重新定位后,页面从 21.200 × 23.200 mm 自动更新为 17.670 × 23.565 mm。引入自动推导前,固定共享页面在 finger/r01 上误差为 6.9%,在 finger-xynova/r01 上误差达到 23%,而且横竖方向相反。

4.5 seam.boundary、pins 与 gap

boundary: use_seam 表示使用 Blender 中人工标记的 seam 作为区域边界。图案生成阶段不会 读取 seam;patch_projection.py 只读取 loop_triangles 和 uv_layers。seam 由后续 ownership 步骤使用。

pins 用于在自动分配不符合设计意图时,强制某个连通区域归指定视图。默认留空,让覆盖率 指标决定归属。每次增加 pin 都应写清原因,避免把几何或相机错误隐藏成“人工设置”。

如果启用 gap,总间隙宽度是 alignment_tolerance_mm 的两倍。原因是两次独立印刷都可能 产生最大对位误差;每一侧退让一个公差,最坏情况下仍不会重叠。

  • apply: post_process:将 UV 多边形裁剪到 seam 偏移后的半平面,生成物理宽度准确的直边;
  • apply: geometry_trim:丢弃整个片段,会产生锯齿边,仅保留作比较。

当前仍可看到的阶梯形状来自按三角形分配的 ownership 边界,而不是 gap 自身。gap 只是 从这条阶梯边界向内退让。要得到平滑边界,需要支持三角形内部的更细粒度分区。

4.6 print.base_fill

base_fill: owned 会把本视图负责的三角形填成白色,其余区域填黑。这样白色底层也已经沿 seam 分开,两张印刷文件不会在同一位置重复打印白底。

5. 常用参数速查

参数 当前值 来源/测量条件 什么时候失效 怎样重测
Xynova incidence_limit_deg 70.0 第 4.2 节记录的相机位姿 移动区域相机或修改表面 对新 ground truth 运行 assign_ownership,扫描 62/65/70/75°
自动页面 见第 4.4 节 对应区域相机位姿 移动相机或修改网格 重跑 texture-views,读取 _print_views.json
px_per_mm 200 设计选择 修改栅格或页面尺寸 计算 width / vector.width_mm 和 height / vector.height_mm
alignment_tolerance_mm 0.1 mm 当前印刷与对位能力假设 更换打印、夹具或对位工艺 通过实际计量重新评估,不由本仓库推断

内置默认值不适合当前表面的键

键 内置默认值 为什么必须保留在 YAML 中
Xynova incidence_limit_deg 62.0 当前表面会产生 4.8% hole;显式 70.0 是必要配置

6. 已知几何漂移

仓库中的历史资料对 Xynova sensing surface 的描述互不一致:

来源 记录值
probe-pipeline.md 2508 vertices / 4834 polygons
xynova-texture-investigation.md 4818 triangles + 16 quads
当前实际测量 2584 vertices / 5007 triangles

build/probe/finger-xynova/r01/probe_frames.npz 来自 2508 顶点的 sensing_surface_918_s,而当前带 seam、uv_1 和 uv_2 的表面有 2584 顶点。因此 tests/test_render.py::test_end_to_end_render_manifest 会在顶点数量检查处失败。

这是过期 fixture,不是纹理生成错误。修复方式需要先决定哪个表面才是该 revision 的唯一权威 表面,然后选择其中一种方案:

  1. 为当前权威表面重新生成 probe frames;或
  2. 把 seam 和 UV 布局制作到已经生成 probe frames 的那个表面上。

当前 revision 包含六个 sensing-surface 变体:

sensing_surface
sensing_surface_0
sensing_surface_0918
sensing_surface_0928
sensing_surface_45
sensing_surface_918_s

其中 _0928 与 sensing_surface 位于相同位置,是重合副本。EXCLUDE collection 的 exclude=False 表明它只是名称叫 EXCLUDE,并没有在 view layer 中真正排除。射线检测会先 击中这个副本,随后把所有片段判为不可用。

目前 _0928 已隐藏;texture-views 现在也会在发生遮挡时失败,并指出拦截射线的对象, 不会继续生成空白纹理。

7. 两个 revision 的差异

项目 finger/r01 finger-xynova/r01
栅格尺寸 3280 × 3200 3700 × 3280
固定页面 16.4 × 16.0 mm 18.5 × 16.4 mm
nx, ny 80, 45(16:9 相机) 80, 80(方形相机)
seam 边数 32 45
自身 hole 0.049 mm²(0.0001) 0.092 mm²(0.00015)
incidence_limit_deg 62.0;对应旧相机位姿,移动后必须重测 70.0
optics 配置 无 有

不能为了让两个 revision 的 YAML 看起来一致,就统一这些值。分辨率、页面、网格比例、相机 位姿和入射角门槛都是各自设计与测量条件的一部分。

8. 新同事修改参数时的建议步骤

  1. 先确认正在修改哪个 sensor 和 revision,不要把 finger 的参数复制到 finger-xynova 后直接使用。
  2. 判断改动属于“图案”“UV 布局”“相机位姿”“印刷工艺”还是“几何”。
  3. 根据第 5 节查找被改动失效的测量值。
  4. 如果移动区域相机,重测 ownership、hole fraction 和自动页面。
  5. 如果修改网格,核对拓扑、UV、seam、probe fixture 和 field-domain 相关输出。
  6. 如果修改栅格或物理页面,重新计算两个方向的 px_per_mm,不能只检查宽度。
  7. 运行对应测试并查看生成任务中的 manifest,不要只凭最终图片看起来正常。
  8. 将新数值、测量条件、结果和下次失效条件一起更新到本文。

遇到结果异常时,可以按以下顺序排查:

输出为空
  → 是否有重合/遮挡对象
  → region camera 是否能看到表面
  → UV map 名称和对象是否正确

表面有漏印
  → 查看 hole fraction
  → 检查 incidence_limit_deg
  → 检查相机位姿和 footprint 条件

页面比例不对
  → 确认 UV 是 unwrap 还是 Project from View (Bounds)
  → unwrap 使用 fixed;投影视图才使用 auto
  → 检查 raster / px_per_mm / page 的关系

接缝呈阶梯形
  → 先区分 ownership 边界与 gap
  → gap 使用 post_process 只能让退让边变直
  → 平滑 ownership 需要更细的分区算法

最后,不要删除“已知缺陷”“撤回结论”和失效条件。这些内容不是历史包袱,而是防止同类问题 再次以合理外观进入生产输出的工程约束。