Category Archives: Industrial Automation

How to Write a Big PLC Program

Staring down the barrel of a big automation programming project is intimidating. It’s hard to even know where to start. Even when you’ve done a few before, you’re only marginally more confident the next time.

I have quite a few big automation programming projects under my belt, so I think I can generalize the process a bit. Here goes:

1. Get the Prints

There’s almost no point in starting to program unless you have an almost final set of electrical drawings. If you don’t have them yet, push for them, and go do something else productive until you get them.

2. Create a Functional Specification

You don’t always have to write out a functional specification, but it at least needs to exist very clearly in your head. If at any point, you don’t know exactly how the machine is supposed to work in excruciating detail, stop writing code and go figure it out. Ask stakeholders, talk to operators, whatever it takes. Functional specifications are best written as a list of “user stories”. If you’re not sure what a functional spec should look like, check out Painless Functional Specifications by Joel Spolsky.

3. Shamelessly Copy

Identify what other projects you can find with logic that you can steal. Any code that works in another machine has the advantage of already being debugged. Don’t re-invent the wheel. (At the same time, never blindly copy logic without understanding it. Copying the code by re-typing it one rung at a time is still faster than writing it from scratch, and it’s a form of software review.)

4. Structure Your Project

Now you break open the ladder logic programming software and start creating your project. Pick your CPU type, setup the I/O cards based on the electrical drawings. Map your inputs. Plan out your program by creating programs or routines for each functional unit of the machine. Setup your fault summary rungs and your alarm logic.

5. Write the Manual Mode Logic

PLC logic is typically written “bottom up.” Manual mode logic is the lowest level of logic because it deals directly with individual functions in the machine. Advance cylinder. Retract cylinder. Home axis. Jog axis. While you’re writing the manual mode, this is when you take extreme care making sure that actions are interlocked so the machine can’t crash. If you’re using the Five Rung Pattern, this means paying attention to what goes in the Safe rung. Does cylinder A always have to advance before cylinder B can advance? The Safe rungs should reflect that. Make sure that even in manual mode, the operator (or you) can’t break the machine. Make sure to hook your faults and alarms into the applicable fault summary rungs and alarm logic.

6. Write Part Tracking Logic

Now that manual mode is complete, write the logic that tracks parts (and their state) through the machine. Remember, you should be able to run the machine in manual mode, and the part tracking should (ideally) work correctly. I know this isn’t always the case but surprisingly part tracking in manual mode can work 95% of the time. That means part tracking works based on the state of the machine. Closing the gripper with the robot in the pick position and the part present in fixture sensor on should latch a bit “remembering” that the gripper has a part in it.

Once you’ve written your part tracking logic, go back and use the part tracking and state bits to condition your Safe rungs. Don’t let the operator (or you) mistakenly open the gripper if the gripper has a part and isn’t in a safe position to let go of the part. Of course, you may need to add a way to manually override this (that’s what output forcing was created for), but in most cases you want to prevent improper operation.

Part of writing the part tracking logic is adding “ghost buster” screens. Operators often need to remove parts from a cell, and if the machine can’t detect their removal, then you have to provide the operator with a way to clear these “ghosts.”

At this point you’re actually ready to dump the program in and start testing out the machine electrically and mechanically. While it’s ideal to have a fairly complete program when you go onsite, we all know that’s not always entirely possible. At the very least you want to get to this point before startup begins.

7. Write the Auto Mode Logic

The complexity of your auto mode logic depends on what type of machine you’re programming. You’ll always need a cycle start and a cycle stop feature. Even if you’re in auto mode, you usually don’t want the machine to start until the operator specifically tells it to start. Once it’s running, we call this “in auto cycle.”

In simple machines, you can write the auto logic by filling in the Trigger rungs in your Five Rung logic. Start by putting the In Cycle contact at the beginning of the rung, and then writing logic after that which expresses when the action should take place. For instance, an advance reject part cylinder’s Trigger rung could be as simple as In Cycle, Part Present, and Part Rejected. As long as the Part Present tracking bit gets cleared once the cylinder is in the advanced position, then this is all the auto mode logic you need for this motion. Have the retract Trigger rung be In Cycle, No Part Present and Not Retracted.

More complicated machines need more complicated auto mode logic. If your machine has to perform a series of steps (even if some of them are in parallel) then consider using the Step Pattern. If your machine needs to choose between several possible courses of action (commonly seen in a storage and retrieval system) then consider using the Mission Pattern.

8. Review

It’s hard to write correct logic. Review your functional specification, point by point, and make sure your logic meets all of the requirements. Check your logic for errors. A fresh look often uncovers incorrect assumptions, typos, and outright mistakes. The earlier you find and fix problems, the easier they are to fix.

Make a list of everything you have to do during startup. Starting up a machine is time consuming and therefore expensive. Anything you can do to prepare saves you time and money.

Good luck, and keep your fingers out of the pinch points!


Price of Oil Affecting Alberta and Ontario

The recent drop in the world price of oil has caused a similar drop in the value of the Canadian dollar. Much of that has to do with the huge oil production in Alberta, so that province is being hit particularly hard right now. The “oil sands” in Alberta are particularly expensive to exploit, as oil sources go, so companies there are quick to make cuts when the price drops below their break-even point.

The previous run-up in oil prices saw a lot of migration out of manufacturing-heavy Ontario westward to Alberta, and control system specialists were a big part of that. It’s difficult to see them facing these challenges, and none of us want to be in that position.

Back here in Ontario, the pendulum looks like it’s swinging the other way. When rising oil prices drive up the Canadian dollar, it makes it a lot harder for Canadian manufacturers to compete in the export market, because their products are automatically more expensive. The recent drop in the Canadian dollar, if it persists, is going to mean growth in the manufacturing sector, especially along the 401 corridor in Ontario. Personally I’m looking forward to seeing some growth in what has been a relatively stagnant market over the past 10+ years.

Automation is poised to be a big part of the new growth in Ontario. At first companies are likely to be cautious about adding new capacity, likely adding temporary labour, but if the trend holds, I think we’re going to see lots of interest in automation projects throughout the area. Companies are still a bit leery of the economy, so they’ll be looking for flexible automation that can adapt to changes in demand and re-tool quickly. Technologies that can support flexibility are going to be winners.

While disappointing for Alberta, I’m excited to see what the future holds for Ontario.

If you do happen to be out in Alberta and you have discrete automation programming experience, and you’re interested in moving back to Ontario, make sure you drop me a line. We’ve experienced lots of growth ourselves in recent years, so we’re interested in hiring an experienced automation programmer for our Electrical Engineer position. We use TwinCAT 3 and C# and mentoring is available to get you up to speed with these technologies. Even if you’re local and you’re tired of the endless travel or 2 am service calls, then maybe this family-friendly company is right for you.

Start your own Automation Blog!

One thing I’ve discovered about automation blogging is that it’s a pretty lonely place. Don’t get me wrong, there are a couple gems out there, but I don’t find many people writing about what it’s like in the trenches wading through rungs of ladder logic. In the .NET world there are tons of programming blogs with posts about every issue you could ever come across. Why such a dearth of information in the automation space?

It occurs to me that blogging seems difficult to a lot of people, as if you need to be a web programmer. That’s totally untrue (I’m absolutely not a web programmer). In fact there are a ton of inexpensive and simple options out there.

Maybe people wonder what to write about. That’s easy. At first I worried about writing something people didn’t already know, but it turns out there’s no shortage of shiny new graduates looking for any toe-hold they can get in this industry. Try to think back to when you didn’t know anything. How did you figure out how to get online with a PLC the first time? Did you have to call the office to ask someone, and feel foolish because you didn’t know what a Null Modem cable was? We were used to asking our peers for help, but the new generation grew up looking up how to do things on the web.

Maybe you’re worried about the cost. Shared hosting plans are very inexpensive. I use DreamHost, specifically because they’re inexpensive (starts at $9 per month including domain name, unlimited storage and bandwidth), the hosting is rock solid, they offer one-click installs of blogging software (such as WordPress), and their technical support is excellent.

It would really be great if I didn’t feel like the only voice in this cloud. Grab a blogging account and chime in. Write about a hard problem and how you solved it. Disagree with me! Help me learn something new!

Offline Changes to a PLC Program

As a PLC programmer, you’ll often be asked to do a change to an existing system. If there’s a significant amount of functionality to be added, you generally get your changes ready “offline” and then do all the changes during a short window of time to minimize disruption to the production schedule.

If you’re using an Allen-Bradley PLC, the procedure is typically this:

  1. Get a copy of the latest program from the PLC (a.k.a. an “upload”)
  2. Make your changes to the offline copy, and write down every change you had to make
  3. Go online with the PLC and apply your changes as online changes

Step 3 is much safer than just taking your modified program and doing a “download”. That’s mainly because when you download, you’re not just downloading the program, but the memory state of the PLC as well. The PLC typically has to track things in memory (like recipe data, part tracking, data collection, sequence numbers, machine counters, etc.). If you do a download, you’re going to overwrite all those values with previous values, and that can cause a lot of problems. The other thing step 3 saves you from is simultaneous changes that were done online while you were busy making offline changes.

The only other option you have is upload-change-download, but you really have to shut the machine down for the duration to make sure that the internal state doesn’t change.

When I did a lot of Allen-Bradley programming, I didn’t question that. It’s just how it was. I remember visiting a plant one time for a service call, and the local maintenance person was a bit suspicious of what I was going to do (after all, I was a young kid who had never seen this machine before). He decided to quiz me a bit, and one of the things he asked was, “when you go online, do you download or upload?” I said “it depends,” but his answer was, “you never download.” I agreed that someone in a maintenance role should never need to do a download unless they’re replacing a CPU, or recovering from a corrupted PLC program.

Now that I mostly do Beckhoff TwinCAT 3 programming, I realized one of the benefits are that offline changes are a breeze. It’s due to the fact that TwinCAT 3 completely separates the program from the memory data. The program is stored in local files on your hard drive and compiled into a TMC file. The persistent data is stored in a different place on your hard drive.

When I want to do offline changes to a TwinCAT 3 project, here’s the procedure:

  1. Get a copy of the latest program
  2. Make your changes to the offline copy
  3. Copy changes back to the machine (keeping a backup, of course), rebuild, and activate configuration

This makes offline changes go a lot more smoothly, of course. I don’t have to copy and paste my changes in while online, so it takes less time and eliminates the possibility of a copy/paste error.

Since we also use Mercurial for version control, getting a copy of the latest program is a matter of pulling the latest from the source control, and copying it to the machine is a matter of pulling the offline changes to the machine. Any changes that were done in parallel can be merged with Mercurial’s built-in diff and merge utilities. (Note: I/O changes can’t be merged nicely, so if someone changed the I/O while you were doing your offline changes, you have to copy those changes in manually, but that’s rare and at least it tells you that it can’t merge them.)

This got me thinking that Allen-Bradley probably has a better way of doing offline changes that most of us just don’t know about. I know that you can do an upload without uploading the memory. However, it seems like it requires you to download both the program and data at the same time. I wonder if anyone out there knows how to do better offline changes to a ControlLogix. If so, I would be interested to know that.

Announcing: the TwinCAT 3 Tutorial

Some of you may have noticed the new section on this site: TwinCAT 3 Tutorial.

I’ll be building this over the next several weeks or months. I’m working on making it more detailed than the RSLogix 5000 Tutorial.

Rather than being an introduction to PLCs, I assume most readers are coming to the new tutorial with some automation experience and they really want to know what this new technology can do for them. I won’t be touching on every single feature of TwinCAT 3, but I certainly want to touch on most of the common ones, and particularly some advanced features that really set TwinCAT 3 apart from traditional PLCs.

As always I greatly appreciate any comments you have. Please send them to my email address (which you can find on the About page).

Sending a Fanuc Robot’s Position to the PLC

This information is for a Fanuc R30-iA, RJ3-iA or RJ3-iB controller but might work with other ones.

If you’re looking for a way to send the robot world TCP position (X, Y, Z, W, P, R) over to the PLC, it’s not actually that difficult. The robot can make the current joint and world position available in variables, and you can copy them to a group output in a background logic task. There is one caveat though: the values only update when you’re running a program. They don’t update while jogging. However, there is a work-around for this too.

First you should make sure that the feature to copy the position to variables is enabled. To get to the variables screen, use MENU, 0 (Next), 6 (System), F1 (Type), Variables.

Find this variable and set it to 1 (or True): $SCR_GRP[1].$m_pos_enb

The name of that variable is Current position from machine pulse

Now create a new robot program, and in it write the following:


GO[1:X POS]=($SCR_GRP[1].$MCH_POS_X*10)
GO[2:Y POS]=($SCR_GRP[1].$MCH_POS_Y*10)
GO[3:Z POS]=($SCR_GRP[1].$MCH_POS_Z*10)
GO[4:W ANG]=($SCR_GRP[1].$MCH_POS_W*100)
GO[5:P ANG]=($SCR_GRP[1].$MCH_POS_P*100)
GO[6:R ANG]=($SCR_GRP[1].$MCH_POS_R*100)

Note that I’ve multiplied the X, Y, and Z positions by 10, so you will have to divide by 10 in your PLC. Likewise I multiplied the W, P, and R angles by 100, so divide by 100 in the PLC.

To run this program in the background, use MENU, 6 (Setup), F1 (Type), 0 (Next), BG Logic. Configure it to run your new program as a background task.

Obviously you need to send these group outputs to the PLC. Ethernet/IP is great for this, but you can use hardwired interlocks too. You need to make sure that you have enough bits to handle the full range of motion. A 16-bit integer should work pretty well for all of these. Note that the robot will happily send negative numbers to a group output as two’s complement, so make sure you map the input to the PLC as a signed 16-bit integer (a.k.a. INT in most PLCs). For the X, Y, and Z positions, a 16-bit integer will give you from +3276.7 mm to -3276.8 mm of total range. For the W, P, and R angles you’ll get +327.67 deg to -327.68 deg. For most applications this is good (remember this is TCP, not joint angles). Please check that these are suitable though.

As I said, these numbers don’t update while you’re jogging, and won’t update until the robot starts a move in a program. One little trick is to do a move to the current position at the start of your program:


PR[100:SCRATCH]=LPOS
J PR[100:SCRATCH] 10% FINE

This starts sending the position without moving the robot. In my programs I typically enter a loop waiting for an input from the PLC, and inside this loop I turn a DO bit on and off. The PLC detects this as a “ready for command” heartbeat, and as long as the PLC sees this pulsing, then it knows the program is running and the position data is valid.

Another trick you can use is to detect when the robot has been jogged:


DO[n]=$MOR_GRP[1].$jogged

The name of this variable is Robot jogged. The description from the manual is: “When set to TRUE,the robot has been jogged since the last program motion. Execution of any user program will reset the flag.”

That’s how you get the world position of the TCP into the PLC. If you just want joint angles, you can use $SCR_GRP[1].$MCH_ANG[n] as the variable, where “n” is the joint number.

Important note: The I/O will probably change asynchronously to the program scan, so what you want to do is make a copy of the X, Y, Z, W, P, R values coming into the PLC and compare the current values to the values from the last scan. If they haven’t changed, then update your actual values, otherwise throw them away because they might not be valid. If you have a fast scanning PLC and I/O then you should still be able to keep up with the robot even during a fast move. If you have a slow scan time on your PLC, then you might only get valid stable values when the robot is stopped.

Now what if you want to know what the TCP position is relative to one of your user frames? The robot controller doesn’t seem to give you access to this, but the PLC can at least calculate the X, Y, and Z positions of the TCP in your user frame itself, given the world position and the user frame parameters.

First you need to find the accurate user frame parameters. Under the normal frames screen you can only get one decimal point of accuracy, but you need the full 3 decimal points to have your numbers in the PLC match the user frame position given in the robot. You can find these accurate positions in a variable: use MENU – 0,6 (SYSTEM) – F1 (TYPE) – Variables – $MNUFRAME[1,9] – F2 (DETAIL). The second index in square bracket is the frame number, so $MNUFRAME[1,1] is frame one and $MNUFRAME[1,2] is frame 2. Copy these numbers down exactly.

Here’s the math for calculating the TCP relative to your user frame. All variables are LREAL (which is a 64-bit floating point variable). I don’t know if you can use a regular 32-bit float or not. Result is your TCP in user frame. Point is your point in world frame (from the robot) and Frame is the accurate user frame data you copied from the $MNUFRAME[] variable.


Result.X_mm := Point.X_mm - Frame.X_mm;
Result.Y_mm := Point.Y_mm - Frame.Y_mm;
Result.Z_mm := Point.Z_mm - Frame.Z_mm;

RadiansW := DegreesToRadians(-Frame.W_deg);
CosOfAngleW := COS(RadiansW);
SinOfAngleW := SIN(RadiansW);

RadiansP := DegreesToRadians(-Frame.P_deg);
CosOfAngleP := COS(RadiansP);
SinOfAngleP := SIN(RadiansP);

RadiansR := DegreesToRadians(-Frame.R_deg);
CosOfAngleR := COS(RadiansR);
SinOfAngleR := SIN(RadiansR);

// Fanuc applies rotations WPR as W (around Z), P (around Y), R (around X)
// AROUND Z
temp := Result.X_mm;
Result.X_mm := Result.X_mm * CosOfAngleR - Result.Y_mm * SinOfAngleR;
Result.Y_mm := Result.Y_mm * CosOfAngleR + temp * SinOfAngleR;
// AROUND Y
temp := Result.Z_mm;
Result.Z_mm := Result.Z_mm * CosOfAngleP - Result.X_mm * SinOfAngleP;
Result.X_mm := Result.X_mm * CosOfAngleP + temp * SinOfAngleP;
// AROUND X
temp := Result.Y_mm;
Result.Y_mm := Result.Y_mm * CosOfAngleW - Result.Z_mm * SinOfAngleW;
Result.Z_mm := Result.Z_mm * CosOfAngleW + temp * SinOfAngleW;

Note that DegreesToRadians() is just PI*deg/180.

Run that on your PLC and check that the values in your Result variable match the user frame TCP position reported on the teach pendant.

I haven’t gotten around to calculating the W, P, and R angles of the TCP in user frame yet. Currently I just look at W, P, and R in world frame if I need to know if I’m “pointed at” something. If you get the math to work for W, P, and R, I’d really appreciate if you could share it.


Announcing: Patterns of Ladder Logic Programming

You may have noticed I recently added a new section to this site: Patterns of Ladder Logic Programming. My goal, as usual, is to try to help new ladder logic programmers come up to speed faster and without all the trial and error I had to go through.

The new Patterns section is an attempt to distill ladder logic programs into their component parts. I assume the reader already knows the basic elements of ladder logic programming, such as contacts, coils, timers, counters, and one-shots. The patterns describe ways of combining these elements into larger patterns that you’re likely to see when you look through real programs. In my experience, you can program 80% of the machines out there by combining these patterns in applicable ways.

The Patterns section isn’t complete yet, but I will be adding to it slowly over time. If you think of a pattern that’s blatantly missing, please send me a note so I can include it.

Upgrading your TwinCAT 3 Version

Beckhoff releases new versions of TwinCAT 3 fairly often, and especially since this is a new platform you probably want to stay on top of their new updates for improved stability and new features. Here are some hard-won lessons I wanted to share with you about how to upgrade your production system to the latest TwinCAT 3 release:

Have a Test System

You should definitely have an offline test system for many good reasons. This test system should have the same operating system version as your production system and should ideally be the same hardware, though I realize that’s not always feasible. It could just be an old desktop PC you have sitting around, but that’s better than nothing. TwinCAT 3 is free for non-production use so you have no excuse for not having an offline test system.

Test your upgrade on the Test System First!

Never try upgrading your production system without running through a dry-run on your test system first. Get a copy of the latest TwinCAT solution from your production machine and get it running on your test system. It doesn’t matter if you don’t have I/O attached because the runtime will work just fine anyway. After your perform the upgrade procedure on your offline test machine, make sure you do a thorough test, including a reboot.

Follow These Steps

  1. Stop the machine and make it safe
  2. Put the runtime into Config mode
  3. Uninstall the old version of TwinCAT 3
  4. Also uninstall the old version of the Beckhoff Real-time Ethernet PnP Drivers
  5. Reboot
  6. Install the new version of TwinCAT 3
  7. Reboot
  8. Open the TwinCAT 3 solution
  9. Re-install any custom libraries, if you have any (optional)
  10. Go to the tool for configuring Real-time Ethernet devices, and install the new driver on your EtherCAT cards
  11. Re-link your EtherCAT master to your EtherCAT adapter under I/O, just in case
  12. Build each PLC project (individually, don’t use Rebuild All because it sometimes ignores errors)
  13. Check that you didn’t lose any I/O mapping
  14. Activate boot project on each PLC project
  15. Check under System that it’s configured to start in Run mode (if that’s what you want)
  16. Activate configuration and restart in run mode
  17. Test by doing a reboot

Upgrading the Real-time Ethernet drivers is critical. We were experiencing cases where the EtherCAT bus would just cut out on us, but only on one machine. All of our machines were upgraded to the same version, so we initially thought it must be a hardware issue. It turned out that we weren’t upgrading the Real-time Ethernet driver when we upgraded TwinCAT 3 versions, so this machine had an old version of the driver loaded, and all the other machines had a newer version. After upgrading the driver, the problem went away, so upgrading the driver is a critical step.

If you find that you did lose your I/O mapping, make sure you’ve built all your PLC projects (which generates a TMC file) and then close TwinCAT 3 XAE down and revert your .tsproj (TwinCAT solution project) file back to the original state. Then start again at the step where you open the TwinCAT 3 solution. Now you should find that your I/O mapping is back. That’s because the inputs and outputs of each PLC project are compiled into the TMC file and TwinCAT 3’s system manager links I/O against that. If the file doesn’t exist (or they changed the format during the upgrade) then it’ll just delete the links. However, the links still exist in the original .tsproj file, so creating the TMC file and then reverting the .tsproj file will put everything back to a happy state. This is also a useful trick when you’re moving the project to a new PC and you didn’t bring the .tmc files along for the ride (because they’re quite large).

Edit: Since writing this article, I’ve added a TwinCAT 3 Tutorial to this site as well.

The TwinCAT 3 Review Revisited

I reviewed TwinCAT 3 in February of 2013 and it was a mixed bag. I lauded the amazing performance but warned about the reliability problems. I think it’s time to revisit the topic.

Things have improved greatly. When I wrote that review we had 2 production systems running TwinCAT 3 (the 32-bit version). We’re now up to 5 production systems with another on the way, all running version 3.1.4016.5 (which is a 64-bit version). The product has been more stable with each release. First we tried switching to a Beckhoff industrial PC, but we still experienced two blue screen crashes. We’ve then turned off anti-virus and disabled automatic windows updates. So far I haven’t seen another blue screen on that system, for about two months.

Manually installing windows updates isn’t a big deal, but it’s unfortunate to be running a PC-based control system with no anti-virus. Our industrial PCs are blocked from going online, and each one is behind a firewall that separates it from our corporate network, but it’s still a risk I don’t want to take. Industrial Control vendors continually tell us their products aren’t supported if you run anti-virus, and I don’t see how anyone can make statements like that in this day and age.

The performance of the runtime (ladder logic) and EtherCAT I/O is still absolutely amazing.

While the IDE is much better than the TwinCAT 2 system, the editor is still quite slow (even on a Core-i7 with a solid state drive).

The Scope is now integrated right into the IDE, and I can’t give that tool enough accolades. I recently had to use Rockwell’s integrated scope for ControlLogix 5000 and it’s pitiful in comparison to the TwinCAT 3 scope.

The TwinSAFE safety PLC editor is light years beyond the TwinCAT 2 editor, but it’s still clunky. It particularly sucks when you install a new revision of TwinCAT 3 and it has to upgrade the safety project to whatever new file format it has. We recently did this, then had to add a new 4-input safety card to the design, and it wouldn’t build the safety project because of a collision on the connection ID. It took us a couple hours of fiddling and we eventually had to manually set the connection ID to a valid value to get it to work. On another occasion, after a version upgrade, I had to go in and add missing lines in the safety program save file because it didn’t seem to upgrade the file format properly (I did this by comparing the save file to another one created in the new version).

The process of upgrading to a new TwinCAT 3 version often involves subtle problems. The rather infamous 3.1.4013 version actually broke the persistent variable feature, so if you restarted your controller, all the persistent variables would be lost. They quickly released a fix, but not before we experienced a bit of pain when I tried it on one of our systems. I’m really stunned that a bug this big and this obvious could actually be released. It’s almost as if Beckhoff doesn’t have a dedicated software testing department performing regression tests before new versions are released, but certainly nobody would develop commercial software like this without a software testing department, would they? That’s a frightening thought.

I ended my previous review by saying I couldn’t recommend TwinCAT 3 at this time. I’m prepared to change my tune a bit. I think TwinCAT 3 is now solid enough for a production environment, but I caution that it’s still a little rough around the edges.

Edit: Note that I’ve since added a TwinCAT 3 Tutorial section to this site.

The Ladder Logic/Motion Controller Impedance Mismatch

Motion control is pretty complicated.

There’s been something really bothering me about the “integrated” motion control you find in PLCs these days (notably Allen-Bradley ControlLogix and Beckhoff TwinCAT). Don’t get me wrong, they’re certainly integrated far better than stand-alone motion controllers. Still, it just doesn’t “feel” right when you’re programming motion control from ladder logic.

When I’m programming a cylinder motion in ladder logic, I would typically use a five-rung logic block for each motion (extend/retract). One of the 5 bits is a “command” bit. This is a bit that means “do such-and-such motion now”. Importantly, if I turn that bit off, it means “stop now!” This works well for a cylinder with a valve controlling it because when I turn off power to that valve, the cylinder will stop trying to move. It would be nice if integrated motion was this simple.

It’s interesting to note that manual moves (a.k.a. “jogging”) are usually this simple. You drop a function block on a rung, give it a speed and direction, and when you execute it based on a push-button, the axis jogs in that direction, and when the push-button turns off, it stops jogging. Unfortunately none of the other features are that simple.

All other moves start motion with one function block and require you to stop it with another. The reason it works like this is because motion controllers also support blended moves. That is, I can first start a move to position (5,3) and after it’s moving there I can queue a second move to position (10,1) and it will guide the axes through a curved geometry that takes it arbitrarily close to my first point (based on parameters I give it) and then continue on to the second point without stopping. In fact you can program arbitrarily complex paths and the motion controller will perform them flawlessly. Unfortunately this means that 90% of the motion control logic out there is much more complex than it needs to be.

Aside: in object-oriented programming, such as in Java or .NET, it’s pretty normal to have to interface with a relational database such as MySQL or Microsoft SQL Server. However, when you try to mesh the two worlds of object-oriented programming and relational databases, you typically run into insidious little problems. Programmers call this the Object-relational impedance mismatch. I’m sure that if you added it up, literally billions of dollars have been spent trying to overcome these issues.

My point is that there is a similar Ladder logic-motion control impedance mismatch. The vast majority of PLC-based motion control is simple point-to-point motion. In that case, the ideal interface from ladder would be a single instance of a “go-to” function block with the following parameters:

  • Target Position (X, Y…)
  • Max Velocity
  • Acceleration
  • Deceleration
  • Acceleration Jerk
  • Deceleration Jerk

When the rung-in-condition goes true on this block, the motion control system moves to the target position with the given parameters, and when the rung-in-condition goes false, it stops. Furthermore, we should be able to change any of those parameters in real-time, and the motion controller should do its best to adjust the trajectory and dynamics to keep up. That would be all you need for most applications.

The remaining applications are cases where you need more complex geometries. Typically this is with multi-axis systems where you want to move through a series of intermediate points without stopping, or you want to follow a curved path through 2D or 3D-space. In my opinion, the ideal solution would be a combination of a path editor (where you use an editing tool to define a path, and it’s stored in an array of structures in the PLC) and a “follow path” function block with the following parameters:

  • Path
  • Path Tolerance

When the rung-in-condition is true, it moves forward along that path, and when it turns off, it stops. You could even add a BOOL parameter called Reverse which makes it go backwards along the path. The second parameter, “Path Tolerance” would limit how far off the path it can be before you get a motion error. I think this parameter is a good idea because it (a) allows you to initiate the instruction as long as your position is anywhere along that path, and (b) makes sure you’re not going to initiate some wild move as it tries to get to the first point.

A neat additional function block would be a way to calculate the nearest point on a path from a given position, so you could recover by jogging onto a path and continue on the path after a fault.

Obviously there needs to be a way for the PLC to generate or edit paths dynamically, but that’s hardly a big deal.

Anyway, these are my ideas. For now we’re stuck with this clunky way of writing motion control logic. Hopefully someone’s listening to us poor saps in the trenches! 🙂