Norm: sweet! Let's see how many likes that video can get.
Norm implemented an idea I have been toying with: keeping track of the last valve used. It would not be needed if the existing system does not overshoot, however, when an initial move is initiated (i.e. via a new button-pressed HMI event), knowing the direction of the move would be useful if the sensor is not, as I said before, "wide" enough and the sliding gate overshoots the target truck position, in which case the targeted sensor changes from 1 back to 0.
[
Sidebar]
N.B. That concept of "wide" enough is a combination of the detection extent of the sensor plus the dynamics of the pneumatic cylinder (friction, stiction, hose and cylinder volumes, etc.).
[/Sidebar]
There could be a special case added, representing an overshoot, where none of the sensors are 1 but the target equals the index position. In that case, the last valve active indicates the direction of the overshoot, and could be checked to automate activation of the opposite valve to push the system back a bit. However, in order to not overshoot again in that opposite direction, when in this "overshoot mode," use some form of PWM to move more slowly.
@PeterN et al.: yes, I freely accept the eye roll; yes it's a cobb-job/Rube-Goldberg/Heath-Robinson, but the employer/customer has decided it is cheaper to pay for a software fix than a physical fix for any system deficiencies, and I for one am happy for OP for that.
Caveats
- Of course, this could lead to cycling
- The PWM could shorten the valve life, although if that motivated a fix to the problem in the physical design, is that such a bad result ?
Anyway, here is an attempt at coding that approach; it is an add-on that would follow the code from
Post #7.
Code:
(*******************************************************************)
(* Calculate direction to bump index on anticipated next overshoot *)
(*******************************************************************)
IF VALVE_A THEN bumpit := +1; (* Valve A increases index *)
ELSIF VALVE_B THEN bumpit := -1; (* Valve B decreases index *)
ELSE bumpit := 0;
(*******************************************************)
(* Detect overshoot: *)
(* - None of position sensors are 1 *)
(* - Neither valve is 1 *)
(* - Last detected position (index) is target position *)
(*******************************************************)
IF NOT (position_0 OR position_25 OR position_50
OR position_75 OR position_100 OR VALVE_A OR VALVE_B)
AND (index = target) THEN
(* Overshoot detected *)
(* *)
(* i. Indicate an inferred overshoot position as the smallest *)
(* non-zero offset, i.e. 1, and less than the 25 between sensors *)
(* *)
(* N.B. this makes the IF expression above a one-shot, i.e. true *)
(* for one scan only *)
index := index + bumpit; (* Model the assumed overshoot *)
(* ii. Start the pseudo-PWM counter *)
(* ii.a. Does nothing i.e. is disabled if less than zero *)
(* ii.b. Counts from 0 to 999 repeating *)
(* ii.c. Valves will be off when recovering from overshoot and *)
(* counter is greater than 499 *)
(* *)
(* N.B. the 999 and 499 values are guesses here and will need *)
(* to be tuned for the physical pneumatic system; the *)
(* values use the scans a poor man's clock and *)
(* assume the mean scan time is small - of order 1ms; *)
(* it may make sense to implement those values as *)
(* variables/global tags with initial estimates, but *)
( adjustable from the HMI *)
iPWM := 0;
ELSIF (index = target) THEN
iPWM := -1; (* Disable pseudo-PWM *)
END_IF;
(* Pseudo-PWM - override open valve for latter part of cycle *)
(* N.B. there is an off-by-1 error here: on the scan when *)
(* the overshoot is detected, the valve bits will both *)
(* be 0 so these IF expressions will evaluate to FALSE *)
IF VALVE_A AND ((target - index) < 2) AND (iPWM > 499) THEN VALVE_A := 0;
IF VALVE_B AND ((index - target) < 2) AND (iPWM > 499) THEN VALVE_B := 0;
IF iPWM > -1 THEN iPWM := iPWM + 1; (* Increment iPWM when active *)
IF iPWM > 999 THEN iPWM := 0; (* Reset iPWM to 0 from 1000 *)
N.B. the [(target - index) < 2] and [(index - target) < 2] tests could be replaced by [target = (index*2)] and [index = (target*2)] in the bit-wise @parky approach as extended by Norm, where index and target are the integers containing the bits. We may need to test for non-zero values, although target should never have all of its bits be zero.
Disclaimer
Norm raises an excellent point about safety that must not be ignored; I will do the same and say this is
only a conceptual approach to solve the smaller problem i.e. overshoot that may not even be present; whether it is safe, or even works at all, is up to the OP and/or other local engineers to determine.