Using Python to program a production line
In this course, you will be controlling a system in Simumatik by reading and writing variables from a Python script running on the local computer.
Start by loading the system called Python intro tutorial.
Now let’s take a look at what is included in the system. From right to left we have the entry, merger, printer, and in the bottom left the control assembly. In this course we will be focusing on the entry section to the right.
The two conveyors in the entry section have already some sensors and cylinders mounted. The connections between components in the workspace has been made as well, so we only need to focus on programming the system.
The inputs that can be used to determine the state of the system are six sensors (three on each conveyor), and a selector switch on the electric box. The outputs can be used to control each cylinder separately, the first two conveyors can also be controlled separately.
The emergency button is directly connected to the power input of the motor contactors. Pressing it will make the conveyors stop no matter which output signals are given from the controller.
Running the program
Go to the system assets and make sure to download both the ‘Controller.py’ and the ‘label_printer.py’ files. You can place those files in any directory, as long as they are together. If you’re having trouble finding the assets, the documentation can be found at https://simumatik.com/learn/Workspace/system_assets/. The browser might give a warning when downloading python files, make sure to ‘keep’ the file to finish the download.
To start using the system, the python control script needs to be running first. Start by opening the ‘label_printer.py’ file in a code editor and start running the script. For example – open the file in the IDLE editor and press F5 to start running the script. After making changes to the script, press F5 again to save the changes and run the new version.
In the workspace, make sure that the Gateway is connected by pressing the ‘Connect Gateway’ button in the toolbar.
Press ‘Play’ to start the emulation, take a look at what happens in the system and compare that behavior to the code in the script to get a feeling for how the inputs and outputs can be used.
Understanding the code
Before making changes to the code, let’s take a look at what’s already provided to get an idea of how it works.
The first part of the script will be the same in any script using this particular controller component. First, we need to import the libraries that will be used in the script. Then an object of the UDP_Controller class is defined, this object will be used to get inputs and send outputs to Simumatik.
import time from Controller import UDP_Controller import logging if __name__ == '__main__': _controller = UDP_Controller(log_lever=logging.ERROR) _controller.addVariable("inputs", "byte", 0) _controller.addVariable("outputs", "byte", 0) _controller.start()
Next, all outputs that will be sent to the controller are initialized and set to ‘False’. This means that unless they are changed later in the script, they will give a 0V output from the controller. ‘last_dropped_green’ is an internal variable that will be used later in the script.
# Initialize all outputs # List of outputs: # DROP_GREEN, DROP_BLUE: Connected to product entries, drops a product when activated # MCL = motor contactor left # MCR = motor contactor right # CR1-2 = cylinder right # CL1-2 = cylinder left DROP_GREEN = DROP_BLUE = MCL = MCR = CR2 = CR1 = CL2 = CL1 = False last_dropped_green = 0.0
Next comes the while loop, which is where the main logic of the program will be written. The first thing that needs to be done every loop is to get the updated inputs. These inputs give us enough information about the state of the system to make the necessary actions with the outputs. A clock variable is updated at the start of every loop, this is used in several places later to keep track of delays etc.
while True: # Start by getting the updated inputs. List of inputs: # - SWITCH_L & SWITCH_R: sigals from the selection switch # - SR1-3: sensors on the right side # - SL1-3: sensors on the left side [SWITCH_L, SWITCH_R, SR3, SR2, SR1, SL3, SL2, SL1] = _controller.getMappedValue("inputs") clock = time.perf_counter()
This part of the code handles how the cylinders mounted on the left conveyor are controlled. When the sensor in the front detects a can, the cylinder in the back extends and a counter starts. When that counter reaches 4 seconds, the cylinder in front retracts to let the can through. After the can is let through, the front cylinder closes the way again and the loop starts over.
# Handling of green cans, cylinder control if SL3: CL1 = True CL2 = False if (clock - last_passed_green) > 4 else True else: CL1 = False CL2 = True last_passed_green = clock
Next, we have the code that stops the conveyor motor if the queue reaches the sensor in the back. The conveyor only stops if both the sensor in front and the one furthest back detects cans at the same time. Also, the ‘if’ case is not entered if ‘CL2’ is retracted because in that case, we want to move the can forward. The ‘green_full’ variable is used later to determine when new products should be dropped.
# Stop left conveyor if the queue has reached the sensor in the back (after duration 5s) # but not if the cylinder is letting the first can through if SL1 and SL3 and CL2: green_full = True if (clock - green_queue_starttime) > 5: MCL = False else: MCL = True # Start counting time when a can leaves the sensor in the back if not SL1: green_queue_starttime = clock green_full = False
The code below tries to spawn a new product every third second. However, this will only happen if the ‘green_full’ variable is False. Otherwise, there is no room for new products.
# Drop a green can every third second if not green_full and (clock - last_dropped_green) > 3: DROP_GREEN = True last_dropped_green = clock else: DROP_GREEN = False
This last part of the code just sends the updated outputs to Simumatik. The ‘time.sleep(0.1)’ statement is there most of all to make sure that the script does not take up 100% CPU power.
# Send updated outputs to controller _controller.setMappedValue("outputs", [DROP_GREEN, DROP_BLUE, MCR, MCL, CR2, CR1, CL2, CL1]) # Sleep for short duration to prevent taking much CPU power time.sleep(0.1)