相机与几何查询:像素、射线和接触各问什么#
本课属于 A6,先修建模与坐标、传感与采样。固定 MuJoCo 3.15.0 源码;公式默认 m–kg–s、列向量。目标是分清模型相机、可视化相机、投影 sensor、ray sensor 和渲染深度的空间契约,避免把同名 depth 当作相同测量。
1. 相机有三个对象层次#
mjModel 中 camera 是 MJCF 编译结果,拥有 body/target/mode、pos/quat、fovy、projection、resolution、intrinsic 等参数;mjData.cam_xpos/cam_xmat 是该状态下的世界 pose。camera 可以随 body、跟踪 body/CoM 或朝向目标,不能永远把声明的局部 quaternion 当最终外参。物理位置计算更新这些 pose。
mjvCamera 是可视化的抽象选择器:FREE、TRACKING、FIXED、USER。FIXED 的 fixedcamid 选择模型 camera;FREE/可视化 TRACKING 在模型 camera 数组之外。mjvGLCamera 才是渲染低层 pose/frustum,场景内含左右眼两个,非立体模式会合成中间视点。因而“viewer 当前相机”和名为 top 的模型 camera 不必相同。模型字段、可视化相机层次。
MuJoCo camera 的 −Z 向前、+Y 向上、+X 向右;site rangefinder 却沿 site +Z。设 R 为 cam_xmat.reshape(3,3)、C 为 cam_xpos,世界点 P 的相机坐标为:
在前方需要 z>0。这是 MuJoCo 相机坐标,不能直接贴上视觉库的 +Z forward/+Y down 标签;转换外参时必须转换轴,而不只是转置矩阵。相机到世界是 R,世界到相机才是 Rᵀ。
2. 居中透视相机的像素与长度#
先限定 perspective、fovy、无显式 sensorsize/intrinsic、居中主点。W/H 是模型 camera.resolution,用于原生相机传感器。fovy 总以度给出,不受 compiler.angle 影响;投影像素原点在左上,u 向右、v 向下。方形像素下:
fx/fy 单位 pixel,X/z 无量纲;(col,row) 像素中心对应 (col+.5,row+.5)。反投影的未归一化局部方向是:
射线为 C+ρd_w,ρ 是距离/range;轴向深度 z=ρ·(−d_c,z)(此处 d_c 是归一化方向)。离光轴越远,相同 z 的 range 越大。对于此居中透视模型:
3. resolution、output、intrinsic 的不同职责#
参数/数组 |
本版意义 |
不能推出的结论 |
|---|---|---|
|
模型记录的像素分辨率;camprojection 和 camera rangefinder 使用它 |
不会自动把 |
|
支持图像类型的元数据位标志 |
不调用 renderer、不自动填 sensordata、不是后台采集任务 |
|
perspective 为度;orthographic 为全高长度 |
orthographic 的45默认长度通常不适合小场景 |
|
开启物理长度内参,fovy 被内参路径取代 |
长度单位不能混 mm 和 m |
|
两轴焦距,长度或 pixel |
像素值优先的具体编译处理见源码,不在读数时二次缩放 |
|
相对图像中心的偏移 |
不要直接把它当传统 K 的绝对 cx/cy |
|
classic offscreen framebuffer 容量 |
与 camera.resolution 是不同存储/生命周期 |
规范及默认值见 XML camera,原生编译见 camera intrinsic。渲染容量见下一课。
固定版本里三个相机路径不完全一致#
这不是可以忽略的“数值误差”:
camprojection 的 cam_project只用 focal/sensorsize 或 fovy,固定加 W/2,H/2;没有读 principal 偏移,也没有 projection 类型参数,因此不能视为任意正交/偏心相机的通用投影函数。
camera rangefinder 的 mju_camIntrinsics在显式 sensorsize 分支中,把 intrinsic[2:4] 缩放后直接作为 cx/cy 传给像素射线函数,而编译器储存的是主点偏移,未在此分支加 W/2,H/2。本课不替上游代码补这个中心偏移,也不声称它与渲染自动像素对齐。
orthographic rangefinder 的 mju_camPixelRay在两个轴都用
fovy/2乘归一化像素偏移;渲染 frustum 构造保留竖直 fovy,横向由视口比例决定。因此非方形视口也不应默认两条路径范围一致。
classic 渲染的 getFrustum实际使用 principal 构造左右/上下范围。应用若依赖相机间像素严格对应,需要明确选择路径、尺寸和内参,并在以后获得运行授权时专门核验。本文只确认上述源码分支,未运行成像标定,也没有给上游提交 bug 修复。
4. camprojection 与 rangefinder#
camprojection 输出指定 site 的2个像素坐标,POS阶段计算。它没有遮挡检测,不裁到图像矩形,镜头后方点也会被除法投影;z≈0 时还有防止极小除数的分支。任务中至少区分前方、画幅内、无遮挡三件事;2D 坐标落在图内只解决其中一项。文档和实现。
rangefinder 必须选择 site 或 camera。site只有一条+Z射线;camera 按 row 外循环、col 内循环排列 H×W 条。默认 data="dist",可按 dist dir origin point normal depth 固定顺序组合。每像素字段连续,sensor_dim=WH×字段总宽;site令WH=1。MJCF契约、运行分支、编译维度。
字段 |
宽度 / 单位 |
命中 |
未命中 |
|---|---|---|---|
dist |
1 / m |
从射线原点沿单位方向的距离 |
−1 |
dir |
3 / 无量纲 |
世界单位方向 |
(0,0,0),不能据此恢复未命中射线 |
origin |
3 / m |
世界射线原点 |
仍写真实原点 |
point |
3 / m |
世界交点 |
(0,0,0) |
normal |
3 / 无量纲 |
世界表面向外法向 |
(0,0,0) |
depth |
1 / m |
camera 为 |
−1 |
只有有效标记/距离判断后才能使用交点;零坐标本身不是无效哨兵,因为真实世界原点也可被击中。正交 camera 的各射线原点不同,不能从相机中心与交点直接求 dist。上述字段由 fill_raydata写入。
原生 rangefinder 对 geom 查询,并排除所属 body id 的 geom,不是整个机器人或整个 weld subtree。camera/site若直接属于 world,会排除 world 上的地面;将它装在明确的 mount body 上会改变这一筛选。rangefinder 传 geomgroup=NULL,所以 viewer 里关掉某一组显示不使该组从读数消失。透明排除按 material 存在与否选用其 alpha 或 geom alpha,不是两个 alpha 无条件相乘;详见下一节。
5. 直接几何查询的过滤和算法边界#
mj_ray(model,data,pnt,vec,geomgroup,flg_static,bodyexclude,geomid,normal) 在本版末尾可返回 geom id 与法向。输入点/方向在世界轴,返回射线参数 x 满足 pnt+x*vec。若 vec 不是单位向量,x 不是米制距离;接口只检查方向非零,不替调用者归一化。没有交点则返回−1、geomid−1。mj_multiRay 共享原点、批量方向并有 cutoff;正交相机因原点不同没有简单复用同一原点假设。签名、逐 geom 实现。
射线排除顺序是 body id、透明度、是否包含与世界焊接的静态体、可选 geomgroup。没有 material 时看 geom alpha==0,有 material 时看 material alpha==0。contype/conaffinity 是接触配对过滤,不能直接作为射线的可见性掩码。ray dispatch 对 mesh、hfield、SDF、primitive 分别查询;mesh 进入 ray tree,不等价于动力学凸包接触。
mj_geomDistance 则询问两个 geom 的 signed distance/见证点,输入 distmax 限制搜索范围,返回值可能被钳为 distmax;fromto 是世界轴两端点。它直接分派 collision function/native CCD,并不通过整套动态碰撞过滤,也不创建接触力。如果该配对没有实现函数,也可返回 distmax。因此 distance==distmax 不能无条件理解为真实距离恰好等于上限。接触力必须经过 E3 的装配/求解链。实现。
原生 distance/normal/fromto collision sensor 对 geom/body 组合做查询;body 枚举其直接附着 geom,不自动递归整个 subtree;cutoff 作为最大距离,不是一般 amplitude clamp。fromto 未找到有效更近结果时为0;normal 从见证点差得到,深穿透/退化下不宜把它当稳定 tracking feature。insidesite 还可查询点/对象位于 site 区域的关系,它不是渲染可见性或接触判据。collision sensor。
6. 深度图不是 rangefinder 的替代名字#
区别 |
原生 camera rangefinder |
classic Renderer depth |
|---|---|---|
生命周期 |
原生POS sensor,可受 history/disable/sleep 影响 |
场景更新及 render 时读取图像 |
shape |
sensordata平面块,按(H,W,fields)解释 |
(H,W) float32 |
空间几何 |
geom射线、所属body排除、原生ray过滤 |
visual scene,可包含装饰geom/site/视觉选项 |
裁剪与无命中 |
射线无命中−1,不使用经典渲染near/far可见区间 |
near/far裁剪;背景深度到far,不能按−1判断 |
距离定义 |
dist是射线距离,depth是相机轴向 |
wrapper返回轴向深度;低层readPixels先给归一化缓冲值 |
尺寸与内参 |
使用模型camera.resolution及前述版本分支 |
viewport/Renderer尺寸与实际frustum |
即使都选择 depth,过滤、时点、内参或装饰不同也会有差异;本课没有建立它们数值相等的回归结论。渲染课给出深度缓冲反变换。以后复用 DexLab 图像或读数时也须保留对应路径,而不是只记录“depth”。
7. 原创阅读片段和练习#
单状态观测片段使用居中透视相机,把 gyro/accelerometer、相机 rangefinder、camprojection 与三类渲染数组放在同一处读。它有意不引入采集器框架;本轮仅 AST/XML 结构检查,未做模型编译或成像。
相机局部点(1,0,−2),fx=100、W=200、H=100,居中透视投影是多少?range与depth是多少?
答:u=150、v=50;depth=2,range=√5。这里是连续像素坐标,不是取整后的数组索引。
把 camera quat 原样复制给 site,二者射线同向吗?
答:默认不,camera看−Z,site rangefinder射+Z。需要有意旋转,而非只改类型名。
camprojection在画面中央能否断言无遮挡?
答:不能;它不做遮挡测试,背后点也能投影。还需前方深度判断、边界判断及适当几何/渲染可见性查询。
camera.resolution=640×480,Renderer(height=240,width=320),哪幅是640×480?
答:原生camera rangefinder按模型640×480;Renderer输出240×320。camera.output不改变任何一个分配。
world camera照向world plane,图像看得到地面而rangefinder为−1,一定是故障吗?
答:不是必然,rangefinder排除所属body的geom,world plane被排除。还要检查其他过滤与射线方向。
ray vec=(0,0,2),返回x=3;路程是3 m吗?
答:不是,在一致单位下路程为x‖vec‖=6 m。ray参数只有单位方向时才直接是距离。
显式principalpixel=0时能否用camprojection确认所有渲染/range像素对齐?
答:不能。固定版本三个路径对主点及正交投影的分支不同;应先限定居中fovy透视路径和尺寸,运行对齐验证仍未进行。
geomDistance返回distmax且fromto全零,是否能把0作为最近接触点?
答:不能;它可能表示超过上限或该配对缺少算法。零fromto不是有力接触记录,也没有经过求解器。