Skip to content

Commit 58f0cb9

Browse files
authored
Merge pull request #87 from compas-dev/simplify-ros-load
Simplify load robot from ROS
2 parents 272a062 + fbb88ba commit 58f0cb9

File tree

8 files changed

+73
-77
lines changed

8 files changed

+73
-77
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Unreleased
1212

1313
**Added**
1414

15+
* Added ``load_robot`` method to ROS client to simplify loading robots from running ROS setup.
1516
* Added ``compas_fab.robots.Wrench``: a Wrench class representing force in free space, separated into its linear (force) and angular (torque) parts.
1617
* Added ``compas_fab.robots.Inertia``: a Inertia class representing spatial distribution of mass in a rigid body
1718

docs/examples/03_backends_ros/files/02_forward_kinematics_ros_loader.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,8 @@
1-
from compas.robots import RobotModel
2-
31
from compas_fab.backends import RosClient
4-
from compas_fab.backends import RosFileServerLoader
52
from compas_fab.robots import Configuration
6-
from compas_fab.robots import Robot
7-
from compas_fab.robots import RobotSemantics
83

94
with RosClient() as client:
10-
loader = RosFileServerLoader(client)
11-
12-
urdf = loader.load_urdf()
13-
srdf = loader.load_srdf()
14-
15-
model = RobotModel.from_urdf_string(urdf)
16-
semantics = RobotSemantics.from_srdf_string(srdf, model)
17-
18-
robot = Robot(model, semantics=semantics, client=client)
5+
robot = client.load_robot()
196

207
configuration = Configuration.from_prismatic_and_revolute_values([0.], [-2.238, -1.153, -2.174, 0.185, 0.667, 0.])
218

docs/examples/03_backends_ros/files/02_inverse_kinematics_ros_loader.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,8 @@
11
from compas.geometry import Frame
2-
from compas.robots import RobotModel
3-
42
from compas_fab.backends import RosClient
5-
from compas_fab.backends import RosFileServerLoader
6-
from compas_fab.robots import Robot
7-
from compas_fab.robots import RobotSemantics
83

94
with RosClient() as client:
10-
loader = RosFileServerLoader(client)
11-
12-
urdf = loader.load_urdf()
13-
srdf = loader.load_srdf()
14-
15-
model = RobotModel.from_urdf_string(urdf)
16-
semantics = RobotSemantics.from_srdf_string(srdf, model)
17-
18-
robot = Robot(model, semantics=semantics, client=client)
5+
robot = client.load_robot()
196

207
frame_WCF = Frame([0.3, 0.1, 0.5], [1, 0, 0], [0, 1, 0])
218
start_configuration = robot.init_configuration()

docs/examples/03_backends_ros/files/04_plan_cartesian_motion_ros_loader.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
11
from compas.geometry import Frame
2-
from compas.robots import RobotModel
3-
42
from compas_fab.backends import RosClient
5-
from compas_fab.backends import RosFileServerLoader
63
from compas_fab.robots import Configuration
7-
from compas_fab.robots import Robot
8-
from compas_fab.robots import RobotSemantics
94

105
with RosClient() as client:
11-
loader = RosFileServerLoader(client)
12-
13-
urdf = loader.load_urdf()
14-
srdf = loader.load_srdf()
15-
16-
model = RobotModel.from_urdf_string(urdf)
17-
semantics = RobotSemantics.from_srdf_string(srdf, model)
18-
19-
robot = Robot(model, semantics=semantics, client=client)
6+
robot = client.load_robot()
207
group = robot.main_group_name
218

229
frames = []

docs/examples/03_backends_ros/files/04_plan_motion_ros_loader.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
import math
22
from compas.geometry import Frame
3-
from compas.robots import RobotModel
43

54
from compas_fab.backends import RosClient
6-
from compas_fab.backends import RosFileServerLoader
75
from compas_fab.robots import Configuration
8-
from compas_fab.robots import Robot
9-
from compas_fab.robots import RobotSemantics
106

117
with RosClient() as client:
12-
loader = RosFileServerLoader(client)
13-
14-
urdf = loader.load_urdf()
15-
srdf = loader.load_srdf()
16-
17-
model = RobotModel.from_urdf_string(urdf)
18-
semantics = RobotSemantics.from_srdf_string(srdf, model)
19-
20-
robot = Robot(model, semantics=semantics, client=client)
8+
robot = client.load_robot()
219
group = robot.main_group_name
2210

2311
frame = Frame([0.4, 0.3, 0.4], [0, 1, 0], [0, 0, 1])

docs/examples/03_backends_ros/files/robot-playground.ghx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4220,11 +4220,7 @@ if sc.sticky[response_key]:
42204220
from scriptcontext import sticky as st
42214221

42224222
import compas
4223-
from compas.robots import RobotModel
42244223
from compas_fab.backends import RosClient
4225-
from compas_fab.backends import RosFileServerLoader
4226-
from compas_fab.robots import Robot
4227-
from compas_fab.robots import RobotSemantics
42284224
from compas_fab.ghpython import RobotArtist
42294225

42304226
compas.PRECISION = '12f'
@@ -4237,21 +4233,11 @@ if robot_key not in st:
42374233
st[robot_key] = None
42384234

42394235
if load:
4240-
with RosClient() as ros:
4241-
# Load URDF from ROS with local cache enabled
4242-
local_directory = os.path.join(os.path.expanduser('~'), 'robot_description')
4243-
loader = RosFileServerLoader(ros, local_cache=True, local_cache_directory=local_directory)
4244-
loader.robot_name = 'panda-0001'
4245-
4246-
urdf = loader.load_urdf()
4247-
srdf = loader.load_srdf()
4236+
local_directory = os.path.join(os.path.expanduser('~'), 'robot_description', 'panda')
42484237

4249-
# Create robot model from URDF and load geometry
4250-
model = RobotModel.from_urdf_string(urdf)
4251-
model.load_geometry(loader)
4252-
robot = Robot(model)
4238+
with RosClient() as ros:
4239+
robot = ros.load_robot(load_geometry=True, local_cache_directory=local_directory)
42534240
robot.artist = RobotArtist(robot.model)
4254-
robot.semantics = RobotSemantics.from_srdf_string(srdf, model)
42554241

42564242
st[robot_key] = robot
42574243

@@ -15078,4 +15064,4 @@ if robot:
1507815064
</items>
1507915065
</chunk>
1508015066
</chunks>
15081-
</Archive>
15067+
</Archive>

src/compas_fab/backends/ros/client.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
from __future__ import print_function
22

3+
import os
4+
5+
from compas.robots import RobotModel
36
from compas.utilities import await_callback
47
from roslibpy import Message
58
from roslibpy import Ros
69
from roslibpy.actionlib import ActionClient
710
from roslibpy.actionlib import Goal
811

912
from compas_fab.backends.ros.exceptions import RosError
13+
from compas_fab.backends.ros.fileserver_loader import RosFileServerLoader
1014
from compas_fab.backends.ros.messages import ExecuteTrajectoryFeedback
1115
from compas_fab.backends.ros.messages import ExecuteTrajectoryGoal
1216
from compas_fab.backends.ros.messages import ExecuteTrajectoryResult
@@ -21,6 +25,8 @@
2125
from compas_fab.backends.ros.messages import Time
2226
from compas_fab.backends.ros.planner_backend_moveit import MoveItPlanner
2327
from compas_fab.backends.tasks import CancellableFutureResult
28+
from compas_fab.robots import Robot
29+
from compas_fab.robots import RobotSemantics
2430

2531
__all__ = [
2632
'RosClient',
@@ -110,6 +116,60 @@ def __exit__(self, *args):
110116

111117
self.close()
112118

119+
def load_robot(self, load_geometry=False, urdf_param_name='/robot_description', srdf_param_name='/robot_description_semantic', precision=None, local_cache_directory=None):
120+
"""Load an entire robot instance -including model and semantics- directly from ROS.
121+
122+
Parameters
123+
----------
124+
load_geometry : bool, optional
125+
``True`` to load the robot's geometry, otherwise ``False`` to load only the model and semantics.
126+
urdf_param_name : str, optional
127+
Parameter name where the URDF is defined. If not defined, it will default to ``/robot_description``.
128+
srdf_param_name : str, optional
129+
Parameter name where the SRDF is defined. If not defined, it will default to ``/robot_description_semantic``.
130+
precision : float
131+
Defines precision for importing/loading meshes. Defaults to ``compas.PRECISION``.
132+
local_cache_directory : str, optional
133+
Directory where the robot description (URDF, SRDF and meshes) are stored.
134+
This differs from the directory taken as parameter by the :class:`RosFileServerLoader`
135+
in that it points directly to the specific robot package, not to a global workspace storage
136+
for all robots. If not assigned, the robot will not be cached locally.
137+
138+
Examples
139+
--------
140+
141+
>>> from compas_fab.backends import RosClient
142+
>>> with RosClient() as client:
143+
... robot = client.load_robot()
144+
... print(robot.name)
145+
ur5
146+
"""
147+
robot_name = None
148+
use_local_cache = False
149+
150+
if local_cache_directory is not None:
151+
use_local_cache = True
152+
path_parts = local_cache_directory.strip(os.path.sep).split(os.path.sep)
153+
path_parts, robot_name = path_parts[:-1], path_parts[-1]
154+
local_cache_directory = os.path.sep.join(path_parts)
155+
156+
loader = RosFileServerLoader(self, use_local_cache, local_cache_directory, precision)
157+
158+
if robot_name:
159+
loader.robot_name = robot_name
160+
161+
urdf = loader.load_urdf(urdf_param_name)
162+
srdf = loader.load_srdf(srdf_param_name)
163+
164+
model = RobotModel.from_urdf_string(urdf)
165+
semantics = RobotSemantics.from_srdf_string(srdf, model)
166+
167+
if load_geometry:
168+
model.load_geometry(loader)
169+
170+
return Robot(model, semantics=semantics, client=self)
171+
172+
113173
def inverse_kinematics(self, frame, base_link, group,
114174
joint_names, joint_positions, avoid_collisions=True,
115175
constraints=None, attempts=8,

src/compas_fab/backends/ros/fileserver_loader.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
from compas.geometry import Transformation
1616
from compas.robots.resources.basic import _get_file_format
1717
from compas.robots.resources.basic import _mesh_import
18-
from compas.utilities import await_callback
1918
from compas.utilities import geometric_key
2019

2120
LOGGER = logging.getLogger('compas_fab.backends.ros')
21+
TIMEOUT = 10
2222

2323
__all__ = [
2424
'RosFileServerLoader',
@@ -119,7 +119,7 @@ def load_urdf(self, parameter_name='/robot_description'):
119119
return _read_file(filename)
120120

121121
param = roslibpy.Param(self.ros, parameter_name)
122-
urdf = await_callback(param.get)
122+
urdf = param.get(timeout=TIMEOUT)
123123

124124
self.robot_name = self._read_robot_name(urdf)
125125

@@ -151,7 +151,7 @@ def load_srdf(self, parameter_name='/robot_description_semantic'):
151151
return _read_file(filename)
152152

153153
param = roslibpy.Param(self.ros, parameter_name)
154-
srdf = await_callback(param.get)
154+
srdf = param.get(timeout=TIMEOUT)
155155

156156
if self.local_cache_enabled:
157157
_write_file(self._srdf_filename, srdf)
@@ -205,7 +205,7 @@ def load_mesh(self, url):
205205
if not use_local_file:
206206
service = roslibpy.Service(self.ros, '/file_server/get_file', 'file_server/GetBinaryFile')
207207
request = roslibpy.ServiceRequest(dict(name=url))
208-
response = await_callback(service.call, 'callback', 'errback', request)
208+
response = service.call(request, timeout=TIMEOUT)
209209

210210
file_content = binascii.a2b_base64(response.data['value'])
211211

0 commit comments

Comments
 (0)