Help With Inspect and Reject...

Eric Nelson

Lifetime Supporting Member + Moderator
Join Date
Apr 2002
Location
Randolph, NJ
Posts
4,346
I often have applications where I need to inspect products on a conveyor belt, and reject the failed ones downstream. Seems simple enough, but I still run into issues... o_O

The inspection system consists of products moving down a conveyor belt. I have a sensor to detect the product, a camera to do the inspection, and a reject station (usually just an air blast). Basically like this:

>>> DETECT >>>>>>> INSPECT >>>>>>> REJECT >>>

I have an encoder on the conveyor to track the products with 0.01" resolution (100 counts per inch). Product enters at random. Therefore, the number of products between the sensor, camera, and reject varies (but there is always a gap between products, and products don't slip on the conveyor).

The product detect sensor is considered the zero location, and I can preset the location of the camera (N10:0) and reject station (N10:1). When a product is detected, I increment two 'queue' pointers which I call 'Camera' and 'Reject'. The 'Camera' queue tracks the product all the way to the reject station (accumulating the encoder value for each product), while the 'reject' queue only tracks the product to the camera location (I should probably swap their names... ;)). This program has the capacity for 16 products between the detect and reject, and 8 products between the detect and camera. When a product reaches the camera location, I trigger the camera to inspect the product, and decrement the 'reject' queue. When the product reaches the reject station (and has failed), I reject that product, shift the camera accumulators, and decrement the 'Camera' queue.

The way I keep track of failed inspections is with individual bits within a 16-bit shift register (#B3:2). I determine which product is in front of the camera by looking at the current value of the 'Camera' and 'Reject' queues (Rungs 58 thru 73 in the Camera subroutine), and then SET the appropriate bit when the inspection passes. The BSL occurs each time I reject.

In my opinion (and probably many of yours), this program is WAY too complicated. It 'works' for the most part, but I'm sure there is a MUCH better approach. I have had to add patches to fix some issues. For example, if the conveyor is not moving and the detect sensor is triggered more than once, the queue gets incremented, which causes identical values to get loaded into multiple accumulator registers. My 'patch' was to prevent more than one trigger when the conveyor is stationary (rungs at the end of the Main subroutine).

The biggest issue I have not solved yet is my 'Camera' queue pointer not decrementing all the way back to zero when all products have exited. It will work for a while, but then end up with a '1' in it with the conveyor empty. Run for a while longer, and it now has a '2'. I re-zero these on the first scan as a temporary fix when it gets screwed up, but I need to figure out why it gets screwed up in the first place! Of course, this occurs much more often on the faster (~200 parts per minute) systems.

I realize the attached program is difficult to follow. I have trouble wrapping my head around it too. I'm sure I have made it way too convoluted, but I don't see any other approach that will work (for me). It's probably better to simply ask how YOU would tackle this, rather than trying to figure out what's wrong with my program.

So, how would YOU program this application? Note: I need to be able to add multiple cameras at different locations, but still with a single reject station (I currently have one system with 4 cameras). Also, I mainly use ML1100 and 1400s for these systems.

🍻

-Eric
 
I didn't have a chance to take a look at the application, however, the "extra" items within the camera queue are probably caused by "double-clutch" inspections (the same product has been inspected more than one time).
Since there are no "exits" between the Detection and Inspection stations I would try to implement and match the count of the "detected" to the one of "inspected" products: I know it seems like some band-aid, however, since there are no entries or exits between the first two stations a NEQ between the two accumulators could automatically "correct" the probable "double inspection".
 
Last edited:
Wow, that's some program you got there. Sure I can come up with ways to do it simpler, but your queue pointer code looks solid. I think you have to eliminate the double clutch theory though I don't see how it could happen if T4:0 has any preset in it. Add this. It's a long shot but the only thing I'm seeing.

oneshots.jpg
 
Wow, Tim, I'm impressed. From your response, it appears you actually UNDERSTOOD my code!... (y)

That is exactly the part I suspect is the issue. If more than one one-shot tries to fire in the same scan, only one gets processed. I suspect it may have to do with the accumulated values from the encoder. If there is too much change from one scan to the next, two (or more) of the accumulators may trigger on the compare statements (on the same scan).

I will add your testing logic the next time I'm at this customer. If there is EVER a number greater than 1, then THAT'S the problem!

Now, let's say that's the issue (a likely suspect, due to a current lack of any others). How do I solve it? I'm thinking I could use this value (the number of 1-shots) as my subtrahend to decrement the Camera queue?... o_O

🍻

-Eric
 
Sorry, don't have Logix here at home to look at your program.
If it were me, since you're using a uLogix which means no FIFO, then I would roll my own.

I would stick with a two-pointer system, and have two arrays for each pointer. Have a "trigger" (encoder value) and "status" (exists, inspection passed) array for the inspect and reject queues.

Each time a product passes the detection sensor, capture the encoder value, add inspection offset, subtract rollover if > rollover, then load this value at Inspect_Trigger[Inspect_Ptr], then OTL Inspect_Status[Inspect_Ptr].0, then increment Inspect_Ptr

Now If Encoder_Value >= Inspect_Trigger[0] and Inspect_Status[0].0 (first trigger entered into array) fire Camera Trigger output. Use a timer to wait for a pass/fail signal from your camera. If no pass input within timer preset, then Reject_Trigger[Reject_Ptr] = Inspect_Trigger[0] + Reject_Offset, and OTL Reject_Status[Reject_Ptr].0, then increment Reject_Ptr, then downshift your Inspect Triggers and Status Words and decrement the Inspect_Ptr.

Then If Encoder_Value >= Reject_Trigger[0] and Reject_Status[0].0, fire the Reject Air Blast SV, then downshift Reject Triggers and Status words, and decrement the Reject_Ptr.

Obviously, you would need to use data tables rather than arrays, but using tag names makes it easier to write out the concept. :oops:

As long as there is no slippage on the conveyor, and no slippage of product ON the conveyor, then this method should work fine, because your pointers will increment and decrement based off of encoder windows, and not off of having a sensor track the product down the conveyor.

Probably would need a debounce on the detect sensor to avoid false inspects, although this may not be too terrible, because then it would just fail the inspection, and fire the air blast with nothing on the conveyor.

I don't know if my generic concept is helpful to your specific application, but I have applied the same sort of code to a conveyor controlled by an L61 processor with good results, so maybe it can help get some juices flowing :)

Good luck,
Dustin
 
Last edited:
Thanks, Dustin. I'll study it a bit more when I have some time... (y)

At first glance, it appears similar to the idea I originally started out with. Simply track the part(s) from the trigger to the reject, and have the pass/fail status follow it. It seemed simple enough at first, but I kept hitting road blocks, which somehow led to the overly complicated program I have now... :rolleyes:

You should see my 4 camera version!.. :oops:

🍻

-Eric
 
Thanks, Dustin. I'll study it a bit more when I have some time... (y)

At first glance, it appears similar to the idea I originally started out with. Simply track the part(s) from the trigger to the reject, and have the pass/fail status follow it. It seemed simple enough at first, but I kept hitting road blocks, which somehow led to the overly complicated program I have now... :rolleyes:

You should see my 4 camera version!.. :oops:

🍻

-Eric

Yeah, I went back and read your previous threads about this project, and it seems you were definitely approaching it the same way I was thinking of.
So you are thinking that you are missing inspection triggers, or crossing two thresholds between scans?
That seems crazy, unless you have really high scan times.
What is the minimum time between leading bottle edges? Hopefully it is at least 5x your max scan time...
I guess you are going to need some of that trap logic. Using that value from TW's example as the subtrahend to your queue wouldd at least keep your queues synced, but wouldn't do anything about the missed inspection. If this is indeed happening, then you may have some design flaws to address.

Keep us posted with how you're making out.

🍻
 
I am seeing so many of these threads and I have a polished and tested solution that is virtually bulletproof and very scalable. My girlfriend has family in N.J. Don't make me drive over there and 'splain it to you in person.

Move nothing.

Update a conveyor position pointer at regular rapid intervals. Set a rollover value in raw counts that exceeds the physical length of the belt. This rollover value should be a variable, and will also determine the number of elements you need in your conveyor map data table(s).

Do this by summing the delta of the encoder, nothing more nothing less, never hardware reset unless you are using the z-pulse for something slick, or for engineering and setup purposes.

Everything else in the program is an offset...examples:

F96:0=Product Entry Station 1=18.286 'measured inches from entry eye PE1 to Kicker OneA
When PE1 goes true, we seal in a timer, and store the position as F96:0 + F8:0 Present Conveyor position. We'll call this F8:1 "Product Detect Position Pending". Be sure to add wrap-aruond-logic (If F8:1>Map_Length, SUB F8:1, Map_Length, F8:1)

When this timer is timing, we want to watch for a false transition of PE1.

We also watch for the timer to get done. Set the preset for the maximum logically possible value times two or with a cushion...this DN bit can act as a shutdown fault, or alarm.

When we see the normal turn off, we qualify (LIM) the value of the timer accumulator, and, if it passes, we drop an indicating value for this product in a data table called "conveyor_map" at an index position equal to the rounded value from that pending position from above.

Repeat this bit of logic for each entry system and exit system. The final (end of the belt) needs some logic to sweep out the data values and put zeros back in there.

I used this code structure to track 210' of belt with 16 different tire drop zones along its length. Then had to sort the tires back out from two different kickers downstream. It was flawless, and my units for the "conveyor map" data table were whole feet, since one foot is much smaller than half the frequency of my product. I made some measurements with a tape, plugged it some values, and wrote some extra screens to tune the offsets.

When you open up your conveyor map data table, you don't see anything shifting, just numbers appearing when new products enter, and a "sweeper" that circles around and around replacing those values with zeroes as it moves them to the next phase of the logic (like a FIFO or MSG system).

If you want graphics, then you needs some copy logic or a driver that supports indirection, and you now have a real graphical map of the process at run-time.

Using real encoders means precision no matter if you stop, reverse, you can tolerate stoppages with carefully designed RTO timers in place of TON for product measuring.

Even with hand measured speeds and a little math you can make sweet sorting code without frequent stops/starts and no encoder or VFD required.

the CPU burden is not increased, no matter how full or empty the system becomes. You are still only doing the math once at entry and once at exit for each item, plus a sweeper.
 
Last edited:
Okay, finally got around to investigating this today. I added Tim's counter logic with one addition. I unconditionally MOV a zero into the counter (N7:4) on the rung following GRT and MOV rung. Therefore, the value in N7:5 should always be a one, unless I get two or more triggers during one scan. When that occurs, I should see N7:5 change to 2 (or higher). It will retain the value until I manually reset it.

I fully expected this to expose the problem, but it doesn't!... o_O

I am just testing this on the bench. I have my cordless drill spinning the encoder at up to 500 RPM, which equates to a belt running 250 feet per minute (Faster than I need). I also have the 'Camera Trigger Position' (N10:0) set a '1', so there really should never be any more than 1 part in the queue. Part is detected, and triggers the camera at 1 encoder count later (IOW, immediately).

I trigger the sensor in rapid succession, and sure enough, I end up with a number greater than zero in the camera queue (N7:12). It takes a while to happen sometimes, but it happens. As high as 3 sometimes, yet the trigger counter max (N7:5) REMAINS AT ONE!

I really thought multiple triggers on the same scan was the culprit here, but this test seems to disprove that theory... :(

I must be overlooking something obvious here. I'll keep playing around, but would love to hear any other ideas.

FYI, scan time is pretty steady around 20-25 (x100 uSec), currently showing a max of 32.

🍻

-Eric

P.S. to Paul. I will seriously consider your method for the future. I'm not sure if it's directly applicable here. I not only need to track the part from entry to exit, I also have to trigger one or more cameras at specific locations AND keep track the pass/fail result from each camera.
 
Since the 'multiple triggers during one scan' doesn't seem to exist. I'm now thinking it might be something to do with rung order? Specifically the location in the program where the encoder value gets updated. If the 'Product Detect' one-shot (B3:4/1) happens to turn on during the same scan as one of the accumulators reaches it's preset value, I might not increment/decrement one of the queues, or conversely, increment/decrement when it shouldn't.

I'm going to try calling the ENCODER subroutine from different locations to see if it makes any difference. Just wanted to throw this out there in hopes that it will spark some ideas as to what's going on.

If anyone is bored ;), this is pretty easy to simulate on the bench if you have a 'spare' ML1100/1400 laying around. Just feed pulses into I:0/0. My drill and encoder gives 5Khz, but it happens even at slower speeds (just less often). The trigger goes to I:0/2. I'm using a photoeye and passing my finger back and forth really fast. The problem usually occurs within 30 seconds. All of a sudden, the value in N7:12 will jump to a 1 (or higher). WHY????... :cry:

Attached is the revised program with Tim's counter added.

🍻

-Eric
 
If it were me, since you're using a uLogix which means no FIFO, then I would roll my own.
I think you can use a FIFO with a Micrologix, but you may have to use Indexed Addressing, because there is no Indirect Addressing in some Micrologix models, such as the 1500.

RSLogix FIFO Instructions- ALL MicroLogix.JPG
 
I've not read the full thread so please forgive me if i repeat anything.

I have a lot of experience tracking objects such as food products and reject or baggage at airports with routing etc.

The basic way I would do what you are doing is as follows:

1. One tracking zone from detection to reject.

2. this tracking zone would contain a single column array where I would track a value (this could be an item number or just a reject code).

3. The number of locations in this array depends on the distance between the start and end of the zone and the length of a tracking pulse. If the pulse is 20mm and the distance is 2m, then I would have 100 places.

4. When the object is detected at the start, place a number in the first location, either a pass code or reject code, whichever you want as priority.

5. Move the number one position every pulse.

6. When the location at the camera is not zero, trigger a read.

7. Next you have to associate the result with the item. In baggage handling we have bar code readers, these are usually set to hold the result for a set time from the trigger, that way we can associate the result to a specific location on the belt. Even when you think there is no slip, the position can vary, so I would open a window when my item number reaches a certain location and wait for a result until a closed location. For example, if the reader is at location 10 and the result is set to return at location 15, I would start looking for the result when the object is at location 12 and stop at 17, if a result returns when no object is between those positions it is ignored.

8. If the result differs from your default, then change the value in the location.

9. I would look at putting a sensor just before the reject system, otherwise you could miss the object or blow the wrong one. This sensor would operate a window system similar to point 7 above.

There will be position error, even though as you say there is no slip.

I wouldn't use a FIFO unless you have two sensors and then I would put in safeguards to ensure you scrap undetected items, a FIFO without that protection would really screw you up.
 
P.S. to Paul. I will seriously consider your method for the future. I'm not sure if it's directly applicable here. I not only need to track the part from entry to exit, I also have to trigger one or more cameras at specific locations AND keep track the pass/fail result from each camera.


On the note of multiple results from multiple cameras, then I would have an ID number, that ID number would be an offset in a result array of UDT's, with a location for each camera, plus overall accept/reject. At each location save the data.

i.e.

Part has ID 5 travelling along the belt.


Result(5).Camera 1
Result(5).Camera 2
Result(5).Camera 3
Result(5).Pass_Reject


I've seen Pauls suggestion in practice, worked OK, but not my favoured choice.
 
I have it narrowed it down, but still no solution... :mad: Every once in a while, a product gets detected, but doesn't trigger the camera, which in turn fails to decrement the reject queue value. I can get into more detail about this, but for the time being...

Can someone double-check my encoder setup and logic? Attached is a printout of the function file for the HSC, plus the subroutine that calculates how far the encoder moves each scan, allowing for rollover. This encoder subroutine gets called unconditionally, ever scan. Just want to make sure I got the parameters and logic correct... :oops:

🍻

-Eric
 

Similar Topics

Hi friends, i need a help to know the procedure and steps to connect Lenze i700 to PC for parameterize. i tried with lenze easy-starter using LAN...
Replies
0
Views
35
HI everyone, i am new to Siemens plc programming and i am in need of some help. yesterday we had an S7-1200 CPU 1214C fail to turn on an output to...
Replies
9
Views
292
I have an old Sentry Palletizer (S/O Number 3007 / Serial Number 1172) that has lost its program as the backup battery died years ago. I can...
Replies
0
Views
118
Hello, I need to write the following program in Ladder language, but I could not integrate the Fibonacci sequence into the ladder. Can you help...
Replies
22
Views
487
this a program to send data to barcode printer I want to integrate a new printer in a new machine and i wanted to adapt the old prgram on it but I...
Replies
4
Views
188
Back
Top Bottom