foodforthought-cli 0.2.8__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.
- ate/__init__.py +6 -0
- ate/__main__.py +16 -0
- ate/auth/__init__.py +1 -0
- ate/auth/device_flow.py +141 -0
- ate/auth/token_store.py +96 -0
- ate/behaviors/__init__.py +12 -0
- ate/behaviors/approach.py +399 -0
- ate/cli.py +855 -4551
- ate/client.py +90 -0
- ate/commands/__init__.py +168 -0
- ate/commands/auth.py +389 -0
- ate/commands/bridge.py +448 -0
- ate/commands/data.py +185 -0
- ate/commands/deps.py +111 -0
- ate/commands/generate.py +384 -0
- ate/commands/memory.py +907 -0
- ate/commands/parts.py +166 -0
- ate/commands/primitive.py +399 -0
- ate/commands/protocol.py +288 -0
- ate/commands/recording.py +524 -0
- ate/commands/repo.py +154 -0
- ate/commands/simulation.py +291 -0
- ate/commands/skill.py +303 -0
- ate/commands/skills.py +487 -0
- ate/commands/team.py +147 -0
- ate/commands/workflow.py +271 -0
- ate/detection/__init__.py +38 -0
- ate/detection/base.py +142 -0
- ate/detection/color_detector.py +399 -0
- ate/detection/trash_detector.py +322 -0
- ate/drivers/__init__.py +18 -6
- ate/drivers/ble_transport.py +405 -0
- ate/drivers/mechdog.py +360 -24
- ate/drivers/wifi_camera.py +477 -0
- ate/interfaces/__init__.py +16 -0
- ate/interfaces/base.py +2 -0
- ate/interfaces/sensors.py +247 -0
- ate/llm_proxy.py +239 -0
- ate/memory/__init__.py +35 -0
- ate/memory/cloud.py +244 -0
- ate/memory/context.py +269 -0
- ate/memory/embeddings.py +184 -0
- ate/memory/export.py +26 -0
- ate/memory/merge.py +146 -0
- ate/memory/migrate/__init__.py +34 -0
- ate/memory/migrate/base.py +89 -0
- ate/memory/migrate/pipeline.py +189 -0
- ate/memory/migrate/sources/__init__.py +13 -0
- ate/memory/migrate/sources/chroma.py +170 -0
- ate/memory/migrate/sources/pinecone.py +120 -0
- ate/memory/migrate/sources/qdrant.py +110 -0
- ate/memory/migrate/sources/weaviate.py +160 -0
- ate/memory/reranker.py +353 -0
- ate/memory/search.py +26 -0
- ate/memory/store.py +548 -0
- ate/recording/__init__.py +42 -3
- ate/recording/session.py +12 -2
- ate/recording/visual.py +416 -0
- ate/robot/__init__.py +142 -0
- ate/robot/agentic_servo.py +856 -0
- ate/robot/behaviors.py +493 -0
- ate/robot/ble_capture.py +1000 -0
- ate/robot/ble_enumerate.py +506 -0
- ate/robot/calibration.py +88 -3
- ate/robot/calibration_state.py +388 -0
- ate/robot/commands.py +143 -11
- ate/robot/direction_calibration.py +554 -0
- ate/robot/discovery.py +104 -2
- ate/robot/llm_system_id.py +654 -0
- ate/robot/locomotion_calibration.py +508 -0
- ate/robot/marker_generator.py +611 -0
- ate/robot/perception.py +502 -0
- ate/robot/primitives.py +614 -0
- ate/robot/profiles.py +6 -0
- ate/robot/registry.py +5 -2
- ate/robot/servo_mapper.py +1153 -0
- ate/robot/skill_upload.py +285 -3
- ate/robot/target_calibration.py +500 -0
- ate/robot/teach.py +515 -0
- ate/robot/types.py +242 -0
- ate/robot/visual_labeler.py +9 -0
- ate/robot/visual_servo_loop.py +494 -0
- ate/robot/visual_servoing.py +570 -0
- ate/robot/visual_system_id.py +906 -0
- ate/transports/__init__.py +121 -0
- ate/transports/base.py +394 -0
- ate/transports/ble.py +405 -0
- ate/transports/hybrid.py +444 -0
- ate/transports/serial.py +345 -0
- ate/urdf/__init__.py +30 -0
- ate/urdf/capture.py +582 -0
- ate/urdf/cloud.py +491 -0
- ate/urdf/collision.py +271 -0
- ate/urdf/commands.py +708 -0
- ate/urdf/depth.py +360 -0
- ate/urdf/inertial.py +312 -0
- ate/urdf/kinematics.py +330 -0
- ate/urdf/lifting.py +415 -0
- ate/urdf/meshing.py +300 -0
- ate/urdf/models/__init__.py +110 -0
- ate/urdf/models/depth_anything.py +253 -0
- ate/urdf/models/sam2.py +324 -0
- ate/urdf/motion_analysis.py +396 -0
- ate/urdf/pipeline.py +468 -0
- ate/urdf/scale.py +256 -0
- ate/urdf/scan_session.py +411 -0
- ate/urdf/segmentation.py +299 -0
- ate/urdf/synthesis.py +319 -0
- ate/urdf/topology.py +336 -0
- ate/urdf/validation.py +371 -0
- {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/METADATA +1 -1
- foodforthought_cli-0.3.0.dist-info/RECORD +166 -0
- {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/WHEEL +1 -1
- foodforthought_cli-0.2.8.dist-info/RECORD +0 -73
- {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/entry_points.txt +0 -0
- {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.0.dist-info}/top_level.txt +0 -0
ate/robot/types.py
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified type definitions for robot joints and servos.
|
|
3
|
+
|
|
4
|
+
This module provides a single source of truth for joint-related enums,
|
|
5
|
+
avoiding inconsistencies across different modules.
|
|
6
|
+
|
|
7
|
+
Two distinct concepts:
|
|
8
|
+
- JointType: How the joint moves mechanically (URDF standard)
|
|
9
|
+
- JointRole: What function the joint serves on the robot body
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Optional, List
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JointType(Enum):
|
|
17
|
+
"""
|
|
18
|
+
Mechanical joint type following URDF conventions.
|
|
19
|
+
|
|
20
|
+
Describes HOW the joint moves, not what it's used for.
|
|
21
|
+
Compatible with standard robotics formats (URDF, SDF, MJCF).
|
|
22
|
+
"""
|
|
23
|
+
UNKNOWN = "unknown"
|
|
24
|
+
REVOLUTE = "revolute" # Rotation around an axis with limits
|
|
25
|
+
PRISMATIC = "prismatic" # Linear translation along an axis
|
|
26
|
+
CONTINUOUS = "continuous" # Rotation without limits (wheels)
|
|
27
|
+
FIXED = "fixed" # No relative motion
|
|
28
|
+
FLOATING = "floating" # 6DOF free motion (rare)
|
|
29
|
+
PLANAR = "planar" # 2D motion in a plane (rare)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_string(cls, value: str) -> "JointType":
|
|
33
|
+
"""
|
|
34
|
+
Parse joint type from string, with helpful error messages.
|
|
35
|
+
|
|
36
|
+
Accepts common variations and provides suggestions on failure.
|
|
37
|
+
"""
|
|
38
|
+
if not value:
|
|
39
|
+
return cls.UNKNOWN
|
|
40
|
+
|
|
41
|
+
normalized = value.lower().strip()
|
|
42
|
+
|
|
43
|
+
# Direct match
|
|
44
|
+
for member in cls:
|
|
45
|
+
if member.value == normalized:
|
|
46
|
+
return member
|
|
47
|
+
|
|
48
|
+
# Common aliases
|
|
49
|
+
aliases = {
|
|
50
|
+
"rotary": cls.REVOLUTE,
|
|
51
|
+
"rotation": cls.REVOLUTE,
|
|
52
|
+
"rotate": cls.REVOLUTE,
|
|
53
|
+
"linear": cls.PRISMATIC,
|
|
54
|
+
"sliding": cls.PRISMATIC,
|
|
55
|
+
"slide": cls.PRISMATIC,
|
|
56
|
+
"wheel": cls.CONTINUOUS,
|
|
57
|
+
"static": cls.FIXED,
|
|
58
|
+
"rigid": cls.FIXED,
|
|
59
|
+
"free": cls.FLOATING,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if normalized in aliases:
|
|
63
|
+
return aliases[normalized]
|
|
64
|
+
|
|
65
|
+
# Fuzzy match - check if value contains any known type
|
|
66
|
+
for member in cls:
|
|
67
|
+
if member.value in normalized or normalized in member.value:
|
|
68
|
+
return member
|
|
69
|
+
|
|
70
|
+
# No match - return UNKNOWN but could raise with suggestions
|
|
71
|
+
return cls.UNKNOWN
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def valid_values(cls) -> List[str]:
|
|
75
|
+
"""Return list of valid values for error messages."""
|
|
76
|
+
return [m.value for m in cls if m != cls.UNKNOWN]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class JointRole(Enum):
|
|
80
|
+
"""
|
|
81
|
+
Semantic role/function of a joint on the robot body.
|
|
82
|
+
|
|
83
|
+
Describes WHAT the joint does, not how it moves mechanically.
|
|
84
|
+
Used for semantic skill development and pose mapping.
|
|
85
|
+
"""
|
|
86
|
+
UNKNOWN = "unknown"
|
|
87
|
+
|
|
88
|
+
# Locomotion - Legs
|
|
89
|
+
HIP_ROLL = "hip_roll"
|
|
90
|
+
HIP_PITCH = "hip_pitch"
|
|
91
|
+
HIP_YAW = "hip_yaw"
|
|
92
|
+
KNEE = "knee"
|
|
93
|
+
ANKLE = "ankle"
|
|
94
|
+
ANKLE_ROLL = "ankle_roll"
|
|
95
|
+
|
|
96
|
+
# Arm - Shoulder
|
|
97
|
+
SHOULDER_PAN = "shoulder_pan"
|
|
98
|
+
SHOULDER_LIFT = "shoulder_lift"
|
|
99
|
+
SHOULDER_ROLL = "shoulder_roll"
|
|
100
|
+
|
|
101
|
+
# Arm - Elbow/Wrist
|
|
102
|
+
ELBOW = "elbow"
|
|
103
|
+
ELBOW_ROLL = "elbow_roll"
|
|
104
|
+
WRIST_ROLL = "wrist_roll"
|
|
105
|
+
WRIST_PITCH = "wrist_pitch"
|
|
106
|
+
WRIST_YAW = "wrist_yaw"
|
|
107
|
+
|
|
108
|
+
# End Effector
|
|
109
|
+
GRIPPER = "gripper"
|
|
110
|
+
FINGER = "finger"
|
|
111
|
+
|
|
112
|
+
# Body/Head
|
|
113
|
+
HEAD_PAN = "head_pan"
|
|
114
|
+
HEAD_TILT = "head_tilt"
|
|
115
|
+
BODY_PITCH = "body_pitch"
|
|
116
|
+
BODY_ROLL = "body_roll"
|
|
117
|
+
TORSO = "torso"
|
|
118
|
+
|
|
119
|
+
# Wheels/Tracks
|
|
120
|
+
WHEEL = "wheel"
|
|
121
|
+
TRACK = "track"
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def from_string(cls, value: str) -> "JointRole":
|
|
125
|
+
"""
|
|
126
|
+
Parse joint role from string, with intelligent fallbacks.
|
|
127
|
+
|
|
128
|
+
Handles common variations and provides best-effort matching.
|
|
129
|
+
"""
|
|
130
|
+
if not value:
|
|
131
|
+
return cls.UNKNOWN
|
|
132
|
+
|
|
133
|
+
normalized = value.lower().strip().replace("-", "_").replace(" ", "_")
|
|
134
|
+
|
|
135
|
+
# Direct match
|
|
136
|
+
for member in cls:
|
|
137
|
+
if member.value == normalized:
|
|
138
|
+
return member
|
|
139
|
+
|
|
140
|
+
# Common aliases and partial matches
|
|
141
|
+
aliases = {
|
|
142
|
+
# Arm shortcuts
|
|
143
|
+
"shoulder": cls.SHOULDER_LIFT,
|
|
144
|
+
"arm_shoulder": cls.SHOULDER_LIFT,
|
|
145
|
+
"arm_elbow": cls.ELBOW,
|
|
146
|
+
"arm_wrist": cls.WRIST_ROLL,
|
|
147
|
+
|
|
148
|
+
# Leg shortcuts
|
|
149
|
+
"hip": cls.HIP_PITCH,
|
|
150
|
+
"thigh": cls.HIP_PITCH,
|
|
151
|
+
"calf": cls.KNEE,
|
|
152
|
+
"shin": cls.KNEE,
|
|
153
|
+
"foot": cls.ANKLE,
|
|
154
|
+
|
|
155
|
+
# Generic URDF types -> infer role from context
|
|
156
|
+
"revolute": cls.UNKNOWN, # Could be anything
|
|
157
|
+
"prismatic": cls.UNKNOWN,
|
|
158
|
+
"continuous": cls.WHEEL,
|
|
159
|
+
|
|
160
|
+
# End effector
|
|
161
|
+
"claw": cls.GRIPPER,
|
|
162
|
+
"hand": cls.GRIPPER,
|
|
163
|
+
"pincer": cls.GRIPPER,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if normalized in aliases:
|
|
167
|
+
return aliases[normalized]
|
|
168
|
+
|
|
169
|
+
# Fuzzy match - check if value contains any known role
|
|
170
|
+
for member in cls:
|
|
171
|
+
if member.value in normalized:
|
|
172
|
+
return member
|
|
173
|
+
if normalized in member.value:
|
|
174
|
+
return member
|
|
175
|
+
|
|
176
|
+
return cls.UNKNOWN
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def valid_values(cls) -> List[str]:
|
|
180
|
+
"""Return list of valid values for error messages."""
|
|
181
|
+
return [m.value for m in cls if m != cls.UNKNOWN]
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def suggest_similar(cls, value: str, max_suggestions: int = 3) -> List[str]:
|
|
185
|
+
"""
|
|
186
|
+
Suggest similar valid values for a given invalid input.
|
|
187
|
+
|
|
188
|
+
Uses simple substring matching for suggestions.
|
|
189
|
+
"""
|
|
190
|
+
if not value:
|
|
191
|
+
return []
|
|
192
|
+
|
|
193
|
+
normalized = value.lower().strip()
|
|
194
|
+
suggestions = []
|
|
195
|
+
|
|
196
|
+
for member in cls:
|
|
197
|
+
if member == cls.UNKNOWN:
|
|
198
|
+
continue
|
|
199
|
+
# Check for partial matches
|
|
200
|
+
if normalized in member.value or member.value in normalized:
|
|
201
|
+
suggestions.append(member.value)
|
|
202
|
+
# Check for word overlap
|
|
203
|
+
elif any(word in member.value for word in normalized.split("_")):
|
|
204
|
+
suggestions.append(member.value)
|
|
205
|
+
|
|
206
|
+
return suggestions[:max_suggestions]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def infer_joint_type_from_role(role: JointRole) -> JointType:
|
|
210
|
+
"""
|
|
211
|
+
Infer the most likely mechanical joint type from a semantic role.
|
|
212
|
+
|
|
213
|
+
Most robot joints are revolute, with some exceptions.
|
|
214
|
+
"""
|
|
215
|
+
prismatic_roles = {
|
|
216
|
+
JointRole.GRIPPER, # Many grippers are linear
|
|
217
|
+
JointRole.FINGER,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
continuous_roles = {
|
|
221
|
+
JointRole.WHEEL,
|
|
222
|
+
JointRole.TRACK,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if role in prismatic_roles:
|
|
226
|
+
return JointType.REVOLUTE # Actually most grippers are revolute too
|
|
227
|
+
elif role in continuous_roles:
|
|
228
|
+
return JointType.CONTINUOUS
|
|
229
|
+
elif role == JointRole.UNKNOWN:
|
|
230
|
+
return JointType.UNKNOWN
|
|
231
|
+
else:
|
|
232
|
+
return JointType.REVOLUTE # Default for most robot joints
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def format_valid_options(enum_class, prefix: str = "Valid options") -> str:
|
|
236
|
+
"""Format valid enum options for error messages."""
|
|
237
|
+
values = [m.value for m in enum_class if m.value != "unknown"]
|
|
238
|
+
return f"{prefix}: {', '.join(values)}"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# Type aliases for backwards compatibility
|
|
242
|
+
ServoType = JointRole # Alias for semantic clarity in servo contexts
|
ate/robot/visual_labeler.py
CHANGED
|
@@ -1037,3 +1037,12 @@ def list_skill_libraries() -> List[str]:
|
|
|
1037
1037
|
if not lib_dir.exists():
|
|
1038
1038
|
return []
|
|
1039
1039
|
return [p.stem for p in lib_dir.glob("*.json")]
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
def save_skill_library(library: SkillLibrary) -> Path:
|
|
1043
|
+
"""Save a skill library to disk."""
|
|
1044
|
+
lib_dir = Path.home() / ".ate" / "skill_libraries"
|
|
1045
|
+
lib_dir.mkdir(parents=True, exist_ok=True)
|
|
1046
|
+
lib_path = lib_dir / f"{library.robot_name}.json"
|
|
1047
|
+
library.save(lib_path)
|
|
1048
|
+
return lib_path
|