Simple XY movements using FWD RE and stop commands on each axis

I used SET/RESET coil logic because when plc enters run mode, and after home routine motor are still. When start is activated x fwd coil is set and it runs until is reset by counter and Y is set to fwd, then again that same Y is reset when Y counter is reached and X REW is set etc...... I tough maybe to use same logic, only not to set/reset coils for motion forward or reverse, but for taking data out from array/table for each next step.
 
Not that it matters as you seem to be on your way.
This is loosley based on the glue spray I did some years ago, the only real difference is I had an extra step the system I did only sprayed when actually on the product & only when in X movement, also I had an offset for spraying i.e. it started from XY of 0, moved to a position from home before spraying for example home was X=0, Y=0
Start Move X = 20 Y = 10
Then move X to 300 with spray on
When X = 100 then move Y 150 (no spray while Y moving)
& so on until Y last move.
There is no need to worry about overshoot for example if your x home position is 0 then if it is commanded to go to 300, but it goes to 305 then when it goes back to 0 it will, even if it goes back to -5, next x command to 300 regardless if it is currently -5 at the start.
I have not shown the logic for the drives but it is balsically a compare of the sequence variable i.e mov x > is if SEQ = 1, or 3, Y is 2,4,5
This all assumes you do not want to move in both directions at the same time as I expect for a polisher it would not.

polisher.png
 
Here is a working prototype; the final attachment is a PDF of the whole program, while these images show the basic approach. I used RSLogix and a MicroLogix 1100, so the detail of the array (or table) handling and syntax in Automation Studio will differ, but the basic concepts and ladder logic should be fairly portable.

I broke the program into four routines

  • The main program (third image), which performs some initialization and calls the other routines in order
  • The DEADBANDS routine (first image), which evaluates the current axes' positions relative the the current step's target positions and deadbands.
  • The COMMANDING routine (second image), which evaluates the result of the DEADBANDS routing and uses that to command the axes to move or not
  • The STEP_LOGIC routine (third image), which controls bot the transitions between steps, as well as the end of a sequence of steps.
.
.
.

The first Rung 0000 in the first image is the key, where the X and Y targets for the current step (which is the accumulator of the counter shown later) from the N9 and N10 data files (equivalent of arrays or tables) are used to calculate the offsets from the target positions to the current positions. Once those offsets are calculated, all other logic is more or less step-agnostic i.e. object oriented i.e. we have separated step-specific data and code, and the rest of the code can focus on commanding the axes to move or not without having the specifics (targets) of the current step.

Those offsets are then used to determine if the current positions are outside the target±5 deadbands, or inside the deadbands and either above or below the targets, and assign bit values for all of those cases.
xy_motionDB.png
.
.
.
The next image shows the logic to command the axes' motors to run in forward, or in reverse, or not at all, based on the bits assigned in the previous section above. The top window in the image shows the logic using my preferred Start/Stop Circuit pattern; the bottom window has equivalent logic using the Set/Reset pattern, which the OP has chosen; only one of those would be used in a program.
xy_motionCMD.png
.
.
.
This final image shows a notional approach to handling transitions between steps. The key is the value of integer FINAL_STEP.

  • The step numbers run from 0 to FINAL_STEP
  • FINAL_STEP is used as the preset (C5:0.PRE) for the counter
  • The commanding logic is inactive (see bit ACTIVE) when FINAL_STEP is 0.
  • As each step completes (current positions reach or pass target positions), all FWD and REV command bits will become 0
  • All command bits being 0 writes a 1 to the STEP_DONE bit (Rung 0000 of STEP_LOGIC)
  • The STEP_DONE bit being 1 starts a 500ms timer (Rung 0001 of STEP_LOGIC) to allow the axis to coast to a stop (inertia + friction)
  • When the timer expires, its DONE bit (T4:0/DN) increments the counter (bottom branch of Rung 0003 of STEP_LOGIC)
    • which results in new offsets being calculated for the offsets (see first image above),
      • and presumably starts a new round of axes' movement
  • There is some additional logic to handle the final step
    • Basically, when the final step completes (the value of the counter DONE bit, C5:0/DN and the STEP_DONE bit are both 1):
      • the value of FINAL_STEP is reset to 0,
      • which returns the value of bit ACTIVE back to 0
        • until FINAL_STEP becomes positive again.
How FINAL_STEP is assigned a positive value, either from internal PLC logic or an HMI is TBD; I only wanted to show how the Object-Oriented approach could work.

N.B. there is a bug in this code: if the deadband of step N overlaps with the deadband of the previous step (N-1), then the logic might get stuck in step N if no motion command bits are triggered on the first scan cycle in step 1, so the STEP_DONE value would still be 1, and the timer would not reset, so the counter would not see a rising edge indicating step N is done.
xy_motionSTEP.png
 
Here is how the per-step data are arranged in that prototype. It uses one array for X and another array for Y instead of a multi-column table or multi-dimensional array.
xy_motionSTEPDATA.png
 
Thanks both for this wide explanation and effort to do it!

In basic my idea was more like parky was show, so without taking care for dead band, as I would always go to next location when reached is equal or greater than target (or equal or less if it was reverse move). And since I am always alternating X and Y, so either move is X than Y, than -X etc....that it makes even simpler in code and array.

I will give it a try, as soon as i got my demo plc back.
 
Yes in your case that's probably the best way to do it.
I do not have the luxury to know what your process really involves but as I hace done something similar it makes sense.
The last code I posted uses an array (well did not use an array as it was so simple) but the "Recipe" as such is based on the following:
Var 1 = X movement (only need one as it is always the same unless you have different x movements i.e. Part height)
Var 2 = Y max (this is the total accumulated Y movement (part width)
Var 3 = Y increments (Assumes polisher diameter plus perhaps a bit of overlap i.e. Y movement every stroke).
so only need 3 parameters, it is simple to select a recipe either stored on the HMI or in PLC Retentive memory, then select recipe or pattern as required & load it into the running recipe variables.
There are many ways to achieve the above using different ideas, you could do some simple calculations i.e. just have the max x/y i.e. size of the part & perhaps a constant of the polisher dia, then calculate the number of X strokes & Y increments (Again I assume there will be some overlap of the X/Y direction to ensure the polisher does not miss any part.
Good luck.
 
That is it. As you mention, first I did with some rungs and pattern in it, works as expected and way better than original (original was X/Y end switches and bunch of relay for logic). And now since there are more possible pattern lines I have decided to not be limited to one, but to add more and add possibility that operator can adjust them. So to go with same logic it will get too complicated, that is why I tough array is way to go.

If you are interested, here is detail how I add "encoder" to the system. I tough to add classic incremental encoder on motor shaft, but since motors have mechanical brake (braked while stopped, losen when activated) so it was too complicated and I have decided to use some machining of parts and add them to gearbox.

IMG_20231022_081953.jpg IMG_20231026_180920.jpg
 
Last edited:
That is what I did, I used gear wheels bolted to the drive shaft, so the proximities picked up the teeth (actually, I think they were off some old 5 gear bicicle wheels, just seperated the ones I wanted).
The more teeth the better the accuracy, however, for a high pulse rate would have needed to use interrupts, I did actually use the high speed counter functions in the PLC, once configured it turns on the first coulple of inputs into a high speed encoder counter, although the speed of the pulses was not fast enough to need the interrupts.
If you need some sort of special pattern then a large array would be needed something like DR: (Brian) suggested, it would be pretty simple by segmenting the array into X/Y movements in blocks or create a UDT (User datatype) or SDT it is sometimes called
i.e.
UDT:
Polisher_Move of XY_Movement

Then the UDT would be something like
XY_Movement.X_Pos Array(0..19) *total of 20 moves*
XY_Movement.Y_Pos Array(0..19)

Then to use them it is a matter of addressing the variables
like Polisher_Move.X_Pos[0]
By using a pointer you can then indirectly call each one

Pointer = integer
Pointer = 0 (set indirect pointer to 0)
Then load the x movement
Polisher_Move.X_Pos[Pointer]
Then when you want the next movement increment the pointer
INC Pointer
Polisher_Move.X_Pos[Pointer]
If you wish then get clever by putting another field in the UDT like
Number_Operations this is the times the polisher does the above operations.
So at the end of every cycle compare the pointer to the Polisher_Move.Number_Operations
to see if it has finished.
 
Here is one using a UDT (SDT as some call it), Index addressing is used to keep the code small, The way it works, on a start the code loads the variables with the required start values, then it indirectly looks at the array x/y positions & moves in the appropriate direction (I have put a timer at the end of the move purely to give the drives time to stop before next operation). then it increments the indirect pointer & goes back for the next move, if it sees both XY positions as 0 then it assumes that is the last move, or if it gets to the last array (50) in my case then it goes back to the start & stops.
It can move in both X&Y directions at the same time i.e. diagonally, however, to do a simple X + direction then Y+ direction then X- direction & so on then each XY step needs to be configured like this:
1: X = 100, Y = 0 Run polisher X + only
2: X = 100, Y= 25 Run polisher Y + only
3: X = 0, Y = 25 Run polisher X- only
4: X = 0, Y = 50 Run Polisher Y + only
5: X = 100, Y = 50 Run polisher X + only
6: X = 100, Y = 75 Run Polisher Y + Only
7: X = 0, Y = 75 Run Polisher X - Only
8: X = 0, Y = 0 Run polisher back to start.
 
I am two weeks off but this is similar to the thing i want to try. Unitronics has table index table function so i can call it either as pointer or index. So depending of pattern i want to use, operator loads pattern and plc reads pattern data from table one step ahead of move. So on start it reads out data for x and y and moves to desi red position, when it reaches position it reads next row and moves there etc, untill end and either is back on 0 and goes again for number of desired steps or goes to next step. So basicly every time x or y position is reached by using compare block it stopa movement and reads another row which fills in data for next step.
 
I have been in some other things and did come back to this again today. Anyway, I did tough about array and pointer and came up that I will spend much of code doing just database. Since user want to do this:
-mark start position;
_mark end position;
-enter shift distance ;
-enter number of cyclus;
:press start......
They will mark start and end position with manual jogging X,Y to desired start position, press START POSITION, then manual jog to end and press END POSITION) that was already implemented.
Next there is shift, for how much X or Y will be shifted on each step, depending on sequence.
When started, first, polisher is moving by complete X movement from Xmin to Xmax and in between shifting for Y shift value, and then, after END position is reached is going back to start by moving complete Y from Ymax to Ymin and in between shifting for X shift. And they want to be simple as it. So I decided to take some idea from you, but did no use data in table, but just using Ymin, Ymax, Xmin, Xmax positions. This is without "logic" part (starting hydraulics, homing, etc.....)
 
Sounds promising, I assumed you would have a sort of recipe but did not think about a teach system so a good idea, however, I would have put them into a recipe so they could be recalled, that would be simple to implement if required, simply at the end of the teach cycle store the X/Y movements into the recipe for later use.
 
[attachment polisher.pdf]

I think there is a problem with this code, and it may be because of a misunderstanding about the PLC scan cycle works.

The point is that the a PLC scan cycle evaluates all the rungs in the program in order, and then the next PLC scan cycle evaluates them all again.

So if we look at rungs 2-6 in your program:

Rung 2 transitions from sequence #1 to sequence #2 when X_ENC ≥ X_max:
Screenshot 2023-12-02 at 05-43-08 polisher.pdf.png
Rungs 3-5 transition through sequences #2, #3, #4, and back to #1 based on various conditions:
Screenshot 2023-12-02 at 05-44-03 polisher.pdf.png
Rung 6 transitions from sequence #1 to sequence #5 when several conditions are met, but the first condition is when X_ENC ≥ X_max:
Screenshot 2023-12-02 at 05-43-41 polisher.pdf.png
However, that last transition on Rung 6 will never happen, because on the PLC scan cycle when sequence == #1 and X_ENC ≥ X_max, when the scan cycle evaluates Rung 2, which happens before evaluation Rung 6, Rung 2 will transition from sequence #1 to sequence #2.

Then later in that same scan cycle when evaluating Rung 6, the sequence value will no longer be #1, so the first comparison sequence == #1 on Rung 6 will evaluate to False, so even if the second comparison X_ENC ≥ X_max and all the subsequenc comparisons on Rung 6 evaluate to True, the rung state will already be False, and so the final Store instruction will never assign a value of #5 to the sequence tag.

There is a similar problem in Rungs 7-11: sequence will never transition from #5 to #1 when any scan cycle evaluates Rung 11 because it will have already transitioned from #5 to #6 earlier on the same scan cycle when evaluating Rung 7.

PLCs are very simple, and we have to think simply to program them. The only thing worse than a PLC not doing what you want it to do, is when it does exactly what you tell it to do.

I suspect that why this was coded like this is that our protagonist @SonusI has experience in procedural programming (C/C++, Python, etc.), and because Rung 6 is immediately after Rung 5, they had a mental model of Rung 6 being evaluated after Rung 5 with no other rungs evaluated in between, and did not consider that Rung 2 would be evaluated, many times, after the Rung 5 evaluation that transitioned sequence back to #1. The hope was probably that Rungs 2-6 could be re-used to repeat the X cycle after reaching sequence #5. That may or may not be the reason this happenen, but either way the code as written is unlikely to work.

One solution may be quite simple: move current Rung 6 to just before Rung 2, and current Rung 11 to just before Rung 7. That way, when the X cycle is complete, whether the sequence goes from #1 either to #2 or to #5, and from #5 either to #6 or to #1, depends on the current Y position.

But a better solution would be to refactor the sequence numbers to take into account whether Y has been increasing or decreasing. The Rungs 2-6 and 8-10 can still be re-used, but they would have two sequence comparisons ORed (i.e. in parallel) as an initial compound condition.
 
drbitboy has such an eye to catch this! Thanks aaaaaa lot for point it out, I would probably get myself wondered and figured it out after some time debugging and watching variables during real time run......

Anyway, as you pointed, i could put both rung 6 and rung 11 before other relevant rungs in that part, as you suggested. And yes, i just did put them one after other based on sequence number. And yes rung 6 should be evaluated in front of all as it is more important condition in system, because if conditions in rung 6 is meet, that means end is reached. Same is with rung 11, because when rung 11 conditions are meet, that means stop position is reached, and that has priority over movements, as than the system needs to stop, or continue another cycle.
 
I suspect that why this was coded like this is that our protagonist @SonusI has experience in procedural programming (C/C++, Python, etc.), and because Rung 6 is immediately after Rung 5, they had a mental model of Rung 6 being evaluated after Rung 5 with no other rungs evaluated in between, and did not consider that Rung 2 would be evaluated, many times, after the Rung 5 evaluation that transitioned sequence back to #1. The hope was probably that Rungs 2-6 could be re-used to repeat the X cycle after reaching sequence #5. That may or may not be the reason this happenen, but either way the code as written is unlikely to work.

A bit truth is there too I must admit that. What you mean by saying Rung 6 is immediately after Rung 5, and they had mental model of Rung6? Are you refering to thing i did not follow logic part or system, that i just did follow rung one after other by my sequence numbers?

X cycle is not repeated in cycles 2-6 after cycle 5 is reached. Than, I move X in cycles 7-10, because in that part X is now moved as shift, and Y is moved from Ymin to Ymax, while in rungs 2-6 X is moved from Xmin to Xmax and Y is moved just as shift.
 

Similar Topics

Hello again..trying something on an existing poorly written program and just wanted to double check something system is an A-B MicroLogix 1200 In...
Replies
5
Views
169
Hi all, Writng a FB in ST on Beckhoff TC for a pulser which turns on and off on a cycle, is paused by turning bControlInput to FALSE, but resumes...
Replies
6
Views
256
I'm trying to build my Classic Step 7 programming skills this weekend. I get stuck on little things that are not covered in YouTube tutorials. I'm...
Replies
7
Views
319
I have a program that does a 7 second "scan" sensor calibration routine whenever a setting (setting is called assistance level or "AL" and ranges...
Replies
3
Views
213
Hi all, I have a simple question that I have overcomplicated and gotten stuck on. I have a variable, we can call it "light" that I need to stay...
Replies
4
Views
318
Back
Top Bottom