Quick Start -- Simple Linear Damper Controller (Python)¶
In this tutorial you will implement a simple linear damper controller for the piston in the WEC Power-Take-Off (PTO). Given motor RPM, it outputs desired motor winding current (interpolated from RPM->Torque lookup table) to generate a torque to resist piston velocity with a damping force. Configurable gains (scale/retract factor) are applied before output. In the end, you will have a working linear damper controller that is very close to the controller running on both the physical and simulated buoy.
Prerequisite¶
This tutorial assumes you are familiar the steps from the previous tutorial and have built your own custom Python ROS 2 controller package from the mbari_wec_template_py template repository which we will use to implement a simple linear damper controller.
To begin, you should have a Python ROS 2 controller package that looks similar to:
mbari_wec_linear_damper_py
├── config
│ └── controller.yaml
├── CONTRIBUTING.md
├── launch
│ └── controller.launch.py
├── LICENSE
├── mbari_wec_linear_damper_py
│ ├── controller.py
│ ├── __init__.py
├── package.xml
├── README.md
├── resource
│ └── mbari_wec_linear_damper_py
├── setup.cfg
├── setup.py
└── test
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py
with the files modified from the previous tutorial. If you haven't already, follow the steps in
the above mentioned link to create a package for this tutorial named mbari_wec_linear_damper_py
.
Linear Damper ControlPolicy¶
A complete example starting from the template may be found here. Line numbers in this tutorial correspond to the lines in relevant files in the full example.
Parameters¶
Parameters for the controller are:
torque_constant
: Motor Torque Constant (N-m/Amp)
Constant to convert desired torque to applied motor winding current
n_spec
: Input Motor Speed (RPM) Breakpoints
(RPM) is the input to the controller andn_spec
are the x-components of the breakpoints for the interpolant,
torque_spec
: Desired Output Motor Torque (N-m) Breakpoints
Torque (N-m) is the eventual desired output of the controller given an inputN
(motor RPM) andtorque_spec
/torque_constant
(Amps) are the y-components of the breakpoints for the interpolant. The controller actually outputs motor winding current (Amps) to generate a torque in the opposite direction of piston velocity to generate a damping force.
These can be configured using the config/controller.yaml
file.
config/controller.yaml | |
---|---|
1 2 3 4 5 |
|
As you can see, as motor speed increases, so does the damping torque. For low RPM (up to 300), there is no damping.
Initialize these variables in ControlPolicy
in mbari_wec_linear_damper_py/controller.py
. This
example makes use of numpy.array
as well as scipy.interpolate.interp1d
, so don't forget to
include those.
mbari_wec_linear_damper_py/controller.py | |
---|---|
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Update the dependent variable, I_Spec
, and create the interpolator, windcurr_interp1d
, which
uses interp1d
from scipy.interpolate
.
mbari_wec_linear_damper_py/controller.py | |
---|---|
43 44 45 46 47 48 49 |
|
Finally, in the Controller
class, declare/get/set/update these parameters from ROS 2 (as set in
config/controller.yaml
).
mbari_wec_linear_damper_py/controller.py | |
---|---|
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
|
Add a helper function, __str__
, in the ControlPolicy
class for this example to report the
parameters used.
mbari_wec_linear_damper_py/controller.py | |
---|---|
66 67 68 69 70 71 72 73 74 |
|
Control Policy Target¶
To implement the torque control control policy, we use the target
function in ControlPolicy
.
This is where we accept feedback data and return a command value. In this case, we need the motor
rpm
, and the gains applied to the winding current damping, scale_factor
and retract_factor
.
Typical values for these gains are
- scale_factor = 1
- retract_factor = 0.6
mbari_wec_linear_damper_py/controller.py | |
---|---|
52 53 54 55 56 57 58 59 60 61 62 63 64 |
|
So, as you can see we apply a positive damping torque when RPM is negative (piston extending), and a positive damping torque when RPM is positive (piston retracting). The damping torque required is reduced when retracting.
Controller¶
All that is left is to connect the necessary feedback data to the ControlPolicy
. In this case,
rpm
, scale
, and retract
are present in buoy_interfaces.msg.PCRecord
on the /power_data
topic published by the Power Controller running on the buoy.
To access the data, all that is required is to define the callback def power_callback(self, data)
in the Controller
class, and pass the data to self.policy.target
to get the desired winding
current command. Various commands are available, and this time we will be using
self.send_pc_wind_curr_command(wind_curr, blocking=False)
mbari_wec_linear_damper_py/controller.py | |
---|---|
107 108 109 110 111 112 113 114 115 116 |
|
Finally, let's set the Power Controller's publish rate to the maximum of 50Hz. Uncomment the line
to set the PC Pack Rate in Controller.__init__
:
mbari_wec_linear_damper_py/controller.py | |
---|---|
79 80 81 82 83 84 85 86 87 88 |
|
In this tutorial, we've named this controller linear_damper
. Don't forget to update controller
names along with other changes according to the previous tutorial.
Try It Out¶
Make sure to build and source your workspace. This tutorial assumes you cloned your package to
~/controller_ws/src
and you have sourced mbari_wec_gz
and mbari_wec_utils
$ cd ~/controller_ws
$ colcon build
$ source install/local_setup.bash
We will be using ros2 launch
and launch/controller.launch.py
to run our new controller.
To run the controller along with the simulation, launch your
controller:
$ ros2 launch mbari_wec_linear_damper_cpp controller.launch.py
Then, in another terminal (with mbari_wec_gz
sourced), launch the sim:
$ ros2 launch buoy_gazebo mbari_wec.launch.py
and click the play button.
You should see output similar to:
[linear_damper-1] [INFO] [1677864397.617058507] [linear_damper]: Found all required services.
[linear_damper-1] [INFO] [1677864397.618426488] [linear_damper]: ControlPolicy:
[linear_damper-1] Torque_constant: 0.438
[linear_damper-1] N_Spec: [ 0. 300. 600. 1000. 1700. 4400. 6790.]
[linear_damper-1] Torque_Spec: [ 0. 0. 0.8 2.9 5.6 9.8 16.6]
[linear_damper-1] I_Spec: [ 0. 0. 1.82648402 6.62100457 12.78538813 22.37442922
[linear_damper-1] 37.89954338]
[linear_damper-1] [INFO] [1677864197.432679525] [linear_damper]: WindingCurrent: f(4962.91, 1.00, 0.60) = -15.62
[linear_damper-1] [INFO] [1677864197.532727531] [linear_damper]: WindingCurrent: f(7764.73, 1.00, 0.60) = -22.74
[linear_damper-1] [INFO] [1677864197.632748699] [linear_damper]: WindingCurrent: f(10504.88, 1.00, 0.60) = -22.74
[linear_damper-1] [INFO] [1677864197.732851121] [linear_damper]: WindingCurrent: f(11491.33, 1.00, 0.60) = -22.74
[linear_damper-1] [INFO] [1677864197.833078440] [linear_damper]: WindingCurrent: f(11075.84, 1.00, 0.60) = -22.74
[linear_damper-1] [INFO] [1677864197.933050356] [linear_damper]: WindingCurrent: f(9546.51, 1.00, 0.60) = -22.74
[linear_damper-1] [INFO] [1677864198.033185882] [linear_damper]: WindingCurrent: f(7499.68, 1.00, 0.60) = -22.74
[linear_damper-1] [INFO] [1677864198.133197926] [linear_damper]: WindingCurrent: f(5190.35, 1.00, 0.60) = -16.51
[linear_damper-1] [INFO] [1677864198.233322713] [linear_damper]: WindingCurrent: f(2353.02, 1.00, 0.60) = -9.06
[linear_damper-1] [INFO] [1677864198.333507127] [linear_damper]: WindingCurrent: f(-257.59, 1.00, 0.60) = 0.00
[linear_damper-1] [INFO] [1677864198.433489830] [linear_damper]: WindingCurrent: f(-2185.58, 1.00, 0.60) = 14.51
[linear_damper-1] [INFO] [1677864198.533538450] [linear_damper]: WindingCurrent: f(-2987.98, 1.00, 0.60) = 17.36
[linear_damper-1] [INFO] [1677864198.633671249] [linear_damper]: WindingCurrent: f(-3513.15, 1.00, 0.60) = 19.22
[linear_damper-1] [INFO] [1677864198.733703803] [linear_damper]: WindingCurrent: f(-3738.12, 1.00, 0.60) = 20.02
[linear_damper-1] [INFO] [1677864198.833889518] [linear_damper]: WindingCurrent: f(-3751.64, 1.00, 0.60) = 20.07
[linear_damper-1] [INFO] [1677864198.933993414] [linear_damper]: WindingCurrent: f(-3595.71, 1.00, 0.60) = 19.52
[linear_damper-1] [INFO] [1677864199.034078009] [linear_damper]: WindingCurrent: f(-3306.87, 1.00, 0.60) = 18.49
[linear_damper-1] [INFO] [1677864199.134273438] [linear_damper]: WindingCurrent: f(-3012.52, 1.00, 0.60) = 17.45
[linear_damper-1] [INFO] [1677864199.234371669] [linear_damper]: WindingCurrent: f(-2617.97, 1.00, 0.60) = 16.05
[linear_damper-1] [INFO] [1677864199.334275962] [linear_damper]: WindingCurrent: f(-2269.58, 1.00, 0.60) = 14.81
[linear_damper-1] [INFO] [1677864199.434369620] [linear_damper]: WindingCurrent: f(-1893.56, 1.00, 0.60) = 13.47
[linear_damper-1] [INFO] [1677864199.534461914] [linear_damper]: WindingCurrent: f(-1513.34, 1.00, 0.60) = 11.14
[linear_damper-1] [INFO] [1677864199.634556815] [linear_damper]: WindingCurrent: f(-1128.46, 1.00, 0.60) = 7.75
[linear_damper-1] [INFO] [1677864199.734798736] [linear_damper]: WindingCurrent: f(-825.91, 1.00, 0.60) = 4.53
[linear_damper-1] [INFO] [1677864199.834753871] [linear_damper]: WindingCurrent: f(-586.78, 1.00, 0.60) = 1.75
[linear_damper-1] [INFO] [1677864199.934809041] [linear_damper]: WindingCurrent: f(-393.25, 1.00, 0.60) = 0.57
[linear_damper-1] [INFO] [1677864200.035109715] [linear_damper]: WindingCurrent: f(-132.04, 1.00, 0.60) = 0.00
[linear_damper-1] [INFO] [1677864200.134981992] [linear_damper]: WindingCurrent: f(92.19, 1.00, 0.60) = -0.00
[linear_damper-1] [INFO] [1677864200.235094219] [linear_damper]: WindingCurrent: f(338.10, 1.00, 0.60) = -0.14
[linear_damper-1] [INFO] [1677864200.335164181] [linear_damper]: WindingCurrent: f(636.96, 1.00, 0.60) = -1.36
[linear_damper-1] [INFO] [1677864200.435227880] [linear_damper]: WindingCurrent: f(863.33, 1.00, 0.60) = -2.99