diff --git a/dimos/mapping/spec.py b/dimos/mapping/spec.py new file mode 100644 index 0000000000..3d82cea0cc --- /dev/null +++ b/dimos/mapping/spec.py @@ -0,0 +1,31 @@ +# Copyright 2025 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Protocol + +from dimos.core import Out +from dimos.msgs.nav_msgs import OccupancyGrid +from dimos.msgs.sensor_msgs import PointCloud2 + + +class Global3DMapSpec(Protocol): + global_pointcloud: Out[PointCloud2] + + +class GlobalMapSpec(Protocol): + global_map: Out[OccupancyGrid] + + +class GlobalCostmapSpec(Protocol): + global_costmap: Out[OccupancyGrid] diff --git a/dimos/navigation/rosnav.py b/dimos/navigation/rosnav.py new file mode 100644 index 0000000000..9bdee3fe3a --- /dev/null +++ b/dimos/navigation/rosnav.py @@ -0,0 +1,39 @@ +# Copyright 2025 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from dimos.core import In, Module, Out +from dimos.msgs.geometry_msgs import PoseStamped, Twist +from dimos.msgs.nav_msgs import Path +from dimos.msgs.sensor_msgs import PointCloud2 + + +class ROSNav(Module): + goal_req: In[PoseStamped] = None # type: ignore + goal_active: Out[PoseStamped] = None # type: ignore + path_active: Out[Path] = None # type: ignore + ctrl: Out[Twist] = None # type: ignore + + # PointcloudPerception attributes + pointcloud: Out[PointCloud2] = None # type: ignore + + # Global3DMapSpec attributes + global_pointcloud: Out[PointCloud2] = None # type: ignore + + def navigate_to_target(self, target: PoseStamped) -> None: + # TODO: Implement navigation logic + pass + + def stop_navigating(self) -> None: + # TODO: Implement stop logic + pass diff --git a/dimos/navigation/spec.py b/dimos/navigation/spec.py new file mode 100644 index 0000000000..69bfdac262 --- /dev/null +++ b/dimos/navigation/spec.py @@ -0,0 +1,31 @@ +# Copyright 2025 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Protocol + +from dimos.core import In, Out +from dimos.msgs.geometry_msgs import PoseStamped, Twist +from dimos.msgs.nav_msgs import Path + + +class NavSpec(Protocol): + goal_req: In[PoseStamped] + goal_active: Out[PoseStamped] + path_active: Out[Path] + ctrl: Out[Twist] + + # identity quaternion (Quaternion(0,0,0,1)) represents "no rotation requested" + def navigate_to_target(self, target: PoseStamped) -> None: ... + + def stop_navigating(self) -> None: ... diff --git a/dimos/navigation/test_rosnav.py b/dimos/navigation/test_rosnav.py new file mode 100644 index 0000000000..5de1c0e6ab --- /dev/null +++ b/dimos/navigation/test_rosnav.py @@ -0,0 +1,37 @@ +# Copyright 2025 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Protocol + +from dimos.mapping.spec import Global3DMapSpec +from dimos.navigation.rosnav import ROSNav +from dimos.navigation.spec import NavSpec +from dimos.perception.spec import PointcloudPerception + + +class RosNavSpec(NavSpec, PointcloudPerception, Global3DMapSpec, Protocol): + """Combined protocol for navigation components.""" + + pass + + +def accepts_combined_protocol(nav: RosNavSpec) -> None: + """Function that accepts all navigation protocols at once.""" + pass + + +def test_typing_prototypes(): + """Test that ROSNav correctly implements all required protocols.""" + rosnav = ROSNav() + accepts_combined_protocol(rosnav) diff --git a/dimos/perception/spec.py b/dimos/perception/spec.py new file mode 100644 index 0000000000..de53ce9bd7 --- /dev/null +++ b/dimos/perception/spec.py @@ -0,0 +1,22 @@ +# Copyright 2025 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Protocol + +from dimos.core import Out +from dimos.msgs.sensor_msgs import PointCloud2 + + +class PointcloudPerception(Protocol): + pointcloud: Out[PointCloud2]