During a 2 week long work experience placement, I was tasked with building a 3 axis router from a set of obsolete NSK axes (video at the bottom). These axes are really good quality, however, companies such as Festo now produce much cheaper versions. The machine had to be capable of cutting large (2m x 2m) sheets of material from a CAD design, drawn up in existing software, as the existing milling machine was far too small.

Putting together and setting up the axes was very simple - I had to make another bracket and modify a couple of existing ones as well as make up a new serial cable, as the axes I was using were non standard RS232; for example, there was an emergency stop on some of the flow control pins.

The NSK axes control box (8kg and 700W constant) takes serial commands from a computer or microcontroller or the "teaching box" supplied with the unit. I tested it with the teaching box and then started working on the serial control. The (interesting) commands are as follows:

- H8F H0D - Hex codes to initialise the interface
- SVON - Release the brakes
- EMST - Emergency stop
- HOM - Home
- ACLR - Clear minor alarms
- VEL XXX - Set velocity where XXX is an integer percentage of the maximum
- MOV Xxxx Yyyy Zzzz - Perfectly interpolated (all axes arrive at the same time) linear movement (xxx etc are absolute positions in millimetres to unlimited decimal places)
- LD P000 Xxxx Yyyy Zzzz - Learn the point into the memory P000, where 000 is the number of the point
- ARC P000 P001 P002 - Circular interpolation (arcs) <180°, where P000 is the start, P002 is the end and P001 is any point in between
- CIR P000 P001 P002 - Circular interpolation (arcs) =360°, where P000 is the start, P002 is the end and P001 is any point in between
- POS - Returns the current position of each axis

When the axis receives a command to move, it starts moving and outputs a "#" character. When it stops moving, it outputs a "*" character. An exclamation mark indicates an error and non movement commands simply return a "*" on acceptance. This allows for pausing in between commands.

It is very easy to control the axis, and I quickly made an interface to edit and run chains of commands of what I referred to as NSK Code. As I needed to integrate my program with existing chains of software, I looked at how the existing milling machine was controlled - a language called G Code. This is an interesting language where each action is given a code beginning with a G, for example G00 is a full speed movement. Other codes, such an M (machine) codes exist, and are used for tool changing and feedrate selection.

My project would simply involve bolting a standard router to a set of axes and then turning it on, so I had no need for most of the codes, other than the movement ones. These boil down to:

- G00 - Rapid positioning
- G01 - Perfectly interpolated linear movement
- G02 - Perfectly interpolated clockwise circular movement
- G03 - Perfectly interpolated counterclockwise circular movement

My program must convert from G code into NSK code much like an interpreter and then run the NSK code. I tried a few approaches with not parsing the linear commands at all, simply replacing the G00 and G01 with a VEL and MOV command, but found that for circular interpolation I needed a value for the position of the head when it reaches the instruction, and as it is interpreted before running, this is different to the current position of the head.

Although it looks like these two notations are directly compatible, they are, unfortunately, not. The linear movements are the most simple part, for example:

- G00 X-569.2 Y88.7 Z120.48

Can be easily converted to:

- VEL 100
- MOV X-569.2 Y88.7 Z120.48

Similarly:

- G01 X-569.2 Y88.7 Z120.48

Becomes:

- VEL 30 (Set from a feedrate variable)
- MOV X-569.2 Y88.7 Z120.48

From here, the rest of the code is pretty much maths. The linear movement commands follow almost identical syntaxes, however, the circular commands are incredibly different, for example:

- G02X-160.295Y96.R12.004

Whereas NSK code defines an arc as:

- MOV P001
- ARC P001 P002 P003

Quick Note: I'm only dealing with arcs and lines now, not circles. Thankfully (explained later) G code splits arcs into arcs less than 90 °. Circles still exist of course, but they take 4 arcs.

From a first glance, it is apparent that the G code takes 2 parameters (actually, 3), including an R code (radius) and a coordinate. The NSK code, however takes three coordinates which have already been learnt. These can be learnt on the fly, however. It is important to note that both methods are valid and non ambiguous.

So the G code arc is defined by:

- Start position (current position, the elusive 3rd parameter)
- End position (the coordinate given)
- Radius
- Direction (very important), specified by G02 or G03

With a single one of these parameters missing, it is impossible to determine the arc. NSK code also defines with a bare minimum, but a different one:

- Start position (the first coordinate which also must be the current position)
- Any other point on the arc (midpoint is the most accurate, must be the second coordinate given)
- End position (the last coordinate given)

To convert, I needed to find the centre of the arc, find the angle between the start and finish and halve it to calculate the midpoint of the arc. This is, of course simplified, but shows the general process. Here's the maths with detailed annotation.

There are a few ways of finding the centre of the circle - however, there are two valid centres for every start and finish point, unless the distance between them is twice the radius and the arc is a semicircle. I did not have to worry about this case, however, as the G code splits arcs larger than 90 degrees. Drawing a circle with the same radius (r) as the arc around the start and finish point gives the centres (where the lines cross), as shown in the diagram.

It is possible to find these intersections using the equation of a circle, solving for both circles, however, I found that method to be a bit clunky and quite tricky on a computer. I therefore used a parallelogram with sides length r. The corners (clockwise) are centre 1, finish, centre 2, start. The line P is the line between the start and finish points and the line Q is the tangent of this line, passing through the midpoint of the start and finish. This is therefore the line between the two centres. In the diagram, the triangles in red show the horizontal and vertical components of segments of Q (referred to as L). There are two more triangles (negative solutions to the Pythagorian equations) which I have omitted for clarity, but they start and finish in the same place and therefore do not affect the calculations.

I start by calculating the midpoint of S and F, which is essentially the average in both dimensions. This point will be referred to as MxMy from now on.

- Mx = (Sx + Fx) / 2
- My = (Sy + Fy) / 2

The gradient of P is simply:

- Pg = (Fy - Sy) / (Fx - Sx)

And the gradient of Q is therefore:

- Qg = (-1) / Pg
- Qg = (-1) / [(Fy - Sy) / (Fx - Sx)]
- Qg = (Sx - Fx) / (Fy - Sy)

If d is the distance between the two centres:

- d = ±√[(Fy - Sy)² + (Fx - Sx)²]

Then L is calculated with:

- L = ±√ [ r² - (d / 2)² ]
- L = ±√[ r² - ( [ ±√[ (Fy - Sy)² + (Fx - Sx)² ] ] /2 )² ]

And the full term for L² is:

- L² = r² - ( Fx - Sx )² / 4 - ( Fy - Sy )² / 4

Next, the horizontal and vertical components of L, Lx and Ly, need to be calculated:

- Lx = ±√[ L² / (1 + [ ( Sx - Fx ) / ( Fy - Sy ) ]² ) ]
- Ly = [( Sx - Fx ) / ( Fy - Sy )] * Lx

These Lx and Ly terms are the offsets of the centres from the MxMy term earlier calculated. To find the two centres, it is simply:

- Cx = Mx + Lx
- Cy = My + Ly

Although this looks like two centres, Lx and Ly have a positive and negative value, so Cx and Cy will each have two values.

By drawing a circle around each centre radius r, the full range of arcs is shown. Doing this with both centres shows that there are now four possible arcs to choose between. It is possible straight away to eliminate two of them, as they are greater than 90° (this is why the G code splits arcs greater than 90°). Surprisingly, this does not have to be done explicitly due to computing quirks. For example, an inverse cosine function will return the smallest value which matches the argument and as the bigger arc is the useless one, this is quite handy.

Now the centres have been found, the next stage would naturally be to eliminate them. This is not possible yet, however, as the computer has no way of knowing that the arc even exists, it is simply aware of a few points. We must therefore caclulate the midpoints of both arcs.

There are many ways of doing this, I worked with two and both work in theory, but only one works in reality. I'll detail both, however.

The first involves an idea I called the "x offset" and it works in theory, but in practice it is limited by the inverse cosine function on a computer. The angle theta is the angle between the start and finish points and alpha is the x offset. Theta is easily calculated:

- θ = cos^-1( ( r^2 + r^2 - d^2 ) / 2r² ) <= 90degrees

Alpha is then calculated with:

- α = tan^-1( (Fy - Cy) / (Fx - Cx) )

This is used to calculate the midpoints with: (P is y offset, Q is x offset)

- P = rsin( ½θ + α)
- Q = rcos( ½θ + α)
- Midx = Cx + Q
- Midy = Cy + P

As I mentioned, this is a nice idea, but it doesn't really work. It is possible to do the whole thing using just Pythagorian methods. This also has the advantage of speed on a computer which could be very useful if machining a complex piece.

The maths for the offsets using only Pythagoras' right angle triangle rules comes out with:

- Rx = ±√[ r² / [1 + Qg²] ]
- Rx = ±√[ r² / [1 + {(Sx-Fx)/(Fy-Sy)}²] ]
- Ry = Qg * Rx
- Ry = (Sx-Fx)/(Fy-Sy) * Rx
- Midx = Cx + Rx
- Midy = Cy + Ry

This gives 4 answers for the midpoint of the arc, 2 of which are completely wrong and one which is not useful leaving the correct, useful value. To decide between them, I calculate a variable, B, and compare the values to remove the wrong arcs:

- B = ±√[ (Midx - ( (Fx - Sx) / 2)²) + (Midy - ( (Fy - Sy) / 2)²) ]
- Values of B1-4
- If (B1 > B2) then B2 is the right arc in that pair
- If (B3 > B4) then B4 is the right arc in that pair

Once the two midpoints are extracted, the only thing left to do is calculate which arc is clockwise and which is anticlockwise (to match with G02 or G03). It is possible to do this a few ways too, but I chose to centre an imaginary origin at the start point and compare cases based on which quadrant the arcs were in. This isn't the neatest way of doing it, but it's pretty fast on a computer and achieves reliable results.

The pseudo-code for this is effectively:

- Check the quadrant based on position of finish relative to start (origin)
- If quadrant 1, the higher midpoint is clockwise
- If quadrant 2, the lower midpoint is clockwise
- If quadrant 3, the lower midpoint is clockwise
- If quadrant 4, the higher midpoint is clockwise

Quadrants are numbered anticlockwise starting with the top right, as with trigonometry.

The arc has now been chosen and the midpoints found. Valid NSK code can be produced by outputting a couple of strings and the arc can be plotted.

Here are some photos of the actual machine:

Controller ("teaching box" on left hand side)

30cm (12 inch) ruler for size comparison

Whole machine - 30cm (12 inch) ruler for size comparison

Here is a video of testing it with a pen. You can see basic lines and a couple of arcs. The text was drawn up in CAD and interpreted with my program.

In terms of actual use, a design must be first drawn up in CAD then exported to a CAM program, such as Sprutcam where a G code file is generated. The origin should be set to the top left, with enough room between the origin and work for the bit to pass through. This file is then opened in my program and converted to G code. The design will then run. For best results, homing (zeroing) should be used before each run. The Z axis should be adjusted to just touch the surface of the work and then zeroed before being raised. For testing, it is a good idea to set the zero very close to the top of the Z axis, to avoid wasting material. I also found that sometimes an arc which looked like 90° was actually 90.0000001 ° causing an arc of 90° and a ridiculously tiny arc. I therefore added a lower limit to the arc size.

Finally,hereis the source code for the program if it is of interest to anyone. It is written in Visual Basic (not a fan) as that is standard for programs at the company I was working. I have also followed their coding standards with (my take on) Hungarian notation etc.