|
Download RealSenseD400/src/SPECS.md from luckyrobots/ContentVault: direct link, hf CLI and curl.
- Browser
- Download file 40.6 kB
-
https://huggingface.co/luckyrobots/ContentVault/resolve/main/RealSenseD400/src/SPECS.md
- Command line
-
hf download hf://luckyrobots/ContentVault/RealSenseD400/src/SPECS.md
-
curl -L -o SPECS.md https://huggingface.co/luckyrobots/ContentVault/resolve/main/RealSenseD400/src/SPECS.md
40.6 kB
| # RealSense D400 series β the numbers this pod graph is built from | |
| Every value in `realsense_d400.hpodgraph` comes from one of the four sources below. | |
| Nothing here is estimated from a photograph or inferred from a rendering. Where a | |
| number is **not published**, it says so and states exactly what was assumed instead. | |
| | Tag | Source | | |
| |-----|--------| | |
| | **DS** | *RealSense Product Family D400 Series Datasheet*, doc 337029-017, rev 021 (Oct 2025) and the Mar 2026 revision (which adds D436). | | |
| | **URDF** | `realsense2_description` xacro macros, `IntelRealSense/realsense-ros`, branch `ros2-development` β the vendor's own published frame tree. | | |
| | **D421** | *RealSense Depth Module D421 Datasheet v1.2*. | | |
| | **D555 / D457** | *D555 PoE Datasheet v1.1*, *RealSense D457 Datasheet Dec 2025*. | | |
| | **ASSUMED** | Not published by any of the above. The assumption is stated inline and the graph exposes the value as an editable parameter. | | |
| --- | |
| ## 1. Frames and axes | |
| ### Vendor frame (URDF) | |
| The vendor publishes two datums per camera: | |
| - `bottom_screw_frame` β the centre of the **1/4-20 UNC tripod hole on the bottom face**. | |
| This is the physical mounting datum. | |
| - `camera_link` β the **depth origin**, which the datasheet defines (Β§4.8.1) as | |
| *"the X-Y center of the left imager"*, at the Z plane where depth reads zero. | |
| URDF body axes are ROS: **+X forward (out of the lens), +Y left, +Z up**. | |
| Optical frames add `rpy = (-Ο/2, 0, -Ο/2)`, giving the usual optical convention | |
| (+Z forward, +X right, +Y down). | |
| ### Graph authoring frame | |
| The graph is authored **Z-up**, converted once by a single `SpaceTransform z_up β y_up` | |
| immediately before `AssetOutput` (pod is Y-up native; LuckyEngine is Y-up). | |
| Authoring axes, Z-up: | |
| | Axis | Meaning | | |
| |------|---------| | |
| | **+X** | the camera's **right** (so the right IR imager is at +X) | | |
| | **+Y** | the direction the camera **looks** (optical axis, out of the lens) | | |
| | **+Z** | **up** | | |
| `SpaceTransform z_up β y_up` is a β90Β° rotation about X, i.e. `(x, y, z) β (x, z, βy)`. | |
| Applied to the axes above: | |
| | Authored (Z-up) | After transform (engine, Y-up) | | |
| |---|---| | |
| | camera right `+X` | `+X` β engine right | | |
| | camera up `+Z` | `+Y` β engine up | | |
| | camera look `+Y` | `βZ` β **the direction a LuckyEngine camera looks** | | |
| That last row is the load-bearing one. `SceneCamera` builds its projection with | |
| `glm::perspectiveFov` (`Hazel/src/Hazel/Renderer/Camera.h`), which is right-handed | |
| looking down **βZ** with +Y up. So an optical frame authored this way needs | |
| **no rotation at all** β a `CameraComponent` sitting on it with an identity local | |
| rotation points out of the lens. | |
| > Do not "fix" the optical frames by adding a 180Β° yaw. If a camera looks backwards, | |
| > the `SpaceTransform` has been changed or removed β fix that instead. | |
| ### Conversion used to derive the tables below | |
| URDF ROS `(x, y, z)` β authored Z-up `(x', y', z')`: | |
| ``` | |
| x' = -y (ROS +Y is left; authored +X is right) | |
| y' = x (ROS +X is forward; authored +Y is the look direction) | |
| z' = z | |
| ``` | |
| --- | |
| ## 2. Optical module properties | |
| | Module | Baseline | Depth FOV HD 16:9 | Depth FOV VGA 4:3 | IR projector FOV | Source | | |
| |---|---|---|---|---|---| | |
| | D401 | 18 mm | H 84 / V 58 / D 92 | β | none (passive) | DS 3-49 | | |
| | D415 | 55 mm | H 65 / V 40 / D 72 | H 50 / V 40 / D 61 | H 67 / V 41 / D 75 | DS 3-49 | | |
| | D430 | 50 mm | H 87 / V 58 / D 95 | H 75 / V 62 / D 89 | H 90 / V 63 / D 99 | DS 3-49 | | |
| | D450 | 95 mm | H 87 / V 58 / D 95 | H 75 / V 62 / D 89 | H 90 / V 63 / D 99 | DS 3-49 | | |
| | D421 | 50 mm | H 75 / V 50 (Β±3) | β | yes | D421 v1.2 | | |
| FOV tolerance is Β±3Β° lens-to-lens (DS 3-49 note 3). Depth FOV is quoted at 2 m | |
| (20 cm for the D401). | |
| ## 3. Colour sensor properties | |
| | Sensor | Used by | Colour FOV | Shutter | Native | | |
| |---|---|---|---|---| | |
| | OV2740 | D415, D435, D435i, D435f, D435if | H 69 / V 42 / D 77 | rolling | 1920Γ1080 | | |
| | OV9782 | D436, D455, D455f, D456, D457, D555 | H 90 / V 65 / D 98 | global | 1280Γ800 | | |
| | (left imager via ISP) | D405 | H 84 / V 58 / D 92 | global | β | | |
| D405 has no separate colour sensor: its colour stream is the **left imager through the | |
| ISP** (DS table 4-4, *"Color Stream from Left RGB via ISP"*), which is why its colour | |
| and depth origins are the same point. | |
| ## 4. Per-model geometry | |
| All offsets are **authored Z-up millimetres from the 1/4-20 tripod-hole centre** unless | |
| the datum column says otherwise. `leftIR.x` is negative because the left imager is to | |
| the camera's left. | |
| | Model | Housing WΓDΓH (mm) | Datum | leftIR x | rightIR x | RGB x | optical y (fwd) | optical z | IMU | | |
| |---|---|---|---|---|---|---|---|---| | |
| | D405 | 42 Γ 23 Γ 42 | 1/4-20 | β9.0 | +9.0 | β9.0 (same as leftIR) | 10.85 | 21.0 | β | | |
| | D415 | 99 Γ 20 Γ 23 | 1/4-20 | β20.0 | +35.0 | β35.0 | 8.67 | 11.5 | β | | |
| | D421 | 95 Γ 10.5 Γ 32 | module back-face centre | β25.0 | +25.0 | β | 10.5 | 16.0 | β | | |
| | D435 | 90 Γ 25 Γ 25 | 1/4-20 | β17.5 | +32.5 | β32.5 | 10.60 | 12.5 | β | | |
| | D435i | 90 Γ 25 Γ 25 | 1/4-20 | β17.5 | +32.5 | β32.5 | 10.60 | 12.5 | yes | | |
| | D435f | 90 Γ 25.8 Γ 25 | 1/4-20 | β17.5 | +32.5 | β32.5 | 10.60 | 12.5 | β | | |
| | D435if | 90 Γ 25.8 Γ 25 | 1/4-20 | β17.5 | +32.5 | β32.5 | 10.60 | 12.5 | yes | | |
| | D436 | 90 Γ 25 Γ 25 | 1/4-20 | β17.5 | +32.5 | β32.5 | 10.60 | 12.5 | yes | | |
| | D455 | 124 Γ 26 Γ 29 | 1/4-20 | β47.5 | +47.5 | +11.5 | 11.15 | 14.5 | yes | | |
| | D455f | 124 Γ 26.8 Γ 29 | 1/4-20 | β47.5 | +47.5 | +11.5 | 11.15 | 14.5 | yes | | |
| | D456 | 124 Γ 26 Γ 29 | 1/4-20 | β47.5 | +47.5 | +11.5 | 11.15 | 14.5 | yes | | |
| | D457 | 124 Γ 36 Γ 29 | 1/4-20 | β47.5 | +47.5 | +11.5 | 11.15 **(ASSUMED)** | 14.5 | yes | | |
| | D555 | 167 Γ 48 Γ 42 | body centre **(ASSUMED)** | β47.5 | +47.5 | +11.5 | 11.15 **(ASSUMED)** | 21.0 | yes | | |
| ### How each column was obtained | |
| **`leftIR.x`** β DS tables 4-20/4-21/4-22 give "From centerline of 1/4-20 to left imager": | |
| D415 20 mm, D43x 17.5 mm, D45x 47.5 Β±0.1 mm, D405 9 mm. D457's own datasheet table 4-11 | |
| repeats 47.5 mm. Sign from URDF (`*_cam_depth_py`, ROS +Y = left) β authored βx. | |
| **`rightIR.x`** β `leftIR.x + baseline`. URDF `*_cam_depth_to_infra2_offset` is | |
| `βbaseline` in ROS Y for every model, i.e. exactly one baseline to the camera's right. | |
| **`RGB.x`** β URDF `*_cam_depth_to_color_offset`, in ROS +Y (left), negated: | |
| | Model | URDF value (ROS +Y) | Authored Ξx from leftIR | | |
| |---|---|---| | |
| | D405 | `0.0` | 0 | | |
| | D415 | `+0.015` | β15 mm | | |
| | D435 family | `+0.015` | β15 mm | | |
| | D455 family | `β0.059` | **+59 mm** | | |
| Note the sign flip on the D45x line: on a D435 the colour lens is outboard of the left | |
| imager, on a D455 it is **inboard**, 11.5 mm to the right of the tripod hole. Getting | |
| this backwards puts the colour camera 118 mm out of place on a D455. | |
| Consistency check (this is why the table can be trusted): on every model the outermost | |
| two optics land symmetrically about the tripod hole β D415 Β±35, D435 Β±32.5, D455 Β±47.5, | |
| D405 Β±9. | |
| **`optical y`** (how far forward of the screw the depth origin sits) β | |
| `(screw β front face) β (front glass thickness) β |Z'|`, where `Z'` is the datasheet | |
| "depth start point" (DS table 4-16/4-17) and the first two terms are URDF | |
| `*_cam_mount_from_center_offset` and `*_glass_to_front`: | |
| | Model | screwβfront | glass | Z' | = optical y | | |
| |---|---|---|---|---| | |
| | D405 | 14.65 | 0.1 | β3.7 | **10.85** | | |
| | D415 | 9.87 | 0.1 | β1.1 | **8.67** | | |
| | D43x | 14.9 | 0.1 | β4.2 | **10.60** | | |
| | D45x | 15.8 | 0.1 | β4.55 | **11.15** | | |
| The `f` variants (D435f/D435if/D455f) publish a 0.5 mm larger `|Z'|` (β4.7, β5.05) | |
| **because the NIR filter moves the cover glass 0.5 mm forward, not because the imager | |
| moves**. Their depth origin is therefore identical to the unfiltered sibling; only the | |
| housing gets 0.8 mm deeper. The graph models it that way. | |
| The D415 is the one place the vendor URDF and the datasheet disagree: the xacro defines | |
| `d415_cam_mount_from_center_offset = 0.00987` and then passes `0` for the link joint's X. | |
| The datasheet's Z' of β1.1 mm says the depth origin is not on the screw plane, so the | |
| graph uses the derived 8.67 mm. The discrepancy is 8.67 mm along the optical axis. | |
| **`optical z`** β `housing height / 2` for every model (URDF `*_cam_depth_pz`); the | |
| optical axis is on the housing's horizontal midline. | |
| **IMU offsets** β URDF, relative to the depth origin, ROS `(x, y, z)`: | |
| | Model | URDF | Authored Ξ from leftIR (x, y, z) | | |
| |---|---|---| | |
| | D435i / D435if | `(β0.01174, β0.00552, +0.0051)` | (+5.52, β11.74, +5.10) mm | | |
| | D455 family | `(β0.01602, β0.03022, +0.0074)` | (+30.22, β16.02, +7.40) mm | | |
| D436 has a 6DoF IMU (BMI085, DS table 3-56) but no published offset; the graph uses the | |
| D435i figures and marks them ASSUMED. D457 and D555 likewise reuse the D455 figures. | |
| ### The four assumptions, stated plainly | |
| 1. ~~**D436 colour offset.**~~ **RESOLVED.** The Mar 2026 revision carries a D436 | |
| mechanical drawing (figure 10-11) whose front view dimensions every aperture | |
| directly, and unusually gives as-built rather than nominal figures: | |
| baseline **49.95**, projector **29.00**, colour **15.05**, and **17.45** from the | |
| 1/4-20 centreline to the left imager. The assumed 15 mm colour offset was right to | |
| within 0.05 mm. The graph keeps the nominal 15 / 50 / 17.5 for consistency with the | |
| rest of the D43x family; the difference is 50 Β΅m. | |
| 2. **D457 forward offset.** Same D450 optics and cover-glass stack as the D455 | |
| (its datasheet repeats both the 47.5 mm and the β4.55 mm figures), but it does not | |
| publish where the 1/4-20 hole sits in its 36 mm-deep housing. The graph assumes the | |
| front face is where the D455's is (15.8 mm ahead of the screw) and puts the extra | |
| 10 mm on the back, where the GMSL connector is. | |
| 3. **D555 datum and layout.** Its datasheet states it is *"composed of the long range | |
| global shutter D450 optical module with IMU"* and gives baseline 95 mm, so the | |
| intra-module layout is the D455's. It publishes no mounting datum at all. The graph | |
| references the D555 to the **housing centre** and centres the stereo pair on it. | |
| 4. **D421 datum.** The D421 datasheet gives "left centre hole to left imager centre = | |
| 18.5 mm" but no drawing locating that hole in the 95 mm width. The graph follows the | |
| vendor URDF, which centres the stereo pair on the module and references it to the | |
| back-face centre. | |
| ## 5. Streaming modes β reference only | |
| LuckyEngine cannot set these. They are shown in the graph's parameter panel so the | |
| recorder can be matched to them by hand. See `README` in the pod graph header. | |
| ### Depth (Z16) | |
| | Models | Resolution | Frame rates (fps) | | |
| |---|---|---| | |
| | D415, D43x, D436 | 1280Γ720 | 6, 15, 30 | | |
| | | 848Γ480, 640Γ480, 640Γ360, 480Γ270, 424Γ240 | 6, 15, 30, 60, 90 | | |
| | D45x, D457 | 1280Γ720 | 5, 15, 30 | | |
| | | 848Γ480, 640Γ480, 640Γ360, 480Γ270, 424Γ240 | 5, 15, 30, 60, 90 | | |
| | D405 | 1280Γ720 | 5, 15, 30 | | |
| | | 848Γ480, 640Γ360, 480Γ270, 424Γ240 | 5, 15, 30, 60, 90 | | |
| | D421 | 1280Γ720 | 6, 15, 30 | | |
| | | 848Γ480, 640Γ480, 640Γ360, 480Γ270 | 6, 15, 30, 60, 90 | | |
| | D555 | 1280Γ720 | 5, 15, 30 | | |
| | | 896Γ504, 640Γ360, 448Γ252 | 5, 15, 30, 60 | | |
| ### Colour (YUY2) | |
| | Models | Resolution | Frame rates (fps) | | |
| |---|---|---| | |
| | D415, D435, D435i, D435f, D435if | 1920Γ1080, 1280Γ720 | 6, 15, 30 | | |
| | | 960Γ540, 848Γ480, 640Γ480, 640Γ360, 424Γ240 | 6, 15, 30, 60 | | |
| | | 320Γ240, 320Γ180 | 6, 30, 60 | | |
| | D436 | 1280Γ720 | 6, 15, 30 | | |
| | | 848Γ480, 640Γ480, 640Γ360, 480Γ270 | 6, 15, 30, 60 | | |
| | | 424Γ240 | 6, 30, 60 | | |
| | D45x, D457 | 1280Γ800 | 5, 15, 30 | | |
| | | 848Γ480, 640Γ480 | 5, 15, 30, 60 | | |
| | | 640Γ360, 480Γ270, 424Γ240 | 5, 15, 30, 60, 90 | | |
| | D405 | 1280Γ720 | 5, 15, 30 | | |
| | | 848Γ480, 640Γ360, 480Γ270, 424Γ240 | 5, 15, 30, 60, 90 | | |
| | D555 | 1280Γ720 | 5, 15, 30 | | |
| | | 896Γ504, 640Γ360, 448Γ252 | 5, 15, 30, 60 | | |
| Source: DS tables 4-2 β¦ 4-5 (Mar 2026 rev), D421 v1.2, D555 v1.1. | |
| The D436 and D555 product pages advertise "up to 1280Γ800" colour; the datasheet | |
| streaming tables cap the USB colour stream at 1280Γ720 and list 1280Γ800 as the | |
| calibration format only. The tables above follow the datasheet. | |
| ### Minimum-Z (mm) β DS table 4-11 | |
| | Resolution | D415 | D43x/D436 | D45x/D457 | D405 | | |
| |---|---|---|---|---| | |
| | 1280Γ720 | 450 | 280 | 520 | 100 | | |
| | 848Γ480 | 310 | 195 | 350 | 70 | | |
| | 640Γ480 | 310 | 175 | 320 | β | | |
| | 640Γ360 | 240 | 150 | 260 | 55 | | |
| | 480Γ270 | 180 | 120 | 200 | 45 | | |
| | 424Γ240 | 160 | 105 | 180 | 40 | | |
| D421: ~200 mm at VGA (D421 v1.2). D555: ~260 mm at VGA, ~520 mm at max resolution, | |
| ideal range 0.6β6 m (D555 v1.1). | |
| ### Depth quality β DS table 4-14 | |
| Z-accuracy Β±2 % for every model; fill rate β₯99 % (β₯99.5 % D405); RMS spatial noise | |
| β€2 % (β€1 % D405); temporal noise β€1 % (β€0.5 % D405). Quoted within 80 % of the depth | |
| FOV, at β€2 m (D415, D43x), β€4 m (D45x), β€0.5 m (D405). | |
| These are what the graph seeds `DepthSensorParams` from β see Β§6. | |
| ## 6. Mapping onto LuckyEngine's depth sensor model | |
| `Hazel::Data::DepthSensorModel::ActiveStereo` is described in | |
| `Hazel/src/Hazel/Data/DataTypes.h:265` as *"RealSense-style active-IR stereo: zΒ² disparity | |
| noise, specular dropout, occlusion shadows"* β it is the right model for every camera in | |
| this pack except the **D405**, which is passive (DS table 4-14 note 4: *"KPIs for D405, a | |
| passive camera, are measured using a textured target"*). ActiveStereo is still the closest | |
| available model for the D405; the difference is that its noise should not be assumed to | |
| improve with projector power. | |
| `DepthSensorParams` fields and the value this pack recommends: | |
| | Field | Meaning | Value | | |
| |---|---|---| | |
| | `Baseline` | virtual stereo baseline, drives zΒ² noise and occlusion width | the model's real baseline (table Β§4) | | |
| | `MinRange` | near blind zone | Min-Z at the resolution you are recording (table above) | | |
| | `MaxRange` | beyond this the sensor returns nothing | D405 0.5 m, D415 3 m, D43x 3 m, D45x/D457/D555 6 m | | |
| | `SubpixelSigma` | disparity 1-sigma noise, px | 0.08 default; the datasheet quotes error as a % of Z, not in disparity px, so this is left at the engine default | | |
| | `DetectFloor` | angular return fraction below which a pixel drops out | 0.03 default | | |
| `MaxRange` uses each model's stated ideal range, not an absolute maximum: | |
| D405 "0.7 m" max ideal (DS quality spec measured β€0.5 m), D415/D43x β€3 m, | |
| D45x β€6 m, D457 "0.6 m to 6 m", D555 "60 cm to 6 m". | |
| `DepthScale` (metres per uint16 code) should be picked as `MaxRange / 65535` rounded up β | |
| the Settings panel tooltip says the same thing. For a 6 m D455 that is 0.0001 m/code; | |
| for a 0.5 m D405, 0.00001 m/code. The engine default of 0.001 wastes 99 % of the 16-bit | |
| range on a close-in camera. | |
| --- | |
| ## 7. The IR projector | |
| The projector is not a stream, so it has no librealsense extrinsic and it appears in | |
| no URDF. It is dimensioned on the datasheet **mechanical drawings**, whose front views | |
| carry an aperture chain measured from the left imager: | |
| | Model | Projector offset from the left imager | Source | | |
| |---|---|---| | |
| | D405 | none β passive | DS 3-49, "IR Projector: N/A" | | |
| | D415 | **28 mm** | DS figure 10-8 | | |
| | D435 / D435i / D435f / D435if | **29 mm** | DS figure 10-9 | | |
| | D436 | **29.00 mm** | DS figure 10-11 (as-built) | | |
| | D455 / D455f / D456 | **37.5 mm** | DS figure 10-12 | | |
| | D457 | 37.5 mm **(ASSUMED)** | same D450 optical module as the D455 | | |
| | D555 | 37.5 mm **(ASSUMED)** | same D450 optical module (D555 v1.1 says so outright) | | |
| | D421 | 29 mm **(ASSUMED)** | not published; a wide 50 mm-baseline module, so the D43x value | | |
| Reading those chains also re-derives the numbers in Β§4 from a source independent of the | |
| URDF, and they agree: the D435 sheet gives (17.5) to the left imager, (15) to the | |
| colour imager and (50) to the right imager; the D415 sheet gives (20), (15) and (55); | |
| the D455 sheet gives (47.5), (59) and (95), plus IMU callouts of (30.22), (β7.4) and | |
| Z (β16.02) that match the URDF's `d455_imu_p*` exactly. | |
| The direction is the same on every sheet: all dimensions run from the left imager, and | |
| the projector sits between the two IR imagers β so its offset is **positive** in the | |
| authored frame (toward the camera's right), unlike the colour imager, which is negative | |
| on everything except the D45x. | |
| **Why the graph emits it as a frame rather than only as an aperture.** The projector is | |
| a second viewpoint. A surface the stereo pair can both see but the projector cannot | |
| reach gets no pattern, so an active stereo camera drops out in the projector's shadow | |
| as well as in the stereo occlusion that `DepthSensorParams::Baseline` already models. | |
| Those are different shadows with different geometry, and only the projector frame | |
| carries the second one. | |
| ## 8. Manufacturer 3D models β what exists | |
| Checked so a proxy can be swapped for the real shape. There is **no manufacturer FBX, | |
| OBJ or glTF** for any of these parts. | |
| **Official CAD**, from <https://dev.realsenseai.com/docs/cad-files/>: | |
| | Download | Contents | Format | | |
| |---|---|---| | |
| | D400 Depth Cameras | D405, D415, D435, D435f, D435i, D455, D455f, D456, D457 | SolidWorks `.SLDPRT` | | |
| | D555 Camera | `D555_SOLID_SOC.STEP` (37.6 MB) | **STEP** β open, directly convertible | | |
| | D400 Depth Modules | D401, D415, D430, D450 | SolidWorks `.SLDPRT` | | |
| No D436 and no D421 in the CAD set. `.SLDPRT` is proprietary and needs SolidWorks or a | |
| commercial converter; the D555 STEP is the only one usable without one. | |
| **Vendor mesh assets**, from `realsense2_description/meshes` in `IntelRealSense/realsense-ros` | |
| (open, already tessellated, Apache-2.0): | |
| | File | Size | | |
| |---|---| | |
| | `d405.stl` | 195 kB | | |
| | `d415.stl` | 21.3 MB | | |
| | `d421.stl` | 20 kB | | |
| | `d435.dae`, `d436.dae` | 15.8 MB each | | |
| | `d455.stl` | 2.6 MB | | |
| | `d585.stl` | 7.0 MB | | |
| These are the practical swap-in candidates. Two things to know before swapping: | |
| 1. **They are outer shells with no optics.** `d455.stl` and `d405.stl` were placed into | |
| this graph's frame and rendered front-on: a rounded housing, a flat recessed front | |
| window, and no lens apertures or projector window at all. Swapping one in gives a | |
| more accurate silhouette and *fewer* visual cues than the proxy in this pack, which | |
| at least shows which opening is which. | |
| 2. **They independently confirm Β§4.** Placed through the URDF's own visual transform, | |
| `d455.stl` measures 124 Γ 26 Γ 29 mm and `d405.stl` 42 Γ 23 Γ 42 mm; both straddle | |
| the tripod datum in X, put the optical axis on the housing's vertical midline, and | |
| put the front face 4.65 mm / 3.80 mm ahead of the depth origin β which is exactly | |
| `front_y β opt_y` in Β§4 for each. | |
| --- | |
| ## 9. The model name engraved into the top face | |
| The proxy cuts the selected model's name into the top of the housing so a scene | |
| full of cameras can be read without selecting each one. It is a real boolean | |
| cutout of the text -- a round-bottomed groove one stroke radius deep -- and | |
| getting there took two fixes in pod rather than anything in this graph. | |
| ### Why it did not work at first | |
| pod's text comes from `StringToCurves`, whose glyph table is a 36-character | |
| *stroke* font: open polylines, not closed outlines. `CurveToMesh` gives those | |
| thickness by sweeping a round profile, so a glyph is a set of tubes, and the | |
| tubes pass through each other wherever two strokes meet. pod's `MeshBoolean` | |
| is Manifold, and Manifold's boolean arranges operand A against operand B; it | |
| never arranges one operand's shells against *each other*. Those crossings | |
| survived into the result as interior faces, and a Difference with the text | |
| came back with a lid of degenerate slivers where the groove should have been. | |
| Blender's exact solver does not have this problem because it arranges every | |
| face against every other before classifying by winding number, which is why a | |
| text cutout "just works" there. Manifold exposes the same capability as | |
| `Decompose()` (split shells by connectivity) plus `BatchBoolean(Add)` (arrange | |
| them against each other); it just has to be asked. | |
| ### The two pod fixes | |
| 1. **`MeshBoolean` resolves each operand against itself first** | |
| (`pod/src/Nodes/Boolean.cpp`, `ResolveSelfOverlap`). Decompose, batch-union, | |
| then the real boolean. A single-shell operand -- nearly every operand there | |
| is -- is handed straight back, so the common path pays one Decompose and no | |
| boolean. `MeshBooleanList` folds through the same code, so it gets it too. | |
| Regression tests assert the *volume* removed by a self-overlapping tool, | |
| which is the number a leftover lid gets wrong. | |
| 2. **`StringToCurves` splits closed-loop glyphs** (`pod/src/Nodes/Curves.cpp`). | |
| `0`, `6`, `8`, `O` and their like are one stroke that ends where it began. | |
| Swept, that tube's first ring is perpendicular to the outgoing segment and | |
| its last to the incoming one, at the same centre, so its two end caps cross | |
| *inside a single shell* -- the one case fix 1 cannot reach, and `0` left | |
| exactly one sliver at every stroke radius tried. A loop is now emitted as | |
| two open strokes overlapping by one segment: each cap lands inside the other | |
| tube and the crossing is between two shells, which fix 1 resolves. A stroke | |
| renderer draws the same ink. | |
| Both were found by measurement, not reasoning: cut each glyph into a box alone | |
| and count downward-facing triangles in the cut plane. Before: `D 0 3 4 6 8 9 F` | |
| all bad. After fix 1: only `0 6 8 9`. After fix 2: only `6 9`, which are open | |
| hooks whose spine turns faster than a 0.45 mm tube can follow -- a genuine | |
| sweep limit, and no model name uses either. Subdividing, resampling or | |
| filleting the curve makes that *worse* (shorter segments beside the same turn), | |
| so it is left alone. | |
| ### How the right name is selected | |
| pod cannot switch a *string* on an index (`IndexSwitch` has Geometry, Float, | |
| Bool and Material variants and no string one; a `Menu` knob carries an | |
| `Identifier` no expression can reach). So all thirteen cutters are built at the | |
| same spot and the **geometry** is switched: two chained `IndexSwitch` nodes | |
| (ten slots each) forward one into the Difference. The second index is | |
| `max(0, model - 9)`, a Float expression feeding an Int pin, which rounds | |
| (`pod/src/Evaluator.cpp:234`). The twelve unselected chains still evaluate and | |
| are dropped; the whole graph is ~11 ms. | |
| ### Sizing and orientation | |
| Cap height is `min(label_cap, body_d * 0.34)`; the clamp is what lets the | |
| 7.5 mm default fit the 10.5 mm-deep D421. `label_cut` is the profile radius: | |
| half the stroke width and exactly the groove depth, because the tubes are | |
| centred on the top face and the inside half is what the boolean removes. | |
| Text is built reading along +X (the camera's own right), so unrotated it reads | |
| from *behind* the unit. `label_rot` defaults to a yaw of pi so it reads from | |
| *in front*, where you stand to check what a camera is pointing at. The yaw is | |
| safe only because the group aligns the text `Middle` in Y as well as `Center` | |
| in X, putting the origin at the centre of the cap box. | |
| ### Checks | |
| `tools/check_marking.py` measures the cut on all thirteen models: nothing | |
| above the top face, a groove exactly one stroke radius deep, zero downward- | |
| facing triangles in the top plane (the lid a failed cut leaves), and a groove | |
| no wider than one name. `tools/verify_marking_selection.py` proves the switch | |
| chain by overriding the names with *N*-stroke markers and recovering *N* from | |
| the groove width. Both isolate the groove geometrically, since it is cut into | |
| the housing and has no material of its own. | |
| pod's glyph table is **uppercase only**, so `D435i` is spelled `D435I`. | |
| ## 10. Mounting hardware and threads | |
| Everything here is read off the vendor mechanical drawings, not inferred. Sources | |
| are the D400 series datasheet 337029-017 (October 2024) figures 10-8 through | |
| 10-14, the D457 datasheet 337029-011 (August 2025), the D421 datasheet v1.2 and | |
| the D555 datasheet v1.1. Positions are authored Z-up millimetres from the same | |
| datum as section 4 - the 1/4-20 tripod centre, except on the D421 and D555. | |
| ### The two mounting systems, and which face each is on | |
| Every camera in this pack (not the D421 module) carries **two independent** | |
| mounting provisions, and they are on **different faces**: | |
| 1. **A 1/4-20 UNC tripod hole on the BOTTOM face.** This is the pack's datum and | |
| was already modelled; what is new here is its thread depth. | |
| 2. **Two threaded holes on the BACK face**, straddling the tripod centreline. | |
| This is the load-bearing one for a bracket - the D457 datasheet calls the back | |
| "the Rear Mounting Surface" and specifies a flatness tolerance for it | |
| (Table 3-20, 0.1 mm), and Β§3.8.2.2 of 337029-017 recommends bracket mounting | |
| through these screws rather than the tripod. | |
| **The back, not the bottom.** Worth stating because the D435's drawing alone | |
| cannot prove it: that camera is 25 mm high and 25 mm deep, so its back and | |
| bottom views are the same size. The D415 (20 deep, 23 high) and the D455 | |
| (26 deep, 29 high) are not square in section, and on both the screw view | |
| measures the HEIGHT - 23 and 29 - so the screws are on the back. The D435 is the | |
| same drawing convention and the same logo placement, so it follows. | |
| ### Per model | |
| | Model | Back-face screws | Pitch (mm) | x from datum | Max insert | Tripod 1/4-20 | | |
| |---|---|---|---|---|---| | |
| | D405 | 2 x M-unread | 20.00 | +/- 10.00 | not readable | yes, 5.0 mm | | |
| | D415 | 2 x M3 | 45.00 | +/- 22.50 | 3 mm | yes, depth not published | | |
| | D421 | 2 x M3 through + 4 x d3.10 corner | 87.00 x 24.00 | +/- 43.50 | through | none | | |
| | D435 | 2 x M3 | 45.00 | +/- 22.50 | 3 mm | yes, depth not published | | |
| | D435i | 2 x M3 | 45.00 | +/- 22.50 | 3 mm | yes, depth not published | | |
| | D435f | 2 x M3 | 45.00 | +/- 22.50 | 3 mm | yes, depth not published | | |
| | D435if | 2 x M3 | 45.00 | +/- 22.50 | 3 mm | yes, depth not published | | |
| | D436 | 2 x M3 **(INHERITED)** | 45.00 | +/- 22.50 | 3 mm | yes, depth not published | | |
| | D455 | 2 x M4 | 95.00 | +/- 47.50 | 4 mm | yes, 9.0 mm | | |
| | D455f | 2 x M4 | 95.00 | +/- 47.50 | 4 mm | yes, 9.0 mm | | |
| | D456 | 2 x M4 | 95.00 | +/- 47.50 | 4 mm | yes, 9.0 mm | | |
| | D457 | 2 x M4 **(pitch INHERITED)** | 95.00 | +/- 47.50 | 4 mm | yes, depth not published | | |
| | D555 | 2 x M4 | 125.40 +/- 0.20 | +/- 62.70 | 6 mm | not shown on its datasheet | | |
| Recommended torque: 0.4 Nm for the M3 pairs (figures 10-8, 10-9); the D457's M4 | |
| inserts withstand <= 3.0 Nm (M4 zinc-plated class 8.8 SHCS); the D421's module | |
| screws should not exceed 5 kgf*cm. | |
| ### Height on the back face | |
| Measured from the drawings rather than assumed: the screw pair sits on the | |
| back face's horizontal centreline on the D415, D435 and D455, i.e. at the | |
| housing's mid-height. On the D405's 42 x 42 back face the pair is centred both | |
| ways. | |
| ### That the pair straddles the tripod axis was measured, not assumed | |
| The D435's figure 10-9 does not dimension the screws against the 1/4-20 - it | |
| only gives the 45 mm pitch. Taking the drawing's own pixels at 900 dpi, the | |
| midpoint of the screw pair lands at 756.0 and the midpoint of the body outline | |
| at 756.75, i.e. they coincide to 0.07 mm at that scale. The optics are already | |
| known to be symmetric about the tripod centreline (section 4 dimensions them | |
| from it), so the body centre IS the tripod axis, and the screws straddle it. | |
| This also explains why the D415 and the D435 share a 45 mm pitch despite | |
| different widths and different baselines: the pattern is referenced to the | |
| common datum so one bracket fits both. | |
| ### USB cable retention | |
| Not a mounting provision for a bracket, but it is on the same faces and will be | |
| in the way of one. The D455 carries 2 x M2 beside its USB-C on the BOTTOM, with | |
| the connector centre 37.01 mm from the 1/4-20 toward the camera's RIGHT. The | |
| D405 carries 2 x M2x0.4 18.00 mm apart beside its screw-locked USB 3 Micro-B | |
| (not a USB-C) on its left side. Both are now modelled -- as cable-lock holes | |
| in the Detailed housing -- see section 12. | |
| ### The D421 is a bare module and mounts differently | |
| It has no tripod hole. Its datasheet gives 2 x M3 threaded-through holes on the | |
| horizontal centreline and 4 non-threaded d3.10 corner holes on an 87.00 x 24.00 | |
| pattern, to be bolted to a flat heat sink. | |
| **This resolves one of the pack's assumed datums.** Section 4 previously carried | |
| the D421's datum as "back-face centre (ASSUMED)". The datasheet states the real | |
| reference: "the Depth module D421 origin reference from the left centre hole to | |
| the left imager centre is 18.5 mm". The left centre hole is the left M3 at | |
| x = -43.5, so the left imager is at -43.5 + 18.5 = **-25.0** - which is exactly | |
| the value the pack already carried. The number was right; it is no longer an | |
| assumption. | |
| ### What is still not published | |
| Carried as gaps rather than invented: | |
| - **The D405's back-face thread size.** Its figure 10-13 calls out "2X M?x?.? | |
| MAXIMUM INSERT ?mm", but the datasheet's embedded drawing raster is only | |
| 1020 x 1320 px for the whole sheet and that annotation does not survive at | |
| any magnification. The 20.00 mm pitch and the hole positions are legible; the | |
| thread is not. Modelled as M3, the smaller of the two sizes used elsewhere in | |
| the family, so a bracket designed against it will not foul an M4 boss. | |
| - **Tripod thread depth on the D415, D435 family, D436 and D457.** Only the D405 | |
| (5.0 mm) and the D455 (9.0 mm) state it. Modelled at 5.0 mm, the shallower | |
| published figure, for the same reason. | |
| - **The D436's screw pattern.** The D436 is not in 337029-017 and its own | |
| datasheet has no mechanical drawing. It shares the D435's 90 x 25 x 25 | |
| housing and every optical offset, so the D435's pattern is inherited. | |
| - **The D457's screw pitch.** Its datasheet states 2 x M4 on the rear mounting | |
| surface and the torque limit, but its mechanical-drawing page renders blank. | |
| It shares the D455's 124 x 29 back face, so the D455's 95 mm is inherited. | |
| - **Whether the D555 has a 1/4-20 at all.** Its datasheet shows only the 2 x M4 | |
| at 125.40. A FRAMOS product listing claims one 1/4-20 and two M3, which | |
| contradicts the vendor drawing on the thread size; the drawing is preferred | |
| and the tripod is left out rather than guessed at. | |
| ## 11. Sources | |
| - RealSense Product Family D400 Series Datasheet, doc 337029-017 rev 021, Oct 2025 β | |
| <https://realsenseai.com/wp-content/uploads/2025/09/Intel-RealSense-D400-Series-Datasheet-October-2025.pdf> | |
| - RealSense D400 Series Datasheet, Mar 2026 (adds D436) β | |
| <https://www.realsenseai.com/wp-content/uploads/2026/03/RealSense-D400-Series-Datasheet-Mar-2026.pdf> | |
| - RealSense Depth Module D421 Datasheet v1.2 β | |
| <https://realsenseai.com/wp-content/uploads/2025/11/D421-Datasheet-v1.2.pdf> | |
| - RealSense D555 PoE Datasheet v1.1 β | |
| <https://www.realsenseai.com/wp-content/uploads/2025/08/D555-Datasheet-v1.1.pdf> | |
| - RealSense D457 GMSL/FAKRA Datasheet, Dec 2025 β | |
| <https://www.realsenseai.com/wp-content/uploads/2025/12/RealSense-D457-Datasheet-Dec-2025.pdf> | |
| - D436 product page β <https://www.realsenseai.com/products/d436/> | |
| - CAD files index β <https://dev.realsenseai.com/docs/cad-files/> | |
| - Vendor meshes β <https://github.com/IntelRealSense/realsense-ros/tree/ros2-development/realsense2_description/meshes> | |
| - `realsense2_description` URDF xacros, branch `ros2-development` β | |
| <https://github.com/IntelRealSense/realsense-ros/tree/ros2-development/realsense2_description/urdf> | |
| ## 12. Quality: the Detailed housing and the real ports | |
| The graph carries a `quality` switch (Camera group, `Proxy` / `Detailed`, default | |
| Proxy). Everything it adds is generated by `tools/gen_detail.py` and appended AFTER | |
| every existing parameter, because pod lowers parameters to nodes in document order | |
| and a scene's per-instance override binds a knob by that node id. Checked by dumping | |
| the lowered graph before and after: **all 129 existing parameter nodes keep their | |
| ids**, `model` is still 905, `quality` is 1144. | |
| Both qualities keep every body, frame and the collision box exactly where they were. | |
| Only the render mesh changes. | |
| ### Proxy (quality 0) | |
| The housing above, unchanged, plus an **opening cut 2 mm into it for every connector | |
| the model has**, at the connector's real position and orientation, with a bright | |
| steel plate at its floor (`mat_port_hint`) so a dark opening in a dark housing still | |
| reads. The covered sync-port slot is indicated the same way on the top face. | |
| ### Detailed (quality 1) | |
| **The housing** is a rounded rectangle in front view -- a STADIUM (radius = half the | |
| height) on every model except the D405's rounded square and the D421 module's box -- | |
| clipped by two elliptic cylinders: one along X that domes the top and rounds the | |
| bottom-front edge, one along Z that pulls the ends in toward the front and back. | |
| The numbers are fitted to the vendor meshes of section 8 (silhouette extents binned | |
| along each axis, each fit checked at three or more depths): | |
| | Model | Front outline | Dome (along X) | Ends (along Z) | Source | | |
| |---|---|---|---|---| | |
| | D405 | rounded square, r 6 mm (17.37 at 0.5 mm from the end, 19.61 at 2.5) | none: flat | none | `d405.stl` | | |
| | D415 | stadium, r 11.5 | top 11.5 -> 9.63 at the front: semi-axis 26.8, crown 15.4 behind the front | 49.5 -> 47.61: semi-axis 53.4 | `d415.stl` | | |
| | D435 family, D436 | stadium, r 12.5 | 12.5 -> 10.05: 31.1 @ 19.5 (predicts 11.76 at 8.7 back, measured 11.64) | 45 -> 42.54: 58.3 | `d435.dae` | | |
| | D455, D455f, D456 | stadium, r 14.5 | 14.5 at 12 back -> 12.34 front, 11.94 back: 22 @ 12 | 62 -> 59.84 / 59.32: 45 | `d455.stl` | | |
| | D457 | stadium, r 14.5 | 30 @ 15 | 60 | **ASSUMED**: no mesh; D457 datasheet fig 3-7 shows the D455's domed silver body, deeper | | |
| | D555 | stadium, r 21 | 60 @ 24 | 110 | **ASSUMED**: no mesh; its datasheet cover photograph | | |
| | D421 | box, 1 mm corners | none | none | bare module | | |
| Every mesh measures the bottom FLAT at the back and rounded only at the front. The | |
| flat part starts at the dome's crown: the meshes put its start a little further | |
| forward, but starting it there leaves a kink of up to 8 degrees and a 0.8 mm step | |
| where the dome's underside meets it. | |
| The front is **black gloss glass** in a 1.0-1.5 mm aluminium frame, following the | |
| housing's front outline, with a ring and a lens disc on it at every aperture of | |
| section 4 (the same `has_rgb_lens` / `has_projector` rules as the proxy). | |
| The housing is **bead-blasted silver aluminium** (`mat_alu`) on the D4xx, and | |
| `mat_dark_body` on the D555 -- dark grey in its datasheet photograph -- and on the | |
| bare D421 module. | |
| **The ports** are pod's own connector graphs (`assets/connectors` in the pod repo, | |
| copied into `groups/`: `usb_c_receptacle`, `usb3_micro_b_receptacle`, `rj45_jack`, | |
| `fakra_jack`), each set into a pocket cut to its outline: | |
| | Model | Connector | Where | Source | | |
| |---|---|---|---| | |
| | D405 | USB 3 Micro-B, 2 x M2x0.4 cable-lock holes 18.00 apart | LEFT side face, long axis vertical, at mid-height, 6.82 forward of the rear face | DS fig 10-13; the vendor mesh has the 12 mm slot exactly there, on its +x face, which the URDF's `rpy (pi/2, 0, pi/2)` makes the camera's left | | |
| | D415, D435 family, D436 | USB-C, vertical | on the floor of an OPEN POCKET in the back face at the camera's RIGHT end, x from +36 to the end, 8 mm deep; socket centred at x = +40 | a mid-height section of `d415.stl` (open end cap, socket at mesh x = -40); the rear views of DS fig 10-8 and 10-9 draw that end cap. D43x pocket depth **ASSUMED** from the D415 (the `d435.dae` is too fragmentary to section there) | | |
| | D455, D455f, D456 | USB-C, 2 x M2 cable-lock holes | BOTTOM face, 4.06 from the rear edge, 37.01 from the 1/4-20 toward the camera's RIGHT, long axis along X | DS fig 10-11 bottom view. It sits below the front view (third-angle projection), so it shares that view's orientation, and the front view puts the left imager at page-right. The lock holes measure 15.4 apart on the drawing; the USB-IF dual-lock pitch 15.00 is used | | |
| | D457 | USB-C as the D455 (its y is the D455's, **ASSUMED**); FAKRA on the rear, camera-LEFT end, 5 mm below the optical axis, x = -52 (**ASSUMED**) | | D457 datasheet fig 3-7: the blue FAKRA leaves the back at the end opposite the bottom USB-C | | |
| | D555 | RJ45 at the camera's RIGHT end of the bottom, USB-C at its LEFT end, tripod between; +/-48 mm, mid-depth (**ASSUMED**) | BOTTOM face | D555 datasheet v1.1 cover photograph; the lens order (camera-right imager, RGB, projector, left imager) fixes which end is which | | |
| | D415, D43x, D436, D45x, D457 | JST SH sync header, **under a covered slot** | TOP face, centred in X: D415 18.3 x 3.6 at 16.2 behind the front; D43x 17.6 x 3.6 at 20.7; D45x and D457 22.4 x 4.4 at 22 | the top views of DS fig 10-8, 10-9, 10-11, and D457 fig 7-12 "External Sensor Sync Connector". Drawn as the slot and its cover plate with a screw, because the header itself is under the cover | | |
| | D421 | none modelled | | the module's board connectors are not in the connector library | | |
| `section 10`'s correction: the pod connector README first placed the D455's USB-C | |
| toward the camera's LEFT. The drawing says right, as above; the README is corrected. | |
| **Shading.** The render mesh is smoothed by angle: 30 degrees on the Proxy, as it | |
| always was, and 12 on the Detailed housing (`shade_angle`). The detailed shell's dome | |
| and corners are faceted at 2.25 and 5.6 degrees, so they stay smooth; where the dome | |
| crosses the stadium ends the crease angle runs from 0 to 90 degrees, and at 30 every | |
| face under it was averaged across the flat faces' long boolean triangles. Measured on | |
| the exported normals: faces bent more than 8 degrees from their own plane, 8.0% of the | |
| D435i housing's area at 30 degrees, 0.7% at 12. (pod's midui editor viewport does not | |
| read these per-corner normals -- it averages every face at a vertex -- so it shows | |
| smears on BOTH qualities that neither the export nor LuckyEngine has.) | |
| **The D457's engraved name moved.** Its 36 mm top carries the sync slot 22 mm back, | |
| and a centred name cut straight through it; `label_y` now keeps a tripod-datum model | |
| deeper than 27 mm (only ever the D457) 13.5 mm behind the front. `check_marking.py` | |
| and `verify_marking_selection.py` measure the groove only within the name's own band, | |
| so the slot is not read as part of it; both pass on all 13. | |
| ### Checks | |
| `tools/verify_models.py` (all 13 bodies on their table values), `tools/check_mounts.py` | |
| (every screw and tripod hole cut to its published depth), `tools/check_marking.py`, | |
| `tools/verify_marking_selection.py` and `tools/check_coplanar.py` all pass on the | |
| default (Proxy) graph. Every model bakes at both qualities in about 0.3 s; the | |
| Detailed housing's extents equal the published housing dimensions exactly (the ports | |
| sit inside them). | |
| ### Still open | |
| - The D405's back-face screws: at 600 dpi the October 2025 drawing reads | |
| "2X M3x0.5, MAXIMUM INSERT 4.0mm". Section 10 carries the thread as unreadable and | |
| the graph models 3 mm; not changed here. | |
| - The D436's two stacked cable-lock screws: position and pitch not dimensioned; not | |
| modelled. | |
| - The D555's port positions and the D457's FAKRA position are photograph-level | |
| estimates, stated as ASSUMED above. | |
| ## 13. Graph layout and recording settings (pack 1.1) | |
| The camera graph is arranged into labeled frames for the proxy housing, engraving, | |
| mounting holes, detailed housing and optics, each connector family, assembly, | |
| optical and mounting frames, the collision box, and output. Connector helper graphs | |
| have separate frames for their shells, inserts, contacts, locking features, and | |
| assembly. Smaller shape helpers have a single workflow frame. | |
| This layout changes no camera dimensions, optical transforms, materials, quality | |
| defaults, render roles, or collision behavior. The top-level authored ID ceiling | |
| remains 904: `model` stays on node 905 and `quality` stays on node 1144. Existing | |
| scene overrides therefore keep their bindings. The thirteen generated model-name | |
| strings have native editor positions beside their engraving nodes. Folded numeric | |
| and parameter nodes remain hidden by the editor's default display settings. | |
| Resolution and FPS are configured **per camera** in Settings > Data > Camera | |
| Bindings. For one recorded image per data step, use the same frequency for every | |
| recorded camera, the Capture runner, and the data recorder. These graph parameters | |
| describe the camera body and optical frames; they do not configure recording. | |
| Enable **Invisible to Cameras** in the Pod component to exclude its generated | |
| housing from captures while retaining it in the editor viewport. | |