Repository navigation
Behaviour Gripper Station Reset
Published from
_docs/reference/gripper-station-reset.md— edit it there, not here. Changes made in the wiki are overwritten on the next sync.
Hand-written. Written from the shipped ST in Beckhoff/TwinCAT_1/PLC/PLC_1/Modules/13 FG_03/FG_03.TcPOU and
Beckhoff/TwinCAT_1/PLC/PLC_1/Modules/15 FG_05/FG_05.TcPOU. Where those disagree with anything here, they win —
the PLC is master for behaviour.
This is the station-level companion to transport-behaviour.md, which owns the same subject
for FG_Transport. The rules it establishes are not restated here, they are applied:
a reset terminates on time and on its own cylinders, and readiness is derived, never
remembered.
FB_DoubleSolenoidFeedback reports where the cylinder is, not what it is holding. Worse,
jaws that close on nothing reach their extended limit sooner than jaws that close on a
part — so .Extended is not a weak grip witness, it is a misleading one. Until the grip
sensors arrived, neither group could tell a successful pick from a miss, and neither reset
could tell an empty gripper from a full one.
So both Resetting methods homed the axes and then opened the grippers unconditionally.
The obvious cost was a dropped part. The expensive one was FG_03-specific:
An E-Stop inside the handover window —
Executesteps 80..90 — leaves both grippers holding the same part. The old reset raised both arms, rotated R1, and traversed the two arms apart while they were still clamped on it.
That is the failure the sensors exist to remove. A dropped part is a scrapped part; two arms pulling against each other is a damaged machine.
| Signal | PLC path | Twin mechanism |
|---|---|---|
B_Detect1 |
MAIN.Machine.FG_03.B_Detect1 |
SignalBinary + GripSensor.cs → Y_Gripper1/Gripper.IsPicked
|
B_Detect2 |
MAIN.Machine.FG_03.B_Detect2 |
SignalBinary + GripSensor.cs → Y_Gripper2/Gripper.IsPicked
|
B_Detect |
MAIN.Machine.FG_05.B_Detect |
SignalBinary + GripSensor.cs → Y_Gripper/Gripper.IsPicked
|
All three are FB_DigitalSensor in PLC_1 and FB_SensorBinary in SIM_1. Channels:
FG_03_EL1809_1 15 and 16 — which fills that terminal — and FG_05_EL1809_1 14.
All three are the optimistic witness, and that is worth knowing when diagnosing. FG_03's
two started as SensorBinary beam sensors and were changed to GripSensor on 2026-08-21 to
match FG_05. IsPicked is the OC gripper component's own record of what it picked up, not an
independent look into the jaws, so none of the three can report a part the gripper has
silently lost — only one it never took or never let go of. On real hardware these would be
beams. The PLC code is written the same either way, but a diagnosis that leans on "the sensor
would have seen it" is leaning on the wrong half of the pair.
B_Detect is not B_Part. B_Part is on the feed and says a cap has arrived at the
pick position; once the gripper lifts one away it is reporting the next cap. B_Detect is
in the jaws and says this unit is carrying one. Two questions, adjacent names, and the
FG_05 block header says so at the declaration for exactly that reason.
Resetting step 0: _Held1 := B_Detect1.Active; // and never read again
_Held2 := B_Detect2.Active;
A live sensor read inside a waiting step would break the reset contract just as surely as
waiting on the index unit did in FG_Transport: a part that never clears the beam would
park the group in Resetting for ever, and every station behind it with it. A snapshot
cannot fail to arrive. Every step in every route then waits on the module's own cylinders,
bounded by the ExtendTime/RetractTime budgets set in Initialize, so a jam alarms out of
the state instead of sitting in it.
The one place a sensor is read after step 0 is immediately after a gripper reports
Retracted — one sample, to decide whether to latch a fault. It is never a wait condition.
A reset does not undo the cycle and it does not finish it. It moves whatever is held to a position it can be held at indefinitely, and the next Start picks the cycle up from there.
Every earlier version of this page and of FG_03.TcPOU had arm 1 wrong. Confirmed against the
twin's Axis components, which is the only place it is written down:
| Symbol | Role | Unity _direction
|
factor | max means |
|---|---|---|---|---|
Y_AxisX1 |
arm 1 vertical | 1 = Y | +0.001 |
raised |
Y_AxisY1 |
arm 1 horizontal | 2 = Z | +0.001 |
at the transfer position |
Y_AxisX2 |
arm 2 horizontal | 0 = X | +0.001 |
at the transfer position |
Y_AxisY2 |
arm 2 vertical | 1 = Y | −0.001 |
lowered — the sign is inverted |
Arm 1 never lowers to place. X1 rises once at step 60 and stays at max for the whole
cycle. The 180° flip on R1 is what carries the part down into slot 2. Reading the sequence
expecting a lowering step is what makes it look as though one is missing.
R1 turns only at the transfer position. Over slot 2 the sweep goes straight through
whatever is in the slot, which is why step 140 traverses back before step 150 turns.
20 Y2.Extend arm 2 down into slot 1
30 G2.Extend grip + B_Detect2
40 Y2.Retract lift -> SAFE A
50 X2.Extend arm 2 across to the transfer
60 X1.Extend arm 1 up (first cycle only; it stays up)
70 Y1.Extend arm 1 across to the transfer
80 G1.Extend grip - both hold + B_Detect1
90 G2.Retract arm 2 lets go + NOT B_Detect2 -> SAFE B
100 X2.Retract arm 2 home
110 R1.Extend THE FLIP - and the placing move
120 Y1.Retract across to slot 2
130 G1.Retract release + NOT B_Detect1
140 Y1.Extend clear of the placed part
150 R1.Retract rotate home
160 Y1.Retract home over slot 2
Arm 2 goes home at 100 before the rotate rather than in parallel with it, so the rotating part can never meet arm 2 standing at the transfer position.
| State | Left by | Resumes at | |
|---|---|---|---|
| SAFE A |
X2 min, Y2 min, part in gripper 2 |
step 40 | step 50 |
| SAFE B |
X1 max, Y1 max, R1 min, part in gripper 1 |
step 90 | step 100 |
Both are states the cycle already rests at, so a resume is the cycle simply carrying on and nothing had to be inserted to make either position real.
Arm 1's safe position is not its home. Home is Y1 min, over slot 2 — which is the place
position. A part parked there hovers exactly where it is about to be put down, and is still the
wrong way up. The transfer position is where arm 1 legitimately stands holding a part.
| Step | |
|---|---|
0 |
sample both sensors; both held → 10, otherwise → 100
|
10 |
both hold. Arm 1 has priority: arm 2 lets go where the arms stand, as step 90 does. → 100. If arm 2 will not let go: fault 1, → 900, nothing moves at all
|
100 |
both arms up — X1.Extend for arm 1, Y2.Retract for arm 2 |
110 |
arm 1 across to the transfer position — SAFE B, and the only place R1 may turn |
120 |
R1 home |
130 |
arm 2 home — SAFE A |
140 |
arm 1 stays at the transfer position if carrying; goes home over slot 2 if not |
150 |
open the empty grippers, and only those |
Step 110 runs whether or not anything is held, because step 120 needs to turn and the transfer position is the only place it may. Traversing there with a rotated part is step 120 in reverse; turning there with a part held is step 110's own move. Both are moves the cycle already makes.
No gripper holding a part is ever opened — and, since §9, it is not merely left alone
either: CyclicLogic holds it closed. Step 150 waits only on the cylinders it actually drove.
Execute step 0 derives the entry point from the two grip sensors and from nothing else — no
stored step index, no "was I interrupted" flag. This is transport-behaviour.md §1 applied to a
sequence rather than to an interface.
B_Detect1 -> 100 B_Detect2 -> 50 neither -> 10
It also checks the arm is at its safe position — X1 and Y1 both extended for arm 1,
X2 and Y2 both retracted for arm 2. Note that those are opposite tests for the same
physical idea, which is the axis-naming trap again. A part held anywhere else means the entry
point would be a guess, so it raises fault 5 and aborts rather than moving.
HMI.Transfer.ResumePoint publishes the same derivation every scan, computed from the live
sensors in PublishStatus — not copied from Resetting's _Held1/_Held2, which are that
state's private snapshot and stale everywhere else.
The pallet stays clamped by Index03 for the whole interruption, so a resumed cycle finishes the
job and Index03 gets its OperationDone as usual. Index03's own operation timeout is the
limit — if the operator takes longer than that to Clear, Reset and Start, Index03 raises its
fault 3 and the station has to be recovered from the transport side too.
A part in a gripper used to be an illegal starting condition and is now a legal one, so the "refuse to start dirty" net is gone and the sensors have to be right. Fault 5 is what is left of it: it no longer asks whether a part is held, only whether it is held somewhere the cycle can be resumed from.
One gripper, so one safe position and one resume point.
SAFE X MAX, Z1 min, Z2 min, R min, cap in the gripper
This one the cycle really does pass through. It is exactly the state Execute step 90
leaves — Z2 up from step 80, Z1 never lowered yet, R not yet turned, and the traverse across to
the pallet just finished. So the resume entry is step 100 and nothing had to be inserted to
make the position real, unlike FG_03's arm 1.
| Step | Resetting |
|---|---|
0 |
Y_CapsSourceStopper.Extend — block the feed; disable the camera; sample B_Detect into _Held
|
10 |
both vertical strokes up |
20 |
R home always; X to max if carrying, min if empty |
30 |
wait for the stopper .Extended; open the gripper only if it is empty |
The feed is blocked at step 0, not at step 30. The stopper's de-energised position no longer
blocks — extended does — and DriveBunker keeps the feed running right through Resetting and
Idle, so commanding it at the end of the reset would let caps spill past the pick position for
the whole of the axis homing. The command latches, so step 30 only waits on .Extended and the
state still terminates on this unit's own travel budget.
Execute step 0 then resumes at 100 if a cap is held and the unit is at X max with both Z
retracted, and raises fault 4 if a cap is held anywhere else.
A resume skips the camera entirely, and that is correct rather than a shortcut. The read identifies the assembly, it happened at steps 20..50 before the cap was ever picked, and it came back OK — a NOK aborts before the pick. The pallet has not moved since. Re-reading would ask a question that has already been answered.
Worth recording, because every obvious answer is wrong:
| Back on the feed | The pick position is not empty. The stopper is held, so the next cap advanced into place the moment this one was lifted. Releasing there jams the feed. |
| Onto the part | A reset does not deliver product, and finishing the placement means knowing Index05 still has a pallet clamped underneath — the "reset waits on another unit" mistake. |
| Open in the air | What the original code did. The cap falls. |
| Keep it and stop | What the version before this did: park, raise code 4, make an operator fish the cap out. Safe, but it turned every E-Stop into a trip into the cell for a cap that was never in danger. |
Parking at the safe position costs none of that. The cap is held clear over the pallet, the unit can stand there indefinitely, and the placement it was three moves away from is still three moves away.
This is what makes the reset snapshot worth trusting. Each check waits on the cylinder limit
first, then on the sensor, bounded by its own TON — a separate timer per step, because
adjacent confirming steps (FG_03's 80 and 90) would never let a shared timer's IN drop.
The timer is armed only once the cylinder is at its limit: the stroke already has the
cylinder's own travel timeout, and T#500MS covers only the part settling in or falling out.
| Group | Step | Confirms | Fault |
|---|---|---|---|
| FG_03 | 0 | both jaws empty before starting | 5 |
| FG_03 | 30 | pick caught the part | 2 |
| FG_03 | 80 | arm 1 has it before arm 2 lets go | 3 |
| FG_03 | 90 | the part left arm 2's jaws — also what makes arm 2's traverse home safe | 4 |
| FG_03 | 130 | the part left arm 1's jaws | 4 |
| FG_05 | 0 | jaws empty before starting | 4 |
| FG_05 | 70 | the cap is in the jaws | 5 |
| FG_05 | 120 | the cap left the jaws | 6 |
FG_03 step 80 is the one that matters most. The module header always claimed the part is
"never held by nobody", and until now that claim rested on Y_Gripper1.Extended — a bit that
is true whether or not the jaws caught anything. Step 90 earns its place too: arm 2 traverses
away at step 100, so a part that never left its jaws would be dragged straight off arm 1.
Raised with AbortImmediate() from Execute, latched without aborting from Resetting, and
cleared in Clearing — the fault is cleared by the act of clearing, not by the condition
going away, because a part jammed in a gripper is still jammed after the sensor stops being
interesting.
FG_03, HMI.Transfer.FaultCode, ST_FG_03_Transfer_Status — the group's first fault code:
| Code | Meaning |
|---|---|
| 1 |
Resetting could not separate the two grippers at the handover; nothing was moved |
| 2 | pick failed |
| 3 | handover failed |
| 4 | release failed inside Execute
|
| 5 | started with a part held by an arm that is not at its safe position |
ResumePoint is published alongside. It is not a fault — it is which sub-sequence the next
Start will enter (1 full cycle, 2 handover, 3 place), derived every scan from the live
sensors so the panel shows what pressing Start would do.
FG_05, HMI.Capping.FaultCode, ST_FG_05_Capping_Status — 1..3 were already the camera's
and are unchanged:
| Code | Meaning |
|---|---|
| 4 | started with a cap held by a unit that is not at its safe position |
| 5 | pick failed |
| 6 | release failed |
-
— closed 2026-08-21. It was a beam sensor parented toB_Detect1sits above the R1 rotary jointAxis_Y1, above the rotary joint, so it would have gone blind in one of the two R1 orientations. Replacing it withGripSensorremoved the problem rather than fixing the parenting:IsPickedis read off theGrippercomponent itself and does not care where the sensor node hangs or which way the arm is turned. -
— closed 2026-08-22. The device was refactored in the twin and EXTENDED now blocks the path; both PLC call sites —Y_CapsSourceStopper's polarity is an assumptionExecutestep 10 andResettingstep 30 — areExtend()and both waits are on.Extended. This is the opposite sense to the nine transport stoppers, and deliberately so; seefg-05-behaviour.md.
Observed 2026-08-21: FG_03 arm 1 let go of its part in the middle of a reset, in code that
never commands Y_Gripper1. The step logic was not at fault; the premise underneath it was.
Y_Gripper1, Y_Gripper2 and FG_05's Y_Gripper each have their OC.Components.Cylinder
wired to the child OC.MaterialFlow.Gripper:
| Cylinder event | Target |
|---|---|
OnLimitMaxEvent |
Gripper.Pick(bool) |
OnLimitMinEvent |
Gripper.Place(bool) |
OnActiveChanged |
Gripper.Place(bool) |
GripperBase.Place(bool place) is if (place) Place(); else Pick(); — so a TRUE on any of
those release-side events drops the payload. And Cylinder.UpdateState() sets
_isActive.Value = !Math.FastApproximately(_target.Value, _value.Value, 1e-1f);— IsActive means the cylinder is moving. So OnActiveChanged(TRUE) → Place() → the part
is released the instant the gripper cylinder travels at all.
The payload is held only while that cylinder is at its max limit and standing still.
Every comment in both modules used to justify no-motion Aborting and Stopping, and an
uncommanded gripper in Resetting, with "a de-energised double solenoid holds its position".
That is correct for the real valve and wrong for the twin: nothing about the twin's grip
survives the cylinder being allowed to drift. Leaving a full gripper uncommanded across
Clearing and Resetting was enough to lose the part.
Both modules re-assert the grip in CyclicLogic, before SUPER^.CyclicLogic():
IF _CurrentState <> E_PMLState.ePMLState_Execute THEN
IF B_Detect1.Active THEN Y_Gripper1.Extend(); END_IF
IF B_Detect2.Active THEN Y_Gripper2.Extend(); END_IF
END_IF
-
Before
SUPER^is what makes it an interlock rather than a suggestion — it overwrites whatever a state method asked for on the previous scan. Same shape as the lift carriage interlock intransport-behaviour.md§3. -
HMICommunicationruns insideSUPER^, so it still gets the last word and an operator can jog a gripper open in Manual. -
Executeis excluded because it is the only state allowed to put a part down. - It commands no motion: the jaws are already closed, so
Extend()on them is a no-op at the valve. It only refuses to let anything else open them.
Never leave a grip uncommanded and expect it to hold. If a part must stay gripped, command the grip. This is a twin-versus-hardware divergence, so it will not show up in reasoning about the real valve — only in the running cell.
Generated from DT_PSA_OPCV by _workflow/tools/build_wiki.py — 39 pages. Edit the source there; changes made here are overwritten.
Getting started
- Home
- Setup
- Using-The-Application
- How-It-Works
- PLC-Connectivity
- Engineering-Workflow
- Machine-Description
- Glossary
The machine
- Machine
- Machine-PLC-Symbols
- Machine-Provenance
- Machine-FG_01
- Machine-FG_02
- Machine-FG_03
- Machine-FG_04
- Machine-FG_05
- Machine-FG_System
- Machine-FG_Transport
Behaviour
- Behaviour-FG_01
- Behaviour-FG_02
- Behaviour-FG_03
- Behaviour-FG_04
- Behaviour-FG_05
- Behaviour-FG_System-Safety
- Behaviour-Gripper-Station-Reset
- Behaviour-State-Lamp
- Behaviour-Transport
Device types
- Device-Button
- Device-ControlBunker
- Device-Cylinder
- Device-DataReader
- Device-DrivePosition
- Device-DriveSimple
- Device-Lamp
- Device-LinkByte
- Device-Lock
- Device-PanelSampler
- Device-SensorBinary
- Device-SignalBinary
Control platforms
- Beckhoff TwinCAT 3
- Siemens TIA Portal — no wiki yet