# r,Theta Gantry System

Hello All,
I am involved in a project that is looking to build a 2-D gantry system.
We have at our disposal two simple stepper motor based laboratory motion
control stages. One is a linear stage and one is a rotory stage. To get the idea of what these things look like, you can see similar products at www.newport.com under their Motion Control line of products.
Our plan is to build a polar system, utilising the linear stage as the radius and then mount the rotory stage on top of this to control the angle. The goal is to position a platform under a stationary tool along user defined Cartesian X-Y path.
We are aware that it would be just easier to use two linear stages however we only have one linear stage and as many rotory stages we need.
Does this sound like a viable solution? Can anyone suggest an easier solution?
And finally does anyone know of any sources (Books, articles, papers etc.) that detail the control of such a system? Specifically we are looking for information on how the stepper motors should be moved if we want to move from point A to point B along a line? along a curve?
Sincerely, James McMillan
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
I think that the reason you're having trouble with this may be that you're trying to solve a problem that is actually easier than it looks. Am I correct in assuming that you wish to know how to control the endpoint of the gantry moving it from point A to point B along a straight line?
If you're interested strictly in position (with no concern about velocity or dynamics-related issues), you can solve this using high school trig. I'll set up a simple construction to show the general approach. If you have more details, maybe I can put together a more complete solution and even some code.
Imagine that you have a case where the linear translation stage lies along the x-axis. We can move the pivot point back and forth along the x-axis and rotate the apparatus freely around that pivot point. At the other end of an "arm" reaching from the pivot point is some kind of end-effector. You wish to move your end effector along a line defined in the slope-intercept form y=m*x+b. The end-effector is some distance r (for radius) from the pivot point.
If we want to move the end effector to some point (x,y), we simply need to position our pivot point at some position (c,0) on the x-axis that is distance r from where we want the end-effector to be. Imagine an x,y point somewhere on your coordinate system and draw a circle around it. If (x,y) is too far away from the x-axis it never touches it (the point (x,y) is unreachable by your apparatus). If it's too close, it the circle cuts the baseline twice. From this, we know that there will be two candidates for (c, 0)... you'll have to write some logic to pick the right one.
Okay, given y=mx+b and r = sqrt( (x-c)^2 + y^2)
(x-c)^2 + (mx+b)^2 = r^2 solving for c we have c^2 - (2*x)*c + (1+m^2)*x^2 + 2*m*x*b + b^2 - r^2 = 0
which is a simple quadratic equation. Use the general solution to a quadratic equation to solve for c. This will give you the position of the linear translation stage (and the pivot point). Solving cos(angle)=(x-c)/r gives you the angle for the rotational stage.
Gary

<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
Gary,
Let me explain the device a little more.
We have the linear stage (correctly stated as running along the x-axis) and on the linear stage we have a rotory stage which will be able to rotate on the point c,0, where c is defined by the position of the linear stage.
For the time being this is simply a plotting device, meaning above the gantry will be a stationary pen device capable of up and down (z-axis movement).
The goal is to position a substrate,paper,whatever laying on the rotary stage under the pen along various paths to draw on the substrate images.
Now, straight lines that are parallel to the axis are simple: Say you want to draw a line from X1,Y1 to X2,Y2 (presume the substrate is currently position so that its X-axis is parallel with the linear stages)
Just move along the linear stage X1 units, rotage 90 degress, Move Y1 units, put the pen down and rotate 90 if need be and move the linear stage.
What we are having difficulty with is creating diagonal lines and arcs. If you want an arc between 2 points of radius R, is the only way to do this by breaking it up into a finite number of lines parallel to the axis and then tracing out those?
or is there another method someone can think of. The key is to remember that we have a linear stage that has a stepper motor that steps in discrete distances and a rotory stage that steps in discrete angles.
James
G.W. Lucas wrote:

<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>

--
- Alan Kilian <alank(at)timelogic.com>
Bioinformatics Applications Director, TimeLogic Corporation
<% if( /^image/.test(type) ){ %>
<% } %>
<%-name%>
James,
Well, the problem of moving the paper under the pen is only a little different than moving the pen over the paper. I think it's just a matter of negating the coordinates and potentially adding some kind of offset
xPen = -xPaper + xOffset yPen = -yPaper + yOffset
Included below is a little snippet of C code for generating stepper settings for drawing a straight line between two coordinates (x0,y0) and (x1,y1). For simplicity, I pretend that you specify steps as if you had some kind of absolute encoder, but you'll get the idea...
The main problem, of course, is that your stepper motors can only "address" discrete positions which means that you can only do absolute positioning to a certain fixed set of points. So if you are attempting to draw a straight line, you can only approximate it by a series of curves. What kind of resolution does your apparatus have?
In this case, I specified a 1mm resolution for the linear stage and a 720 step (1/2 degree) resolution for the angular stage. The length of the "arm" was 10cm. The program draws a straight line from (3,3) to (5,4) and computes the error at various points along the way. The errors all came in under 1mm. However, in another test I tried a lower resolution rotation stage (200 steps, 1.8 degree) and got much worse results. Of course, the resolution isn't just a factor of how many steps your stepper motor supports, but what kind of reduction gearing you can apply to the motor's output.
It turns out that the algebra in my previous post was needlessly complex. In this version, I use the following:
given a desired (x,y) for some fixed r find point (c,0) and angle theta such that r^2 = (x-c)^2 + y^2 x = c+r*cos(theta) y = 0+r*sin(theta)
You'll see that the calcSettings() function is very simple.
The program below works as follows. Given two points defining a straight line, it loops on x coordinates generating y coordinates using y = m*x+b. Then it tries to find the C (linear displacement) and theta (rotation angle) values that would result in the appropriate (x,y). Now, of course, because we have to work in the real world, we can only position our equipment in terms of integer step settings based on the resolution of our steppers. So while calcSettings() produces real-valued (c,theta) we do have to limit them to integral multiples of our resolution. Doing so introduces some predictable position error. This program generates a table of numbers showing the result.
The other thing I tried to do was to generate settings that varied only a single step or so at each stage of the process... that's what's going on in the "factor" loop.
One other hint is that if your line has a steep slope (delta Y dominates), try computing x as a function of y rather than y as a function of x.
As I said, there's still a lot of room for improvement.
Gary
The Results ------------------------------------------ Steps Position Lin Ang X Y X err Y err Abs Err -65, 35, 3.037, 3.007, 0.037, 0.007, 0.038 -64, 36, 3.111, 3.090, -0.027, 0.022, 0.034 -63, 36, 3.211, 3.090, -0.000, -0.015, 0.015 -62, 37, 3.283, 3.173, -0.027, 0.018, 0.033 -61, 37, 3.383, 3.173, -0.000, -0.019, 0.019 -60, 38, 3.455, 3.256, -0.028, 0.014, 0.031 -59, 38, 3.555, 3.256, -0.000, -0.022, 0.022 -58, 39, 3.626, 3.338, -0.029, 0.010, 0.031 -57, 39, 3.726, 3.338, 0.000, -0.025, 0.025 -56, 40, 3.797, 3.420, -0.029, 0.007, 0.030 -55, 40, 3.897, 3.420, 0.000, -0.028, 0.028 -54, 41, 3.967, 3.502, -0.030, 0.004, 0.030 -53, 41, 4.067, 3.502, 0.000, -0.031, 0.031 -52, 42, 4.136, 3.584, -0.031, 0.000, 0.031 -51, 42, 4.236, 3.584, 0.000, -0.034, 0.034 -50, 43, 4.304, 3.665, -0.032, -0.003, 0.032 -49, 43, 4.404, 3.665, 0.000, -0.037, 0.037 -48, 44, 4.472, 3.746, -0.032, -0.006, 0.033 -47, 44, 4.572, 3.746, 0.000, -0.040, 0.040 -46, 45, 4.639, 3.827, -0.033, -0.009, 0.034 -45, 46, 4.705, 3.907, -0.034, 0.038, 0.051 -44, 46, 4.805, 3.907, 0.000, 0.005, 0.005 -43, 47, 4.871, 3.987, -0.034, 0.035, 0.049 -42, 47, 4.971, 3.987, 0.000, 0.002, 0.002 -41, 48, 5.035, 4.067, -0.035, 0.032, 0.048 The Program --------------------------------------------------
#include <stdio.h> #include <math.h>
// given r,x,y, find c and theta calcSettings( double r, double x, double y, double c[2], double theta[2]) { double d; // discriminant d = r*r-y*y; if(d<0) return -1; // unreachable point d = sqrt(d); c[0] = x-d; c[1] = x+d; theta[0] = atan2(y, x-c[0]); theta[1] = atan2(y, x-c[1]); return 0; }
// given r, c, and theta, find resultant x,y static void calcSetPoint( double r, double c, double theta, double *x, double *y){ *x = c+r*cos(theta); *y = r*sin(theta); }
// convert value to its equivalent integral "steps" static long resolve(double v, double resolution){ return (long)floor(v/resolution+0.5); }
int main(int argc, char *argv[]){
double r; double sLinear; double sAngular;
double x0, y0; double x1, y1;
double dx, dy, m, b;
int status; double c[2], theta[2], factor; double x, y, px, py, qx, qy, xErr, yErr; double pc, ptheta; double qc, qtheta; long pStepsL, pStepsA; long qStepsL, qStepsA;
// arbitrary settings describing the physical apparatus // in this case, units are in cm, though anything would work. r = 10; // cm sLinear = 0.1; // 1 mm. resolution sAngular = 2*M_PI/720; // 1/2 degree resolution
// (x0,y0), (x1,y1) is the line we want to draw x0 = 3.0; y0 = 3.0; x1 = 5.0; y1 = 4.0;
dx = x1-x0; dy = y1-y0; m = dy/dx; b = y0-m*x0;
status = calcSettings(r, x0, y0, c, theta); pStepsL = resolve(c[0], sLinear); pStepsA = resolve(theta[0], sAngular); pc = pStepsL*sLinear; ptheta = pStepsA*sAngular; calcSetPoint(r, pStepsL*sLinear, pStepsA*sAngular, &px, &py);
printf(" Lin Ang X Y X err Y err Abs err\n"); xErr = px-x0; yErr = py-y0; printf("%4d, %4d, %6.3f, %6.3f, %6.3f, %6.3f, %6.3f\n", pStepsL, pStepsA, px, py, xErr, yErr, sqrt(xErr*xErr+yErr*yErr));
while(1){ factor = 4; do{ if(dx>0){ x = px+sLinear*factor; }else{ x = px-sLinear*factor; } y = m*x+b; calcSettings(r, x, y, c, theta); qStepsL = resolve(c[0], sLinear); qStepsA = resolve(theta[0], sAngular); factor/=2; // now compare previous results (pStep) to query (qStep) }while(fabs(qStepsL-pStepsL)>1 || fabs(qStepsA-pStepsA)>1); qc = qStepsL*sLinear; qtheta = qStepsA*sAngular; calcSetPoint(r, qc, qtheta, &qx, &qy); xErr = qx-x; yErr = qy-y; printf("%4d, %4d, %6.3f, %6.3f, %6.3f, %6.3f, %6.3f\n", qStepsL, qStepsA, qx, qy, xErr, yErr, sqrt(xErr*xErr+yErr*yErr)); if(dx>0){ if(qx>=x1) break; }else{ if(qx<=x1) break; } pStepsL = qStepsL; pStepsA = qStepsA; px = qx; py = qy; } }