@@ -141,6 +141,36 @@ of joints:
141141* **Fixed **: Not really a joint because it cannot move, all degrees of freedom
142142 are locked.
143143
144+
145+ Visualizing Robots
146+ ==================
147+
148+ Before jumping into how to build a robot model, let's first see how to visualize
149+ one. This can be done with Blender, Rhino or Grasshopper using one of COMPAS's
150+ artists. The basic procedure is the same in
151+ any of the CAD software (aside from the import statement), so for simplicity we
152+ will demonstrate the use of :class: `compas_rhino.artists.RobotModelArtist ` in Rhino.
153+ Be sure to first install COMPAS for Rhino. While the following code is incomplete,
154+ it can be used as a scaffolding for code to be run in a Python script editor within Rhino.
155+
156+ .. code-block :: python
157+
158+ import compas
159+ from compas.robots import RobotModel
160+ from compas_rhino.artists import RobotModelArtist
161+
162+ model = RobotModel(' Robby' )
163+
164+ # Add some geometry to Robby here
165+
166+ artist = RobotModelArtist(model, layer = ' COMPAS::Example Robot' )
167+ artist.clear_layer()
168+ artist.draw_visual()
169+
170+
171+ See below for a complete example.
172+
173+
144174Building robots models
145175======================
146176
@@ -175,35 +205,170 @@ shows how to construct one programmatically:
175205
176206This approach can end up being very verbose, so the methods ``add_link ``
177207and ``add_joint `` of :class: `compas.robots.RobotModel ` offer an alternative that
178- significantly reduces the amount of code required:
208+ significantly reduces the amount of code required. Starting with an empty
209+ robot model, adding a link in the shape of a box is as easy as:
179210
180211::
181212
182213 >>> from compas.geometry import Box, Frame
183- >>> from compas.datastructures import Mesh
184- >>> from compas.robots import Joint, Link, RobotModel
214+ >>> from compas.robots import RobotModel
185215
186216::
187217
188- >>> length, width, axis = 5, 0.4, (0, 0, 1)
189- >>> box = Box.from_diagonal([(0, width / -2, width / -2), (length, width / 2, width / 2)])
190- >>> robot = RobotModel('bender')
191- >>> link_last = robot.add_link('world')
192- >>> robot.name
193- 'bender'
194-
195- >>> for i in range(6):
196- ... visual_mesh = Mesh.from_shape(box)
197- ... origin = Frame.from_quaternion((1, 0, 0, 0), point=(length, 0, 0))
198- ... link = robot.add_link('link_{}'.format(i), visual_mesh)
199- ... robot.add_joint('joint_{}'.format(i), Joint.CONTINUOUS, link_last, link, origin, axis)
200- ... link_last = link
201- ...
202- >>> robot.get_configurable_joint_names()
203- ['joint_0', 'joint_1', 'joint_2', 'joint_3', 'joint_4', 'joint_5']
218+ >>> model = RobotModel(name='Boxy')
219+ >>> box = Box(Frame.worldXY(), 1, 2, .5)
220+ >>> model.add_link(name='box_link', visual_meshes=[box])
221+
222+ This code snippet can be modified and run in a Rhino python editor
223+ to visualize Boxy. Throughout the rest of this tutorial, the code
224+ snippets will include the lines for visualization in Rhino, but be
225+ aware that the class :class: `compas.robots.RobotModel ` can be used,
226+ and is useful, outside of a CAD environment.
227+
228+ .. code-block :: python
229+
230+ from compas.geometry import Box, Frame
231+ from compas.robots import RobotModel
232+ from compas_rhino.artists import RobotModelArtist
233+
234+ model = RobotModel(name = ' Boxy' )
235+ box = Box(Frame.worldXY(), 1 , 2 , .5 )
236+ model.add_link(name = ' box_link' , visual_meshes = [box])
237+
238+ artist = RobotModelArtist(model, layer = ' COMPAS::Example Robot' )
239+ artist.clear_layer()
240+ artist.draw_visual()
241+
242+
243+ .. figure :: files/boxy_1.png
244+ :figclass: figure
245+ :class: figure-img img-fluid
246+
247+ As can be seen, this has added a box of dimensions ``1 `` x ``2 `` x ``.5 ``
248+ whose geometric center and orientation coincides with the world XY frame.
249+ The ``visual_meshes `` argument can be given a list containing COMPAS
250+ primitives such as :class: `compas.geometry.Box ` or the more complex
251+ COMPAS meshes :class: `compas.geometry.Mesh `. For simplicity,
252+ this tutorial uses only primitives.
253+
254+ To reposition the box relative to the link's
255+ origin, simply change the frame of the provided box. To move the box so that it sits
256+ above the XY plane, the origin must be shifted in the z-direction by half the height
257+ of the box. The box is also shifted slightly forward in the y-direction:
258+
259+ .. code-block :: python
260+
261+ from compas.geometry import Box, Frame
262+ from compas.robots import RobotModel
263+ from compas_rhino.artists import RobotModelArtist
264+
265+ model = RobotModel(name = ' Boxy' )
266+ box = Box(Frame([0 , .5 , .25 ], [1 , 0 , 0 ], [0 , 1 , 0 ]), 1 , 2 , .5 )
267+ model.add_link(name = ' box_link' , visual_meshes = [box])
268+
269+ artist = RobotModelArtist(model, layer = ' COMPAS::Example Robot' )
270+ artist.clear_layer()
271+ artist.draw_visual()
204272
205- However, more often than not, robot models are loaded from URDF files instead of being
206- defined programmatically. To load a URDF into a robot model, use the
273+ .. figure :: files/boxy_2.png
274+ :figclass: figure
275+ :class: figure-img img-fluid
276+
277+ A link may have more than one geometric element associated to it. Now
278+ there is a stack of two boxes:
279+
280+ .. code-block :: python
281+
282+ from compas.geometry import Box, Frame
283+ from compas.robots import RobotModel
284+ from compas_rhino.artists import RobotModelArtist
285+
286+ model = RobotModel(name = ' Boxy' )
287+ box_1 = Box(Frame([0 , .5 , .25 ], [1 , 0 , 0 ], [0 , 1 , 0 ]), 1 , 2 , .5 )
288+ box_2 = Box(Frame([0 , 0 , 4 ], [1 , 0 , 0 ], [0 , 1 , 0 ]), .5 , 1 , 7 )
289+ model.add_link(name = ' box_link' , visual_meshes = [box_1, box_2])
290+
291+ artist = RobotModelArtist(model, layer = ' COMPAS::Example Robot' )
292+ artist.clear_layer()
293+ artist.draw_visual()
294+
295+ .. figure :: files/boxy_3.png
296+ :figclass: figure
297+ :class: figure-img img-fluid
298+
299+ Remember that the frame of the box is the geometric center of the box relative
300+ to the link's origin (which, in this case, happens to be the world XY frame).
301+ So to stack the boxes, the center of ``box_2 `` must be placed at a height of
302+ ``<height of box_1> + 1/2 * <height of box_2> = .5 + .5 * 7 = 4 ``.
303+
304+ One link does not an interesting robot make. The following code snippet adds
305+ a cylindrical second link as well as a joint connecting the two.
306+
307+ .. code-block :: python
308+
309+ from compas.geometry import Box, Circle, Cylinder, Frame, Plane, Vector
310+ from compas.robots import Joint, RobotModel
311+ from compas_rhino.artists import RobotModelArtist
312+
313+ model = RobotModel(name = ' Jointy' )
314+ box_1 = Box(Frame([2 , .5 , .25 ], [1 , 0 , 0 ], [0 , 1 , 0 ]), 1 , 2 , .5 )
315+ box_2 = Box(Frame([2 , 0 , 4 ], [1 , 0 , 0 ], [0 , 1 , 0 ]), .5 , 1 , 7 )
316+ box_link = model.add_link(name = ' box_link' , visual_meshes = [box_1, box_2])
317+
318+ cylinder = Cylinder(Circle(Plane([0 , 0 , 0 ], [0 , 0 , 1 ]), .5 ), 8 )
319+ cylinder_link = model.add_link(name = ' cylinder_link' , visual_meshes = [cylinder])
320+
321+ origin = Frame([0 , 0 , 7 ], [1 , 0 , 0 ], [0 , 1 , 0 ])
322+ axis = Vector(1 , 0 , 0 )
323+ model.add_joint(
324+ name = ' box_cylinder_joint' ,
325+ type = Joint.CONTINUOUS ,
326+ parent_link = box_link,
327+ child_link = cylinder_link,
328+ origin = origin,
329+ axis = axis,
330+ )
331+
332+ artist = RobotModelArtist(model, layer = ' COMPAS::Example Robot' )
333+ artist.clear_layer()
334+ artist.draw_visual()
335+
336+
337+ .. figure :: files/jointy.png
338+ :figclass: figure
339+ :class: figure-img img-fluid
340+
341+ There's a lot going on in this snippet, so let's break it down. First, the link
342+ containing the stacked boxes is added, but the geometry has been shifted to the
343+ side ``2 `` units in the x-direction. Second, a cylindrical link is added.
344+ The cylinder has height ``8 `` and radius ``.5 `` and is vertically oriented. Finally, a
345+ joint is added. This is the most involved operation so far, so let's explore
346+ this in detail.
347+
348+ Several parameters must be defined for the joint to be sensible.
349+ The required parameters for a minimally defined joint are the ``name ``, ``type ``,
350+ ``parent_link `` and ``child_link ``. The location and orientation of the joint is defined
351+ by the ``origin ``, which specifies a frame relative to the origin of the ``parent_link ``.
352+ In this case, the location of the joint is located ``7 `` units above (ie,
353+ in the z-direction) the origin of the ``box_link ``, making it ``.5 `` units below the top
354+ of the second box. The origin of the joint also specifies the origin of its
355+ ``child_link ``. Here, this means that the center of the cylinder will coincide with
356+ the location of the joint. Since the geometries of the boxes have been shifted,
357+ the boxes and the cylinder do not overlap. If the joint origin is not specified,
358+ it will default to coincide with the origin of the ``parent_link ``. Since a continuous
359+ joint was added, it makes sense that the axis of rotation would also need to be given.
360+ Here, the joint will rotate about the joint origin's x-axis.
361+
362+ Adding a bit more geometry and a few links with fixed joints and we arrive at a rough
363+ model of the classic drinking bird toy (full code
364+ :download: `here <files/drinking_bird.py >`).
365+
366+ .. figure :: files/drinking_bird.png
367+ :figclass: figure
368+ :class: figure-img img-fluid
369+
370+ While it can be useful to programmatically create a robot, more often than not, robot
371+ models are loaded from URDF files. To load a URDF into a robot model, use the
207372``from_urdf_file `` method::
208373
209374 >>> model = RobotModel.from_urdf_file('ur5.urdf')
@@ -249,33 +414,6 @@ The following snippet shows how to load the robot model currently active in ROS:
249414 ... print(robot.model)
250415
251416
252- Visualizing Robots
253- ==================
254-
255- Once a robot has been built or loaded, it can be visualized in Blender, Rhino or
256- Grasshopper using one of COMPAS's artists. The basic procedure is the same in
257- any of the CAD software (aside from the import statement), so for simplicity we
258- will demonstrate the use of :class: `compas_rhino.artists.RobotModelArtist ` in Rhino. Once
259- COMPAS has been installed for Rhino,
260- the following can be run in a Python script editor within Rhino.
261-
262- .. code-block :: python
263-
264- import compas
265- from compas.robots import GithubPackageMeshLoader
266- from compas.robots import RobotModel
267- from compas_rhino.artists import RobotModelArtist
268-
269- compas.PRECISION = ' 12f'
270- github = GithubPackageMeshLoader(' ros-industrial/abb' , ' abb_irb6600_support' , ' kinetic-devel' )
271- model = RobotModel.from_urdf_file(github.load_urdf(' irb6640.urdf' ))
272- model.load_geometry(github)
273-
274- artist = RobotModelArtist(model, layer = ' COMPAS FAB::Example' )
275- artist.clear_layer()
276- artist.draw_visual()
277-
278-
279417FK, IK & Path Planning
280418======================
281419
0 commit comments