Native 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 native external function examples, with each example contained in the appropriately named sub-folder.

This example demonstrates a heave compensated winch. The external function that implements the compensation system uses a generic PID controller. We have implemented this PID controller in a completely general manner and so it can be used for many other purposes without need to modify the code. Because of this generality the external function is named PIDController and the source code can be found in PIDController.cpp.

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

In order to write a general purpose controller, the behaviour within the OrcaFlex model that is monitored to generate the error signal is defined by configuration data from the model, instead of being defined directly within the function source code. This adds some code complexity, as the price for generality.

The derivative and the integral of the error signal are dependent on the history of the simulation. These values are referred to as the state of our external function. This state data must be stored and reloaded if a simulation is to be paused or extended correctly. This requirement is implemented as part of our example.

PID controllers are described as being tuned for their application. The control parameters must be set to different values when a controller is used for different purposes. Consider introducing a controller into the dynamics of a high mass object that is softly restrained, or introducing a controller into the dynamics of a small object held by taut springs of high stiffness. The PID parameters for these two cases should be different, in order for the controller to have a positive impact on the dynamics.

If you apply this example function to a different OrcaFlex model, you should expect to use different PID parameters, and you must dedicate some time to testing and tuning your controller.

The OrcaFlex example models a subsea template lowered by a winch from the stern of a handling vessel. The template object is named "Template". The error signal in our example case is \begin{equation*} e = \text{Target} - \text{Template Z} \end{equation*} The target value is -85 m.

This PID controller is given a control start time, so that behaviour with or without the controller acting can be compared within a single run of dynamics. In our example, the control start time is set to 50 s. For the first part of the simulation the winch has a constant length and the template responds to the vessel motions as expected. At time t=50 seconds, the compensation system is switched on, and the change to response can be seen.

Initialise

Configuration data are obtained from the winch object tags during the initialisation phase. As noted above, some of these data are made available to allow for general use of the controller. This does make the parameter list a little longer. The example function can accept the following parameters:

Many of the parameters can be omitted and will use default values as specified in the code.

The PID parameters are used as follows:

As the PID parameters do not change during the simulation, they can be stored in memory, and a pointer to this memory is placed in the info.lpData input parameter.

The PersistentData struct that holds our decoded parameters also has a member that holds PID controller state data. This state is set and evolved within the calculate action, but initialisation must also be aware that the PID controller has state. The final section of the initialisation code checks whether existing state data is held within info. If state data is present, we assume that initialisation is happening because a paused simulation is being resumed, or a completed simulation is being extended or restarted. As part of that activity, the saved state data from the OrcaFlex simulation file is used as the initial state of the controller, here via memcpy. The saved state data exists because of actions taken during the StoreStateCreate action, see later in this commentary.

Finalise

In the finalise action, we release the memory that was used for our persistent data. The state data has already been provided to the OrcaFlex simulation file for saving as part of the StoreStateCreate action.

Calculate

The instantaneous value for the controlled result is requested, then integral and differential terms are calculated before the PID output is returned. In this example the PID output is the winch payout rate. When the external function is asked to calculate for the first time, initial values of integral and derivative from the configuration data are used.

At a new time step of the simulation, the previous state is advanced to the current values of state. For a simulation using implicit integration, the current values of state may vary with the new estimate of dynamic equilibrium at each iteration. Advancing the state at new time step means that the state from the accepted dynamics of the previous time step is placed into the previous values. State data from earlier iterations within a time step correspond to dynamic equilibrium estimates that did not achieve the solver tolerance.

Store state actions

During the initialisation, the code checks to see if state data has been passed to the external function. If this is the case, then a part run simulation file has been loaded and the external function must load this state data in order to restart the simulation in the correct state. Management of that state is done within the store state actions.

On receiving a eaStoreStateCreate the external function should provide an address and a size for a block of memory containing the external function's state – this is the information that the external function requires to put itself back into exactly the same state it is now. A call to eaStoreStateDestroy allows the DLL to deallocate any memory allocated in the eaStoreStateCreate call. This mechanism allows a user to save a partially run simulation and then reload it at a later date to continue.

Results

The PID integral and derivative terms are registered as results from our function. For notes on the result actions; register, log and derive, see the dynamic positioning example page. The results in that example have the same behaviour as here.

Diagnostics

One of the configuration parameters is used to request that diagnostic output is printed or suppressed. When OutputDiagnostics is set to Yes, messages output with the PrintDiagnostics function will appear in the OrcaFlex GUI, in the external code output window. The value of the OutputDiagnostics tag in the example file is No, so you should change this before running the simulation if you wish to receive the output.