Skip to content

quick fiduciary 3d printable generator - #3166

Merged
leshy merged 13 commits into
mainfrom
ivan/feat/printable_tags
Jul 27, 2026
Merged

quick fiduciary 3d printable generator#3166
leshy merged 13 commits into
mainfrom
ivan/feat/printable_tags

Conversation

@leshy

@leshy leshy commented Jul 24, 2026

Copy link
Copy Markdown
Member

quick fiduciary 3d printable generator, I don't have a paper printer :D

dimos apriltag --3d --ids 0 --legs 250 --size-mm 75

2026-07-24_14-30

`dimos apriltag --3d` writes a directory holding, per tag, a base STL, a
marker STL, a back-text STL and a 3MF with the colors baked in, alongside
the existing PDF. The plate is a rounded rectangle whose side flanges carry
four mounting holes; base and marker meet flush at the top face so the
printed surface is flat rather than embossed.

`--legs H` adds a matching pair of T-footed stands, H measured floor to tag
centre, taking their hole spacing from the plate the tag actually generated.

Also prints the resolved configuration before generating anything.
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds 3D-printable AprilTag generation and inverted-marker detection.

  • Extends dimos apriltag with plate, mounting-hole, text-inlay, leg, STL, and colored 3MF generation options.
  • Adds request validation, output descriptions, summaries, and generator tests.
  • Adds optional detection of light-on-dark fiducial markers to the mapping and fiducial pipelines.
  • Updates project dependencies and the lockfile for mesh generation.

Confidence Score: 4/5

The PR is not yet safe to merge because invalid 3D geometry can still leave a misleading partial output set.

TagRequest does not validate 3D geometry before render writes the PDF, so later mesh-validation failures leave a completed PDF without the requested 3D artifacts.

Files Needing Attention: dimos/utils/cli/apriltag.py

Important Files Changed

Filename Overview
dimos/robot/cli/dimos.py Adds the 3D generator options and adapts the command to the new request-based rendering flow.
dimos/utils/cli/apriltag.py Introduces TagRequest orchestration, but its early validation remains incomplete and permits partial output from invalid 3D requests.
dimos/utils/cli/apriltag3d.py Implements mesh construction and STL/3MF serialization for plates, marker layers, text, and mounting legs.
dimos/perception/fiducial/marker_pose.py Adds OpenCV's inverted-marker detector option.
dimos/perception/fiducial/marker_transformer.py Propagates and preserves the inverted-marker option across detector initialization and calibration refresh.
dimos/mapping/utils/cli/map.py Exposes inverted fiducial detection through the mapping CLI.
pyproject.toml Adds the dependencies needed for 3D mesh generation.
uv.lock Updates the resolved dependency graph for the new mesh-generation packages.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  CLI["dimos apriltag options"] --> Request["TagRequest"]
  Request --> PDF["Generate printable PDF"]
  Request --> Mesh["Build plate, marker, text, and leg meshes"]
  Mesh --> STL["Write component STLs"]
  Mesh --> ThreeMF["Write colored 3MF"]
  ThreeMF --> Print["Multicolor 3D print"]
  Print --> Camera["Camera image"]
  Camera --> Detector["Optional inverted-marker detector"]
Loading

Reviews (8): Last reviewed commit: "fix(apriltag3d): drop __all__, the codeb..." | Re-trigger Greptile

Comment thread dimos/robot/cli/dimos.py Outdated
Comment thread dimos/utils/cli/apriltag3d.py Outdated
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.69231% with 38 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/utils/cli/apriltag3d.py 91.32% 8 Missing and 15 partials ⚠️
dimos/robot/cli/dimos.py 0.00% 12 Missing ⚠️
dimos/utils/cli/apriltag.py 96.66% 1 Missing and 2 partials ⚠️
@@            Coverage Diff             @@
##             main    #3166      +/-   ##
==========================================
+ Coverage   74.09%   74.18%   +0.09%     
==========================================
  Files        1103     1105       +2     
  Lines      104077   104577     +500     
  Branches     9520     9575      +55     
==========================================
+ Hits        77114    77585     +471     
- Misses      24270    24283      +13     
- Partials     2693     2709      +16     
Flag Coverage Δ
OS-ubuntu-24.04-arm 68.10% <92.69%> (+0.11%) ⬆️
OS-ubuntu-latest 70.15% <92.69%> (+0.11%) ⬆️
Py-3.10 70.15% <92.69%> (+0.11%) ⬆️
Py-3.11 70.14% <92.69%> (+0.11%) ⬆️
Py-3.12 70.15% <92.69%> (+0.11%) ⬆️
Py-3.13 70.15% <92.69%> (+0.11%) ⬆️
Py-3.14 70.15% <92.69%> (+0.10%) ⬆️
Py-3.14t 70.14% <92.69%> (+0.10%) ⬆️
SelfHosted-Large 29.63% <26.53%> (-0.02%) ⬇️
SelfHosted-Linux 36.07% <26.53%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/mapping/utils/cli/map.py 35.34% <ø> (ø)
dimos/perception/fiducial/marker_detect.py 80.48% <100.00%> (ø)
dimos/perception/fiducial/marker_pose.py 73.91% <100.00%> (+0.38%) ⬆️
dimos/perception/fiducial/marker_transformer.py 86.74% <100.00%> (+0.07%) ⬆️
dimos/perception/fiducial/test_helpers.py 100.00% <100.00%> (ø)
...mos/perception/fiducial/test_marker_transformer.py 100.00% <100.00%> (ø)
dimos/utils/cli/test_apriltag.py 100.00% <100.00%> (ø)
dimos/utils/cli/test_apriltag3d.py 100.00% <100.00%> (ø)
dimos/utils/cli/apriltag.py 94.34% <96.66%> (+1.01%) ⬆️
dimos/robot/cli/dimos.py 66.92% <0.00%> (-1.07%) ⬇️
... and 1 more

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

leshy added 2 commits July 24, 2026 14:50
Review fixes: describing a request indexed its IDs and looked up its family
before any error handling, so an empty --ids or an unknown --family produced
a traceback rather than a parameter error; and colors went into the 3MF
unchecked, so --marker-color red silently wrote an invalid displaycolor.
TagRequest now validates on construction and write_3mf validates the color,
with the whole CLI flow inside the error boundary. Catches --ids 10-5 too.

--frame swaps the mounting holes for a picture frame emitted as its own part,
to print in another material: a stepped moulding with a bead run and corner
rosettes, and a rabbet the tag drops into. The lip is opaque and a different
color, so it takes only what the margin can spare beyond the one-cell quiet
zone the detector needs, and is refused outright when there is nothing spare.

Back text now fills 90% of the plate rather than 80%, for fatter strokes.
Comment thread dimos/utils/cli/apriltag.py
leshy added 7 commits July 24, 2026 15:03
…ults to 2

--frame now takes the band width in mm (15 is the usual) rather than being a
flag, and the frame color is fixed rather than exposed. --margin-cells
defaults to 2, which is what a frame needs anyway to keep the quiet zone clear
of the lip.

The frame's top rail carries a recess for hanging it on a nail by a rope. Both
the rope and the nail head sit inside it, so the back stays flat against the
wall — anything proud of the back holds the frame off the wall, and its mass,
which sits in front of the wall plane, then swings it until the centre of mass
hangs under the nail. The rope threads under a bar at each end of the recess;
each bar bridges a short span so it prints without support, and the recess
closes before the face so none of it shows from the front.
The frame's hanging recess now holds two free-standing posts, joined to the
frame only at their base so a rope can pass right around each. A rope ties
round both and loops over a nail above; a door above each post lets its end
out through the top edge, leaving the rail solid between them. Nothing
protrudes past the back plane and nothing is cut through to the face, so the
frame sits flat on the wall and none of it shows from the front.

--margin-cells now defaults to what the build needs rather than a flat 2: the
detector wants one light cell around the black border and nothing more, and
only a frame needs a second, since its lip laps over the plate edge and must
leave that cell still showing. Flanges and legs reach outward and never touch
the quiet zone. A bare plate is 62.5 mm rather than 75 mm for a 50 mm tag,
31% less plastic. build_tag_meshes and generate_3d default to one cell to
match, which they previously did not.
The frame, its rope hanger and --frame come out so this PR stays about the
printable tag itself. Kept on ivan/feat/printable_tags_frame.

--margin-cells goes back to a plain default of one cell, which is what the
detector wants around the black border; the second cell only ever existed for
the frame's lip.
A multicolor 3D print with its two filaments swapped comes out as a
photographic negative — dark plate, light tag border — and OpenCV skips it
silently. Wire detectInvertedMarker through create_aruco_detector,
detect_markers_in_image and DetectMarkers, and expose it as `dimos map
--marker-inverted`.

Found on a Go2 recording where all 432 frames detected nothing; with the flag
99 detections of tag 0 come out at 0.33 px median reprojection error.

Off by default: it costs a little false-positive headroom in cluttered scenes,
and the real fix is to print the tag the right way round.
Nobody picks these. The 3MF only needs the two solids to read as different
filaments in the slicer, so pin them to module constants and delete
--base-color / --marker-color / --text-color along with the hex validation and
the colours row they existed to feed.
The mesh tests ran 11.4s for a print-file generator. Nearly all of it was the
back-text raster: build_tag_meshes is 3.09s with it and 0.16s without, and it
gets built twice per tag (pocket cut, then inlay). Nothing that checks plate
geometry needs an engraving, so nothing here asks for one; text_solid keeps its
own coverage via a single glyph.

Also swaps trimesh's contains() — the only thing pulling rtree in — for a probe
cube intersected with the mesh, which the boolean engine already present answers
in one call. Verified identical on all 64 tag cells, and the suite passes with
rtree blocked from import.

11.42s -> 1.03s, and pyproject/uv.lock go back to no rtree.
Comment thread dimos/utils/cli/apriltag.py
@leshy
leshy requested a review from Dreamsorcerer as a code owner July 27, 2026 09:54
Comment thread dimos/utils/cli/apriltag.py
paul-nechifor
paul-nechifor previously approved these changes Jul 27, 2026
@leshy
leshy enabled auto-merge July 27, 2026 10:09
Comment thread dimos/utils/cli/apriltag.py
@leshy
leshy added this pull request to the merge queue Jul 27, 2026
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jul 27, 2026
Merged via the queue into main with commit 0be2f0e Jul 27, 2026
34 of 35 checks passed
@leshy
leshy deleted the ivan/feat/printable_tags branch July 27, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PlzReview ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants