clawdb-ros2 0.4.2__tar.gz
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.
- clawdb_ros2-0.4.2/LICENSE +21 -0
- clawdb_ros2-0.4.2/PKG-INFO +163 -0
- clawdb_ros2-0.4.2/README.md +123 -0
- clawdb_ros2-0.4.2/clawdb_ros2/__init__.py +21 -0
- clawdb_ros2-0.4.2/clawdb_ros2/client.py +174 -0
- clawdb_ros2-0.4.2/clawdb_ros2/fleet_sync.py +33 -0
- clawdb_ros2-0.4.2/clawdb_ros2/node.py +184 -0
- clawdb_ros2-0.4.2/clawdb_ros2/obstacle_subscriber.py +39 -0
- clawdb_ros2-0.4.2/clawdb_ros2/spatial_index.py +69 -0
- clawdb_ros2-0.4.2/clawdb_ros2/tf_listener.py +29 -0
- clawdb_ros2-0.4.2/clawdb_ros2.egg-info/PKG-INFO +163 -0
- clawdb_ros2-0.4.2/clawdb_ros2.egg-info/SOURCES.txt +24 -0
- clawdb_ros2-0.4.2/clawdb_ros2.egg-info/dependency_links.txt +1 -0
- clawdb_ros2-0.4.2/clawdb_ros2.egg-info/requires.txt +8 -0
- clawdb_ros2-0.4.2/clawdb_ros2.egg-info/top_level.txt +1 -0
- clawdb_ros2-0.4.2/clawdb_ros2.egg-info/zip-safe +1 -0
- clawdb_ros2-0.4.2/config/bridge_params.yaml +16 -0
- clawdb_ros2-0.4.2/launch/memory_bridge.launch.py +60 -0
- clawdb_ros2-0.4.2/package.xml +32 -0
- clawdb_ros2-0.4.2/pyproject.toml +60 -0
- clawdb_ros2-0.4.2/setup.cfg +10 -0
- clawdb_ros2-0.4.2/setup.py +35 -0
- clawdb_ros2-0.4.2/tests/test_client.py +42 -0
- clawdb_ros2-0.4.2/tests/test_node.py +34 -0
- clawdb_ros2-0.4.2/tests/test_spatial_index.py +46 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ClawDB Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clawdb-ros2
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: Official ROS2 Robotics SDK & Spatial Memory Bridge for ClawDB
|
|
5
|
+
Home-page: https://github.com/Claw-DB/clawdb-ros2
|
|
6
|
+
Author-email: ClawDB Robotics Team <dev@clawdb.dev>
|
|
7
|
+
Maintainer: ClawDB Robotics Team
|
|
8
|
+
Maintainer-email: dev@clawdb.dev
|
|
9
|
+
License: MIT
|
|
10
|
+
Project-URL: Homepage, https://clawdb.dev
|
|
11
|
+
Project-URL: Repository, https://github.com/Claw-DB/clawdb-ros2
|
|
12
|
+
Project-URL: Documentation, https://docs.clawdb.dev/robotics
|
|
13
|
+
Project-URL: Bug Tracker, https://github.com/Claw-DB/clawdb-ros2/issues
|
|
14
|
+
Keywords: ros2,robotics,clawdb,memory,amr,navigation,nav2,spatial-ai,autonomous-systems
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: requests>=2.28.0
|
|
30
|
+
Requires-Dist: numpy>=1.20.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: maintainer
|
|
39
|
+
Dynamic: maintainer-email
|
|
40
|
+
|
|
41
|
+
# ClawDB ROS2 Robotics SDK (`clawdb-ros2`)
|
|
42
|
+
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
[](https://docs.ros.org)
|
|
45
|
+
[](https://pypi.org/project/clawdb-ros2/)
|
|
46
|
+
|
|
47
|
+
Official ROS2 package and Python SDK for connecting autonomous mobile robots (AMRs), inspection drones, and robotic manipulators to **ClawDB** persistent spatial memory.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ⚡ Key Capabilities
|
|
52
|
+
|
|
53
|
+
- **Sub-0.1ms Local Spatial Recall**: In-memory spatial index ensures trajectory planners and obstacle avoidance controllers run at full frequency without cloud latency bottlenecks.
|
|
54
|
+
- **Multi-Robot Collective Memory**: Synchronize obstacles, blocked warehouse aisles, and spatial landmarks across an entire fleet in real-time.
|
|
55
|
+
- **Nav2 Costmap Integration**: Easily feed dynamic spatial memories directly into ROS2 Nav2 costmap layers.
|
|
56
|
+
- **Cloud & Edge Resilience**: Local offline caching with automatic background delta sync when network connectivity is restored.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 🚀 Installation
|
|
61
|
+
|
|
62
|
+
### Option 1: Standard Python Package (PyPI)
|
|
63
|
+
```bash
|
|
64
|
+
pip install clawdb-ros2
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Option 2: Build inside your ROS2 Colcon Workspace
|
|
68
|
+
```bash
|
|
69
|
+
cd ~/ros2_ws/src
|
|
70
|
+
git clone https://github.com/Claw-DB/clawdb-ros2.git
|
|
71
|
+
cd ~/ros2_ws
|
|
72
|
+
colcon build --packages-select clawdb_ros2
|
|
73
|
+
source install/setup.bash
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 🤖 Quickstart
|
|
79
|
+
|
|
80
|
+
### 1. Launch the Spatial Memory Bridge Node
|
|
81
|
+
```bash
|
|
82
|
+
ros2 launch clawdb_ros2 memory_bridge.launch.py \
|
|
83
|
+
api_key:="your_clawdb_api_key" \
|
|
84
|
+
workspace_id:="your_workspace_id" \
|
|
85
|
+
robot_id:="amr_01"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 2. Record an Obstacle Memory in Python
|
|
89
|
+
```python
|
|
90
|
+
from clawdb_ros2 import ClawClient, SpatialMemoryRecord
|
|
91
|
+
|
|
92
|
+
# Initialize client
|
|
93
|
+
client = ClawClient(
|
|
94
|
+
api_key="your_clawdb_api_key",
|
|
95
|
+
workspace_id="your_workspace_id"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Record spatial blockage
|
|
99
|
+
client.record_spatial_event(
|
|
100
|
+
SpatialMemoryRecord(
|
|
101
|
+
frame_id="map",
|
|
102
|
+
x=14.5,
|
|
103
|
+
y=8.2,
|
|
104
|
+
z=0.0,
|
|
105
|
+
obstacle_type="pallet_spill",
|
|
106
|
+
confidence=0.98,
|
|
107
|
+
tags=["aisle_4", "amr_01"]
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 3. Query Spatial Horizon in Real-Time (<0.1ms)
|
|
113
|
+
```python
|
|
114
|
+
from clawdb_ros2 import SpatialMemoryIndex
|
|
115
|
+
|
|
116
|
+
index = SpatialMemoryIndex(memory_ttl_seconds=3600.0)
|
|
117
|
+
|
|
118
|
+
# Query obstacles within 5.0m radius of current robot coordinates
|
|
119
|
+
nearby = index.query_radius(x=14.0, y=7.5, radius=5.0)
|
|
120
|
+
|
|
121
|
+
for record, dist in nearby:
|
|
122
|
+
print(f"Warning: {record.obstacle_type} at distance {dist:.2f}m")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 🧭 ROS2 Node Topics & Parameters
|
|
128
|
+
|
|
129
|
+
### Subscribed Topics
|
|
130
|
+
| Topic | Type | Description |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `~/detected_obstacle` | `geometry_msgs/Point` | Local point detection to ingest into memory |
|
|
133
|
+
|
|
134
|
+
### Published Topics
|
|
135
|
+
| Topic | Type | Description |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `~/fleet_updates` | `std_msgs/String` | JSON stream of spatial deltas received from fleet |
|
|
138
|
+
|
|
139
|
+
### Parameters
|
|
140
|
+
| Parameter | Type | Default | Description |
|
|
141
|
+
|---|---|---|---|
|
|
142
|
+
| `api_key` | string | `""` | ClawDB API Key |
|
|
143
|
+
| `workspace_id` | string | `""` | Workspace namespace ID |
|
|
144
|
+
| `robot_id` | string | `"amr-01"` | Machine node identifier |
|
|
145
|
+
| `store_name` | string | `"robot-fleet-memory"` | ClawDB memory store name |
|
|
146
|
+
| `frame_id` | string | `"map"` | Coordinate frame ID |
|
|
147
|
+
| `sync_rate_hz` | double | `1.0` | Background fleet sync frequency |
|
|
148
|
+
| `local_ttl_seconds` | double | `7200.0` | Cache retention TTL (seconds) |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 🧪 Testing
|
|
153
|
+
|
|
154
|
+
Run unit tests locally:
|
|
155
|
+
```bash
|
|
156
|
+
pytest tests/
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 📜 License
|
|
162
|
+
|
|
163
|
+
MIT Licensed. Maintained by the ClawDB Robotics Team and open-source contributors.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# ClawDB ROS2 Robotics SDK (`clawdb-ros2`)
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://docs.ros.org)
|
|
5
|
+
[](https://pypi.org/project/clawdb-ros2/)
|
|
6
|
+
|
|
7
|
+
Official ROS2 package and Python SDK for connecting autonomous mobile robots (AMRs), inspection drones, and robotic manipulators to **ClawDB** persistent spatial memory.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ⚡ Key Capabilities
|
|
12
|
+
|
|
13
|
+
- **Sub-0.1ms Local Spatial Recall**: In-memory spatial index ensures trajectory planners and obstacle avoidance controllers run at full frequency without cloud latency bottlenecks.
|
|
14
|
+
- **Multi-Robot Collective Memory**: Synchronize obstacles, blocked warehouse aisles, and spatial landmarks across an entire fleet in real-time.
|
|
15
|
+
- **Nav2 Costmap Integration**: Easily feed dynamic spatial memories directly into ROS2 Nav2 costmap layers.
|
|
16
|
+
- **Cloud & Edge Resilience**: Local offline caching with automatic background delta sync when network connectivity is restored.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🚀 Installation
|
|
21
|
+
|
|
22
|
+
### Option 1: Standard Python Package (PyPI)
|
|
23
|
+
```bash
|
|
24
|
+
pip install clawdb-ros2
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Option 2: Build inside your ROS2 Colcon Workspace
|
|
28
|
+
```bash
|
|
29
|
+
cd ~/ros2_ws/src
|
|
30
|
+
git clone https://github.com/Claw-DB/clawdb-ros2.git
|
|
31
|
+
cd ~/ros2_ws
|
|
32
|
+
colcon build --packages-select clawdb_ros2
|
|
33
|
+
source install/setup.bash
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🤖 Quickstart
|
|
39
|
+
|
|
40
|
+
### 1. Launch the Spatial Memory Bridge Node
|
|
41
|
+
```bash
|
|
42
|
+
ros2 launch clawdb_ros2 memory_bridge.launch.py \
|
|
43
|
+
api_key:="your_clawdb_api_key" \
|
|
44
|
+
workspace_id:="your_workspace_id" \
|
|
45
|
+
robot_id:="amr_01"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Record an Obstacle Memory in Python
|
|
49
|
+
```python
|
|
50
|
+
from clawdb_ros2 import ClawClient, SpatialMemoryRecord
|
|
51
|
+
|
|
52
|
+
# Initialize client
|
|
53
|
+
client = ClawClient(
|
|
54
|
+
api_key="your_clawdb_api_key",
|
|
55
|
+
workspace_id="your_workspace_id"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Record spatial blockage
|
|
59
|
+
client.record_spatial_event(
|
|
60
|
+
SpatialMemoryRecord(
|
|
61
|
+
frame_id="map",
|
|
62
|
+
x=14.5,
|
|
63
|
+
y=8.2,
|
|
64
|
+
z=0.0,
|
|
65
|
+
obstacle_type="pallet_spill",
|
|
66
|
+
confidence=0.98,
|
|
67
|
+
tags=["aisle_4", "amr_01"]
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3. Query Spatial Horizon in Real-Time (<0.1ms)
|
|
73
|
+
```python
|
|
74
|
+
from clawdb_ros2 import SpatialMemoryIndex
|
|
75
|
+
|
|
76
|
+
index = SpatialMemoryIndex(memory_ttl_seconds=3600.0)
|
|
77
|
+
|
|
78
|
+
# Query obstacles within 5.0m radius of current robot coordinates
|
|
79
|
+
nearby = index.query_radius(x=14.0, y=7.5, radius=5.0)
|
|
80
|
+
|
|
81
|
+
for record, dist in nearby:
|
|
82
|
+
print(f"Warning: {record.obstacle_type} at distance {dist:.2f}m")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 🧭 ROS2 Node Topics & Parameters
|
|
88
|
+
|
|
89
|
+
### Subscribed Topics
|
|
90
|
+
| Topic | Type | Description |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `~/detected_obstacle` | `geometry_msgs/Point` | Local point detection to ingest into memory |
|
|
93
|
+
|
|
94
|
+
### Published Topics
|
|
95
|
+
| Topic | Type | Description |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `~/fleet_updates` | `std_msgs/String` | JSON stream of spatial deltas received from fleet |
|
|
98
|
+
|
|
99
|
+
### Parameters
|
|
100
|
+
| Parameter | Type | Default | Description |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
| `api_key` | string | `""` | ClawDB API Key |
|
|
103
|
+
| `workspace_id` | string | `""` | Workspace namespace ID |
|
|
104
|
+
| `robot_id` | string | `"amr-01"` | Machine node identifier |
|
|
105
|
+
| `store_name` | string | `"robot-fleet-memory"` | ClawDB memory store name |
|
|
106
|
+
| `frame_id` | string | `"map"` | Coordinate frame ID |
|
|
107
|
+
| `sync_rate_hz` | double | `1.0` | Background fleet sync frequency |
|
|
108
|
+
| `local_ttl_seconds` | double | `7200.0` | Cache retention TTL (seconds) |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🧪 Testing
|
|
113
|
+
|
|
114
|
+
Run unit tests locally:
|
|
115
|
+
```bash
|
|
116
|
+
pytest tests/
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 📜 License
|
|
122
|
+
|
|
123
|
+
MIT Licensed. Maintained by the ClawDB Robotics Team and open-source contributors.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ClawDB ROS2 Robotics SDK
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Official ROS2 persistent spatial memory bridge and cognitive state client for ClawDB.
|
|
5
|
+
|
|
6
|
+
:copyright: (c) 2026 ClawDB Inc.
|
|
7
|
+
:license: MIT, see LICENSE for more details.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .client import ClawClient, SpatialMemoryRecord, QueryResult
|
|
11
|
+
from .spatial_index import SpatialMemoryIndex
|
|
12
|
+
from .node import ClawSpatialMemoryNode
|
|
13
|
+
|
|
14
|
+
__version__ = "0.4.2"
|
|
15
|
+
__all__ = [
|
|
16
|
+
"ClawClient",
|
|
17
|
+
"SpatialMemoryRecord",
|
|
18
|
+
"QueryResult",
|
|
19
|
+
"SpatialMemoryIndex",
|
|
20
|
+
"ClawSpatialMemoryNode",
|
|
21
|
+
]
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ClawDB Client for Robotics & ROS2 Nodes
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
from typing import List, Dict, Any, Optional, Tuple
|
|
9
|
+
from dataclasses import dataclass, field, asdict
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("clawdb_ros2.client")
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class SpatialMemoryRecord:
|
|
16
|
+
"""Represents a spatial memory event, obstacle, or waypoint heuristic."""
|
|
17
|
+
id: Optional[str] = None
|
|
18
|
+
frame_id: str = "map"
|
|
19
|
+
x: float = 0.0
|
|
20
|
+
y: float = 0.0
|
|
21
|
+
z: float = 0.0
|
|
22
|
+
obstacle_type: str = "general_obstacle"
|
|
23
|
+
confidence: float = 1.0
|
|
24
|
+
tags: List[str] = field(default_factory=list)
|
|
25
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
26
|
+
timestamp: float = field(default_factory=time.time)
|
|
27
|
+
|
|
28
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
29
|
+
return asdict(self)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_dict(cls, data: Dict[str, Any]) -> "SpatialMemoryRecord":
|
|
33
|
+
return cls(
|
|
34
|
+
id=data.get("id"),
|
|
35
|
+
frame_id=data.get("frame_id", "map"),
|
|
36
|
+
x=float(data.get("x", 0.0)),
|
|
37
|
+
y=float(data.get("y", 0.0)),
|
|
38
|
+
z=float(data.get("z", 0.0)),
|
|
39
|
+
obstacle_type=data.get("obstacle_type", "general_obstacle"),
|
|
40
|
+
confidence=float(data.get("confidence", 1.0)),
|
|
41
|
+
tags=data.get("tags", []),
|
|
42
|
+
metadata=data.get("metadata", {}),
|
|
43
|
+
timestamp=float(data.get("timestamp", time.time())),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class QueryResult:
|
|
48
|
+
"""Result from spatial radius or semantic memory search."""
|
|
49
|
+
records: List[SpatialMemoryRecord]
|
|
50
|
+
latency_ms: float
|
|
51
|
+
total_count: int
|
|
52
|
+
|
|
53
|
+
class ClawClient:
|
|
54
|
+
"""
|
|
55
|
+
High-performance HTTP/REST Client for synchronizing robot memory with ClawDB.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
api_key: str,
|
|
61
|
+
workspace_id: str,
|
|
62
|
+
endpoint: str = "https://api.clawdb.dev/v1",
|
|
63
|
+
timeout_seconds: float = 2.0,
|
|
64
|
+
store_name: str = "robot-fleet-memory"
|
|
65
|
+
):
|
|
66
|
+
self.api_key = api_key
|
|
67
|
+
self.workspace_id = workspace_id
|
|
68
|
+
self.endpoint = endpoint.rstrip("/")
|
|
69
|
+
self.timeout = timeout_seconds
|
|
70
|
+
self.store_name = store_name
|
|
71
|
+
|
|
72
|
+
self._session = requests.Session()
|
|
73
|
+
self._session.headers.update({
|
|
74
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
75
|
+
"X-Workspace-Id": self.workspace_id,
|
|
76
|
+
"User-Agent": "clawdb-ros2/0.4.2",
|
|
77
|
+
"Content-Type": "application/json",
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
def record_spatial_event(self, record: SpatialMemoryRecord) -> Dict[str, Any]:
|
|
81
|
+
"""
|
|
82
|
+
Record a spatial memory point (obstacle, landmark, or navigation failure).
|
|
83
|
+
"""
|
|
84
|
+
url = f"{self.endpoint}/workspaces/{self.workspace_id}/memory/insert"
|
|
85
|
+
payload = {
|
|
86
|
+
"store": self.store_name,
|
|
87
|
+
"fields": {
|
|
88
|
+
"frame_id": record.frame_id,
|
|
89
|
+
"x": record.x,
|
|
90
|
+
"y": record.y,
|
|
91
|
+
"z": record.z,
|
|
92
|
+
"obstacle_type": record.obstacle_type,
|
|
93
|
+
"confidence": record.confidence,
|
|
94
|
+
"timestamp": record.timestamp,
|
|
95
|
+
**record.metadata,
|
|
96
|
+
},
|
|
97
|
+
"tags": list(set(record.tags + ["robotics", record.obstacle_type])),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
start_t = time.perf_counter()
|
|
102
|
+
resp = self._session.post(url, json=payload, timeout=self.timeout)
|
|
103
|
+
resp.raise_for_status()
|
|
104
|
+
elapsed_ms = (time.perf_counter() - start_t) * 1000.0
|
|
105
|
+
data = resp.json()
|
|
106
|
+
data["latency_ms"] = elapsed_ms
|
|
107
|
+
return data
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.warning(f"Failed to push spatial memory to ClawDB cloud: {e}")
|
|
110
|
+
return {"status": "offline_cached", "error": str(e)}
|
|
111
|
+
|
|
112
|
+
def search_nearby(
|
|
113
|
+
self,
|
|
114
|
+
x: float,
|
|
115
|
+
y: float,
|
|
116
|
+
radius_meters: float = 5.0,
|
|
117
|
+
frame_id: str = "map",
|
|
118
|
+
limit: int = 10
|
|
119
|
+
) -> QueryResult:
|
|
120
|
+
"""
|
|
121
|
+
Query nearby spatial memories and obstacle alerts around (x, y) coordinate.
|
|
122
|
+
"""
|
|
123
|
+
url = f"{self.endpoint}/workspaces/{self.workspace_id}/memory/search"
|
|
124
|
+
payload = {
|
|
125
|
+
"store": self.store_name,
|
|
126
|
+
"query": f"spatial location near ({x:.2f}, {y:.2f}) in frame {frame_id}",
|
|
127
|
+
"filter": {
|
|
128
|
+
"frame_id": frame_id,
|
|
129
|
+
},
|
|
130
|
+
"limit": limit,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
start_t = time.perf_counter()
|
|
134
|
+
try:
|
|
135
|
+
resp = self._session.post(url, json=payload, timeout=self.timeout)
|
|
136
|
+
resp.raise_for_status()
|
|
137
|
+
elapsed_ms = (time.perf_counter() - start_t) * 1000.0
|
|
138
|
+
data = resp.json()
|
|
139
|
+
records: List[SpatialMemoryRecord] = []
|
|
140
|
+
|
|
141
|
+
for item in data.get("records", []):
|
|
142
|
+
fields = item.get("fields", {})
|
|
143
|
+
records.append(SpatialMemoryRecord(
|
|
144
|
+
id=item.get("id"),
|
|
145
|
+
frame_id=fields.get("frame_id", frame_id),
|
|
146
|
+
x=float(fields.get("x", x)),
|
|
147
|
+
y=float(fields.get("y", y)),
|
|
148
|
+
z=float(fields.get("z", 0.0)),
|
|
149
|
+
obstacle_type=fields.get("obstacle_type", "obstacle"),
|
|
150
|
+
confidence=float(fields.get("confidence", 1.0)),
|
|
151
|
+
tags=item.get("tags", []),
|
|
152
|
+
metadata=fields,
|
|
153
|
+
timestamp=float(fields.get("timestamp", time.time()))
|
|
154
|
+
))
|
|
155
|
+
|
|
156
|
+
return QueryResult(records=records, latency_ms=elapsed_ms, total_count=len(records))
|
|
157
|
+
except Exception as e:
|
|
158
|
+
logger.warning(f"Failed to query ClawDB cloud: {e}")
|
|
159
|
+
elapsed_ms = (time.perf_counter() - start_t) * 1000.0
|
|
160
|
+
return QueryResult(records=[], latency_ms=elapsed_ms, total_count=0)
|
|
161
|
+
|
|
162
|
+
def pull_fleet_deltas(self, since_timestamp: float) -> List[SpatialMemoryRecord]:
|
|
163
|
+
"""
|
|
164
|
+
Pull memory records written by other fleet nodes since timestamp.
|
|
165
|
+
"""
|
|
166
|
+
url = f"{self.endpoint}/workspaces/{self.workspace_id}/memory/sync"
|
|
167
|
+
try:
|
|
168
|
+
resp = self._session.get(url, params={"store": self.store_name, "since": since_timestamp}, timeout=self.timeout)
|
|
169
|
+
if resp.status_code == 200:
|
|
170
|
+
data = resp.json()
|
|
171
|
+
return [SpatialMemoryRecord.from_dict(item) for item in data.get("deltas", [])]
|
|
172
|
+
return []
|
|
173
|
+
except Exception:
|
|
174
|
+
return []
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Standalone Fleet Memory Daemon & Sync CLI
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import argparse
|
|
8
|
+
from .client import ClawClient
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
parser = argparse.ArgumentParser(description="ClawDB Fleet Memory Synchronization CLI")
|
|
12
|
+
parser.add_argument("--api-key", required=True, help="ClawDB API Key")
|
|
13
|
+
parser.add_argument("--workspace-id", required=True, help="ClawDB Workspace ID")
|
|
14
|
+
parser.add_argument("--store", default="robot-fleet-memory", help="Memory store name")
|
|
15
|
+
parser.add_argument("--interval", type=float, default=2.0, help="Sync interval in seconds")
|
|
16
|
+
args = parser.parse_args()
|
|
17
|
+
|
|
18
|
+
client = ClawClient(api_key=args.api_key, workspace_id=args.workspace_id, store_name=args.store)
|
|
19
|
+
print(f"ClawDB Fleet Sync daemon started for workspace: {args.workspace_id} (Store: {args.store})")
|
|
20
|
+
|
|
21
|
+
last_sync = time.time()
|
|
22
|
+
try:
|
|
23
|
+
while True:
|
|
24
|
+
deltas = client.pull_fleet_deltas(since_timestamp=last_sync)
|
|
25
|
+
last_sync = time.time()
|
|
26
|
+
if deltas:
|
|
27
|
+
print(f"[{time.strftime('%X')}] Pulled {len(deltas)} updates from fleet.")
|
|
28
|
+
time.sleep(args.interval)
|
|
29
|
+
except KeyboardInterrupt:
|
|
30
|
+
print("\nFleet sync daemon stopped.")
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
main()
|