Dec-30-2020, 12:28 PM
If I see PLC Content, I can't resist...
Just as additional information:
You can have different measurement ranges.
The most used ranges I know:
Current:
Voltage:
The namedtuple is very useful.
If the input was valid, the return value is a namedtuple.
You can access the values with dot.:
Example: https://cdn.sick.com/media/pdf/7/47/747/dataSheet_UC30-214163_6054712_en.pdf
The operating range is: 350 mm ... 3400 mm
The scaling with a Siemens PLC will look like this:
For example, the sensor could output a higher current/voltage than 10V/20mA to signalling the PLC that something is not in operation range. If the range is 4 - 20 mA, then you can also detect a broken cable. In this case, the current is 0 mA.
SIEMENS has defined these areas for example:
I've also made for this a function (but without the units):
Otherwise, you've a pattern like:
Just as additional information:
You can have different measurement ranges.
The most used ranges I know:
Current:
- 0 - 20 mA [no cable break detection]
- 4 - 20 mA
Voltage:
- 0 - 10 V
- -10 V - +10V # also negative Voltages are possible
The namedtuple is very useful.
from collections import namedtuple def select_range(): Range = namedtuple("range", "text low high unit") # just to have dot access ranges = [ Range("0 - 10 V", 0, 10, "V"), Range("-10 - +10 V", -10, 10, "V"), Range("0 - 20 mA", 0, 20, "mA"), Range("4 - 20 mA", 4, 20, "mA"), ] while True: for idx, key in enumerate(ranges, 1): print(f"{idx}) {key.text}") print() selector = input("Please select a range: ") try: # two possible Exceptions # ValueError if the conversion to an int is not possible # IndexError if you selected a not existing index return ranges[int(selector) - 1] except (ValueError, IndexError): # catching this two exceptions to continue the while True loop # and ask again print("Invalid input")I named the namedtuple Range, which is not a good name. It has nothing to do with
range()
.If the input was valid, the return value is a namedtuple.
You can access the values with dot.:
my_range = select_range() print(my_range.text) low, high = my_range.low, my_range.high unit = my_range.unitAnd to normalize and scale, I use these functions:
def scale(value, low, high): """ Scale the value from low .. high >>> scale(0, -10, 10) -10 >>> scale(1, -10, 10) 10 """ return value * (high - low) + low def normalize(value, low, high): """ Normalize value to 0 .. 1 >>> normalize(0, 0, 10) 0.0 >>> normalize(10, 0, 10) 1.0 >>> normalize(5, 0, 10) 0.5 """ return (value - low) / (high - low)Example with selected range:
# normalize 0 .. 27648 to 0 .. 1 # scale 0 .. 1 to 0 .. 100 percent = scale(normalize(15000, 0, 27648), 0, 100)The thing is, if you know the minimum range and maximum range of the detected physical quantity, the step to convert it to a current or voltage, is not required. Only on PLC-Side the right range must be selected and the right connections must be used.
Example: https://cdn.sick.com/media/pdf/7/47/747/dataSheet_UC30-214163_6054712_en.pdf
The operating range is: 350 mm ... 3400 mm
The scaling with a Siemens PLC will look like this:
input_value = 1337 normalized_value = normalize(input_value, 0, 27648) # 0 - 10 V range # the 27648 is the highest value of Rated Range scaled = scale(normalized_value, 350, 3400)In real world applications you can get also values, which are out of normal range.
For example, the sensor could output a higher current/voltage than 10V/20mA to signalling the PLC that something is not in operation range. If the range is 4 - 20 mA, then you can also detect a broken cable. In this case, the current is 0 mA.
SIEMENS has defined these areas for example:
- Underflow, at zero voltage and current
- Undershoot range
- Rated range
- Overshoot range
- Overflow, off power
I've also made for this a function (but without the units):
def scale_siemens(value, min_scale, max_scale, bipolar=False): """ Scale Analog Values with siemens components: https://support.industry.siemens.com/cs/mdm/8859629?c=23218577931&t=1&s=27648&lc=en-DE Look at Table: Representation of analog values in the ±10 V output range """ if bipolar: normalized = normalize(value, -27648, 27648) else: normalized = normalize(value, 0, 27648) ranges = (-32513, -27649, 27648, 32511, 32767) areas = [ "Underflow, at zero voltage and current", "Undershoot range", "Rated range", "Overshoot range", "Overflow, off power", ] idx = bisect.bisect_left(ranges, value) try: kind = areas[idx] except IndexError: kind = areas[-1] if idx == 0 or idx + 1 >= len(ranges): return 0.0, kind return scale(normalized, min_scale, max_scale), kindThe interesting part here is the use of the bisect module.
Otherwise, you've a pattern like:
value = 32760 # overshoot range # From High to low... if 32512 <= value <= 32767: kind = "Overflow, off power" elif 27649 <= value <= 32511: kind = "Overshoot range" elif -27648 <= value <= 27648: kind = "Rated range" elif -32512 <= value <= -27649: kind = "Undershoot range" elif -32768 <= value <= -32513: kind = "Underflow, at zero voltage and current" print(kind)
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
All humans together. We don't need politicians!