foodforthought-cli 0.2.8__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. ate/__init__.py +6 -0
  2. ate/__main__.py +16 -0
  3. ate/auth/__init__.py +1 -0
  4. ate/auth/device_flow.py +141 -0
  5. ate/auth/token_store.py +96 -0
  6. ate/behaviors/__init__.py +12 -0
  7. ate/behaviors/approach.py +399 -0
  8. ate/cli.py +855 -4551
  9. ate/client.py +90 -0
  10. ate/commands/__init__.py +168 -0
  11. ate/commands/auth.py +389 -0
  12. ate/commands/bridge.py +448 -0
  13. ate/commands/data.py +185 -0
  14. ate/commands/deps.py +111 -0
  15. ate/commands/generate.py +384 -0
  16. ate/commands/memory.py +907 -0
  17. ate/commands/parts.py +166 -0
  18. ate/commands/primitive.py +399 -0
  19. ate/commands/protocol.py +288 -0
  20. ate/commands/recording.py +524 -0
  21. ate/commands/repo.py +154 -0
  22. ate/commands/simulation.py +291 -0
  23. ate/commands/skill.py +303 -0
  24. ate/commands/skills.py +487 -0
  25. ate/commands/team.py +147 -0
  26. ate/commands/workflow.py +271 -0
  27. ate/detection/__init__.py +38 -0
  28. ate/detection/base.py +142 -0
  29. ate/detection/color_detector.py +402 -0
  30. ate/detection/trash_detector.py +322 -0
  31. ate/drivers/__init__.py +18 -6
  32. ate/drivers/ble_transport.py +405 -0
  33. ate/drivers/mechdog.py +360 -24
  34. ate/drivers/wifi_camera.py +477 -0
  35. ate/interfaces/__init__.py +16 -0
  36. ate/interfaces/base.py +2 -0
  37. ate/interfaces/sensors.py +247 -0
  38. ate/llm_proxy.py +239 -0
  39. ate/memory/__init__.py +35 -0
  40. ate/memory/cloud.py +244 -0
  41. ate/memory/context.py +269 -0
  42. ate/memory/embeddings.py +184 -0
  43. ate/memory/export.py +26 -0
  44. ate/memory/merge.py +146 -0
  45. ate/memory/migrate/__init__.py +34 -0
  46. ate/memory/migrate/base.py +89 -0
  47. ate/memory/migrate/pipeline.py +189 -0
  48. ate/memory/migrate/sources/__init__.py +13 -0
  49. ate/memory/migrate/sources/chroma.py +170 -0
  50. ate/memory/migrate/sources/pinecone.py +120 -0
  51. ate/memory/migrate/sources/qdrant.py +110 -0
  52. ate/memory/migrate/sources/weaviate.py +160 -0
  53. ate/memory/reranker.py +353 -0
  54. ate/memory/search.py +26 -0
  55. ate/memory/store.py +548 -0
  56. ate/recording/__init__.py +42 -3
  57. ate/recording/session.py +12 -2
  58. ate/recording/visual.py +416 -0
  59. ate/robot/__init__.py +142 -0
  60. ate/robot/agentic_servo.py +856 -0
  61. ate/robot/behaviors.py +493 -0
  62. ate/robot/ble_capture.py +1000 -0
  63. ate/robot/ble_enumerate.py +506 -0
  64. ate/robot/calibration.py +88 -3
  65. ate/robot/calibration_state.py +388 -0
  66. ate/robot/commands.py +143 -11
  67. ate/robot/direction_calibration.py +554 -0
  68. ate/robot/discovery.py +104 -2
  69. ate/robot/llm_system_id.py +654 -0
  70. ate/robot/locomotion_calibration.py +508 -0
  71. ate/robot/marker_generator.py +611 -0
  72. ate/robot/perception.py +502 -0
  73. ate/robot/primitives.py +614 -0
  74. ate/robot/profiles.py +6 -0
  75. ate/robot/registry.py +5 -2
  76. ate/robot/servo_mapper.py +1153 -0
  77. ate/robot/skill_upload.py +285 -3
  78. ate/robot/target_calibration.py +500 -0
  79. ate/robot/teach.py +515 -0
  80. ate/robot/types.py +242 -0
  81. ate/robot/visual_labeler.py +9 -0
  82. ate/robot/visual_servo_loop.py +494 -0
  83. ate/robot/visual_servoing.py +570 -0
  84. ate/robot/visual_system_id.py +906 -0
  85. ate/transports/__init__.py +121 -0
  86. ate/transports/base.py +394 -0
  87. ate/transports/ble.py +405 -0
  88. ate/transports/hybrid.py +444 -0
  89. ate/transports/serial.py +345 -0
  90. ate/urdf/__init__.py +30 -0
  91. ate/urdf/capture.py +582 -0
  92. ate/urdf/cloud.py +491 -0
  93. ate/urdf/collision.py +271 -0
  94. ate/urdf/commands.py +708 -0
  95. ate/urdf/depth.py +360 -0
  96. ate/urdf/inertial.py +312 -0
  97. ate/urdf/kinematics.py +330 -0
  98. ate/urdf/lifting.py +415 -0
  99. ate/urdf/meshing.py +300 -0
  100. ate/urdf/models/__init__.py +110 -0
  101. ate/urdf/models/depth_anything.py +253 -0
  102. ate/urdf/models/sam2.py +324 -0
  103. ate/urdf/motion_analysis.py +396 -0
  104. ate/urdf/pipeline.py +468 -0
  105. ate/urdf/scale.py +256 -0
  106. ate/urdf/scan_session.py +411 -0
  107. ate/urdf/segmentation.py +299 -0
  108. ate/urdf/synthesis.py +319 -0
  109. ate/urdf/topology.py +336 -0
  110. ate/urdf/validation.py +371 -0
  111. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.1.dist-info}/METADATA +1 -1
  112. foodforthought_cli-0.3.1.dist-info/RECORD +166 -0
  113. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.1.dist-info}/WHEEL +1 -1
  114. foodforthought_cli-0.2.8.dist-info/RECORD +0 -73
  115. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.1.dist-info}/entry_points.txt +0 -0
  116. {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.1.dist-info}/top_level.txt +0 -0
ate/urdf/collision.py ADDED
@@ -0,0 +1,271 @@
1
+ """
2
+ Collision mesh generation via convex decomposition.
3
+
4
+ This module handles Phase 4b of the pipeline:
5
+ 1. Load visual meshes
6
+ 2. Decompose into convex hulls using CoACD or V-HACD
7
+ 3. Save collision meshes
8
+
9
+ Physics engines require convex collision shapes for stable simulation.
10
+ """
11
+
12
+ import logging
13
+ from typing import Dict, List, Optional, Tuple
14
+ from pathlib import Path
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ try:
19
+ import numpy as np
20
+ NUMPY_AVAILABLE = True
21
+ except ImportError:
22
+ NUMPY_AVAILABLE = False
23
+ np = None
24
+
25
+ try:
26
+ import trimesh
27
+ TRIMESH_AVAILABLE = True
28
+ except ImportError:
29
+ TRIMESH_AVAILABLE = False
30
+ trimesh = None
31
+
32
+
33
+ class CollisionError(Exception):
34
+ """Error during collision mesh generation."""
35
+ pass
36
+
37
+
38
+ def coacd_decompose(
39
+ mesh: "trimesh.Trimesh",
40
+ max_hulls: int = 8,
41
+ threshold: float = 0.05,
42
+ ) -> List["trimesh.Trimesh"]:
43
+ """
44
+ Decompose mesh into convex hulls using CoACD.
45
+
46
+ CoACD (Approximate Convex Decomposition) is more accurate than V-HACD
47
+ but may not be available on all systems.
48
+
49
+ Args:
50
+ mesh: Input mesh to decompose
51
+ max_hulls: Maximum number of convex hulls
52
+ threshold: Concavity threshold
53
+
54
+ Returns:
55
+ List of convex hull meshes
56
+ """
57
+ try:
58
+ import coacd
59
+
60
+ # CoACD expects vertices and faces
61
+ vertices = np.array(mesh.vertices, dtype=np.float64)
62
+ faces = np.array(mesh.faces, dtype=np.int32)
63
+
64
+ # Run decomposition
65
+ parts = coacd.run_coacd(
66
+ vertices,
67
+ faces,
68
+ threshold=threshold,
69
+ max_convex_hull=max_hulls,
70
+ )
71
+
72
+ # Convert to trimesh objects
73
+ hulls = []
74
+ for verts, tris in parts:
75
+ hull = trimesh.Trimesh(vertices=verts, faces=tris)
76
+ hull.fix_normals()
77
+ hulls.append(hull)
78
+
79
+ logger.debug(f"CoACD decomposed into {len(hulls)} convex hulls")
80
+ return hulls
81
+
82
+ except ImportError:
83
+ logger.warning("CoACD not available, falling back to V-HACD")
84
+ return vhacd_decompose(mesh, max_hulls)
85
+
86
+
87
+ def vhacd_decompose(
88
+ mesh: "trimesh.Trimesh",
89
+ max_hulls: int = 8,
90
+ resolution: int = 100000,
91
+ ) -> List["trimesh.Trimesh"]:
92
+ """
93
+ Decompose mesh into convex hulls using V-HACD.
94
+
95
+ V-HACD (Volumetric Hierarchical Approximate Convex Decomposition)
96
+ is a classic algorithm available in trimesh.
97
+
98
+ Args:
99
+ mesh: Input mesh to decompose
100
+ max_hulls: Maximum number of convex hulls
101
+ resolution: Voxelization resolution
102
+
103
+ Returns:
104
+ List of convex hull meshes
105
+ """
106
+ if not TRIMESH_AVAILABLE:
107
+ raise CollisionError("trimesh not available")
108
+
109
+ try:
110
+ # Try trimesh's built-in convex decomposition
111
+ hulls = mesh.convex_decomposition(
112
+ maxhulls=max_hulls,
113
+ resolution=resolution,
114
+ )
115
+
116
+ if isinstance(hulls, trimesh.Trimesh):
117
+ hulls = [hulls]
118
+
119
+ logger.debug(f"V-HACD decomposed into {len(hulls)} convex hulls")
120
+ return hulls
121
+
122
+ except Exception as e:
123
+ logger.warning(f"V-HACD failed: {e}, using single convex hull")
124
+ return [mesh.convex_hull]
125
+
126
+
127
+ def simple_convex_hull(mesh: "trimesh.Trimesh") -> List["trimesh.Trimesh"]:
128
+ """
129
+ Create a single convex hull from mesh.
130
+
131
+ Simplest collision shape, fastest simulation.
132
+
133
+ Args:
134
+ mesh: Input mesh
135
+
136
+ Returns:
137
+ List containing single convex hull
138
+ """
139
+ if not TRIMESH_AVAILABLE:
140
+ raise CollisionError("trimesh not available")
141
+
142
+ hull = mesh.convex_hull
143
+ return [hull]
144
+
145
+
146
+ def generate_collision_mesh(
147
+ visual_mesh_path: Path,
148
+ output_path: Path,
149
+ max_hulls: int = 8,
150
+ method: str = "auto",
151
+ ) -> Path:
152
+ """
153
+ Generate collision mesh from visual mesh.
154
+
155
+ Args:
156
+ visual_mesh_path: Path to visual mesh (OBJ)
157
+ output_path: Path for collision mesh output
158
+ max_hulls: Maximum convex hulls
159
+ method: "coacd", "vhacd", "hull", or "auto"
160
+
161
+ Returns:
162
+ Path to collision mesh
163
+ """
164
+ if not TRIMESH_AVAILABLE:
165
+ raise CollisionError("trimesh not available. Run: pip install trimesh")
166
+
167
+ # Load visual mesh
168
+ mesh = trimesh.load(str(visual_mesh_path))
169
+
170
+ if not isinstance(mesh, trimesh.Trimesh):
171
+ # Handle scene objects
172
+ if hasattr(mesh, 'geometry'):
173
+ meshes = list(mesh.geometry.values())
174
+ if meshes:
175
+ mesh = meshes[0]
176
+ else:
177
+ raise CollisionError(f"No geometry in {visual_mesh_path}")
178
+ else:
179
+ raise CollisionError(f"Invalid mesh type in {visual_mesh_path}")
180
+
181
+ # Decompose into convex hulls
182
+ if method == "auto":
183
+ try:
184
+ hulls = coacd_decompose(mesh, max_hulls)
185
+ except Exception:
186
+ try:
187
+ hulls = vhacd_decompose(mesh, max_hulls)
188
+ except Exception:
189
+ hulls = simple_convex_hull(mesh)
190
+ elif method == "coacd":
191
+ hulls = coacd_decompose(mesh, max_hulls)
192
+ elif method == "vhacd":
193
+ hulls = vhacd_decompose(mesh, max_hulls)
194
+ elif method == "hull":
195
+ hulls = simple_convex_hull(mesh)
196
+ else:
197
+ raise CollisionError(f"Unknown decomposition method: {method}")
198
+
199
+ # Combine hulls into single mesh for export
200
+ if len(hulls) == 1:
201
+ combined = hulls[0]
202
+ else:
203
+ combined = trimesh.util.concatenate(hulls)
204
+
205
+ # Export
206
+ output_path.parent.mkdir(parents=True, exist_ok=True)
207
+ combined.export(str(output_path))
208
+
209
+ logger.info(
210
+ f"Generated collision mesh: {output_path} "
211
+ f"({len(hulls)} hulls, {len(combined.faces)} faces)"
212
+ )
213
+ return output_path
214
+
215
+
216
+ def generate_all_collision_meshes(
217
+ session: "ScanSession",
218
+ max_hulls: int = 8,
219
+ method: str = "auto",
220
+ progress_callback: Optional[callable] = None,
221
+ ) -> Dict[str, Path]:
222
+ """
223
+ Generate collision meshes for all links.
224
+
225
+ Args:
226
+ session: ScanSession with visual meshes
227
+ max_hulls: Maximum hulls per link
228
+ method: Decomposition method
229
+ progress_callback: Optional progress callback
230
+
231
+ Returns:
232
+ Dict mapping link_name -> collision_mesh_path
233
+ """
234
+ meshes_dir = session.meshes_dir
235
+ visual_meshes = list(meshes_dir.glob("*_visual.obj"))
236
+
237
+ if not visual_meshes:
238
+ raise CollisionError(f"No visual meshes found in {meshes_dir}")
239
+
240
+ result = {}
241
+ total = len(visual_meshes)
242
+
243
+ for i, visual_path in enumerate(visual_meshes):
244
+ link_name = visual_path.stem.replace("_visual", "")
245
+ collision_path = meshes_dir / f"{link_name}_collision.obj"
246
+
247
+ try:
248
+ generate_collision_mesh(visual_path, collision_path, max_hulls, method)
249
+ result[link_name] = collision_path
250
+ except Exception as e:
251
+ logger.error(f"Failed to generate collision mesh for {link_name}: {e}")
252
+
253
+ if progress_callback:
254
+ progress_callback(i + 1, total)
255
+
256
+ # Update session
257
+ session.metadata.mesh_complete = True
258
+ session.save_metadata()
259
+
260
+ logger.info(f"Generated {len(result)} collision meshes")
261
+ return result
262
+
263
+
264
+ __all__ = [
265
+ "CollisionError",
266
+ "coacd_decompose",
267
+ "vhacd_decompose",
268
+ "simple_convex_hull",
269
+ "generate_collision_mesh",
270
+ "generate_all_collision_meshes",
271
+ ]