Python external function examples: PID controller

This topic is intended to be read in conjunction with the main external function documentation, and the example source code.

This ZIP file contains all of the Python external function examples, with each example contained in the appropriately named sub-folder.

This example models a subsea template being lowered by a heave compensated winch from the stern of a handling vessel. The Python external function that implements the compensation system uses a generic PID controller (Proportional, Integral, Differential) that we have implemented in a fairly general manner, so that it could be used for other control purposes as well, with only small changes.

This example comprises OrcaFlex file HeaveCompensatedWinch.dat and Python script PIDController.py. There is also a Workspace file which is set to be the default file for the simulation file from our data file.

In OrcaFlex, open the model HeaveCompensatedWinch.dat. On the Control page of the Winch data form, the winch dynamics mode is set to pay out at a rate determined by PIDWinchControl, which is an external function variable data source that is specified, on the variable data form, to be the class PIDController in the Python module PIDController.py.

The winch tags (defined on the data form) contain text strings that specify various model-specific parameters for the controller. These are read by the external function and are used as parameters to the PID controller that controls the winch wire payout.

Run the simulation, and on the Workspace menu select Use file default workspace. This sets up windows showing time histories of the winch wire length paid out and resulting Template Z coordinate.

For the first part of the simulation the controller is not active, since the ControlStartTime is specified to be 20 seconds in the winch object tags. So, until simulation time t=20 seconds the winch has a constant length of wire paid out, and so the template rises and falls with the wave-induced vessel motion. At time t=20 seconds, the PID controller becomes active and after that the winch wire is paid out and controlled in order to lower the Template to a Z coordinate of -85m and then keep it steady at that depth.

Controller Theory

The PID controller has the following form: \begin{equation*} \text{Winch payout rate} = k_0 + k_P e + k_I \int{e dt} + k_D \frac{de}{dt} \end{equation*} where

Code details

Initialise sets up some object variables, periodNow and ObjectExtra, that will be needed in the Calculate method to ask OrcaFlex for the current value of the controlled variable.

It then gets the parameters, which are specified in winch object tags. If this is a new simulation then info.StateData will be None, so it initialises the controller. But if a saved simulation file is being opened then info.StateData will contain the state that the StoreState method saved, so that state is restored.

ObjectExtra contains any extra information that is needed when a result value is requested. In this example the controlled variable is a buoy Z-coordinate, and this depends on the Position on the buoy, which is specified on the OrcaFlex results form when that result is requested. In Python this is defined by ObjectExtra.RigidBodyPos (this attribute of ObjectExtra is also used for vessels, which are also rigid bodies in OrcaFlex). Some OrcaFlex result values (e.g. winch tension) do not require any extra information, in which case ObjectExtra is not needed. But others require different extra information to define them e.g. winch Connection Force requires ObjectExtra.WinchConnectionPoint and many line results require ObjectExtra attributes such as NodeNum or ArcLength to be set, in order to define the point on the line at which the result value is wanted.

In the Calculate method the controller is not active until simulation time reaches the ControlStartTime specified in the winch object tags. Before this time the Calculate method does nothing, so the controlled value (winch payout rate) stays at the initial value that is specified on the variable data form in the OrcaFlex model, which is zero.

The OrcaFlex model can perform multiple calculations in a single time step e.g. if implicit integration is used, since then each time step is an iterative calculation. Because of this the Calculate method must be careful to only step forward when this is a new time step, which is indicated by info.NewTimeStep.

The Python code implements the controller theory given above. The controller needs to maintain calculation data from the previous time step, in order to calculate the integral and differential parts of the control. This is done using two instances (self.prev and self.now) of a PIDstate helper class. This is a simple class with no methods, and the two instances are created in the Initialise method and used to record data for the current and previous time step.

The e.dt and de/dt terms in the control equation cannot be calculated on the first time step of control, since no self.prev data is available. So for this first time step these terms are set to the Initial e/D and Initial De values specified in the winch object tags. These parameters are both set to zero, and that value is probably appropriate in almost all cases.

The time histories of Winch length and Template Z show that the controller does indeed lower the Template to the target depth and hold it there, by paying out or hauling in winch wire to compensate for the vessel heave motion.

The StoreState method is called if the simulation is saved to a file. It uses the Python json module to save the current state of the controller as a string in info.StateData, which OrcaFlex saves in the simulation file. When the simulation is re-loaded OrcaFlex calls the external function s Initialise method with this data put back into info.StateData, so that the Initialise method can restore the controller state to what it was when the simulation was saved.