foodforthought-cli 0.2.7__py3-none-any.whl → 0.3.0__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 (131) 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 +100 -0
  7. ate/behaviors/approach.py +399 -0
  8. ate/behaviors/common.py +686 -0
  9. ate/behaviors/tree.py +454 -0
  10. ate/cli.py +855 -3995
  11. ate/client.py +90 -0
  12. ate/commands/__init__.py +168 -0
  13. ate/commands/auth.py +389 -0
  14. ate/commands/bridge.py +448 -0
  15. ate/commands/data.py +185 -0
  16. ate/commands/deps.py +111 -0
  17. ate/commands/generate.py +384 -0
  18. ate/commands/memory.py +907 -0
  19. ate/commands/parts.py +166 -0
  20. ate/commands/primitive.py +399 -0
  21. ate/commands/protocol.py +288 -0
  22. ate/commands/recording.py +524 -0
  23. ate/commands/repo.py +154 -0
  24. ate/commands/simulation.py +291 -0
  25. ate/commands/skill.py +303 -0
  26. ate/commands/skills.py +487 -0
  27. ate/commands/team.py +147 -0
  28. ate/commands/workflow.py +271 -0
  29. ate/detection/__init__.py +38 -0
  30. ate/detection/base.py +142 -0
  31. ate/detection/color_detector.py +399 -0
  32. ate/detection/trash_detector.py +322 -0
  33. ate/drivers/__init__.py +39 -0
  34. ate/drivers/ble_transport.py +405 -0
  35. ate/drivers/mechdog.py +942 -0
  36. ate/drivers/wifi_camera.py +477 -0
  37. ate/interfaces/__init__.py +187 -0
  38. ate/interfaces/base.py +273 -0
  39. ate/interfaces/body.py +267 -0
  40. ate/interfaces/detection.py +282 -0
  41. ate/interfaces/locomotion.py +422 -0
  42. ate/interfaces/manipulation.py +408 -0
  43. ate/interfaces/navigation.py +389 -0
  44. ate/interfaces/perception.py +362 -0
  45. ate/interfaces/sensors.py +247 -0
  46. ate/interfaces/types.py +371 -0
  47. ate/llm_proxy.py +239 -0
  48. ate/mcp_server.py +387 -0
  49. ate/memory/__init__.py +35 -0
  50. ate/memory/cloud.py +244 -0
  51. ate/memory/context.py +269 -0
  52. ate/memory/embeddings.py +184 -0
  53. ate/memory/export.py +26 -0
  54. ate/memory/merge.py +146 -0
  55. ate/memory/migrate/__init__.py +34 -0
  56. ate/memory/migrate/base.py +89 -0
  57. ate/memory/migrate/pipeline.py +189 -0
  58. ate/memory/migrate/sources/__init__.py +13 -0
  59. ate/memory/migrate/sources/chroma.py +170 -0
  60. ate/memory/migrate/sources/pinecone.py +120 -0
  61. ate/memory/migrate/sources/qdrant.py +110 -0
  62. ate/memory/migrate/sources/weaviate.py +160 -0
  63. ate/memory/reranker.py +353 -0
  64. ate/memory/search.py +26 -0
  65. ate/memory/store.py +548 -0
  66. ate/recording/__init__.py +83 -0
  67. ate/recording/demonstration.py +378 -0
  68. ate/recording/session.py +415 -0
  69. ate/recording/upload.py +304 -0
  70. ate/recording/visual.py +416 -0
  71. ate/recording/wrapper.py +95 -0
  72. ate/robot/__init__.py +221 -0
  73. ate/robot/agentic_servo.py +856 -0
  74. ate/robot/behaviors.py +493 -0
  75. ate/robot/ble_capture.py +1000 -0
  76. ate/robot/ble_enumerate.py +506 -0
  77. ate/robot/calibration.py +668 -0
  78. ate/robot/calibration_state.py +388 -0
  79. ate/robot/commands.py +3735 -0
  80. ate/robot/direction_calibration.py +554 -0
  81. ate/robot/discovery.py +441 -0
  82. ate/robot/introspection.py +330 -0
  83. ate/robot/llm_system_id.py +654 -0
  84. ate/robot/locomotion_calibration.py +508 -0
  85. ate/robot/manager.py +270 -0
  86. ate/robot/marker_generator.py +611 -0
  87. ate/robot/perception.py +502 -0
  88. ate/robot/primitives.py +614 -0
  89. ate/robot/profiles.py +281 -0
  90. ate/robot/registry.py +322 -0
  91. ate/robot/servo_mapper.py +1153 -0
  92. ate/robot/skill_upload.py +675 -0
  93. ate/robot/target_calibration.py +500 -0
  94. ate/robot/teach.py +515 -0
  95. ate/robot/types.py +242 -0
  96. ate/robot/visual_labeler.py +1048 -0
  97. ate/robot/visual_servo_loop.py +494 -0
  98. ate/robot/visual_servoing.py +570 -0
  99. ate/robot/visual_system_id.py +906 -0
  100. ate/transports/__init__.py +121 -0
  101. ate/transports/base.py +394 -0
  102. ate/transports/ble.py +405 -0
  103. ate/transports/hybrid.py +444 -0
  104. ate/transports/serial.py +345 -0
  105. ate/urdf/__init__.py +30 -0
  106. ate/urdf/capture.py +582 -0
  107. ate/urdf/cloud.py +491 -0
  108. ate/urdf/collision.py +271 -0
  109. ate/urdf/commands.py +708 -0
  110. ate/urdf/depth.py +360 -0
  111. ate/urdf/inertial.py +312 -0
  112. ate/urdf/kinematics.py +330 -0
  113. ate/urdf/lifting.py +415 -0
  114. ate/urdf/meshing.py +300 -0
  115. ate/urdf/models/__init__.py +110 -0
  116. ate/urdf/models/depth_anything.py +253 -0
  117. ate/urdf/models/sam2.py +324 -0
  118. ate/urdf/motion_analysis.py +396 -0
  119. ate/urdf/pipeline.py +468 -0
  120. ate/urdf/scale.py +256 -0
  121. ate/urdf/scan_session.py +411 -0
  122. ate/urdf/segmentation.py +299 -0
  123. ate/urdf/synthesis.py +319 -0
  124. ate/urdf/topology.py +336 -0
  125. ate/urdf/validation.py +371 -0
  126. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/METADATA +9 -1
  127. foodforthought_cli-0.3.0.dist-info/RECORD +166 -0
  128. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/WHEEL +1 -1
  129. foodforthought_cli-0.2.7.dist-info/RECORD +0 -44
  130. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/entry_points.txt +0 -0
  131. {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,408 @@
1
+ """
2
+ Manipulation interfaces for robot arms and grippers.
3
+
4
+ Supports:
5
+ - Single arms (Franka, UR5, xArm, low-cost arms)
6
+ - Dual arms (PR2, humanoids)
7
+ - Grippers (parallel jaw, vacuum, soft grippers)
8
+
9
+ Key design principle: The interface is about WHAT you want to do,
10
+ not HOW the hardware does it. "Move end effector to position X"
11
+ works whether you have a 6-DOF arm or a 7-DOF arm.
12
+ """
13
+
14
+ from abc import ABC, abstractmethod
15
+ from typing import Optional, List, Tuple
16
+ from enum import Enum, auto
17
+
18
+ from .types import (
19
+ Vector3,
20
+ Quaternion,
21
+ Pose,
22
+ JointState,
23
+ JointLimits,
24
+ GripperState,
25
+ GripperStatus,
26
+ ForceTorqueReading,
27
+ ActionResult,
28
+ )
29
+
30
+
31
+ class MotionType(Enum):
32
+ """Type of motion for arm movements."""
33
+ JOINT = auto() # Move joints directly
34
+ LINEAR = auto() # Straight line in Cartesian space
35
+ ARC = auto() # Arc motion through via point
36
+
37
+
38
+ class ArmInterface(ABC):
39
+ """
40
+ Interface for a robot arm (manipulator).
41
+
42
+ All positions are in the robot's base frame unless otherwise specified.
43
+ All units are SI (meters, radians, Newtons).
44
+ """
45
+
46
+ # =========================================================================
47
+ # End-effector control (Cartesian space)
48
+ # =========================================================================
49
+
50
+ @abstractmethod
51
+ def get_end_effector_pose(self) -> Pose:
52
+ """
53
+ Get current end-effector pose.
54
+
55
+ Returns:
56
+ Pose of end-effector in base frame
57
+ """
58
+ pass
59
+
60
+ @abstractmethod
61
+ def move_to_pose(
62
+ self,
63
+ pose: Pose,
64
+ speed: float = 0.1,
65
+ motion_type: MotionType = MotionType.LINEAR
66
+ ) -> ActionResult:
67
+ """
68
+ Move end-effector to target pose.
69
+
70
+ Args:
71
+ pose: Target pose in base frame
72
+ speed: Speed in m/s
73
+ motion_type: Type of motion path
74
+
75
+ Returns:
76
+ ActionResult (when motion complete)
77
+ """
78
+ pass
79
+
80
+ @abstractmethod
81
+ def move_to_position(
82
+ self,
83
+ position: Vector3,
84
+ orientation: Optional[Quaternion] = None,
85
+ speed: float = 0.1
86
+ ) -> ActionResult:
87
+ """
88
+ Move end-effector to position, optionally with orientation.
89
+
90
+ Args:
91
+ position: Target position in base frame
92
+ orientation: Target orientation (None = keep current)
93
+ speed: Speed in m/s
94
+
95
+ Returns:
96
+ ActionResult
97
+ """
98
+ pass
99
+
100
+ def move_relative(
101
+ self,
102
+ delta: Vector3,
103
+ speed: float = 0.1
104
+ ) -> ActionResult:
105
+ """
106
+ Move end-effector relative to current position.
107
+
108
+ Args:
109
+ delta: Relative movement in base frame
110
+ speed: Speed in m/s
111
+
112
+ Returns:
113
+ ActionResult
114
+ """
115
+ current = self.get_end_effector_pose()
116
+ target = Vector3(
117
+ current.position.x + delta.x,
118
+ current.position.y + delta.y,
119
+ current.position.z + delta.z
120
+ )
121
+ return self.move_to_position(target, current.orientation, speed)
122
+
123
+ # =========================================================================
124
+ # Joint control
125
+ # =========================================================================
126
+
127
+ @abstractmethod
128
+ def get_joint_state(self) -> JointState:
129
+ """
130
+ Get current joint state.
131
+
132
+ Returns:
133
+ JointState with positions, velocities, efforts
134
+ """
135
+ pass
136
+
137
+ @abstractmethod
138
+ def move_to_joints(
139
+ self,
140
+ positions: List[float],
141
+ speed: float = 0.5
142
+ ) -> ActionResult:
143
+ """
144
+ Move to target joint positions.
145
+
146
+ Args:
147
+ positions: Target joint angles in radians
148
+ speed: Joint speed in rad/s
149
+
150
+ Returns:
151
+ ActionResult
152
+ """
153
+ pass
154
+
155
+ @abstractmethod
156
+ def get_joint_limits(self) -> List[JointLimits]:
157
+ """
158
+ Get limits for all joints.
159
+
160
+ Returns:
161
+ List of JointLimits for each joint
162
+ """
163
+ pass
164
+
165
+ def get_num_joints(self) -> int:
166
+ """Get number of joints in the arm."""
167
+ return len(self.get_joint_state().positions)
168
+
169
+ def get_joint_names(self) -> List[str]:
170
+ """
171
+ Get joint names.
172
+
173
+ Default: joint_0, joint_1, ...
174
+ Override for meaningful names.
175
+ """
176
+ return [f"joint_{i}" for i in range(self.get_num_joints())]
177
+
178
+ # =========================================================================
179
+ # Common poses
180
+ # =========================================================================
181
+
182
+ @abstractmethod
183
+ def go_home(self) -> ActionResult:
184
+ """
185
+ Move to home/ready position.
186
+
187
+ Returns:
188
+ ActionResult
189
+ """
190
+ pass
191
+
192
+ def go_to_named_pose(self, name: str) -> ActionResult:
193
+ """
194
+ Move to a named pose (e.g., "home", "ready", "stow").
195
+
196
+ Override to support robot-specific poses.
197
+ """
198
+ if name == "home":
199
+ return self.go_home()
200
+ return ActionResult.error(f"Unknown pose: {name}")
201
+
202
+ # =========================================================================
203
+ # Motion control
204
+ # =========================================================================
205
+
206
+ @abstractmethod
207
+ def stop(self) -> ActionResult:
208
+ """Stop current motion immediately."""
209
+ pass
210
+
211
+ @abstractmethod
212
+ def is_moving(self) -> bool:
213
+ """Check if arm is currently moving."""
214
+ pass
215
+
216
+ def set_speed_factor(self, factor: float) -> ActionResult:
217
+ """
218
+ Set global speed factor (0.0 to 1.0).
219
+
220
+ Affects all subsequent motions.
221
+ """
222
+ return ActionResult.error("Speed factor not supported")
223
+
224
+ # =========================================================================
225
+ # Force/compliance control (optional)
226
+ # =========================================================================
227
+
228
+ def enable_compliance(self, stiffness: Vector3) -> ActionResult:
229
+ """
230
+ Enable compliant/impedance control.
231
+
232
+ Args:
233
+ stiffness: Stiffness in N/m for x, y, z
234
+
235
+ Returns:
236
+ ActionResult
237
+ """
238
+ return ActionResult.error("Compliance control not supported")
239
+
240
+ def disable_compliance(self) -> ActionResult:
241
+ """Disable compliant control, return to position control."""
242
+ return ActionResult.error("Compliance control not supported")
243
+
244
+ def get_force_torque(self) -> Optional[ForceTorqueReading]:
245
+ """
246
+ Get force/torque at end effector.
247
+
248
+ Returns:
249
+ ForceTorqueReading or None if no sensor
250
+ """
251
+ return None
252
+
253
+
254
+ class GripperInterface(ABC):
255
+ """
256
+ Interface for grippers/end-effectors.
257
+
258
+ Supports various gripper types:
259
+ - Parallel jaw (most common)
260
+ - Vacuum/suction
261
+ - Soft grippers
262
+ - Multi-finger hands
263
+ """
264
+
265
+ @abstractmethod
266
+ def grasp(self, force: float = 10.0) -> ActionResult:
267
+ """
268
+ Close gripper to grasp an object.
269
+
270
+ Args:
271
+ force: Gripping force in Newtons
272
+
273
+ Returns:
274
+ ActionResult (success if object detected)
275
+ """
276
+ pass
277
+
278
+ @abstractmethod
279
+ def release(self) -> ActionResult:
280
+ """
281
+ Open gripper to release object.
282
+
283
+ Returns:
284
+ ActionResult
285
+ """
286
+ pass
287
+
288
+ @abstractmethod
289
+ def get_state(self) -> GripperState:
290
+ """
291
+ Get current gripper state.
292
+
293
+ Returns:
294
+ GripperState (OPEN, CLOSED, HOLDING, etc.)
295
+ """
296
+ pass
297
+
298
+ @abstractmethod
299
+ def get_status(self) -> GripperStatus:
300
+ """
301
+ Get detailed gripper status.
302
+
303
+ Returns:
304
+ GripperStatus with position, force, object detection
305
+ """
306
+ pass
307
+
308
+ def set_position(self, position: float) -> ActionResult:
309
+ """
310
+ Set gripper to specific position.
311
+
312
+ Args:
313
+ position: 0.0 = fully closed, 1.0 = fully open
314
+
315
+ Returns:
316
+ ActionResult
317
+ """
318
+ if position < 0.5:
319
+ return self.grasp()
320
+ else:
321
+ return self.release()
322
+
323
+ def is_holding_object(self) -> bool:
324
+ """Check if gripper is holding an object."""
325
+ return self.get_state() == GripperState.HOLDING
326
+
327
+ # =========================================================================
328
+ # Gripper-specific capabilities
329
+ # =========================================================================
330
+
331
+ def set_speed(self, speed: float) -> ActionResult:
332
+ """
333
+ Set gripper open/close speed.
334
+
335
+ Args:
336
+ speed: Speed factor (0.0 to 1.0)
337
+ """
338
+ return ActionResult.error("Speed control not supported")
339
+
340
+ def calibrate(self) -> ActionResult:
341
+ """Calibrate gripper (find limits)."""
342
+ return ActionResult.error("Calibration not supported")
343
+
344
+
345
+ class DualArmInterface(ABC):
346
+ """
347
+ Interface for dual-arm robots.
348
+
349
+ Adds coordination primitives beyond two separate arms.
350
+ """
351
+
352
+ @abstractmethod
353
+ def get_left_arm(self) -> ArmInterface:
354
+ """Get interface to left arm."""
355
+ pass
356
+
357
+ @abstractmethod
358
+ def get_right_arm(self) -> ArmInterface:
359
+ """Get interface to right arm."""
360
+ pass
361
+
362
+ @abstractmethod
363
+ def move_both_to_poses(
364
+ self,
365
+ left_pose: Pose,
366
+ right_pose: Pose,
367
+ speed: float = 0.1
368
+ ) -> ActionResult:
369
+ """
370
+ Move both arms simultaneously to target poses.
371
+
372
+ Args:
373
+ left_pose: Target for left arm
374
+ right_pose: Target for right arm
375
+ speed: Speed in m/s
376
+
377
+ Returns:
378
+ ActionResult
379
+ """
380
+ pass
381
+
382
+ def bimanual_grasp(
383
+ self,
384
+ left_pose: Pose,
385
+ right_pose: Pose,
386
+ force: float = 10.0
387
+ ) -> ActionResult:
388
+ """
389
+ Coordinated grasp with both arms.
390
+
391
+ Moves arms to approach poses, then closes both grippers
392
+ with coordinated force control.
393
+ """
394
+ # Default implementation - override for better coordination
395
+ result = self.move_both_to_poses(left_pose, right_pose)
396
+ if not result.success:
397
+ return result
398
+
399
+ # Close grippers
400
+ left_gripper = getattr(self.get_left_arm(), 'gripper', None)
401
+ right_gripper = getattr(self.get_right_arm(), 'gripper', None)
402
+
403
+ if left_gripper:
404
+ left_gripper.grasp(force)
405
+ if right_gripper:
406
+ right_gripper.grasp(force)
407
+
408
+ return ActionResult.ok("Bimanual grasp complete")