Using Godot game engine to simulate shop floor machinery controlled by ProviewR

  • Last Update:2024-08-09
  • Version:001
  • Language:en

Objective

The whole idea is to make Godot simulate industrial devices and expose them with the modbus protocol so you can use any modbus-capable industrial control suite out there, in our case it's ProviewR. This way we can build test and our Plc in a virtual shop floor.

Our concrete example is to reproduce a setup in a real fruit selection machine using ProviewR, modbus and different sensors

Technologies

Godot

Godot is a 2D and 3D, cross-platform, free and open-source game engine released under the MIT license. It was initially developed by argentinian Juan Linietsky and Ariel Manzurfor for several companies in Latin America prior to its public release. The development environment runs on multiple operating systems including Linux, macOS, and Windows. Godot can create games targeting PC, mobile, and web platforms.

ProviewR

ProviewR is probably the first Open Source system for process control and automation in the world. Originally developed in Sweden by Mandator and SSAB Oxelösund as a process control system based on standard computers, the system has become a fully-fledged, integrated and low-cost solution that is running on standard PC's with Linux as operating system.
ProviewR is Open Source and the license is GNU/GPL.

Modbus

Modbus is a data communications protocol originally published by Modicon (now Schneider Electric) in 1979 for use with its programmable logic controllers (PLCs). Modbus has become a de facto standard communication protocol and is now a commonly available means of connecting industrial electronic devices. Modbus is popular in industrial environments because it is openly published and royalty-free. It was developed for industdeploy and maintain compared to other standards, and rial applications, is relatively easy to places few restrictions - other than the datagram (packet) size - on the format of the data to be transmitted. Modbus uses the RS485 or Ethernet as its wiring type. Modbus supports communication to and from multiple devices connected to the same cable or Ethernet network.

Godot shop floor

Before we begin, be aware that we will not cover in details how to setup and use Godot, there is a lot of good tutorials out there for you to reproduce or modify our simulation. If you want to test our simulation, just follow the steps in the README of our repo.

Here is our scene:

There are five objects in the scene:

  • The hatched cube is a cube spawner. It produces a new cube every three seconds. This cube will fall and land on...
  • ... a rectangle with yellow arrow on it which represent a convoyer (which on the real setup has its speed controlled by a VFD using by modbus) belt. The cube is then displaced from left to right on it.
  • A cube. obviously. which is in the middle of the conveyor belt on this image. This cube represents the coffee cup of the real world shop floor.
  • The blue rectangle with a black sphere in it in the sky is an optical sensor. It can detect a cube on the conveyor belt.
  • And finally there is the pusher (which on the real world setup is an air valve with a compressor) on the left side of the conveyor belt. It can be activated to push a cube outside the conveyor belt, making it fall into the underworld.

Cube spawner

Here is the code for spawning a new cube every 3 seconds:

extends Spatial # We need this to be able to call add_child
export var frequency = 0.3
onready var timer = Timer.new()
var boxscene = preload("res://TestBox.tscn") # Preload cube object

func _ready():
	timer.wait_time = 1.0/float(frequency)
	timer.connect("timeout",self,"spawn") # On each timer trigger, call spawn function
	add_child(timer)
	timer.start()
	spawn() # Spawn the first cube to avoid waiting for the first timer trigger

func spawn():
	var box = boxscene.instance()
	add_child(box)


The ``_ready`` is a function called when a scene (in our case the cube spawner) is loaded and "ready" to be used in the 3D world (see Godot documentation) )

Conveyor belt

Nothing fancy here, a force is applied to the cube touching it. This can be done directly in the editor, but as we want the yellow arrow to go at the same speed than the cube, we used a script in order to change the speed in only one place.

Modbus in Godot

This was the simple part. Now let's focus on our modbus devices. Before going into the scripts we are using, we need to understand how modbus work.
Modbus devices are communicating using a client/server relationship (new wording of the Modbus Organization), where clients make requests and servers respond to it (except for Modbus TCP where every devices can be a client). Here are the different data types that a ModBus server can provide:

Object type Access Size Address Space
Coil Read-write 1 bit 00001 - 09999
Discrete input Read-only 1 bit 10001 - 19999
Input register Read-only 16 bits 30001 - 39999
Holding register Read-write 16 bits 40001 - 49999

A modbus client can ask a modbus server to:

  • change the value in one of its registers, that is written to Coil and Holding registers.
  • read an I/O port: Read data from a Discrete and Coil ports,
  • command the device to send back one or more values contained in its Coil and Holding registers.

This is enough technical stuff to understand what we will do in order to communicate data between ProviewR and Godot.

As one can except, Godot does not provide ModBus communication protocol directly. We will need to use libmodbus for this.

To use it, we have created dynamic library which will be loaded and used by some GDScript later on. We will skip the setup part as this not really interesting and very well documented in the Godot documentation. Our dynamic library provides four functions used in Godot:

  • start_server(address, port): This function call modbus_mapping_new in order to setup the registers of the modbus servers. Then we spin a new thread using pthread_create which call the server_routine. This routine will wait for a connection from a client and the accept it, then it's waiting for a request sent by the client using modbus_receive. We directly respond to it using modbus_reply which also read or write different register/coils if this what was asked in the request. And that it, we are ready to read and write server register/coils directly in Godot using:
  • set_holding_register(value) which set a register with a given value,
  • get_holding_register() which returns the value of a register,
  • get_coil() which returns the value of a coil.

Here is how we load and wrap our dynamic library symbol in Godot:

class_name ModbusServer
var c_backend = load('res://simple.gdns').new()
var hold_register = 17 setget set_holding_register, get_holding_register # getter and setter for smother GDScript integration

func _init(address, port):
    c_backend.start_server(address, port)

func set_holding_register(value):
    c_backend.set_holding_register(value)

func get_holding_register():
    return c_backend.get_holding_register()

func get_coil():
    return c_backend.get_coil()

Now that we can communicate, let's add some logic to our sensor and pusher

Sensor

extends ModbusSpatial # By extanding ModbusSpatial which call ModbusServer.new(), we create a new ModBus server

func _physics_process(delta):
    ms.hold_register = floor($RayCast.distance*100.0) # This call ms.set_holding_register as we have set a setter and a getter

Nothing fancy here, we get the distance to the cube using a raycast and store the result in a register of the modbus server corresponding to the sensor.

Pusher


extends ModbusSpatial # By extanding ModbusSpatial which call ModbusServer.new(), we create a new ModBus server device

... # Varibles initialization

func _physics_process(delta):
    var needs_to_extend = ms.get_coil()

    if needs_to_extend and extension < max_extension:
        ... # Extend
    if !needs_to_extend and extension > 0:
        ... # Retract

We read the coil of the modbus server corresponding to the pusher and extend the pusher if the coil is set and retract it otherwise.

We used the _physics_process callback because it provides consistent updates over time, regardless of how fast or slow time advances, see Godot documentation.

That the end of the Godot part. We have two modbus server, but nobody is talking to them, what a shame. Let's fix that by using ProviewR.

ProviewR automaton:

ProviewR is a complex system to setup and since we already covered Modbus setup in ProviewR in another article.

Our setup is one Modbusmaster (I can't use client/server wording as ProviewR hasn't made the switch...) with two Modbusslaves, one for the sensor and one for the pusher, with one Modbusmodule containing one signal each. For the Plant we have one Digital Input and one Digital Output connect to their respective node counterpart.

Let's talk about our automaton. We are using grafcet to represent our automaton.

From top to bottom we find:

  • ISO: Start step
  • T0: Transition triggered by a value of 1 while reading the sensor register
  • S0 and S1: Two step running in parallel. S0 set a timer of 1.25 second and S1 set the coil of the pusher to 1.
  • T1: Transition triggered by the end of the timer. A coil is unset by default while your not actively pushing a 1 one it, so when the transition happens, the coil of the pusher is set to 0 and the pusher retract itself.
  • Loop to ISO

This plc is then compiled (stay tuned for an article on this) to a native executable that ProviewR will run.

If we start both Godot and our ProviewR runtime....

Et voilà!

Thanks for reading this long article.

Sources

  • https://en.wikipedia.org/wiki/Modbus
  • https://en.wikipedia.org/wiki/Godot_(game_engine)
  • http://www.proview.se/v3/e
  • https://docs.godotengine.org/en/stable/index.html
  • https://lab.nexedi.com/nexedi/godot-modbus-demo

Contact

  • Logo Nexedi
  • Esteban Blanc
  • esteban (dot) blanc (at) nexedi (dot) com
  • Photo Ivan Tyagov
  • Logo Nexedi
  • Ivan Tyagov
  • ivan (at) nexedi (dot) com
  • Photo Jean-Paul Smets
  • Logo Nexedi
  • Jean-Paul Smets
  • jp (at) rapid (dot) space
  • Jean-Paul Smets is the founder and CEO of Nexedi. After graduating in mathematics and computer science at ENS (Paris), he started his career as a civil servant at the French Ministry of Economy. He then left government to start a small company called “Nexedi” where he developed his first Free Software, an Enterprise Resource Planning (ERP) designed to manage the production of swimsuits in the not-so-warm but friendly north of France. ERP5 was born. In parallel, he led with Hartmut Pilch (FFII) the successful campaign to protect software innovation against the dangers of software patents. The campaign eventually succeeeded by rallying more than 100.000 supporters and thousands of CEOs of European software companies (both open source and proprietary). The Proposed directive on the patentability of computer-implemented inventions was rejected on 6 July 2005 by the European Parliament by an overwhelming majority of 648 to 14 votes, showing how small companies can together in Europe defeat the powerful lobbying of large corporations. Since then, he has helped Nexedi to grow either organically or by investing in new ventures led by bright entrepreneurs.
  • Photo Romain Courteaud
  • Logo Nexedi
  • Romain Courteaud
  • romain (at) nexedi (dot) com
  • Diplôme ingénieur ENSSAT (année d'obtention 2003)