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