Quick Start -- Simple Linear Damper Controller (C++)¶
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 C++ ROS 2 controller package from the mbari_wec_template_cpp template repository which we will use to implement a simple linear damper controller.
To begin, you should have a C++ ROS 2 controller package that looks similar to:
mbari_wec_linear_damper_cpp
├── CMakeLists.txt
├── config
│ └── controller.yaml
├── include
│ └── mbari_wec_linear_damper_cpp
│ ├── controller.hpp
│ └── control_policy.hpp
├── launch
│ └── controller.launch.py
├── LICENSE
├── package.xml
├── README.md
└── src
└── controller.cpp
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_cpp
.
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 and create the interpolator,
winding_current
, in ControlPolicy
in
include/mbari_wec_linear_damper_cpp/control_policy.hpp
. This
example makes use of <simple_interp/interp1d.hpp>
from mbari_wec_utils
, so don't forget to
include that as well as <algorithm>
and <vector>
.
include/mbari_wec_linear_damper_cpp/control_policy.hpp | |
---|---|
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
Update the dependent variable, I_Spec
, as well as the interpolator, winding_current
.
include/mbari_wec_linear_damper_cpp/control_policy.hpp | |
---|---|
58 59 60 61 62 63 64 65 66 67 |
|
Finally, define the set_params
function of the Controller
class and declare/get/set/update
these parameters from ROS 2 (as set in config/controller.yaml
).
include/mbari_wec_linear_damper_cpp/control_policy.hpp | |
---|---|
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
|
Add a helper function, for the ControlPolicy
class for this example to report the
parameters used.
include/mbari_wec_linear_damper_cpp/control_policy.hpp | |
---|---|
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
|
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
include/mbari_wec_linear_damper_cpp/control_policy.hpp | |
---|---|
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
|
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
void Controller::power_callback(const buoy_interfaces::msg::PCRecord & data)
in the Controller
class, and pass the data to this->policy_->target
to get the desired winding
current command. Various commands are available, and this time we will be using
this->send_pc_wind_curr_command(wind_curr_amps);
src/controller.cpp | |
---|---|
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
|
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
constructor:
src/controller.cpp | |
---|---|
22 23 24 25 26 27 28 29 30 31 32 33 |
|
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:
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.623306255] [mbari_wec_linear_damper_cpp]: Found all required services.
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.623437968] [mbari_wec_linear_damper_cpp]: ControlPolicy:
[mbari_wec_linear_damper_cpp-1] Torque_constant: 0.438
[mbari_wec_linear_damper_cpp-1] N_Spec: 0,300,600,1000,1700,4400,6790
[mbari_wec_linear_damper_cpp-1] Torque_Spec: 0,0,0.8,2.9,5.6,9.8,16.6
[mbari_wec_linear_damper_cpp-1] I_Spec: 0,0,1.82648,6.621,12.7854,22.3744,37.8995
[mbari_wec_linear_damper_cpp-1]
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.623585767] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2911.62, 1, 0.6) = -10.2531
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.623769091] [mbari_wec_linear_damper_cpp]: Successfully set publish_rate for power_controller
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.723139301] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2881.34, 1, 0.6) = -10.1886
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.723199020] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2881.34, 1, 0.6) = -10.1886
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.743295542] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2864.91, 1, 0.6) = -10.1535
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.763406662] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2845.41, 1, 0.6) = -10.112
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.783518884] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2822.43, 1, 0.6) = -10.063
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.803625212] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2796.29, 1, 0.6) = -10.0073
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.823736947] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2767.06, 1, 0.6) = -9.94502
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.843817290] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2734.81, 1, 0.6) = -9.87631
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.863931284] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2699.67, 1, 0.6) = -9.80143
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.884041064] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2661.74, 1, 0.6) = -9.7206
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.904159386] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2621.09, 1, 0.6) = -9.63398
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.924232170] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2577.85, 1, 0.6) = -9.54184
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.944361837] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2532.15, 1, 0.6) = -9.44445
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.964467851] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2484.08, 1, 0.6) = -9.34203
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215528.984588134] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2433.76, 1, 0.6) = -9.23479
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215529.004697490] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2381.3, 1, 0.6) = -9.12301
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215529.024807195] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2326.81, 1, 0.6) = -9.00691
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215529.044928940] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2270.4, 1, 0.6) = -8.88669
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215529.065039660] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2212.17, 1, 0.6) = -8.76262
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215529.085145551] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2152.24, 1, 0.6) = -8.63491
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215529.105256007] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2090.71, 1, 0.6) = -8.50379
[mbari_wec_linear_damper_cpp-1] [INFO] [1680215529.125363653] [mbari_wec_linear_damper_cpp]: WindingCurrent: f(2027.67, 1, 0.6) = -8.36947