luckyrobots 0.1.72__py3-none-any.whl → 0.1.73__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.
@@ -1,125 +0,0 @@
1
- """
2
- Download and update LuckyEngine executable files.
3
-
4
- This module handles downloading updates and applying changes to the LuckyEngine binary.
5
- """
6
-
7
- import logging
8
- import os
9
- import platform
10
- from typing import Optional
11
-
12
- import requests
13
- from tqdm import tqdm
14
-
15
- from .check_updates import check_updates
16
-
17
- logger = logging.getLogger("luckyrobots.engine.download")
18
-
19
- BASE_URL = "https://builds.luckyrobots.xyz/"
20
-
21
-
22
- def get_base_url() -> str:
23
- """
24
- Get the base URL for downloads, checking local server first.
25
-
26
- Returns:
27
- Base URL string (local or remote).
28
- """
29
- local_url = "http://192.168.1.148/builds"
30
- remote_url = "https://builds.luckyrobots.xyz"
31
-
32
- try:
33
- response = requests.get(local_url, timeout=1)
34
- if response.status_code == 200:
35
- logger.info(f"Using local server: {local_url}")
36
- return local_url
37
- except requests.RequestException:
38
- pass
39
-
40
- logger.info(f"Using remote server: {remote_url}")
41
- return remote_url
42
-
43
-
44
- def get_os_type() -> str:
45
- """
46
- Get the operating system type as a string.
47
-
48
- Returns:
49
- "mac", "win", or "linux"
50
-
51
- Raises:
52
- ValueError: If the OS is not supported.
53
- """
54
- os_type = platform.system().lower()
55
- if os_type == "darwin":
56
- return "mac"
57
- elif os_type == "windows":
58
- return "win"
59
- elif os_type == "linux":
60
- return "linux"
61
- else:
62
- raise ValueError(f"Unsupported operating system: {os_type}")
63
-
64
-
65
- def apply_changes(changes: list[dict], binary_path: str = "./Binary") -> None:
66
- """
67
- Apply changes by downloading new/modified files and deleting removed files.
68
-
69
- Args:
70
- changes: List of change dictionaries with 'change_type', 'path', etc.
71
- binary_path: Base path where binary files are stored.
72
- """
73
- base_url = get_base_url()
74
- os_type = get_os_type()
75
-
76
- for item in changes:
77
- change_type = item.get("change_type")
78
- item_path = os.path.join(binary_path, item["path"])
79
-
80
- if change_type in ["modified", "new_file"]:
81
- if item.get("type") == "directory":
82
- # Create the directory
83
- os.makedirs(item_path, exist_ok=True)
84
- logger.debug(f"Created directory: {item_path}")
85
- else:
86
- # Handle file download with progress bar
87
- file_url = f"{base_url}{os_type}/{item['path']}"
88
-
89
- # Ensure the directory exists
90
- item_dir = os.path.dirname(item_path)
91
- os.makedirs(item_dir, exist_ok=True)
92
-
93
- # Download the file with progress bar
94
- try:
95
- response = requests.get(file_url, stream=True, timeout=30)
96
- response.raise_for_status()
97
- total_size = int(response.headers.get("content-length", 0))
98
-
99
- with open(item_path, "wb") as f, tqdm(
100
- desc=f"{item['path'][:8]}...{item['path'][-16:]}",
101
- total=total_size,
102
- unit="iB",
103
- unit_scale=True,
104
- unit_divisor=1024,
105
- ascii=" ▆",
106
- ) as progress_bar:
107
- for data in response.iter_content(chunk_size=1024):
108
- size = f.write(data)
109
- progress_bar.update(size)
110
-
111
- logger.debug(f"Downloaded: {item_path}")
112
- except requests.RequestException as e:
113
- logger.error(f"Error downloading {item_path}: {e}")
114
-
115
- elif change_type == "deleted":
116
- # Delete the file or directory
117
- try:
118
- if os.path.isdir(item_path):
119
- os.rmdir(item_path)
120
- logger.debug(f"Deleted directory: {item_path}")
121
- else:
122
- os.remove(item_path)
123
- logger.debug(f"Deleted file: {item_path}")
124
- except OSError as e:
125
- logger.error(f"Error deleting {item_path}: {e}")
@@ -1,97 +0,0 @@
1
- """
2
- Camera models for LuckyRobots.
3
-
4
- These models handle camera frame data from LuckyEngine.
5
- """
6
-
7
- from typing import Any, Dict, Optional, Union
8
- from pydantic import BaseModel, Field, ConfigDict
9
- import numpy as np
10
- import cv2
11
-
12
-
13
- class CameraShape(BaseModel):
14
- """Shape of camera images."""
15
-
16
- width: float = Field(description="Width of the image")
17
- height: float = Field(description="Height of the image")
18
- channel: int = Field(description="Number of color channels")
19
-
20
-
21
- class CameraData(BaseModel):
22
- """Camera frame data."""
23
-
24
- model_config = ConfigDict(populate_by_name=True, arbitrary_types_allowed=True)
25
-
26
- camera_name: str = Field(alias="CameraName", description="Name of the camera")
27
- dtype: str = Field(default="uint8", description="Data type of the image")
28
- shape: Optional[Union[CameraShape, Dict[str, Union[float, int]]]] = Field(
29
- default=None, description="Shape of the image"
30
- )
31
- time_stamp: Optional[str] = Field(
32
- None, alias="TimeStamp", description="Camera timestamp"
33
- )
34
- image_data: Optional[Any] = Field(
35
- None, alias="ImageData", description="Image data (bytes or numpy array)"
36
- )
37
- width: Optional[int] = Field(default=None, description="Image width")
38
- height: Optional[int] = Field(default=None, description="Image height")
39
- channels: Optional[int] = Field(default=None, description="Number of channels")
40
- format: Optional[str] = Field(
41
- default=None, description="Image format (raw, jpeg, png)"
42
- )
43
- frame_number: Optional[int] = Field(default=None, description="Frame number")
44
-
45
- def process_image(self) -> None:
46
- """Process the image data into a numpy array."""
47
- if self.image_data is None:
48
- return None
49
-
50
- # If already a numpy array, skip processing
51
- if isinstance(self.image_data, np.ndarray):
52
- return
53
-
54
- # Handle bytes data
55
- if isinstance(self.image_data, bytes):
56
- nparr = np.frombuffer(self.image_data, np.uint8)
57
-
58
- # Check if it's raw RGBA/RGB data or encoded
59
- if self.format == "raw" and self.width and self.height:
60
- channels = self.channels or 4
61
- try:
62
- self.image_data = nparr.reshape((self.height, self.width, channels))
63
- # Convert RGBA to BGR for OpenCV compatibility
64
- if channels == 4:
65
- self.image_data = cv2.cvtColor(
66
- self.image_data, cv2.COLOR_RGBA2BGR
67
- )
68
- elif channels == 3:
69
- self.image_data = cv2.cvtColor(
70
- self.image_data, cv2.COLOR_RGB2BGR
71
- )
72
- except ValueError:
73
- # Fallback to decode
74
- self.image_data = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
75
- else:
76
- # Encoded image (JPEG, PNG)
77
- self.image_data = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
78
-
79
- @classmethod
80
- def from_grpc_frame(cls, frame: Any, camera_name: str = "camera") -> "CameraData":
81
- """Create CameraData from a gRPC ImageFrame message."""
82
- return cls(
83
- camera_name=camera_name,
84
- dtype="uint8",
85
- width=frame.width,
86
- height=frame.height,
87
- channels=frame.channels,
88
- format=frame.format,
89
- time_stamp=str(frame.timestamp_ms),
90
- frame_number=frame.frame_number,
91
- image_data=frame.data,
92
- shape=CameraShape(
93
- width=frame.width,
94
- height=frame.height,
95
- channel=frame.channels,
96
- ),
97
- )
@@ -1,77 +0,0 @@
1
- """
2
- Domain randomization configuration for physics parameters.
3
-
4
- This model maps to the DomainRandomizationConfig proto message in agent.proto.
5
- """
6
-
7
- from typing import Optional
8
- from pydantic import BaseModel, Field, ConfigDict
9
-
10
-
11
- class DomainRandomizationConfig(BaseModel):
12
- """Domain randomization configuration for physics parameters.
13
-
14
- All range fields are [min, max] tuples. None/empty means "use defaults".
15
-
16
- Usage:
17
- randomization_cfg = DomainRandomizationConfig(
18
- friction_range=(0.5, 1.5),
19
- mass_scale_range=(0.8, 1.2),
20
- joint_position_noise=0.05,
21
- )
22
- client.reset_agent(randomization_cfg=randomization_cfg)
23
- """
24
-
25
- model_config = ConfigDict(frozen=True)
26
-
27
- # Initial state randomization
28
- pose_position_noise: Optional[tuple[float, float, float]] = Field(
29
- default=None, description="[x, y, z] position noise std"
30
- )
31
- pose_orientation_noise: Optional[float] = Field(
32
- default=None, description="Orientation noise std (radians)"
33
- )
34
- joint_position_noise: Optional[float] = Field(
35
- default=None, description="Joint position noise std"
36
- )
37
- joint_velocity_noise: Optional[float] = Field(
38
- default=None, description="Joint velocity noise std"
39
- )
40
-
41
- # Physics parameters (all [min, max] ranges)
42
- friction_range: Optional[tuple[float, float]] = Field(
43
- default=None, description="Surface friction coefficient [min, max]"
44
- )
45
- restitution_range: Optional[tuple[float, float]] = Field(
46
- default=None, description="Bounce/restitution coefficient [min, max]"
47
- )
48
- mass_scale_range: Optional[tuple[float, float]] = Field(
49
- default=None, description="Body mass multiplier [min, max]"
50
- )
51
- com_offset_range: Optional[tuple[float, float]] = Field(
52
- default=None, description="Center of mass offset [min, max]"
53
- )
54
-
55
- # Motor/actuator randomization
56
- motor_strength_range: Optional[tuple[float, float]] = Field(
57
- default=None, description="Motor strength multiplier [min, max]"
58
- )
59
- motor_offset_range: Optional[tuple[float, float]] = Field(
60
- default=None, description="Motor position offset [min, max]"
61
- )
62
-
63
- # External disturbances
64
- push_interval_range: Optional[tuple[float, float]] = Field(
65
- default=None, description="Time between pushes [min, max]"
66
- )
67
- push_velocity_range: Optional[tuple[float, float]] = Field(
68
- default=None, description="Push velocity magnitude [min, max]"
69
- )
70
-
71
- # Terrain configuration
72
- terrain_type: Optional[str] = Field(
73
- default=None, description="Terrain type identifier"
74
- )
75
- terrain_difficulty: Optional[float] = Field(
76
- default=None, description="Terrain difficulty level"
77
- )