Tuesday, March 29, 2016

Working with Planes in Kuka | prc

Kuka | prc is a method of driving Kuka robots using Grasshopper. This topic describes the use of planes when programming robot motion. (The larger topic of programming using Kuka|prc is here.)

You can think of a plane as an origin (location in space) and an orientation for the X, Y and Z axes (rotational direction of the axes). Planes are drawn in Grasshopper as a tiny grid, with the X and Y axes shown. You control the display size using the Grasshopper pull-down menu option Display > Preview Plane Size, then enter a size in model units.

You can create a Plane component in Grasshopper then right-click on it and choose Set one Plane. You are first asked to set the origin then asked to define the location of the X axis, then the Y axis. Note that once you define X and Y, then Z is defined automatically, perpendicular to X and Y, using the right-hand rule.

Planes are what are fed into the motion commands to move the robot. The plane specifies the location of the Tool Center Point (TCP) and the orientation for the robot face plate. So an understanding of them is essential to using Kuka | prc.

Generating Planes from Geometry

Planes drive the motion of the robot. Since we often want to follow geometry we've modeled or generated algorithmically in Grasshopper, there are a number of easy to use component which generate planes from geometry.

A simple example is creating planes to cause the robot to follow a curve. The Divide Curve component can be used to output points at equal intervals along the curve.

The planes appear in the viewport like this:

Note that these planes all have the exact same orientation. That's because the points were wired into a World XY component which set the constant orientation to match world XY. This is fine is the tool doesn't need to rotate along the curve.

It's also possible to generate the planes so they oriented tangent or perpendicular to a curve. To do so use the Horizontal Frames component.

The resulting planes. The X axis is tangent to the curve.

This can be used when you want the tool to rotate along the curve. For example if the tool was a knife cutter it needs to follow along tangent to the curve at all times.

It's also possible to generate frames which are perpendicular to the axis along the curve. To do so use the Perp Frames component.

The resulting frames:

Components

There are a number of useful Grasshopper components for dealing with planes, available in the Vector menu / Plane panel.

XY Plane, XZ Plane, YZ Plane

These components output planes matching the world XY, XZ and YZ coordinate systems all centered on the origin. You may wire in a different origin to move them around.

If you right-click on the origin socket and choose Extract Parameter Grasshopper wire in a Point component. You can then enable Gumballs in Grasshopper menus (Display > Gumballs). When you select the Point component you can use a small Gumball widget to graphically alter the point. Doing so interactively adjusts the robot position in the viewport.


Unit X, Unit Y, Unit Z

Also handy are the built in unit vectors for X, Y and Z. These are available in the Vector menu / Vector panel. You can wire in scale factors to change the magnitude of the vectors, or negative values to change their direction and magnitude.

Construct Plane

This component lets you construct planes by specifying an origin, and separate X, Y, and Z vectors to define the plane. See the image below under Deconstruct plane for an example in use.

Deconstruct Plane

This component lets you deconstruct a plane by breaking it into an origin and separate XYZ vectors. The example shown below uses both Construct and Deconstruct; you can see an origin point is wired into Construct Plane on the left (1.0 2.0 3.0). Also wired in are the standard X and Y vectors. This builds a plane, centered at world 1, 2, 3 aligned with the world XY plane.

The plane is then Deconstructed to show they match. So the origin output is 1, 2, 3. The X vector is in fact 1, 0, 0 and Y is 0, 1, 0. Z is, as expected, 0, 0, 1.

Adjust Plane

This component is very useful in re-orienting one of the standard planes. For example you can flip the Z axis of the plane to point in the opposite direction. Here you see a -1 factor wired into a Z vector which is wired into the normal of the Alter Plane. This flips +Z so it aligns with world -Z (down).

Note too how this example divides a curve up to generate the various origin points for the plane. You can see a list is output from Divide Curve. That's wired into the origin of the World XY plane.

Cross Product

Given two vectors this will output a new vector which is perpendicular to both those wired in. In the example below unit X and unit Y vectors are wired in. The output is unit Z (0, 0, 1) which, of course, is perpendicular to X and Y. 

Monday, March 28, 2016

Data Matching and Data Trees in Grasshopper

An understanding of data matching and data trees is critical to successfully developing more complicated definitions in Grasshopper. This post will help you understand these concepts.
The Grasshopper examples (and corresponding Rhino file) shown below can be downloaded here. You can disable all the components in the definition, then Enable those in a single group to see its effect.

Data in Grasshopper

Data in Grasshopper can be single values, lists of values, or what are known as trees of data.

Single Value

A single value is obvious, for example the output of a number slider is single value. You can see this when you wire it into a panel:

You can also see this in the wire itself - it is shown as single solid line which indicates a single value.

Here's another example. The single value is fed into the radius of a Circle component. A single plane specifies the plane to draw the circle on. The result is one circle drawn.

List of Values

A list of values results when a component outputs several values. An example is the Series component which outputs, well, a series of numbers. The user specifies a start value, and an increment for one value to the next, and a total count of values. In the example below the series starts at 1, is incremented by 2, with a total number of values of 3.

The Panel shows the output values, 1, 3, 5. Also note the wire is shown as double lines indicating the data is a list rather than a single value. The Series is wired into a Circle component which uses the value as a Radius. The result is three circles of radius 1, 3, and 5.

There are some components commonly used with lists. List Item returns a single item in the list. List Length returns the number of items in the list. Usage of these is shown below:

Tree of Data

A tree of data is output when a hierarchy is involved in the data. It's called a tree because the data is organized into separate branches. A simple example is when you use the Rectangular component to create a grid of cells/points, each column in the grid is in an independent branch. In the rectangular grid below there are 4 columns and 6 rows. The tree has four branches (one for each column). In each branch is a list of 6 items. A circle is drawn at every point. 


Grasshopper provides tools to help you visualize data trees. First, note the wire coming out of the grid is drawn as a dashed line. That always means the data is in the form of a tree. There is a component called the Param Viewer which lets you visualize the structure of the tree.

The Param Viewer shows there are 4 branches, where each one has 6 elements (N=6). The Param Viewer has a right-click menu option to draw the tree graphically.

With that enabled you see a graphic representation of the tree. Note it only shows the 4 branches, not the "leaves" (the list of 6 items in each branch).

Another tool useful in visualizing a tree like this is the Point List component. When there is a tree or a list of points, Point List labels them in the Rhino viewport by using numeric text to show the position in the list of each point. Here it is wired in, with a slider to set the text size, shown in Grasshopper and the viewport.

It's very important to note the following; There are four branches, each with six points. The Point List shows quite clearly that each branch is independent of the others. The points in each branch are labelled 0 to 5. Then the numbering starts over again at 0 again. Why? Because each branch of the tree is independent of the others. The data is not shared between or across branches. Therefore the Point List shows them independent of one another.

This independence of the branches is usually very useful. It can also be controlled by using specialized components to manipulate the branch structure. Your skill in using these components has a big impact on your overall skill with Grasshopper in general.

There are some common components used with data trees. These are Flatten and Graft. Have a look at the Param Viewer examples below to see the structure of each tree after being processed by Flatten and Graft.

Flatten
This component converts the data from a tree into a list. It removes the hierarchy information entirely. The grid point labeling, after flattening, looks like this:

As you can see the hierarchy is gone and the points are numbered in order, 0-23.

Graft
This component takes every leaf in the tree and makes it its own branch. Since the branches are independent, the point list is all 0's. That's because as soon as it labels one point it starts a new branch and the labels its first point. There's always one per branch.

Flip Matrix
This component flips a "matrix-like" (think two dimensional grid or array) data tree by swapping the rows and columns. Note how the numbering is flipped so the columns climb in value (where previously the rows climbed in value):

Data Matching

This section discusses what data matching is and why it is important to understand.

Components in Grasshopper automatically handle wired-in single values, lists, as well as trees.

  • A component will process a single value once. 
  • A component will automatically loop or iterate over a list and process each element in the list. 
  • A component will automatically loop over a tree, dealing with each branch independently, and process each value or element in the list in the leaves of each branch. 
But what happens when a component is required to process both a list of values and a single value? Or a list and a tree? Whenever a situation like that arises, data matching is involved. This term refers to how the single value and the list of values are matched up and processed. Or how a list of values and trees of values are matched up and processed. Some simple examples will make this clear. You'll be introduce to the Grasshopper components which give you precise control over the processing.

Data Matching Lists of Points

The first few examples shows two lists of points. These points are fed into a Line component which takes two endpoint inputs and draws a line between them. One list is shorter (has less data) than the other.

A series component generates a list of values which become points with different Y coordinates. A second series generates another list of Y values, however these points are shifted one unit in X. These two lists are wired into the Line component which draws the line between them. This allows you to easily visualize the various types of matching between the two lists. Here's the simple definition:

Here's the result in the viewport - lines are drawn between points in the two lists:

As you can see, one list is shorter than the other. When Grasshopper does data matching to match up the values between the two lists, its default behavior is "longest list, repeat last". That means it uses the longest list for drawing the lines, thus there are 10 of them. It repeats the last item of the shorter list when it matches to the items in the longer list. This is the default behavior of Grasshopper.

There are two basic components which let you control data matching. Shortest List and Longest List. We'll look at Longest List first. The definition below is the same as the one above with the addition of a single Longest List component. As we saw above, longest list will repeat to the length of the longest list wired in. It has a right-click menu which let's you control how the repetition happens:

Longest List - Repeat First
You can see how the first item in the short list is repeated until there are enough items left to match up with the longer list.

Longest List - Repeat Last
This is the default case. You can see how the short list items are matched up with the longer list until they run out then the last item is repeated to match up with the remaining longest list.

Longest List - Interpolate
The items in the short list are uniformly distrubuted to the longer list. This results in four items being reused twice.

Longest List - Wrap
Here, after the shortest list items are used up they wrap back to beginning of that list and the matching happens from the beginning again.

Longest List - Flip
In this case, after the shortest list items are matched up, the longest list items starting matching back down, 7 to 5, 8 to 4, 9 to 3, and 10 to 2.

There's also a Shortest List component. In this case only the number of elements in the shortest list are matched up with those in the longer list. Right-click menu options let you choose between Trim End, Trim Start, and Interpolate. Here's the simple definition which demonstrates this:

Shortest List - Trim End
In this case the items at the end of the longest list are trimmed (not matched).

Shortest List - Trim Start
In this case, the items at the start of the longest list are trimmed (not matched).

Shortest List - Interpolate
In this case the items in the shortest list are distributed across the items in the longest list. As you can see this leaves four gaps in the matching but spans from the start to the end of the longest list.

Data Matching Shapes in a Loft

Here's an example that lofts three different shapes:

  • 0: Square
  • 1: Circle
  • 2: Ellipse

The various Longest List options are shown when repeating the 3 items 9 times


Longest List - Repeat First
The first shape, the square, is repeated 6 times, then the list is used. 


Longest List - Repeat Last
The three shapes are used, then the last shape is repeated 6 times. 


Longest List - Interpolate
Each shape is used repeatedly to spread out over the range. So three of the first, three of the second and three of the third. 


Longest List - Wrap
The three shapes are used, in sequence, over and over (square, circle, ellipse, square, circle...). 


Longest List - Flip
The three shapes are used in order, then in reverse order, then in order again (square, circle, ellipse, circle, square, circle, ellipse, circle, square). 


Data Matching between a Tree and List

The next example shows data matching between a tree and a list. The tree is generated by the Rectangular grid component. The list is generated by a Series component. These are wired into a Longest List component which is set to Warp. 

You can see the results below. As we've seen previously each branch is treated independently. The series of 4 radius values are wired into the Circle component. The radius values wrap, that is they turn back to the smallest value once the list is fully used. So they get bigger for the 4 values in the series, then start over small. This happens independently in each branch. 

Compare this to when the tree is flattened (converted to a list). Take Note: these type of conversions are very commonly used and so there are right-click menus on the sockets of nodes to flatten, graft, etc. without having to wire in a component each time. That's what's done below: 

Here's the result - the values continue wrapping across all the (former) branches. Once the first column is done the radius values continue at the bottom of the next column. 

What happens if we Graft the list - turning it into a tree - how would this affect the data matching? This is done below: 

Now the matching is occurring between four branches of the radius data and four branches of the grid point data. Each branch is matched up one for one, so the first branch gets a branch with the smallest value. The next branch with the next biggest value, and so on. 


A Practical Example

It's a common practice in digital fabrication to create objects using stack lamination. That's the process of making a representation of a 3D form by taking slices through it, cutting sheet material to match the outline of those slices, then stacking them up to form a representation of the object.

Here's an example of a figure sculpture produced from sheet acrylic using this method:


To keep the parts organized during the cutting and assembly it's important to keep contours that are on the same level of the form together.

Here's a simple example using a sphere, a block form, and a torus (donut). The definition contours the objects and lays them out for cutting. The contours are group by the object they are within and by the contour they are on within the object:

The definition lets you contour the object at a user specified spacing. Here the sphere is being contoured every 0.5":

What if you wanted to contour everything: the sphere, the block and the torus. You could copy all the components in the definition three times and assign a different object to each. But that's not necessary, the definition will work if you simply assign all three objects to the same Contour component. It works because Grasshopper organizes the data trees by object and also by contour. Each object will be its own branch in the tree. And each level in the contouring will be its own branch.

Summary

By understanding how Grasshopper matches data, and knowing the components which give you control over that matching, you can more quickly and successfully develop your definitions. This topic has presented the basics of dealing with single values, lists and trees and managing the flow of data between components.

If you'd like to read a more technical account of data trees written by Grasshopper developer David Rutten please see The Why and How of Data Trees.