18 #include <boost/algorithm/clamp.hpp> 37 #if GAZEBO_MAJOR_VERSION >= 8 49 std::lock_guard<std::mutex> lock(this->
plugin->
mutex);
50 #if GAZEBO_MAJOR_VERSION >= 8 63 std::lock_guard<std::mutex> lock(this->
plugin->
mutex);
64 this->
desiredAngle = boost::algorithm::clamp(_msg->data, -this->maxAngle,
70 const std::string &_paramName,
const double _defaultVal)
const 72 if (!_sdfPtr->HasElement(_paramName))
75 "Using default value of <" << _defaultVal <<
">.");
79 double val = _sdfPtr->Get<
double>(_paramName);
81 "> to <" << val <<
">.");
88 ROS_DEBUG(
"Loading usv_gazebo_thrust_plugin");
89 this->model = _parent;
90 this->world = this->model->GetWorld();
93 std::string nodeNamespace =
"";
94 if (_sdf->HasElement(
"robotNamespace"))
96 nodeNamespace = _sdf->Get<std::string>(
"robotNamespace") +
"/";
100 this->cmdTimeout = this->SdfParamDouble(_sdf,
"cmdTimeout", 1.0);
105 int thrusterCounter = 0;
106 if (_sdf->HasElement(
"thruster"))
108 sdf::ElementPtr thrusterSDF = _sdf->GetElement(
"thruster");
118 if (thrusterSDF->HasElement(
"linkName"))
120 std::string linkName = thrusterSDF->Get<std::string>(
"linkName");
121 thruster.
link = this->model->GetLink(linkName);
125 <<
"> in the model!");
138 if (thrusterSDF->HasElement(
"propJointName"))
140 std::string propName =
141 thrusterSDF->GetElement(
"propJointName")->Get<std::string>();
142 thruster.
propJoint = this->model->GetJoint(propName);
146 << propName <<
"> in the model!");
151 "> added to thruster");
161 if (thrusterSDF->HasElement(
"engineJointName"))
163 std::string engineName =
164 thrusterSDF->GetElement(
"engineJointName")->Get<std::string>();
165 thruster.
engineJoint = this->model->GetJoint(engineName);
169 engineName <<
"> in the model!");
174 "> added to thruster");
184 if (thrusterSDF->HasElement(
"cmdTopic"))
186 thruster.
cmdTopic = thrusterSDF->Get<std::string>(
"cmdTopic");
191 "for each thruster!");
195 if (thrusterSDF->HasElement(
"angleTopic"))
197 thruster.
angleTopic = thrusterSDF->Get<std::string>(
"angleTopic");
202 "for each thruster!");
206 if (thrusterSDF->HasElement(
"enableAngle"))
208 thruster.
enableAngle = thrusterSDF->Get<
bool>(
"enableAngle");
213 "angle adjustment (for ROS subscription)!");
218 if (thrusterSDF->HasElement(
"mappingType"))
220 thruster.
mappingType = thrusterSDF->Get<
int>(
"mappingType");
228 "Using default value of <" << thruster.
mappingType <<
">.");
231 thruster.
maxCmd = this->SdfParamDouble(thrusterSDF,
"maxCmd", 1.0);
233 this->SdfParamDouble(thrusterSDF,
"maxForceFwd", 250.0);
235 this->SdfParamDouble(thrusterSDF,
"maxForceRev", -100.0);
236 thruster.
maxAngle = this->SdfParamDouble(thrusterSDF,
"maxAngle",
240 this->thrusters.push_back(thruster);
241 thrusterSDF = thrusterSDF->GetNextElement(
"thruster");
247 ROS_WARN_STREAM(
"No 'thruster' tags in description - how will you move?");
257 this->jointStatePub =
258 this->rosnode->advertise<sensor_msgs::JointState>(
"joint_states", 1);
259 this->jointStateMsg.name.resize(2 * thrusters.size());
260 this->jointStateMsg.position.resize(2 * thrusters.size());
261 this->jointStateMsg.velocity.resize(2 * thrusters.size());
262 this->jointStateMsg.effort.resize(2 * thrusters.size());
264 for (
size_t i = 0; i < this->thrusters.size(); ++i)
267 this->jointStateMsg.name[2 * i] = this->thrusters[i].engineJoint->GetName();
268 this->jointStateMsg.name[2 * i + 1] =
269 this->thrusters[i].propJoint->GetName();
272 this->thrusters[i].cmdSub = this->rosnode->subscribe(
274 &this->thrusters[i]);
279 this->thrusters[i].angleSub = this->rosnode->subscribe(
281 &this->thrusters[i]);
285 this->updateConnection = event::Events::ConnectWorldUpdateBegin(
291 const double _maxPos,
const double _maxNeg)
const 296 val = _cmd / _maxCmd * _maxPos;
297 val = std::min(val, _maxPos);
301 double absMaxNeg = std::abs(_maxNeg);
302 val = _cmd / _maxCmd * absMaxNeg;
303 val = std::max(val, -1.0 * absMaxNeg);
310 const float _B,
const float _v,
const float _C,
const float _M)
const 312 return _A + (_K - _A) / (pow(_C + exp(-_B * (_x - _M)), 1.0 / _v));
317 const double _maxPos,
318 const double _maxNeg)
const 323 val = this->Glf(_cmd, 0.01
f, 59.82
f, 5.0
f, 0.38
f, 0.56
f, 0.28
f);
324 val = std::min(val, _maxPos);
326 else if (_cmd < 0.01)
328 val = this->Glf(_cmd, -199.13
f, -0.09
f, 8.84
f, 5.34
f, 0.99
f, -0.57
f);
329 val = std::max(val, _maxNeg);
337 #if GAZEBO_MAJOR_VERSION >= 8 338 common::Time now = this->world->SimTime();
340 common::Time now = this->world->GetSimTime();
343 for (
size_t i = 0; i < this->thrusters.size(); ++i)
346 std::lock_guard<std::mutex> lock(this->mutex);
348 double dtc = (now - this->thrusters[i].lastCmdTime).Double();
349 if (dtc > this->cmdTimeout && this->cmdTimeout > 0.0)
351 this->thrusters[i].currCmd = 0.0;
359 ignition::math::Vector3d tforcev(0, 0, 0);
363 tforcev.X() = this->ScaleThrustCmd(this->thrusters[i].
currCmd/
364 this->thrusters[i].
maxCmd,
365 this->thrusters[i].maxCmd,
370 tforcev.X() = this->GlfThrustCmd(this->thrusters[i].
currCmd/
371 this->thrusters[i].maxCmd,
372 this->thrusters[i].maxForceFwd,
373 this->thrusters[i].maxForceRev);
377 this->thrusters[i].mappingType);
382 this->thrusters[i].link->AddLinkForce(tforcev);
385 this->SpinPropeller(i);
391 this->jointStatePub.publish(this->jointStateMsg);
399 #if GAZEBO_MAJOR_VERSION >= 8 400 double currAngle = this->thrusters[_i].engineJoint->Position(0);
402 double currAngle = this->thrusters[_i].engineJoint->GetAngle(0).Radian();
406 double effort = this->thrusters[_i].engineJointPID.Update(angleError,
408 this->thrusters[_i].engineJoint->SetForce(0, effort);
411 #if GAZEBO_MAJOR_VERSION >= 8 412 ignition::math::Angle position =
413 this->thrusters[_i].engineJoint->Position(0);
415 gazebo::math::Angle position = this->thrusters[_i].engineJoint->GetAngle(0);
417 position.Normalize();
418 this->jointStateMsg.position[2 * _i] = position.Radian();
419 this->jointStateMsg.velocity[2 * _i] =
420 this->thrusters[_i].engineJoint->GetVelocity(0);
421 this->jointStateMsg.effort[2 * _i] = effort;
424 this->thrusters[_i].lastAngleUpdateTime += _stepTime;
430 const double kMinInput = 0.1;
431 const double kMaxEffort = 2.0;
434 physics::JointPtr propeller = this->thrusters[_i].propJoint;
437 if (std::abs(this->thrusters[_i].
currCmd/
438 this->thrusters[_i].
maxCmd) > kMinInput)
439 effort = (this->thrusters[_i].currCmd /
440 this->thrusters[_i].maxCmd) * kMaxEffort;
442 propeller->SetForce(0, effort);
445 #if GAZEBO_MAJOR_VERSION >= 8 446 ignition::math::Angle position = propeller->Position(0);
448 gazebo::math::Angle position = propeller->GetAngle(0);
450 position.Normalize();
451 this->jointStateMsg.position[2 * _i + 1] = position.Radian();
452 this->jointStateMsg.velocity[2 * _i + 1] = propeller->GetVelocity(0);
453 this->jointStateMsg.effort[2 * _i + 1] = effort;
double desiredAngle
Most recent desired angle.
GZ_REGISTER_MODEL_PLUGIN(UsvThrust)
void RotateEngine(size_t _i, common::Time _stepTime)
Rotate engine using engine joint PID.
physics::WorldPtr world
Pointer to the Gazebo world, retrieved when the model is loaded.
void OnThrustCmd(const std_msgs::Float32::ConstPtr &_msg)
Callback for new thrust commands.
A plugin to simulate a propulsion system under water. This plugin accepts the following SDF parameter...
physics::JointPtr propJoint
Joint controlling the propeller.
physics::LinkPtr link
Link where thrust force is applied.
double Glf(const double _x, const float _A, const float _K, const float _B, const float _v, const float _C, const float _M) const
Generalized logistic function (GLF) used for non-linear thruster model.
void OnThrustAngle(const std_msgs::Float32::ConstPtr &_msg)
Callback for new thrust angle commands.
virtual void Update()
Callback executed at every physics update.
double maxForceFwd
Max forward force in Newtons.
#define ROS_DEBUG_STREAM_THROTTLE(period, args)
double GlfThrustCmd(const double _cmd, const double _maxPos, const double _maxNeg) const
Uses GLF function to map thrust command to thruster force in Newtons.
double maxForceRev
Max reverse force in Newtons.
common::PID engineJointPID
PID for engine joint angle.
double ScaleThrustCmd(const double _cmd, const double _max_cmd, const double _max_pos, const double _max_neg) const
Takes ROS Drive commands and scales them by max thrust.
#define ROS_FATAL_STREAM(args)
Thruster(UsvThrust *_parent)
Constructor.
common::Time lastCmdTime
Last time received a command via ROS topic.
UsvThrust * plugin
Plugin parent pointer - for accessing world, etc.
#define ROS_WARN_STREAM(args)
#define ROS_DEBUG_STREAM(args)
void SpinPropeller(size_t _i)
Spin a propeller based on its current command.
common::Time lastAngleUpdateTime
Last time of update.
std::mutex mutex
A mutex to protect member variables accessed during OnThustCmd() and Update().
bool enableAngle
If true, thruster will have adjustable angle. If false, thruster will have constant angle...
#define ROS_INFO_STREAM(args)
virtual void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
double maxCmd
Maximum abs val of incoming command.
int mappingType
Thruster mapping (0=linear; 1=GLF, nonlinear).
std::string cmdTopic
Topic name for incoming ROS thruster commands.
#define ROS_ERROR_STREAM(args)
std::string angleTopic
Topic name for incoming ROS thruster angle commands.
double maxAngle
Maximum abs val of angle.
physics::JointPtr engineJoint
Joint controlling the engine.
double SdfParamDouble(sdf::ElementPtr _sdfPtr, const std::string &_paramName, const double _defaultVal) const
Convenience function for getting SDF parameters.
double currCmd
Current, most recent command.