According to the TIOBE programming index the world’s most popular programming language in 2015 is once again Java, followed by C and it’s object-oriented successor C++. However, there are other programming languages, which in some aspects affect your everyday life at a considerably deeper level than some JavaScript applet on your mobile phone. “Huge as information industries have become, they’re still a sideshow in the world economy”, writes Chris Anderson in his best-selling business book “Makers”, where he compares the $20 trillion dollar digital industry to the$130 trillion dollar1 industry that leads to products beyond the realm of bits and bytes. Therefore, I am talking about programming languages that dominate the realm of industrial control and production—like G-code.

The goal of this article2 is not to provide a complete and thorough introduction to the G-code programming language. There are already plenty of references and lots of material out there; see the list at the end. However, this is going to cover the basics and principles in a reasonable level of detail, such that after reading through these pages (should come out to 20–25 printed pages, which I personally much prefer to screen reading) it should be straightforward to pick up a reference document for further details.

Act I: What is G-code?

In laymen’s terms, G-code is the programming language of tool-based manufacturing. It tells computerized machine tools how to move the tool and how to interact in order to produce something. For example, a 3d printer needs to know how to move the printing head within the work space and when to start the flow of the material. Likewise, a laser cutter has to be positioned and needs to be told when to turn the laser on and off. Milling and turning machines also require some sort of prescription how to remove the material in order to reveal the finished product.

Computerized manufacturing machines

Before we can get started, we need a basic level of understanding how industrial production machines are designed from the control perspective. A modern manufacturing machine typically consists of two levels of automation:

• The PLC (programmable logic controller) takes care of lower-level functionality. From today’s perspective, a PLC at first appears to be an almost ancient computer with extremely limited capabilities—and from a certain perspective this is not entirely wrong. Underlying most PLCs is a cyclic execution architecture: the PLC program is executed in fixed intervals typically every few microseconds, where it checks a number of inputs and sets a number of outputs. If the program run does not finish in its allocated time slice, it terminates and the cycle starts anew.3 The scope of a program crash or hang on a PLC is therefore quite limited, which makes PLC ideally suited for basic low-level and machine security tasks. On the other hand, such a design does not allow for particularly sophisticated software. The programming languages for PLCs are rather simplistic but also very standardized, which is quite important to provide a safe low-level functionality. Bugs in a software that controls turbines and big motors in general can have very nasty side effects.4 Thus, PLCs are found in virtually any electric cabinet of industrial machines that require some sort of control or logic.
• The NC (numerical control) provides high-level functionality in more sophisticated machines. Whenever several physically independent motors and drives need to be working in a synchronized fashion to position a tool correctly, a numerical control is taking care of the associated computations. Each one of those independently controllable motor drives is referred to as an axis in this context and needs to be told where, when and how to move. Associated NC programs therefore describe the movement and operation of the machine, and this is where the G-code programming language comes into play.

PLC and NC are fundamentally different yet interacting components of an entire machine control system, but from the users perspective the NC is typically seen as the “brain” of a machine. The PLC may prevent or interrupt the execution of the NC program due to a sudden error (overheated cooling liquid, for example). On the other hand, the NC can call certain PLC functions, like triggering an automated tool change. Both systems together allow for a smooth and relatively safe operation of a machine.

Note: In order to make subsequent examples and discussions more transparent, the following sections have been written with a CNC milling machine of three or more axis in mind.

The G-code programming language

G-code, which is also called RS-274 or G-code programming language and which comes in many manufacturer-specific variants,5 is the common name for the most widely used NC programming language. Its series of instructions essentially defines how the machine axes are moving and interacting together. Numerical controls and the G-code language were first developed in the 1950s in close cooperation between the aircraft industry and the MIT in the context of computerized turning and milling machines. A lot of the design fundamentals of the G-code language have to be seen in the context of this time frame. Originally, NC programs were stored on punched cards and tapes, which is very apparent from the resulting structure of the language.

Preliminaries

In order to describe any movements, the independent physical axes (i.e. motors and drives) need to be mapped to associated variables or commands in the programming language. Typically, a machine with three-dimensional positioning capabilities of the tool—a drill for example—has three axes, where the direction vectors are mutually perpendicular towards one another. Thus, in an associated orthogonal 3d coordinate system, one would naturally refer to the three directions via coordinates $(x,y,z)$ and the associated variables in the NC are likewise called X, Y and Z. Depending on the machine parameters, the position is usually specified in “inch” units or “millimeter” units, but we return to this later.

Furthermore, G-code is a modal language. This means that once a certain operation mode or parameter has been set, it remains active until it is changed by another command. Intuitively, this makes a lot of sense: Once you have set the movement rate of the tool, you do not want to set this value every time for each subsequent movement command, that describes the path of the tool. Likewise, the machine tool remains at the same position until the next movement command comes up. Movements are essentially programmed by telling the machine the destination location in terms of the destination axes values. The machine then tries to move from the current axes positions to this destination positions as fast as possible under the given movement rate limitations, interpolation modes etc.

Fundamental structure and basic commands

Each basic G-code command consists of two components: a letter and a number. The letter is usually written in upper-case form, though this is not mandatory. The number can be either an integer (6, 12, 512) or a floating-point number (-3.41, 15, 0.62). Due to the huge variety of machines that are fitted with a numerical control, there is a huge variety of G-codes which are often very specific to the machine. Fortunately, there is at least a small core language upon which almost all numerical control manufacturers agree and which has its roots in the original milling and turning applications:

• F followed by a number, e.g. F76.322 specifies the feed rate of the machine, which is essentially the target velocity of the machine tool’s end point. If you have a drill, this could be the speed by which you would like to drill into the material. Likewise, in a milling machine this describes how fast the tool is moving trough the material. Depending on the length units used in the machine, the feed rate is usually specified either in millimeters or inch per minute, i.e. “mm/min” or “inch/min”.
• G0 activates the rapid movement operation mode, which means that the machine goes as fast as possible—the feed rate is ignored in this operation mode, but it remains set. This is used if the tool is not in contact with the material, i.e. for fast positioning tasks.
• G1 activates the linear interpolation operation mode, where the tool end point is moves with the feed rate specified by the F code. This is the typical operation mode if the tool is in contact with the material (plus some minor margin for error).
• G2 and G3 activate the circular interpolation mode, which allows to mill circles and arcs. However, we are not going to discuss those two codes in this article.
• X, Y and Z followed by a number sets the tools target axis position, which in effect corresponds to a movement command.

The G-code language is structured into execution blocks and a block is essentially just a single line of the program—the punched card heritage of the language becomes very visible at this point. Several commands can be used in the same block, for example setting the feed rate, interpolation mode and so on, and the order of the commands within the block does not matter.6 A block is always executed as fast as possible, which implies that blocks containing only modal changes are quasi-instantaneous and blocks containing movements take as long as the movement requires.

Those few commands are in principle enough for a simple movement operation. However, to do something remotely useful, we also need a number of additional commands, which already are somewhat machine-specific (or rather milling operation specific). Consider for example a 3-axis milling machine. Modern milling machine are fitted with an integrated tool magazine, such that a tool change is a completely automated process that can be called from the NC program. We use the following additional commands:

• S followed by a number defines the rotational speed of the spindle. For example, S2000 sets the spindle rotation to 2000 rpm (rotations per minute).
• M3 turns the spindle on and activates a clockwise spindle rotation with the speed set by the S command.
• M4 likewise turns on the spindle in a counter-clockwise spindle rotation direction.
• M5 stops the spindle rotation.
• T followed by an identifier—a number (e.g. T1017) or a string name via assignment (e.g. T="MILL_1")—selects a tool for an upcoming automated tool change. Note that this operation only selects the tool, but does not actually carry out the tool change.
• M6 then takes care of the tool change. The logic behind using two separate commands for tool pre-selection and the actual change is simple: in a modern production center the tool magazines can be pretty big (several hundreds of tools), such that it may take a while to transport the tool from the magazine to the automated tool change location within the machine. Using pre-selection the tool can be grabbed from the magazine while the machine is still working, such that the prepared tool exchange only takes a couple of seconds.
• M30 defines the end of the program. Nowadays this command is not really necessary anymore, but considering the punched card days such a closing symbol was rather important to define a stopping point. Therefore, even today one usually finds a closing M30 at the end of the NC program.

The tool number 0 is often reserved for “no tool” or tool removal, such that the block T0 M6 actually removes the current tool from the spindle. This is sometimes found at the end of a program.

• Fanuc controllers use round brackets (...) to identify comments.
• On a Siemens controller everything following a ; symbol is ignored and interpreted as a comment.
• On Heidenhain controllers the % can be used for comments. If a range %...% is encapsulated in brackets, the code before and after the percent signs is executed.

Introductory G-code example

Given our 3-axis milling machine let us assume that our tool is a regular flat end mill and is referred to as tool number 1001 in the numerical control. If the tool is supposed to mill along a rectangle shape with 10 cm by 20 cm lengths into some block of material, we assume that coordinates $(0,0,0)$ would be in the middle and right on top of our material (e.g. some aluminum block). Then the following commands would do so:

There we go: our first G-code program, which mills a squared shape 1 cm deep into the material. Considering that we have selected a feed rate of 200 mm/min, moving down into the material by 10 mm plus the additional 5 mm safety margin and retracting up at the end each takes 4.5 seconds. The first movement of 10 cm in the $x$-direction then takes half a minute, the subsequent 20 cm $y$ movement takes a minute, such that we end up with a total milling time of 3 minutes and 9 seconds. We have a add a couple of seconds for the initial tool change, the final tool removal and the rapid movements to the safe location, but in a modern machine a tool change just take a few seconds. In the end we would most likely observe a real world run time of about 3:20-3:30 for our little program.

Generics

In our example we can see that the G commands usually change the state of programming on the machine (mnemonic: “geometry”), whereas the M commands change some state of the machine itself (mnemonic: “machine”). However, considering the T and S commands, this definition is somewhat ambiguous. There are long lists of G and M commands (on a modern machine there are several hundreds of those), which typically respect the common quasi-standard and add additional manufacturer-specific codes. In the following, we will only discuss the functionality that has become the quasi-standard in NC controls, but once the concept is understood, one can easily expand on this knowledge.

One can easily recognize the historic heritage from a different time period, namely punched cards. G-code has no deeper syntactic structure, it is just a sequence of commands, each of which changes a certain state of the machine. Moreover, the commands itself are not exactly revealing or self-explanatory regarding their meaning. Some NC manufacturer have tried to get rid of the classical G-code programming by introducing new NC languages. For example, consider Heidenhain’s “conversational NC program syntax”, which is offered as an alternative on their NC controls:

This code still follows the same underlying block-like programming structure and (at least in my humble opinion) is not really a significant improvement to plain G-code. Personally, I would avoid such manufacturer-specific alternatives and stick to the quasi-standard.

Act II - The abridged guide to G-code

As we have seen, writing and understanding elementary G-code is not particularly complicated. The majority of the world’s production can be expressed in terms of those elementary commands, which is a remarkable feat for such a simple design. However, as the language evolved due to the numerous mechanical and computational advanced over the years more and more extensions were added and special cases had to be considered. Some of them have found their way into the quasi-standard, that we are discussing here. In particular, certain high-level language constructions like variables and loops have also found their way into modern G-code interpreters—unfortunately only in a particularly manufacturer-specific fashion.

Geometry is a bitch: Units, axis and offset allies

In our introductory example we have limited ourselves to a 3-axis milling machine for simplicity, where the three independent machine axes could be directly identified with the basis vectors of a right-handed coordinate system.7 However, given the fact that our G-code contains the fixed axis values in the movement commands, this naive identification would imply, that we have to place our work piece always exactly at the same location within the machine. Only in this situation the origin of the machine axis coordinate system would precisely correspond to the supposed origin of the object we are going to produce. Obviously, this is not very practical—in fact, considering that in modern milling and turning machines the machine axes easily reach a positional precision of around 5–10 µm, it is in practice almost impossible to reposition each and every raw block of material at that level of precision.

Offsets

Fortunately, with a little bit of mathematics, one can easily circumvent this by defining an offset vector between the coordinate system of the program and the fixed coordinate system of the machine’s hardware axes. In fact, the machine’s NC takes care of all the mathematics. After placing a new raw material into the machine, one can then simple use measuring tools (or other means) to determine the offset vector with respect to the hardware axes coordinate system and use it in the NC program. Once such an offset has been activated, the new origin for the G-code program, called the work piece coordinate system (WCS), may be located anywhere within the machine. The concept of an offset vector effectively detaches the NC program from the clamping situation found inside the machine.

In order to activate a given offset, one simply has to use the right G code. A modern NC allows to store multiple different offset vectors, which allows to write long programs that can easily switch between different work pieces. Furthermore, it is possible to relate the offsets, which gives a sort of hierarchical structure. We will not cover this, though. Instead we are interested in the following G-codes:

• G54 to G59 activate offset vectors 1 to 6, which in effect selects the work piece coordinate systems 1 to 6. There are manufacturer-specific extensions in case more than six offset vectors are required.
• G53 deactivates all offsets and uses the hardware machine axes coordinate system. However, this command is non-modal and resets to G54 in the next block.

Using G53 only makes sense for very specific situations, for example a manual tool change, where one would like the spindle to retract to a predefined and fixed hardware position. In practice one never uses hardware axes for the actual milling due to the variability of clamping possibilities, thus the automatic reset to G54.

There are further, somewhat odd constructions, like a local coordinate system, which is activated via G52. It temporarily shifts the origin of the newly defined local coordinate system to the current location of the spindle. For example G52 X50 Y80 first defines the current location of the spindle as the new origin, such that the two movement commands can be interpreted as $x=x+50$ and $y=y+80$. However, the same can be achieved by simply using multiple offset vectors G54 to G59, such that G52 is not really used much these days, except for reusable NC program portions. Some of these commands are simply the result of “quick & dirty solutions” to everyday problems arising in a manufacturing environment.

Positioning modes

Instead of programming in terms of absolute coordinates with respect to the selected work piece coordinate system G54 to G59, one can also activate relative or incremental coordinates. In effect, this redefines the current location of the spindle as the origin of a local coordinate system.

• G90 activates absolute positioning, which is the normal operation mode on most machines.
• G91 activates relative or incremental positioning, where each block’s movement operations are specified relative to the previous block.

While relative coordinates may at first seem more generic and easier to use for copy & paste situations, one on the other hand needs to constantly keep track of each movement command. A single inserted movement command affects the rest of the entire NC program. In absolute positioning each movement command effectively defines the starting location of the next movement operation. This makes it relatively easy to jump to some arbitrary location of the NC program, which is one of the reasons why absolute positioning is used almost exclusively in practice.

Units

One thing that we have omitted in our introductory example is the definition of units. On a US-built machine everything will typically be defined relative to “inch” units, whereas other machines are using “mm” SI units by default. One can easily switch between the two unit systems:

• G20 activates Imperial “inch” units.
• G21 activates Metric “millimeter” units.

The unit system should only be set at the top of the program and never be changed in between to avoid confusion. In fact, it is good practice to never change the unit system on a machine at all. Consider for example the G-code fragment:

In essence the two unit systems are simple related to one another by a constant factor of 25.4 since 1 inch is equal to 25.4 millimeters.

An improved example

We can easily rewrite our earlier example, where we milled a 10 cm by 20 cm rectangle shape, to incremental programming:

While being useful, incremental programming is typically only used in short sections of the G-code. The majority of NC programs never deviate from the absolute programming paradigm, yet it is useful to have something in your bag of tricks.

Supporting codes

At this point we see that despite its ancient origins G-code is a reasonable programming language, which is comparable to a sort of “Basic with unintuitive commands” in the sense that a lot of the rather useful G and M functions are just referenced via numbers instead of proper names. However, we have so far ignored some of the more important supporting corrections that are built into the G-code in order to aid the user.

Consider again our introductory example, where we milled a 10 cm by 20 cm shape. Well, using this precise code snipped, we would actually not have milled this exact shape, but instead something smaller, since our used tool has a size of its own. If our mill has a diameter of 6 mm, using our code, the resulting shape would have the dimensions 9.4 cm by 19.4 cm, i.e. along each edge we are loosing half a diameter as our tool center moves along our programmed 10x20 cm path. Fortunately, it occurred to the NC manufacturers a long time ago, that it might be useful to have some sort of automated correction for this sort of problem—another step to make the NC program even more independent from the details of the machine.

In order to correct for the tool diameter during a milling operation, in is necessary to tell the NC on which side of the path the material has to remain intact, i.e. whether we have to move the tool to the “right” or “left” of the programmed path when milling from bottom to top. Three commands are available in this context:

• G40 turns the tool radius compensation off.
• G41 activates the tool radius compensation on the “left”, i.e. if we are milling from bottom to top the tool is shifted to the left.
• G42 activates the tool radius compensation on the “right”, meaning that the tool is shifted right.

Obviously, some attention to detail is required in order to select the correct tool radius correction by hand. However, in the end the NC takes care of all the nasty operations, because such a radius compensation can become quite involved in a 5-axis milling, where rotational axis are involved and one needs to keep track of the local geometric center or rotation in order to perform the proper shifting. Ignorance is a bliss!

Tool length compensation

In a similar fashion one can compensate for the length of a tool. Technically, the plain NC program positions the spindle directly at the location specified in the movement commands. However, tools have a certain length and diameter, and ideally one would like to compensate for both the length and the radius automatically. This can be easily done by the following to commands:

• G49 turns the tool length compensation off.
• G43 activates the tool length compensation as a negative, which is the commonly used variant considering the typically right-handed coordinate system and the tool extending in the negative $z$ direction relative to the spindle.
• G44 activates the tool length compensation in the positive. This is rarely used.

In practice, both the tool length and radius (and potentially other geometric parameters of the tool) are precisely measured (at µm scale) and the information is transferred to the NCs tool table, which also contains the (often numeric) tool identifiers like 1001 or 1002. In the end, the command pair T1001 M6, which pre-selects and changes the tool from the magazine to the spindle also activates the corresponding tool length and tool radius compensation parameters. Once again, the NC hides a lot of nasty geometric computations from the end user in order to simplify the operation.

Universal robots and machines

Up to this point all the moving in the 3-axis milling machine implies that the orientation of the tool remains fixed throughout the entire operation: we can change the position of the tool, but—in case of a drill for example—it will always be pointed in the same direction, usually the negative $z$-direction. Modern production machines, often have more than three axes which provides additional degrees of freedom8 such that the orientation of the spindle relative to the material can be changed as well. Nowadays 5-axis milling machines9 define the high-end standard for the manufacturing of complex products.

Using universal milling (and turning) machines allows to manufacture a much wider variety of goods. Many components of modern turbines and engines, which use intricately curved propeller blades—called blisk or impeller—or complicated housings can only be manufactured using the additional degrees of freedom of an universal 5-axis milling machine.

More than three axes

While the common $x$, $y$ and $z$ axes are positional degrees of freedom, which are referred to as prismatic joints in the field of robotics, the additional axes are rotational degrees of freedom, called revolute joints. A prismatic joint is easily described by a direction vector, and as mentioned one can identify the 3d coordinate system with the three directional vectors of the $x$, $y$ and $z$ axes (up to some offset). Likewise, a rotational degree of freedom can be specified by a direction vector that is normal to the plane of rotation—appropriately called a normal vector. In order to use the additional axes of a machine, we need associated G-code variables: A, B and C are the three standard variables used for up to three additional (rotational) axes. According to the standard, the A axis rotates around the X axis, B around the Y axis and C around the Z axis, i.e. in each pair the normal and direction vector point in the same direction. Depending on the hardware design of the machine, one therefore typically finds 5-axis milling machines with axes (X, Y, Z, A, C) and (X, Y, Z, B, C), but once again this is there is no definite standard. Furthermore, the rotational axes’ normal vectors may point in a different direction than the X, Y and Z axes—there are really a lot of possibilities.

Adding such rotational axes to the mix immediately opens up a number of important question: What about the feed rate set by the F command? What does the linear interpolation used by both G0 and G1 mean in this context? What is an offset vector for rotational axes? In which units are the rotational axes programmed? Short answer: ordinary degrees, i.e. 360° is a full rotation.

Multi-axis feed rate considerations

Well, from the users perspective the additional rotational axes really do not change the programming concepts of the machine that much. The feed rate, for example, still refers to the velocity of the tool end. The NC takes care of all the nasty mathematics of computing this local velocity, as a rotation has vastly different effects depending on the tools location relative to the rotation center. Consider this: If you are 10 mm from the center of rotation and rotate by 90° (a quarter circle), your path has a length of 7.85 mm. But if the tool is 100 mm from the center of rotation and rotated by the same amount, the traveled length of the tool path is 78.5 mm, i.e. ten times the amount. This becomes apparent in the following code fragment, where we assume that in the chosen WCS the origin corresponds to the center of rotation of the C axis:

The point I am trying to make here is that a feed rate controlled movement is location dependent once rotational axes come into play, which becomes apparent by the two 90° rotations with different milling times in the above example. Fortunately, the NC takes care of all the associated computations, so from the user’s perpective the machine tool just appears to be moving with a constant speed through the material.

Linear interpolation in multi-axis settings

In a simular fashion the linear interpolation is applied to rotational axes as well: The value of the rotational axis is linearly interpolated from start to end just like the linear axes. If $(x_0, y_0, z_0, a_0, b_0)$ are the starting coordinates and $(x_1, y_1, z_1, a_1, b_1)$ are the destination coordinates we can write the linear interpolation simply as $(1-t)(x_0, y_0, z_0, a_0, b_0) + t(x_1, y_1, z_1, a_1, b_1),$ where $t \in [0,1] \subset \mathbb{R}$ is the interpolation parameter going smoothly from $0$ to $1$. For example, in order to mill a equidistant spiral that takes 10 turns around the center of rotation, the following code can be used:

As mentioned, the NC automatically computes the local velocity, such that the mill appears to moving with a constant speed through the material, but in effect the rotation of the C axis will be slowing down while the mill is moving farther from the center of rotation. Pretty neat, but a very important concept to understand properly.10

General offsets

An offset vector in a 5-axis milling machine in principle behaves like the offsets for the three linear axes X, Y and Z. Essentially, one defines a new origin in the machine axis coordinate system, which in this case may imply a tilted machine table or a tool pointing in an odd direction. Again the possibilities are only limited by the hardware capabilities of the machine.

Variables and parameters

Using the concept of coordinate system offsets in effect introduces a form of variability to the G-code. It is no longer a static construction that determines absolute hardware axes positions, but instead becomes flexible. In principles, instead of changing the coordinate system used for the NC program, one could change the entire NC program and then use the hardware machine axes again—but for obvious reasons this would be an extremely cumbersome and awkward process.

In any case, there is a lot of value to have parts of the program determined solely by a small number of external parameters or variables. G-code variables are once again an area that is not exactly specified and—despite being conceptually equivalent—the manufacturer implementations really are different here: The Fanuc Macro B dialect uses the # character followed by a number to access a “variable slot”. On a modern Siemens NC control the notion of an R parameters essentially serves as the variable system, where an R followed by a number also represents a variable location. Let us go a little bit deeper into the details.

Fanuc

On a Fanuc controller the variable #0 contains the null value, i.e. it essentially clears the value of a variable on assignment, i.e. #3=#0 clears variable #3. This variable cannot be overwritten, it is a special purpose read-only constant. Furthermore, different numerical ranges of variables have different uses:

• #0 contains the read-only null constant for clearing other variables.
• #1 to #33 are local variables that can be used for computations and parameter passing.
• #100 to #199 are non-persistent global variables, that are shared between all NC programs on a machine. They can be used for passing information between different programs. However, like on a regular computer, those variables are being cleared if the machine is turned off.
• #500 to #999 are persistent global variables, which are likewise shared between all programs. Those variables retain their value even when the machine is turned off. One usage example would be a global work piece counter.
• #1000 and up are Fanuc NC parameters. Hands off! Unless you really know what you are doing, you will most likely be screwing up the machine.

Within the NC code the usage of variables is pretty intuitive. Using the = sign one can perform assignments and the common mathematical operators are available as well. One weird oddity of G-code is the usage of squared brackets [...] instead of ordinary brackets (...) for mathematical operations, i.e. one needs to use #4 = [#1 + #2] * #3 instead of #4 = (#1 + #2) * #3 to perform a proper computation. On a Fanuc controller the variables are also used for macro calls, i.e. one stores certain values for a subprogram in the local variables and then calls the subprogram.11

Siemens

A Siemens controller on the other hand uses R parameters as the variable system. The choosing of the letter R has its roots in the German word for “computational parameters” (“Rechenparameter”). On most modern Siemens NC controllers R0 to R99 are available as local variables (comparable to the range #1 to #33 on a Fanuc controller), but the actual maximum number of variables in stored in a system variable. Overall, Siemens uses a more sophisticated system of variables and parameters, such that the R parameters can be used like variables in any other higher-level language:

Likewise, common mathematical functions like SIN(), COS(), TAN(), ABS() and so on are available. This makes using the R parameters on a Siemens controller quite convenient. They can also be accessed in an array-style fashion, which allows to compute the index:

In order to use a computed variable value as a new axis value, one uses that the axes values themselves are managed as variables. The “command” X86.5 is essentially a short form of the assignment X=86.5 and in a similar fashion one can simply assign an R parameter value via X=R4. However, note that aside from a negation, i.e. X=-R3, one cannot perform computations during the assignment to an axis value parameter. Thus, X=R4+3*R5 will throw an error, but using another R parameter R6=R4+3*R5 and a subsequent X=R6 circumvents this issue.

Jumps and control structure

Up to this point we have only considered NC programs with a completely linear structure, i.e. the NC interpreter starts at the top of the program, executes the first G-code block, then the second one, and so on. However, there are situations where the linear flow of commands is not optimal. Consider for example a situation where the same shape has to be milled several times, but at different locations. In this case it is a good approach, to develop some kind of loop structure or subprogram structure around the repeated G-code portion in order to avoid repetition. Another example would be to skip a larger block of commands by just jumping to a different location of the NC program.

Line numbers and unconditional jumps

Long story short: there are more than enough good reasons why one would like to deviate from the sequential linear command flow. The simplest example of such a deviation is an unconditional jump, which basically means, that the interpreter moves to a different location of the NC program. However, this requires that we give the new location a name or have some other method to specify the location.

Once again the ancient origins of the G-code programming language strike again. Just like Basic, COBOL and other “old” programming languages, G-code has a command for naming its input blocks:

• N followed by a number defines a line number or block number (remember that individual lines and execution blocks are essentially the same in G-code). The numbers do not have to be sequential or in steps of 1, but they obviously have to be unique. Often steps of 2 or 10 are used—N10, N20, N30 and so on—such that there is space to add some code blocks in between at a later time.

Naming a G-code block in this fashion makes it possible to reference the number in a GOTO command, which is something awkwardly familiar from the old days of software programming. The following example shows how to do this:

In this example the GOTO statement jumps over the removal of the tool from the spindle (T0 M6) and the change to the rapid movement mode (G0), such that the actually executed code reduces to:

On a number of NC controllers—a modern Siemens NC, for example—one can also use user defined string labels instead of the line numbers, which makes the jump destination somewhat more visible:

Pretty simple, right? Well, very annoying for anyone trying to read the G-code as well. Ask any sane programmer and he will throw bricks at you for even suggesting the usage of the GOTO command, which is usually seen as some unworldly demon from the depths of coding hell—a statement that applies to all programming languages.

There are a number of significant drawbacks: While our example above is pretty obvious to understand, in any real-world situation a jump is most likely going to happen over a wider portion the NC program. Scrolling through a longer program, the flow of command blocks is no longer obvious. Furthermore, one easily misses important changes in the machine or geometry state, that are happening (or have been later added) to the over-jumped portion of the NC program. Despite the fact that you should know of this command and be able to use it, the recommendation is rather negative: Avoid if possible!

Conditional jumps

Fortunately, there are other constructions available to modify the linear NC program flow which are more in line with modern programming languages. The most important one is the IF conditional jump. On a Fanuc controller one can use the following code snipped:

Therefore, the tool removal and rapid mode activation is skipped if the value stored in #84 is zero. According to the computation in block N1010 this only happens if either #85 has value -8 or #77 is zero or both. The same code on a Siemens machine has a structurally equivalent form with a slightly different syntax:

Both code snippets are fairly self-explanatory: Here the tool removal and rapid mode activation is skipped if R7 has zero value, which according to block N1010 only happens if R4 has value -8 or R5 is zero or both.

Instead of using unconditional jumps and GOTO as well as line numbers, modern G-code interpreters also support the more modern IF-ELSE-ENDIF structure:

Loop constructions

Modern G-code interpreters also support loop constructions like WHILE or REPEAT, but the details are very much manufacturer-specific. On a Siemens controller those are paired similarly using WHILE/ENDWHILE keyword pairs. Once the basics of the G-code language are understood, one can easily read up on those constructions in the documentations of the respective NC. Therefore, we will not go into the respective details.

Macros, procedures, cycles and functions

The backbone of a modern NC are the manufacturer subroutines and supplied technological assistance functions. Conceptually, macros, procedures, cycles and functions are all the same, despite the fact that the NC manufacturers try to make certain aspects appear to be mystical and complicated. All those names plain and simply refer to the idea of a function or procedure just like the familiar one from any higher-level programming language. However, there are some significant differences in the implementation: In the simplest case a subroutine is written in the G-code language like the main NC program itself. But due to the real-time nature of a numeric control, there is also the possibility that some subroutines are implemented at a deeper NC kernel level. Developing such core routines is an intricate task and the necessary development SDKs are rather expensive, such that in effect most of those routines are supplied by the NC manufacturer.

For example, drilling a hole or milling a pocket with several islands are functions that can be implemented by the NC manufacturer as “cycles”, such that with a number of geometric parameters a complicated operation can be packaged for the user. Usually those routines are very well-tested and provide a decent technological standard for the NC control, which is typically shipped with a manufacturer-specific user interface that simplifies the usage of those cycles. On the downside is the fact that the code of those manufacturer-specific cycles is hidden from the user. If a cycle is implemented at kernel level, it is written in a completely different, compiled language, e.g. a C/C++ component directly attached to the NC kernel. But manufacturer-supplied cycles written in the G-code language can also be hidden from the user using cryptographic methods.

In the end, the usage of cycles can simplify the operation of the machine dramatically, especially if the machine operator is writing the G-code directly at the machine instead of using a prepared NC program. On the other hand, one has to deal with numerous manufacturer-specific “black boxes” which have an obvious lock-in effect. Especially the German NC manufacturers Siemens and Heidenhain, which are predominantly used in the European marked, are providing huge libraries of such technological manufacturer cycles—which are unfortunately incompatible. Very often this leads to entire machine shop floors dominated by a single NC manufacturer due to the lock-in of the NC programs that are being created over time and which cannot be transferred over to any other control without significant manual work.12

Act III - Advanced topics and conceptual limitations of G-code

We have reached a point where we could in principle write quite sophisticated NC programs in the G-code language, where we are potentially using some additional manufacturer-specific extensions from the official documentations. Therefore it seems to be appropriate to take a step back and look at the big picture for a moment. As mentioned several times in the previous text, G-code is a very old programming language—developed in the 1950s—and in some aspects very much shows its age. Furthermore, aside from pure convenience issues, there are also conceptual drawbacks, which have only become more and more pressing during the last couple of years in the context of modern multi-axis manufacturing machines and advanced production techniques.

Modern production process chains

Abstract machine programming and post-processors

One of the first obstacles that we can observe is the strong dependency of G-code on the machine’s hardware configuration. Movements are programmed with respect to the geometric arrangement of the machine axes, i.e. a G-code program depends on the kinematic structure of the machine. This makes it almost impossible to reuse an NC program on a machine with a different hardware configuration. Likewise, many machine-specific extensions and specialties are accessed via manufacturer-defined G and M commands, such that aside from the programming of movements the entire functionality of the machine is accessed by strongly machine-dependent G-code.

With those considerations in mind, we essentially realize that G-code is nothing else than a form of “high-level assembler” for manufacturing machines, which implies that it is in effect a highly machine-dependent low-level language. If one has an executable for an x86 CPU architecture, it will not run on an ARM CPU and vice verse. Likewise, most NC programs written for different machines are incompatible. In the realm of computers this issue has long ago been resolved by the introduction of compilers, i.e. programs that take a generic, abstract high-level language and translate it to the CPU-specific assembler code.

A similar process is also available in the context of machine programming: There are (to some extend) higher-level languages like APT and CL which contain abstract position and orientation information of the cutting tool (“CL” actually means “cutter location”) that are later “compiled” to the machine-specific G-code of the NC program. Those “compilers” are called post-processors, which take the abstract tool path information and compute the corresponding G-code movements from this. Furthermore, abstract commands like “turn cooling on” are translated to the corresponding M commands of the machine. There is a small industry solely dealing with the development of post-processors that take care of those tasks.

However, unlike in the software industry, there are no open post-processor frameworks or templates. Due to the great variety of target machines, post-processors are individually configured or patched together from existing components. This situation is rather unsatisfactory and offers quite some space for improvement.

CAD/CAM applications and the process chain

G-code is nothing else than a description of the path that a tool is following to interact with the work piece plus some commands to control other machine functions. In other terms, it describes a “history of material removal” (or in the context of additive manufacturing the “history of material accumulation”). However, given a sufficiently complicated geometry, the derivation of the precise tool path is very hard to compute by hand. In particular, it is almost impossible to program universal milling or turning machines—having more than three linear axes—without some kind of computational aid in order to break down the tool path into sequences of axis values. This is particularly true for curved surfaces and other intricately shaped contours.

Nowadays virtually any manufactured goods are being professionally drawn using CAD programs (“computer-aided design”), where an abundance of assistance and shaping wizards aid the user in the precise specification of complex geometries. Once such a 3d model of the product is finished, the CAM process (“computer-aided manufacturing”) takes places. Using various assistance functions and computational support, the CAM programmer can essentially piece together a variety of prepared operation templates from the CAM software library, and the software automatically computes the tool path. For example, a curved surface can be automatically covered by selecting a certain material removal strategy, the right tool, a suitable feed rate and spindle speed. However, this is by no means an automated process, where one just imports a CAD model, presses a few buttons and a complete NC program (or rather the abstract APT or CL form) comes out at the other side. The CAM process is more like a complex puzzle game, where a lot of the experience of the NC programmer is being utilized in order to patch together the entire NC program from simple primitives. The software itself essentially just aids with the necessary geometric computations to specify the tool path, but the selection of tools, the material removal strategies and so on is still mostly within the manual domain of the programmer.

Modern software tools unify the 3d modelling and NC programming operations and are therefore called CAD/CAM programs. Two of the largest software suites are Siemens NX and CATIA, which in addition to the CAD and CAM operations offer significant simulation features to test the manufacturing process. CAD/CAM experts, who really know how to use those software suites, are highly sought after experts. Once the CAM process is finished, one essentially has an abstract NC program defined within the software, which can then either be exported to the (still abstract) APT/CL languages or be directly post-processed. In some software suites the post-processor is nowadays embedded as a plugin, which removes an intermediate export/import step. A lot of the information required for the post-processing step (geometry of the machine, geometry and information of the tools, etc.) is often already available in the CAD/CAM software in order to allow a proper programming.

At the very end of this chain of steps, one is left with a machine-specific NC program written in the G-code language that we discussed. Within the business, providing the proper inter-operation of all the different software packages is quite a challenging task due to numerous data interfaces, conventions, machine-specific extensions and other trickery. Altogether, one refers to this as the process chain: given a rough drawing of a product (“idea”), a precise technical drawing has to be created (“CAD”), the material removal tool path has to be programmed (“CAM”) and then the abstract program has to be translated into the machine-specific G-code commands of the NC program (“post-processing”). Furthermore, particularly complex products may warrant the additional expenses of virtual machining (“simulation”) or further optimization. The price for all the software packages and the (almost always necessary) customization easily runs into 6-digit figures.

However, in the very end, one is left with an NC program written in the G-code language, which makes it quite important to understand this simple yet powerful language at least to some extend. Like in regular computer programming, one can be a very good coder if all the tricks of the high-level languages like C#, Python, Java, Lisp, Ruby and so on are known. However, one will never be a great programmer unless at least the fundamentals of the “assembler code” that comes out of all those abstraction layers are understood, because in the end the CPU is only able to execute primitive assembler codes. The analogy to G-code is only partially given, yet it is important to be able to read and understand the final output of the CAD/CAM process chain that ultimately runs on the machine’s NC.

Limitations of G-code

Interpolators, splines and complex tool paths

We have discussed linear interpolation briefly when we introduced the G0 and G1 interpolation modes as well as the generalization to universal machines with more than three axes. Like so often, this is only part of the story. While a linear interpolation appears to be a reasonably straightforward operation, we already discussed the arising complexity of computing local velocities of the tool end in order to keep a constant feed rate once rotational degrees of freedom come into play.

Another aspect to consider is the smoothing of movements that needs to be considered in order to provide both an economic operation of the machine as well as a good surface quality of the produced part. If the machine would stop and come to a hold after each an every execution block, the complete machining time of the NC program would take much longer, and the wear and tear on the mechanics would be significantly increased. Furthermore, any abrupt change in movement creates minor vibrations and jolts. The massive components of a modern milling or turning machine naturally have quite a significant momentum once they are set into motion. This makes it necessary—or at least very sensible—to anticipate changes in the movement and to smooth over those variations. In modern NCs the interpreter therefore “looks ahead” and takes a certain number of execution blocks (e.g. 10–20 block) into account while computing the tool path. Mathematical methods like splines and Bezier curves are used to smoothen over the trajectories and to provide a seamless operation.

Ultimately, this is the reason why the milling of a complicated and smoothly curved surface actually is carried out in a nice, smooth motion of the tool. The programming of such work pieces is usually done using CAD/CAM software suites, which break the tool path down into numerous very closely located points—often less than 1 mm apart—connected by the linear interpolation mode G1. The resulting tool path is basically a linear approximation of the curved surface.

There are, however, two significant drawbacks to this approach. In extreme cases, the file size of the NC program for such a linear approximation of a freely shaped object can grow to several gigabytes. While file storage on a modern production machine is not exactly a problem and is able to use network drives and external storage, handling such large files is usually cumbersome. It is a strong hint at a very ineffective way to describe a relatively simple contour.

In the end, using G-code and its linear approximations of a curved surface represents a rather awkward loss of information within the process chain: Given a CAD model at the beginning, one essentially has the full information on all the shapes, curves and parts of the target product. Within the CAM process of programming the tool-path and the post-processing to the G-code, one essentially looses the precise information on the curved surfaces and the corresponding smooth curves of the tool. Those are replaced by a linear approximation, i.e. a polygonal chain, which blows up the NC programs file size to immense proportions. The G-code interpreter within the NC, on the other hand, then has to use sophisticated look-ahead and tool path smoothing methods in order to reconstruct the original shape of the smooth curve as best as possible from the polygonal chain, which is described in terms of numerous G1 movements. In effect, within our modern CAD/CAM process chains, we are dealing with a huge loss of information by approximating the original work piece and then use complicated methods of smoothing in order to reproduce the lost information as best as possible.

This is obviously a somewhat unfortunate situation, which will only become more important in the future. Would is not be much more sensible to convert the information of the curved surface into a form of parameterized curve? Even better: instead of several thousand approximation points on the surface connected by linear interpolation, could we not provide the information of the surface directly to the NC together with a description of the supposed milling strategy, such that the NC itself can do the precise path planning on its own account?

A look into the future: STEP-NC?!

One such attempt was the creation the language (or rather: the data format) STEP-NC, which exactly addresses the issue that G-code is a non-portable and machine-specific language that just describes axis motion, but does not really contain any deeper information about the resulting work piece. Ideas of a more complete data exchange format, that also contained the CAD model, tolerance information, portable machining strategies and a lot of other information, where put into motion during the turn of the millennium. In effect, the information that is essentially available to the CAM programmer is being collected in a new data format and allows a modern NC to take it into account.

Unfortunately, aside from a several demonstrations and a number of running prototype implementations, there has not been a wide-spread adoption of this approach yet. The work on the specification of the STEP-NC data format continues, but it remains doubtful if there will ever be a revolution that in effect replaces traditional G-code with more advanced descriptions. Further work into STEP-NC developments ist mostly driven by the aircraft industry, namely manufacturers like Airbus and Boing in cooperation with various tool and tool machine manufacturers like Sandvik and Okuma. Here, in the production of modern turbine and engine components that often have to meet the strictest tolerance levels while being produced from the most difficult to handle materials, the more flexible nature of the STEP-NC approach really begins to shine, in particular in comparison to gigabytes worth of polygonal chains of G1-connected axis values. In a lot of ways there are several such “software revolutions” waiting and about to happen in the manufacturing industry within the next decade.

References and further material

Further literature:
• CNC Cookbook: a huge collection of articles that goes well beyond simple teaching of G-code but also focuses on the mechanics and the machines being driven by the G-code language
• Wikipedia page on G-code: contains a huge collection of quasi-standard G and M codes based on the Fanuc G-code standard which is a good reference
Open source NC controllers and projects:
• LinuxCNC / EMC: One of the most famous open source motion controller and interpolator implementations, which uses an adapted version of the Linux operating system to provide real-time control. EMC stands for Enhanced Machine Controller. Pretty dated at this point, hard to set up, but still an important reference.
• Machinekit: A modern reincarnation and fork of the LinuxCNC motion controller, which has been expanded by numerous communication layers to provide a modern basis for projects in the “Internet of Things” era of connected machines.
• grbl: An open-source motion controller designed for the AVR Atmega 168 and 328 microcontrollers found on the Arduino boards.
• Smoothieware: Another modern collection of tools—a basic controller board and firmware—which is based on the grbl motion controller.

There are a lot of further sources for information. For example, Siemens provides excellent manuals that describe their specific G-code dialect.

1. According to the book, the numbers are taken from the research papers “The New Digital Economy” from mid-2011. Considering the growth of the digital economy in recent years—especially the mobile sector—I would not be too surprised if the numbers are much closer now. However, the (still valid) point made here is that the economy of non-digital products is significantly larger compared to the realm of bits and bytes. Furthermore, it has to be noted that the manufacturing industry has a value creation rooted firmly in the actual production of goods, whereas a significant amount of the digital industry’s revenue is still strongly tied to the selling of advertisements.

2. In case someone is wondering why I wrote this text: Well, for once, I work in an industry which directly relies on the G-code programming language—DMG MORI is the world’s largest manufacturer of NC controlled turning and milling machines. When I started I kind of missed an easy-digestible introduction to this topic. This is now something, that I can potentially give to a new colleague for orientation and first steps. Furthermore, I am working on a basic G-code interpreter in my spare time, as well as some deep learning experiments involving recurrent neural networks (RNNs) and G-code, so sooner or later I will need a basic G-code introduction that I can reference.

3. This fixed cyclic interval architecture has been refined in recent years to allow for timeouts, finish-execution options, event-based triggers and similar extensions. Often, however, the classic cyclic operation mode is still used.

4. Despite the premise of extreme safety, it is not impossible to hack PLCs in large industrial environments. The extremely sophisticated multi-layer PLC virus Stuxnet, which infected and modified the STEP7 programming software of Siemens PLCs in order to reach its embedded destination, proved this in an astounding fashion, when it severely set back Iran’s nuclear enrichment plans. The modified PLC control software changed the rotational frequency of the uranium centrifuges just a little bit, but due to resonance effects this reduced the lifespan of the centrifuges dramatically. Small bug, huge implications!

5. G-code is largely defined in the NIST RS-274D standard in the US and in the international DIN 66025/ISO 6983 standards. However, every manufacturer adds additional codes, custom cycles and additional components, such that there is no single G-code standard. Most manufacturer follow the quasi-standard set by Fanuc.

6. Unfortunately, the specification of the G-code language is not very strict. In principle it is possible to use the same command several times in within the same block, as later commands—following the modal paradigm—overwrite earlier commands. Therefore a block F200 G0 X17.3 F30 X80 Y123.4 G1 X100 F40 can logically be reduced to the block F40 G1 X100 Y123.4. Here the later called G1 operation mode overwrites the earlier G0 mode, the latest X100 overwrites the two earlier positioning commands of the axis, F40 overwrites F200 and F30 and Y123.4 remains unchanged. This can be confusing, as a line like F20 X100 F50 seems to imply “first move with feed rate $20$ to position $X=100$, then set feed rate to $50$”, but it is in effect equivalent to the cleaned up command line F50 X100. For obvious reasons this kind of programming should be avoided at all costs. In fact, some newer numerical control interpreters outright prohibit this kind of language abuse and throw errors, but faced with an old-school NC it is good to know what such G-code language border cases actually mean.

7. Coordinate systems in manufacturing and numerical control are (almost) always right-handed, except otherwise noted. While there is no particular reason for this convention, it has become the quasi-standard and you will be hard-pressed to ever find a manufacturing machine with a left-handed coordinate system. So just keep in mind: Your (right hand’s) thumb is the $x$ axis, the pointer the $y$ axis and the middle finger goes into the positive $z$ direction.

8. It can be shown that six degrees of freedom are sufficient to define an arbitrary position and orientation. This follows from the dimension of $SE(3) = \mathbb{R}^3 \times SO(3)$, which is often referred to as the robot group. However, this mathematical consideration does not take obstacles or physical limitations within the machine into account, such that machines and robots with more than six axes are being built.

9. Considering the previous footnote, a 5-axis milling machine is in fact an universal machine: one degree of (rotational) freedom is effectively removed due to the rotation-symmetric tools that are typically used for milling. Thus, within the physical limitations of the machine, the tool can reach any position from any direction, which is the defining quality of a universal milling machine.

10. In fact, the mathematical complexity of the task that is being carried out by the NC is often lost on the user: in order to keep the velocity of the tool end as close as possible to the set feed rate, the NC has to recompute the local velocity at the end of the tool every few milliseconds and adjust the movement rates of all individual axis motors appropriately in order to not deviate from the path implied by the NC program commands. In the literature this is called the inverse kinematics problem, which can become quite involved.

11. The Fanuc macro system is very much comparable to the low-level implementation of a function call on a regular computer: The function variables are pushed onto the stack—or stored in certain variable slots—and then the function is called, executes and returns to the location from where it was called. Though completely valid and generic, from the modern perspective this approach is no a very elegant and rather cumbersome to interpret.

12. In my personal opinion this is a very unfortunate situation. Imagine that there would exist five different, mutually incompatible implementations of the Python language and each one of them brings along its own extended standard library. This is unfortunately the situation that currently dominates the NC world.

Updated: