Skip to content

Commit 1125f05

Browse files
authored
DOCS-1877: Add motion, nav, slam method examples (#550)
1 parent a111146 commit 1125f05

File tree

3 files changed

+303
-3
lines changed

3 files changed

+303
-3
lines changed

src/viam/services/motion/client.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Transform,
1515
WorldState,
1616
)
17+
1718
from viam.proto.service.motion import (
1819
Constraints,
1920
GetPlanRequest,
@@ -33,6 +34,7 @@
3334
StopPlanRequest,
3435
StopPlanResponse,
3536
)
37+
3638
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
3739
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, Subtype
3840
from viam.services.service_client_base import ServiceClientBase
@@ -74,6 +76,24 @@ async def move(
7476
resource_name = Arm.get_resource_name("externalFrame")
7577
success = await MotionServiceClient.move(resource_name, ...)
7678
79+
::
80+
81+
motion = MotionClient.from_robot(robot=robot, name="builtin")
82+
83+
# Assumes a gripper configured with name "my_gripper" on the machine
84+
gripper_name = Gripper.get_resource_name("my_gripper")
85+
my_frame = "my_gripper_offset"
86+
87+
goal_pose = Pose(x=0, y=0, z=300, o_x=0, o_y=0, o_z=1, theta=0)
88+
89+
# Move the gripper
90+
moved = await motion.move(component_name=gripper_name,
91+
destination=PoseInFrame(reference_frame="myFrame",
92+
pose=goal_pose),
93+
world_state=worldState,
94+
constraints={},
95+
extra={})
96+
7797
Args:
7898
component_name (viam.proto.common.ResourceName): Name of a component on a given robot.
7999
destination (viam.proto.common.PoseInFrame): The destination to move to, expressed as a ``Pose`` and the frame in which it was
@@ -121,6 +141,23 @@ async def move_on_globe(
121141
122142
You can monitor the progress of the ``move_on_globe()`` call by querying ``get_plan()`` and ``list_plan_statuses()``.
123143
144+
::
145+
146+
motion = MotionClient.from_robot(robot=robot, name="builtin")
147+
148+
# Get the ResourceNames of the base and movement sensor
149+
my_base_resource_name = Base.get_resource_name("my_base")
150+
mvmnt_sensor_resource_name = MovementSensor.get_resource_name(
151+
"my_movement_sensor")
152+
# Define a destination GeoPoint at the GPS coordinates [0, 0]
153+
my_destination = movement_sensor.GeoPoint(latitude=0, longitude=0)
154+
155+
# Move the base component to the designated geographic location, as reported by the movement sensor
156+
execution_id = await motion.move_on_globe(
157+
component_name=my_base_resource_name,
158+
destination=my_destination,
159+
movement_sensor_name=mvmnt_sensor_resource_name)
160+
124161
Args:
125162
component_name (ResourceName): The ResourceName of the base to move.
126163
destination (GeoPoint): The location of the component’s destination, represented in geographic notation as a
@@ -188,6 +225,23 @@ async def move_on_map(
188225
189226
You can monitor the progress of the ``move_on_map()`` call by querying ``get_plan()`` and ``list_plan_statuses()``.
190227
228+
::
229+
230+
motion = MotionClient.from_robot(robot=robot, name="builtin")
231+
232+
# Get the ResourceNames of the base component and SLAM service
233+
my_base_resource_name = Base.get_resource_name("my_base")
234+
my_slam_service_name = SLAMClient.get_resource_name("my_slam_service")
235+
236+
# Define a destination pose with respect to the origin of the map from the SLAM service "my_slam_service"
237+
my_pose = Pose(y=10)
238+
239+
# Move the base component to the destination pose of Y=10, a location of
240+
# (0, 10, 0) in respect to the origin of the map
241+
execution_id = await motion.move_on_map(component_name=my_base_resource_name,
242+
destination=my_pose,
243+
slam_service_name=my_slam_service_name)
244+
191245
Args:
192246
component_name (ResourceName): The ResourceName of the base to move.
193247
destination (Pose): The destination, which can be any Pose with respect to the SLAM map’s origin.
@@ -232,6 +286,14 @@ async def stop_plan(
232286
):
233287
"""Stop a component being moved by an in progress ``move_on_globe()`` or ``move_on_map()`` call.
234288
289+
::
290+
291+
# Assuming a `move_on_globe()` started the execution
292+
# Stop the base component which was instructed to move by `move_on_globe()`
293+
# or `move_on_map()`
294+
my_base_resource_name = Base.get_resource_name("my_base")
295+
await motion.stop_plan(component_name=mvmnt_sensor)
296+
235297
Args:
236298
component_name (ResourceName): The component to stop
237299
@@ -258,7 +320,7 @@ async def get_plan(
258320
extra: Optional[Mapping[str, ValueTypes]] = None,
259321
timeout: Optional[float] = None,
260322
) -> GetPlanResponse:
261-
"""By default: returns the plan history of the most recent ``move_on_globe()`` or ``move_on_map()``call to move a component.
323+
"""By default: returns the plan history of the most recent ``move_on_globe()`` or ``move_on_map()`` call to move a component.
262324
263325
The plan history for executions before the most recent can be requested by providing an ExecutionID in the request.
264326
@@ -275,6 +337,13 @@ async def get_plan(
275337
276338
All repeated fields are in time ascending order.
277339
340+
::
341+
342+
motion = MotionClient.from_robot(robot=robot, name="builtin")
343+
my_base_resource_name = Base.get_resource_name("my_base")
344+
# Get the plan(s) of the base component which was instructed to move by `MoveOnGlobe()` or `MoveOnMap()`
345+
resp = await motion.get_plan(component_name=my_base_resource_name)
346+
278347
Args:
279348
component_name (ResourceName): The component to stop
280349
last_plan_only (Optional[bool]): If supplied, the response will only return the last plan for the component / execution
@@ -311,6 +380,12 @@ async def list_plan_statuses(
311380
312381
All repeated fields are in chronological order.
313382
383+
::
384+
385+
motion = MotionClient.from_robot(robot=robot, name="builtin")
386+
# List the plan statuses of the motion service within the TTL
387+
resp = await motion.list_plan_statuses()
388+
314389
Args:
315390
only_active_plans (Optional[bool]): If supplied, the response will filter out any plans that are not executing
316391
@@ -346,6 +421,18 @@ async def get_pose(
346421
Note that the example uses the ``Arm`` class, but any component class that inherits from ``ComponentBase`` will work
347422
(``Base``, ``Gripper``, etc).
348423
424+
::
425+
426+
from viam.components.gripper import Gripper
427+
from viam.services.motion import MotionClient
428+
429+
# Assume that the connect function is written and will return a valid machine.
430+
robot = await connect()
431+
432+
motion = MotionClient.from_robot(robot=robot, name="builtin")
433+
gripperName = Gripper.get_resource_name("my_gripper")
434+
gripperPoseInWorld = await motion.get_pose(component_name=gripperName,
435+
destination_frame="world")
349436
350437
Args:
351438
component_name (viam.proto.common.ResourceName): Name of a component on a robot.
@@ -371,6 +458,18 @@ async def get_pose(
371458
async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **__) -> Mapping[str, ValueTypes]:
372459
"""Send/receive arbitrary commands
373460
461+
::
462+
463+
# Access the motion service
464+
motion = MotionClient.from_robot(robot=robot, name="builtin")
465+
466+
my_command = {
467+
"command": "dosomething",
468+
"someparameter": 52
469+
}
470+
471+
await motion.do_command(my_command)
472+
374473
Args:
375474
command (Dict[str, ValueTypes]): The command to execute
376475

src/viam/services/navigation/navigation.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,209 @@ class Navigation(ServiceBase):
2222

2323
@abc.abstractmethod
2424
async def get_paths(self, *, timeout: Optional[float]) -> List[Path]:
25+
"""
26+
Get each path, the series of geo points the robot plans to travel through
27+
to get to a destination waypoint, in the machine’s motion planning.
28+
29+
::
30+
31+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
32+
33+
# Get a list containing each path stored by the navigation service
34+
paths = await my_nav.get_paths()
35+
36+
Args:
37+
timeout (Optional[float]): An option to set how long to wait (in seconds)
38+
before calling a time-out and closing the underlying RPC call.
39+
40+
Returns:
41+
(List[navigation.Path]): An array comprised of Paths, where each path is either a user-provided destination or
42+
a Waypoint, along with the corresponding set of geopoints. This outlines the route the machine is expected to take to
43+
reach the specified destination or Waypoint.
44+
"""
2545
...
2646

2747
@abc.abstractmethod
2848
async def get_location(self, *, timeout: Optional[float]) -> GeoPoint:
49+
"""
50+
Get the current location of the robot in the navigation service.
51+
52+
::
53+
54+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
55+
56+
# Get the current location of the robot in the navigation service
57+
location = await my_nav.get_location()
58+
59+
Args:
60+
timeout (Optional[float]): An option to set how long to wait (in seconds)
61+
before calling a time-out and closing the underlying RPC call.
62+
63+
Returns:
64+
(navigation.GeoPoint): The current location of the robot in the navigation service,
65+
represented in a GeoPoint with latitude and longitude values.
66+
"""
2967
...
3068

3169
@abc.abstractmethod
3270
async def get_obstacles(self, *, timeout: Optional[float]) -> List[GeoObstacle]:
71+
"""
72+
Get an array or list of the obstacles currently in the service’s data storage.
73+
These are objects designated for the robot to avoid when navigating.
74+
These include all transient obstacles which are discovered by the vision services configured for the navigation service,
75+
in addition to the obstacles that are configured as a part of the service.
76+
77+
::
78+
79+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
80+
81+
# Get a list containing each obstacle stored by the navigation service
82+
obstacles = await my_nav.get_obstacles()
83+
84+
Args:
85+
timeout (Optional[float]): An option to set how long to wait (in seconds)
86+
before calling a time-out and closing the underlying RPC call.
87+
88+
Returns:
89+
(List[navigation.GeoObstacle]): A list comprised of each GeoObstacle in the service’s data storage.
90+
These are objects designated for the robot to avoid when navigating.
91+
"""
3392
...
3493

3594
@abc.abstractmethod
3695
async def get_waypoints(self, *, timeout: Optional[float]) -> List[Waypoint]:
96+
"""
97+
Get an array of waypoints currently in the service’s data storage.
98+
These are locations designated within a path for the robot to navigate to.
99+
100+
::
101+
102+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
103+
104+
# Get a list containing each waypoint stored by the navigation service
105+
waypoints = await my_nav.get_waypoints()
106+
107+
Args:
108+
timeout (Optional[float]): An option to set how long to wait (in seconds)
109+
before calling a time-out and closing the underlying RPC call.
110+
111+
Returns:
112+
(List[navigation.Waypoint]): An array comprised of each Waypoint in the service’s data storage.
113+
These are locations designated within a path for the robot to navigate to.
114+
"""
37115
...
38116

39117
@abc.abstractmethod
40118
async def add_waypoint(self, point: GeoPoint, *, timeout: Optional[float]):
119+
"""
120+
Add a waypoint to the service’s data storage.
121+
122+
::
123+
124+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
125+
126+
# Create a new waypoint with latitude and longitude values of 0 degrees
127+
location = GeoPoint(latitude=0, longitude=0)
128+
129+
130+
# Add your waypoint to the service's data storage
131+
await my_nav.add_waypoint(point=location)
132+
133+
Args:
134+
point(navigation.GeoPoint): The current location of the robot in the navigation service,
135+
represented in a GeoPoint with latitude and longitude values.
136+
timeout (Optional[float]): An option to set how long to wait (in seconds)
137+
before calling a time-out and closing the underlying RPC call.
138+
"""
41139
...
42140

43141
@abc.abstractmethod
44142
async def remove_waypoint(self, id: str, *, timeout: Optional[float]):
143+
"""
144+
Remove a waypoint from the service’s data storage. If the robot is currently navigating to this waypoint,
145+
the motion will be canceled, and the robot will proceed to the next waypoint.
146+
147+
::
148+
149+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
150+
151+
# Remove the waypoint matching that ObjectID from the service's data storage
152+
await my_nav.remove_waypoint(waypoint_id)
153+
154+
Args:
155+
id(str): The MongoDB ObjectID of the Waypoint to remove from the service’s data storage.
156+
timeout (Optional[float]): An option to set how long to wait (in seconds)
157+
before calling a time-out and closing the underlying RPC call.
158+
"""
45159
...
46160

47161
@abc.abstractmethod
48162
async def get_mode(self, *, timeout: Optional[float]) -> Mode.ValueType:
163+
"""
164+
Get the Mode the service is operating in.
165+
166+
There are two options for modes: MODE_MANUAL or MODE_WAYPOINT.
167+
168+
MODE_WAYPOINT: Start to look for added waypoints and begin autonomous navigation.
169+
MODE_MANUAL: Stop autonomous navigation between waypoints and allow the base to be controlled manually.
170+
171+
::
172+
173+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
174+
175+
# Get the Mode the service is operating in
176+
await my_nav.get_mode()
177+
178+
Args:
179+
timeout (Optional[float]): An option to set how long to wait (in seconds)
180+
before calling a time-out and closing the underlying RPC call.
181+
182+
Returns:
183+
navigation.Mode.ValueType: The Mode the service is operating in.
184+
"""
49185
...
50186

51187
@abc.abstractmethod
52188
async def set_mode(self, mode: Mode.ValueType, *, timeout: Optional[float]):
189+
"""
190+
Set the Mode the service is operating in.
191+
192+
There are two options for modes: MODE_MANUAL or MODE_WAYPOINT.
193+
194+
MODE_WAYPOINT: Start to look for added waypoints and begin autonomous navigation.
195+
MODE_MANUAL: Stop autonomous navigation between waypoints and allow the base to be controlled manually.
196+
197+
::
198+
199+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
200+
201+
# Set the Mode the service is operating in to MODE_WAYPOINT and begin navigation
202+
await my_nav.set_mode(Mode.ValueType.MODE_WAYPOINT)
203+
204+
Args:
205+
timeout (Optional[float]): An option to set how long to wait (in seconds)
206+
before calling a time-out and closing the underlying RPC call.
207+
mode (navigation.Mode.ValueType): The Mode for the service to operate in.
208+
"""
53209
...
54210

55211
@abc.abstractmethod
56212
async def get_properties(self, *, timeout: Optional[float]) -> MapType.ValueType:
213+
"""
214+
Get information about the navigation service.
215+
216+
::
217+
218+
my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")
219+
220+
# Get the properties of the current navigation service.
221+
nav_properties = await my_nav.get_properties()
222+
223+
Args:
224+
timeout (Optional[float]): An option to set how long to wait (in seconds)
225+
before calling a time-out and closing the underlying RPC call.
226+
227+
Returns:
228+
MapType.ValueType: Information about the type of map the service is using.
229+
"""
57230
...

0 commit comments

Comments
 (0)