luckyrobots 0.1.68__py3-none-any.whl → 0.1.70__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 (57) hide show
  1. luckyrobots/__init__.py +23 -12
  2. luckyrobots/client.py +800 -0
  3. luckyrobots/config/robots.yaml +231 -71
  4. luckyrobots/engine/__init__.py +23 -0
  5. luckyrobots/{utils → engine}/check_updates.py +108 -48
  6. luckyrobots/{utils → engine}/download.py +61 -39
  7. luckyrobots/engine/manager.py +427 -0
  8. luckyrobots/grpc/__init__.py +6 -0
  9. luckyrobots/grpc/generated/__init__.py +18 -0
  10. luckyrobots/grpc/generated/agent_pb2.py +61 -0
  11. luckyrobots/grpc/generated/agent_pb2_grpc.py +255 -0
  12. luckyrobots/grpc/generated/camera_pb2.py +45 -0
  13. luckyrobots/grpc/generated/camera_pb2_grpc.py +155 -0
  14. luckyrobots/grpc/generated/common_pb2.py +39 -0
  15. luckyrobots/grpc/generated/common_pb2_grpc.py +27 -0
  16. luckyrobots/grpc/generated/media_pb2.py +35 -0
  17. luckyrobots/grpc/generated/media_pb2_grpc.py +27 -0
  18. luckyrobots/grpc/generated/mujoco_pb2.py +47 -0
  19. luckyrobots/grpc/generated/mujoco_pb2_grpc.py +248 -0
  20. luckyrobots/grpc/generated/scene_pb2.py +54 -0
  21. luckyrobots/grpc/generated/scene_pb2_grpc.py +248 -0
  22. luckyrobots/grpc/generated/telemetry_pb2.py +43 -0
  23. luckyrobots/grpc/generated/telemetry_pb2_grpc.py +154 -0
  24. luckyrobots/grpc/generated/viewport_pb2.py +48 -0
  25. luckyrobots/grpc/generated/viewport_pb2_grpc.py +155 -0
  26. luckyrobots/grpc/proto/agent.proto +152 -0
  27. luckyrobots/grpc/proto/camera.proto +41 -0
  28. luckyrobots/grpc/proto/common.proto +36 -0
  29. luckyrobots/grpc/proto/hazel_rpc.proto +32 -0
  30. luckyrobots/grpc/proto/media.proto +26 -0
  31. luckyrobots/grpc/proto/mujoco.proto +64 -0
  32. luckyrobots/grpc/proto/scene.proto +70 -0
  33. luckyrobots/grpc/proto/telemetry.proto +43 -0
  34. luckyrobots/grpc/proto/viewport.proto +45 -0
  35. luckyrobots/luckyrobots.py +212 -0
  36. luckyrobots/models/__init__.py +13 -0
  37. luckyrobots/models/camera.py +97 -0
  38. luckyrobots/models/observation.py +135 -0
  39. luckyrobots/{utils/helpers.py → utils.py} +75 -40
  40. luckyrobots-0.1.70.dist-info/METADATA +262 -0
  41. luckyrobots-0.1.70.dist-info/RECORD +44 -0
  42. {luckyrobots-0.1.68.dist-info → luckyrobots-0.1.70.dist-info}/WHEEL +1 -1
  43. luckyrobots/core/luckyrobots.py +0 -628
  44. luckyrobots/core/manager.py +0 -236
  45. luckyrobots/core/models.py +0 -68
  46. luckyrobots/core/node.py +0 -273
  47. luckyrobots/message/__init__.py +0 -18
  48. luckyrobots/message/pubsub.py +0 -145
  49. luckyrobots/message/srv/client.py +0 -81
  50. luckyrobots/message/srv/service.py +0 -135
  51. luckyrobots/message/srv/types.py +0 -83
  52. luckyrobots/message/transporter.py +0 -427
  53. luckyrobots/utils/event_loop.py +0 -94
  54. luckyrobots/utils/sim_manager.py +0 -413
  55. luckyrobots-0.1.68.dist-info/METADATA +0 -253
  56. luckyrobots-0.1.68.dist-info/RECORD +0 -24
  57. {luckyrobots-0.1.68.dist-info → luckyrobots-0.1.70.dist-info}/licenses/LICENSE +0 -0
@@ -1,413 +0,0 @@
1
- import os
2
- import platform
3
- import subprocess
4
- import tempfile
5
- import threading
6
- import time
7
- import logging
8
-
9
- from typing import Optional
10
-
11
- if not os.getenv("PYTEST_CURRENT_TEST") and not os.getenv("LUCKYROBOTS_NO_LOGS"):
12
- logging.basicConfig(
13
- level=logging.INFO,
14
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
15
- )
16
- logger = logging.getLogger("luckyworld")
17
-
18
- LOCK_FILE = os.path.join(tempfile.gettempdir(), "luckyworld_lock")
19
- _process = None
20
- _monitor_thread = None
21
- _shutdown_event = threading.Event()
22
-
23
-
24
- def remove_lock_file() -> None:
25
- """Remove the lock file"""
26
- try:
27
- if os.path.exists(LOCK_FILE):
28
- os.remove(LOCK_FILE)
29
- logger.debug("Lock file removed successfully")
30
- else:
31
- logger.debug("Lock file doesn't exist, nothing to remove")
32
- except Exception as e:
33
- logger.error(f"Error removing lock file: {e}")
34
-
35
-
36
- def monitor_process():
37
- """Monitor the LuckyWorld process and handle its termination"""
38
- global _process
39
- try:
40
- while not _shutdown_event.is_set():
41
- if _process is None or _process.poll() is not None:
42
- logger.info("LuckyWorld process has terminated")
43
- # Don't kill the main process, just break out of monitoring
44
- break
45
- time.sleep(1)
46
- except Exception as e:
47
- logger.error(f"Error in process monitor: {e}")
48
- finally:
49
- # Ensure cleanup happens when monitoring stops
50
- if not _shutdown_event.is_set():
51
- remove_lock_file()
52
-
53
-
54
- def is_luckyworld_running() -> bool:
55
- """Check if LuckyWorld is running by checking the lock file"""
56
- return os.path.exists(LOCK_FILE)
57
-
58
-
59
- def create_lock_file(pid: int) -> None:
60
- """Create a lock file with the process ID"""
61
- with open(LOCK_FILE, "w") as f:
62
- f.write(str(pid))
63
-
64
-
65
- def find_luckyworld_executable() -> Optional[str]:
66
- """Automatically find LuckyWorld executable in common locations"""
67
- is_wsl = "microsoft" in platform.uname().release.lower()
68
-
69
- # 1. Check environment variable first (highest priority)
70
- env_path = os.environ.get("LUCKYWORLD_PATH")
71
- if env_path:
72
- logger.info(f"Using LUCKYWORLD_PATH environment variable: {env_path}")
73
- if os.path.exists(env_path):
74
- return env_path
75
- else:
76
- logger.warning(f"LUCKYWORLD_PATH points to non-existent file: {env_path}")
77
-
78
- # 2. Check LUCKYWORLD_HOME environment variable
79
- env_home = os.environ.get("LUCKYWORLD_HOME")
80
- if env_home:
81
- logger.info(f"Using LUCKYWORLD_HOME environment variable: {env_home}")
82
- if platform.system() == "Linux" and not is_wsl:
83
- env_executable = os.path.join(env_home, "LuckyWorld.sh")
84
- elif platform.system() == "Darwin":
85
- env_executable = os.path.join(
86
- env_home, "LuckyWorld.app", "Contents", "MacOS", "LuckyWorld"
87
- )
88
- else:
89
- env_executable = os.path.join(env_home, "LuckyWorldV2.exe")
90
-
91
- if os.path.exists(env_executable):
92
- return env_executable
93
- else:
94
- logger.warning(
95
- f"LUCKYWORLD_HOME does not contain executable: {env_executable}"
96
- )
97
-
98
- # 3. System installation paths
99
- if platform.system() == "Linux" and not is_wsl:
100
- system_paths = [
101
- "/opt/LuckyWorld/LuckyWorld.sh",
102
- "/usr/local/bin/LuckyWorld.sh",
103
- os.path.expanduser("~/.local/share/LuckyWorld/LuckyWorld.sh"),
104
- os.path.expanduser("~/LuckyWorld/LuckyWorld.sh"),
105
- ]
106
- elif platform.system() == "Darwin":
107
- system_paths = [
108
- "/Applications/LuckyWorld/LuckyWorld.app/Contents/MacOS/LuckyWorld",
109
- os.path.expanduser(
110
- "~/Applications/LuckyWorld/LuckyWorld.app/Contents/MacOS/LuckyWorld"
111
- ),
112
- ]
113
- else: # Windows or WSL2
114
- system_paths = [
115
- "C:\\Program Files\\LuckyWorld\\LuckyWorldV2.exe",
116
- "C:\\Program Files (x86)\\LuckyWorld\\LuckyWorldV2.exe",
117
- os.path.expanduser("~\\AppData\\Local\\LuckyWorld\\LuckyWorldV2.exe"),
118
- ]
119
-
120
- if is_wsl:
121
- system_paths.extend(
122
- [
123
- "/mnt/c/Program Files/LuckyWorld/LuckyWorldV2.exe",
124
- "/mnt/c/Program Files (x86)/LuckyWorld/LuckyWorldV2.exe",
125
- ]
126
- )
127
-
128
- for path in system_paths:
129
- if os.path.exists(path):
130
- logger.info(f"Found LuckyWorld at: {path}")
131
- return path
132
-
133
- return None
134
-
135
-
136
- def launch_luckyworld(
137
- scene: str = "ArmLevel",
138
- robot: str = "so100",
139
- task: Optional[str] = None,
140
- debug: bool = False,
141
- executable_path: Optional[str] = None,
142
- headless: bool = False,
143
- windowed: bool = True,
144
- verbose: bool = False,
145
- ) -> bool:
146
- """Launch LuckyWorld with simplified parameters"""
147
- global _process, _monitor_thread
148
-
149
- # Check if already running
150
- if is_luckyworld_running():
151
- logger.error(
152
- "LuckyWorld is already running. \
153
- Stop the existing instance or remove the luckyworld lock file located at /tmp/luckyworld_lock."
154
- )
155
- return False
156
-
157
- # Find executable if not provided
158
- if executable_path is None:
159
- executable_path = find_luckyworld_executable()
160
- if executable_path is None:
161
- logger.error(
162
- "Could not find LuckyWorld executable. Please provide the path manually."
163
- )
164
- logger.info("You can set the path using environment variables:")
165
- logger.info(" LUCKYWORLD_PATH=/full/path/to/LuckyWorldV2.exe")
166
- logger.info(" LUCKYWORLD_HOME=/path/to/luckyworld/directory")
167
- logger.info("Or check these common locations:")
168
- logger.info(" Development: Builds/Windows/LuckyWorldV2.exe")
169
- logger.info(" Windows: C:\\Program Files\\LuckyWorld\\LuckyWorldV2.exe")
170
- logger.info(" WSL2: /mnt/c/Program Files/LuckyWorld/LuckyWorldV2.exe")
171
- return False
172
-
173
- if not os.path.exists(executable_path):
174
- logger.error(f"Executable not found at: {executable_path}")
175
- return False
176
-
177
- try:
178
- # Set execute permissions on Unix systems
179
- if platform.system() != "Windows":
180
- os.chmod(executable_path, 0o755)
181
-
182
- logger.info(f"Launching LuckyWorld: {executable_path}")
183
-
184
- # Build command
185
- command = [executable_path]
186
- command.append(f"-Scene={scene}")
187
- command.append(f"-Robot={robot}")
188
-
189
- if task:
190
- command.append(f"-Task={task}")
191
-
192
- if debug:
193
- command.append("-Debug")
194
-
195
- if headless:
196
- command.append("-Headless")
197
- else:
198
- if windowed:
199
- command.append("-windowed")
200
- else:
201
- command.append("-fullscreen")
202
-
203
- command.append("-Realtime")
204
-
205
- logger.info(f"Command: {' '.join(command)}")
206
-
207
- # Launch process
208
- if platform.system() == "Windows":
209
- DETACHED_PROCESS = 0x00000008
210
- _process = subprocess.Popen(
211
- command,
212
- creationflags=DETACHED_PROCESS,
213
- close_fds=True,
214
- stdout=None if verbose else subprocess.DEVNULL,
215
- stderr=None if verbose else subprocess.DEVNULL,
216
- )
217
- else:
218
- _process = subprocess.Popen(
219
- command,
220
- start_new_session=True,
221
- stdout=None if verbose else subprocess.DEVNULL,
222
- stderr=None if verbose else subprocess.DEVNULL,
223
- )
224
-
225
- # Start monitoring
226
- _shutdown_event.clear()
227
- _monitor_thread = threading.Thread(target=monitor_process, daemon=True)
228
- _monitor_thread.start()
229
-
230
- create_lock_file(_process.pid)
231
-
232
- logger.info(f"LuckyWorld started successfully (PID: {_process.pid})")
233
- logger.info(f"Scene: {scene}, Robot: {robot}, Task: {task or 'None'}")
234
-
235
- return True
236
-
237
- except Exception as e:
238
- logger.error(f"Failed to launch LuckyWorld: {e}")
239
- stop_luckyworld()
240
- return False
241
-
242
-
243
- def kill_processes():
244
- """Kill all LuckyWorld processes"""
245
- system = platform.system()
246
- is_wsl = "microsoft" in platform.uname().release.lower()
247
-
248
- if is_wsl:
249
- return _kill_wsl_processes()
250
- elif system == "Windows":
251
- return _kill_windows_processes()
252
- elif system == "Darwin":
253
- return _kill_macos_processes()
254
- else:
255
- return _kill_linux_processes()
256
-
257
-
258
- def _kill_wsl_processes():
259
- try:
260
- result = subprocess.run(
261
- [
262
- "/mnt/c/Windows/System32/taskkill.exe",
263
- "/F",
264
- "/IM",
265
- "LuckyWorldV2.exe", # NOTE: Make sure this is the name of the LuckyWorld executable
266
- ],
267
- capture_output=True,
268
- text=True,
269
- timeout=10,
270
- )
271
-
272
- if result.returncode == 0:
273
- return True
274
- elif result.returncode == 128: # Process not found
275
- logger.error("No LuckyWorld processes found running")
276
- return False
277
- else:
278
- logger.warning(
279
- f"taskkill failed with code {result.returncode}: {result.stderr}"
280
- )
281
- return False
282
-
283
- except subprocess.TimeoutExpired:
284
- logger.error("taskkill command timed out")
285
- return False
286
- except Exception as e:
287
- logger.error(f"Failed to kill processes: {e}")
288
- return False
289
-
290
-
291
- def _kill_windows_processes():
292
- """Windows-specific process killing"""
293
- try:
294
- # Kill LuckyWorldV2.exe
295
- result = subprocess.run(
296
- ["taskkill", "/F", "/IM", "LuckyWorldV2.exe"],
297
- capture_output=True,
298
- text=True,
299
- timeout=10,
300
- )
301
-
302
- if result.returncode == 0:
303
- logger.info("Successfully killed LuckyWorldV2.exe processes")
304
- return True
305
- elif result.returncode == 128: # Process not found
306
- logger.info("No LuckyWorldV2.exe processes found")
307
- return True
308
- else:
309
- logger.warning(f"taskkill failed: {result.stderr}")
310
- return False
311
-
312
- except subprocess.TimeoutExpired:
313
- logger.error("taskkill command timed out")
314
- return False
315
- except FileNotFoundError:
316
- logger.error("taskkill command not found")
317
- return False
318
- except Exception as e:
319
- logger.error(f"Failed to kill Windows processes: {e}")
320
- return False
321
-
322
-
323
- def _kill_macos_processes():
324
- """macOS-specific process killing"""
325
- try:
326
- # Kill LuckyWorld processes
327
- result = subprocess.run(
328
- ["pkill", "-f", "LuckyWorld"],
329
- capture_output=True,
330
- text=True,
331
- timeout=10,
332
- )
333
-
334
- if result.returncode == 0:
335
- logger.info("Successfully killed LuckyWorld processes")
336
- return True
337
- elif result.returncode == 1: # No processes found
338
- logger.info("No LuckyWorld processes found")
339
- return True
340
- else:
341
- logger.warning(f"pkill failed: {result.stderr}")
342
- return False
343
-
344
- except subprocess.TimeoutExpired:
345
- logger.error("pkill command timed out")
346
- return False
347
- except FileNotFoundError:
348
- logger.error("pkill command not found")
349
- return False
350
- except Exception as e:
351
- logger.error(f"Failed to kill macOS processes: {e}")
352
- return False
353
-
354
-
355
- def _kill_linux_processes():
356
- """Linux-specific process killing"""
357
- try:
358
- # Kill LuckyWorld processes
359
- result = subprocess.run(
360
- ["pkill", "-f", "LuckyWorld"],
361
- capture_output=True,
362
- text=True,
363
- timeout=10,
364
- )
365
-
366
- if result.returncode == 0:
367
- logger.info("Successfully killed LuckyWorld processes")
368
- return True
369
- elif result.returncode == 1: # No processes found
370
- logger.info("No LuckyWorld processes found")
371
- return True
372
- else:
373
- logger.warning(f"pkill failed: {result.stderr}")
374
- return False
375
-
376
- except subprocess.TimeoutExpired:
377
- logger.error("pkill command timed out")
378
- return False
379
- except FileNotFoundError:
380
- logger.error("pkill command not found")
381
- return False
382
- except Exception as e:
383
- logger.error(f"Failed to kill Linux processes: {e}")
384
- return False
385
-
386
-
387
- def stop_luckyworld() -> bool:
388
- """Stop the running LuckyWorld instance"""
389
- global _process
390
-
391
- if not is_luckyworld_running():
392
- logger.info("LuckyWorld is not running")
393
- return True
394
-
395
- try:
396
- if _process:
397
- logger.info("Stopping LuckyWorld...")
398
-
399
- try:
400
- _process.terminate()
401
- _process.wait(timeout=5)
402
- except subprocess.TimeoutExpired:
403
- logger.info("Graceful termination timeout, using kill_processes...")
404
- except Exception:
405
- logger.info("Graceful termination failed, using kill_processes...")
406
-
407
- kill_processes()
408
-
409
- remove_lock_file()
410
- return True
411
- except Exception as e:
412
- logger.error(f"Error stopping LuckyWorld: {e}")
413
- return False
@@ -1,253 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: luckyrobots
3
- Version: 0.1.68
4
- Summary: Robotics-AI Training in Hyperrealistic Game Environments
5
- Project-URL: Homepage, https://github.com/luckyrobots/luckyrobots
6
- Project-URL: Bug Tracker, https://github.com/luckyrobots/luckyrobots/issues
7
- Author-email: Devrim Yasar <braces.verbose03@icloud.com>, Ethan Clark <ethan@luckyrobots.com>
8
- License-File: LICENSE
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Programming Language :: Python :: 3
12
- Requires-Python: >=3.10
13
- Requires-Dist: asyncio
14
- Requires-Dist: beautifulsoup4
15
- Requires-Dist: fastapi
16
- Requires-Dist: msgpack
17
- Requires-Dist: numpy
18
- Requires-Dist: opencv-python
19
- Requires-Dist: packaging
20
- Requires-Dist: psutil
21
- Requires-Dist: pydantic
22
- Requires-Dist: pyyaml
23
- Requires-Dist: requests
24
- Requires-Dist: tqdm
25
- Requires-Dist: ultralytics
26
- Requires-Dist: uvicorn[standard]
27
- Requires-Dist: watchdog
28
- Requires-Dist: websocket-client
29
- Requires-Dist: websockets
30
- Provides-Extra: dev
31
- Requires-Dist: black; extra == 'dev'
32
- Requires-Dist: build; extra == 'dev'
33
- Requires-Dist: pre-commit; extra == 'dev'
34
- Requires-Dist: twine; extra == 'dev'
35
- Description-Content-Type: text/markdown
36
-
37
- <p align="center">
38
- <img width="384" alt="Default_Logo_Horizontal@2x" src="https://github.com/user-attachments/assets/ae6ad53a-741e-4e7a-94cb-5a46a8e81398" />
39
- </p>
40
-
41
- <p align="center">
42
- Infinite synthetic data generation for embodied AI
43
- </p>
44
-
45
- <!--
46
- <p align="center">
47
- <a href="https://luckyrobots.github.io/ReleaseV0.1/" target="_blank">
48
- <img src="https://img.shields.io/badge/Explore_V0.1-Get_Started-grey?style=for-the-badge&labelColor=grey&color=blue" alt="Get Started" />
49
- </a>
50
- </p>
51
- -->
52
-
53
- <div align="center">
54
-
55
- [![PyPI version](https://img.shields.io/pypi/v/luckyrobots.svg)](https://pypi.org/project/luckyrobots/)
56
- [![Documentation](https://img.shields.io/badge/docs-read%20the%20docs-blue)](https://luckyrobots.readthedocs.io)
57
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
58
- [![Python Version](https://img.shields.io/pypi/pyversions/luckyrobots)](https://pypi.org/project/luckyrobots/)
59
- [![Status](https://img.shields.io/badge/Status-Alpha-orange)](https://pypi.org/project/luckyrobots/)
60
- [![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/5CH3wx3tAs)
61
-
62
- </div>
63
-
64
- https://github.com/user-attachments/assets/0ab2953d-b188-4af7-a225-71decdd2378c
65
-
66
- # Lucky Robots
67
-
68
- Hyperrealistic robotics simulation framework with Python API for embodied AI training and testing.
69
-
70
- <p align="center">
71
- <img width="49%" alt="Bedroom environment in Lucky World" src="https://github.com/user-attachments/assets/279a7864-9a8b-453e-8567-3a174f5db8ab" />
72
- <img width="49%" alt="Open floor plan in Lucky World" src="https://github.com/user-attachments/assets/68c72b97-98ab-42b0-a065-8a4247b014c7" />
73
- </p>
74
-
75
- ## Quick Start
76
-
77
- 1. **Download Lucky World Executable from our [releases page](https://github.com/luckyrobots/luckyrobots/releases/latest) and add its path to your system variables**
78
- ```bash
79
- # Set environment variables (choose one method):
80
-
81
- # Method 1: Set LUCKYWORLD_PATH directly to the executable
82
- export LUCKYWORLD_PATH=/path/to/LuckyWorld.exe # Windows
83
- export LUCKYWORLD_PATH=/path/to/LuckyWorld # Linux/Mac
84
-
85
- # Method 2: Set LUCKYWORLD_HOME to the directory containing the executable
86
- export LUCKYWORLD_HOME=/path/to/luckyworld/directory
87
- ```
88
-
89
- 2. **Create conda environment (recommended)**
90
- ```bash
91
- conda create -n luckyrobots python
92
- conda activate luckyrobots
93
- ```
94
-
95
- 3. **Install**
96
- ```bash
97
- pip install luckyrobots
98
- ```
99
-
100
- 4. **Run Example**
101
- ```bash
102
- git clone https://github.com/luckyrobots/luckyrobots.git
103
- cd luckyrobots/examples
104
- python controller.py
105
- ```
106
-
107
- ## Basic Usage
108
-
109
- ```python
110
- from luckyrobots import LuckyRobots, Node
111
- import numpy as np
112
-
113
- # Create controller node
114
- class RobotController(Node):
115
- async def control_loop(self):
116
- # Reset environment
117
- reset_response = await self.reset_client.call(Reset.Request())
118
-
119
- # Send actions
120
- actuator_values = np.array([0.1, 0.2, -0.1, 0.0, 0.5, 1.0])
121
- step_response = await self.step_client.call(Step.Request(actuator_values=actuator_values))
122
-
123
- # Access observations
124
- observation = step_response.observation
125
- joint_states = observation.observation_state
126
- camera_data = observation.observation_cameras
127
-
128
- # Start simulation
129
- luckyrobots = LuckyRobots()
130
- controller = RobotController()
131
- luckyrobots.register_node(controller)
132
- luckyrobots.start(scene="kitchen", robot="so100", task="pickandplace")
133
- ```
134
-
135
- ## Available Robots & Environments
136
-
137
- ### Robots
138
- - **so100**: 6-DOF manipulator with gripper
139
- - **stretch_v1**: Mobile manipulator
140
- - **dji300**: Quadcopter drone
141
-
142
- ### Scenes
143
- - **kitchen**: Residential kitchen environment
144
- - **loft**: Open floor plan apartment
145
- - **drone_flight**: Outdoor flight area
146
-
147
- ### Tasks
148
- - **pickandplace**: Object manipulation
149
- - **navigation**: Path planning and movement
150
-
151
- ## API Reference
152
-
153
- ### Core Classes
154
-
155
- **LuckyRobots**: Main simulation manager
156
- - `start(scene, robot, task, observation_type)`: Initialize simulation
157
- - `register_node(node)`: Add controller node
158
- - `spin()`: Run main loop
159
-
160
- **Node**: Base class for robot controllers
161
- - `create_client(service_type, service_name)`: Create service client
162
- - `create_service(service_type, service_name, handler)`: Create service server
163
-
164
- ### Services
165
-
166
- **Reset**: Reset robot to initial state
167
- ```python
168
- request = Reset.Request(seed=42, options={})
169
- response = await reset_client.call(request)
170
- ```
171
-
172
- **Step**: Send action and get observation
173
- ```python
174
- request = Step.Request(actuator_values=[0.1, 0.2, -0.1])
175
- response = await step_client.call(request)
176
- ```
177
-
178
- ### Observations
179
-
180
- Access sensor data from step responses:
181
- ```python
182
- # Joint positions and velocities
183
- joint_states = response.observation.observation_state
184
-
185
- # Camera images (RGB + depth)
186
- for camera in response.observation.observation_cameras:
187
- image = camera.image_data # numpy array
188
- name = camera.camera_name # "head_cam", "hand_cam", etc.
189
- ```
190
-
191
- ## Command Line Interface
192
-
193
- ```bash
194
- # Basic usage
195
- python controller.py --robot so100 --scene kitchen --task pickandplace
196
-
197
- # With camera display
198
- python controller.py --show-camera --rate 30
199
-
200
- # Custom host/port
201
- python controller.py --host 192.168.1.100 --port 3001
202
- ```
203
-
204
- ## Configuration
205
-
206
- Robot configurations are defined in `src/luckyrobots/config/robots.yaml`:
207
-
208
- ```yaml
209
- so100:
210
- action_space:
211
- actuator_names: [shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll, gripper]
212
- actuator_limits:
213
- - name: shoulder_pan
214
- lower: -2.2
215
- upper: 2.2
216
- available_scenes: [kitchen]
217
- available_tasks: [pickandplace]
218
- ```
219
-
220
- ## Architecture
221
-
222
- Lucky Robots uses a distributed node architecture:
223
-
224
- - **Manager Node**: Central message routing
225
- - **LuckyRobots Node**: Simulation interface
226
- - **Controller Nodes**: User-defined robot controllers
227
- - **WebSocket Transport**: Inter-node communication
228
- - **Lucky World**: Physics simulation backend
229
-
230
- ## Development
231
-
232
- ### Setup Development Environment
233
- ```bash
234
- git clone https://github.com/luckyrobots/luckyrobots.git
235
- cd luckyrobots
236
- pip install -e .
237
- ```
238
-
239
- ### Contributing
240
- 1. Fork the repository
241
- 2. Create a feature branch
242
- 3. Make changes and add tests
243
- 4. Submit a pull request
244
-
245
- ## License
246
-
247
- MIT License - see [LICENSE](LICENSE) file.
248
-
249
- ## Support
250
-
251
- - **Issues**: [GitHub Issues](https://github.com/luckyrobots/luckyrobots/issues)
252
- - **Discussions**: [GitHub Discussions](https://github.com/luckyrobots/luckyrobots/discussions)
253
- - **Discord**: [Community Server](https://discord.gg/5CH3wx3tAs)
@@ -1,24 +0,0 @@
1
- luckyrobots/__init__.py,sha256=yr8nMUVzAe8ohD7XNywTDwLX98HwjT4cagxARkld1HQ,426
2
- luckyrobots/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- luckyrobots/config/robots.yaml,sha256=w0HwB35WRd7X0-hQRgtYQaRWmOk32-e6pMxT88aereE,1806
4
- luckyrobots/core/luckyrobots.py,sha256=IhAtQoHExMLm6j7HrjUUe6o56IzEY2X0Nt3CPgL9_5g,23312
5
- luckyrobots/core/manager.py,sha256=p9Ehs2SKLpBEEJf8hvTFUG_foPrQszgH6BUbIzBTtBo,9212
6
- luckyrobots/core/models.py,sha256=fbAKpIOZM8D6rKxRgo1_9rioNa99bT_phCZ9xrwWFaE,2108
7
- luckyrobots/core/node.py,sha256=lDKqAQSMEctjU-v4F9Lifz_ZsKG6wxsNuyRqtOWrQRA,10512
8
- luckyrobots/message/__init__.py,sha256=7oM16w34XBc8xuKZh3rNRPnqHMjhyYYG0bqLS3j2PjQ,432
9
- luckyrobots/message/pubsub.py,sha256=iPMhRCq7IASqSO8eXs8LwMUayJp-piYdPSc741a3qKs,5490
10
- luckyrobots/message/transporter.py,sha256=5I1YCv8jlG-swM__ZTQ4L8BLmUfOSfs_3iLWXeprC6M,15815
11
- luckyrobots/message/srv/client.py,sha256=wT8Q-cuu3aXeRmkO_8CTFSO9aJqtrzyyxBopyUl3HNw,2971
12
- luckyrobots/message/srv/service.py,sha256=zmOFt6VBGUiy5btZo5Wh4-vr2oJfhzHjOV3hB2IzAFw,4495
13
- luckyrobots/message/srv/types.py,sha256=j-VuYsum_98gQBj9-37vsHWC8PcvPd15UP-NkoEtPYw,1948
14
- luckyrobots/utils/check_updates.py,sha256=5CpAe2jK1igjJVIoZKzRWgg9jwKsmiyEwqgldyek6H8,6439
15
- luckyrobots/utils/download.py,sha256=UWQkB6yp-UsOK93pw0noHmv2rVLXcjY5JKd9EkKUG-c,3593
16
- luckyrobots/utils/event_loop.py,sha256=r2sn7G9JHFKUri4aUnZ_1D82CTZnEsr3woLLKiDx8Dw,2638
17
- luckyrobots/utils/helpers.py,sha256=4o3Jy0-otnVl0duqncoIuEufEo2AP9BDdgKSEYUOCYQ,2193
18
- luckyrobots/utils/sim_manager.py,sha256=wQlfsmRk6ZdrZBdBwnth4UBlrcsLA7_oO_4VVvegcNU,13237
19
- luckyrobots/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- luckyrobots/config/robots.yaml,sha256=w0HwB35WRd7X0-hQRgtYQaRWmOk32-e6pMxT88aereE,1806
21
- luckyrobots-0.1.68.dist-info/METADATA,sha256=kQmpIJA3C-GiGUpe5ptDGDMShZm4W8YBcoz7u7oEj3Q,7683
22
- luckyrobots-0.1.68.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- luckyrobots-0.1.68.dist-info/licenses/LICENSE,sha256=xsPYvRJPH_fW_sofTUknI_KvZOsD4-BqjSOTZqI6Nmw,1069
24
- luckyrobots-0.1.68.dist-info/RECORD,,