Skip to content

Commit 82070e1

Browse files
committed
feat: add bidirectional support
1 parent eea1c9a commit 82070e1

File tree

1 file changed

+95
-15
lines changed

1 file changed

+95
-15
lines changed

src/systems/multicopter_motor_model/MulticopterMotorModel.cc

+95-15
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,15 @@ using namespace systems;
110110
namespace turning_direction {
111111
static const int kCcw = 1;
112112
static const int kCw = -1;
113+
static const int kBidirectional = 1;
113114
} // namespace turning_direction
114115

115116
/// \brief Type of input command to motor.
116117
enum class MotorType {
117118
kVelocity,
118119
kPosition,
119-
kForce
120+
kForce,
121+
kForcePolynomial
120122
};
121123

122124
class gz::sim::systems::MulticopterMotorModelPrivate
@@ -218,6 +220,10 @@ class gz::sim::systems::MulticopterMotorModelPrivate
218220
/// \brief Mutex to protect recvdActuatorsMsg.
219221
public: std::mutex recvdActuatorsMsgMutex;
220222

223+
/// \brief Polynomial for RPM to Thrust conversion - admits 5 degrees.
224+
public: std::vector<double> TorquePolynomial = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
225+
public: std::vector<double> ThrustPolynomial = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
226+
221227
/// \brief Gazebo communication node.
222228
public: transport::Node node;
223229
};
@@ -308,8 +314,10 @@ void MulticopterMotorModel::Configure(const Entity &_entity,
308314
this->dataPtr->turningDirection = turning_direction::kCw;
309315
else if (turningDirection == "ccw")
310316
this->dataPtr->turningDirection = turning_direction::kCcw;
317+
else if (turningDirection == "bidirectional")
318+
this->dataPtr->turningDirection = turning_direction::kBidirectional;
311319
else
312-
gzerr << "Please only use 'cw' or 'ccw' as turningDirection.\n";
320+
gzerr << "Please only use 'cw', 'ccw', or 'bidirectional' as turningDirection.\n";
313321
}
314322
else
315323
{
@@ -331,10 +339,58 @@ void MulticopterMotorModel::Configure(const Entity &_entity,
331339
this->dataPtr->motorType = MotorType::kForce;
332340
gzerr << "motorType 'force' not supported" << std::endl;
333341
}
342+
else if (motorType == "force_polynomial")
343+
{
344+
this->dataPtr->motorType = MotorType::kForcePolynomial;
345+
346+
if (sdfClone->HasElement("a0ThrustConstant") &&
347+
sdfClone->HasElement("a1ThrustConstant") &&
348+
sdfClone->HasElement("a2ThrustConstant") &&
349+
sdfClone->HasElement("a3ThrustConstant") &&
350+
sdfClone->HasElement("a0TorqueConstant") &&
351+
sdfClone->HasElement("a1TorqueConstant") &&
352+
sdfClone->HasElement("a2TorqueConstant") &&
353+
sdfClone->HasElement("a3TorqueConstant"))
354+
{
355+
// Add polynomial coefficients
356+
if (this->dataPtr->jointName != "") {
357+
dataPtr->parentLinkName = _ecm.Component<components::ParentLinkName>(dataPtr->jointEntity)->Data();
358+
dataPtr->parentLinkEntity = dataPtr->model.LinkByName(_ecm, dataPtr->parentLinkName);
359+
}
360+
361+
auto a0Thrust = sdfClone->Get<double>("a0ThrustConstant");
362+
auto a1Thrust = sdfClone->Get<double>("a1ThrustConstant");
363+
auto a2Thrust = sdfClone->Get<double>("a2ThrustConstant");
364+
auto a3Thrust = sdfClone->Get<double>("a3ThrustConstant");
365+
366+
this->dataPtr->ThrustPolynomial = {a0Thrust, a1Thrust, a2Thrust, a3Thrust};
367+
368+
auto a0Torque = sdfClone->Get<double>("a0TorqueConstant");
369+
auto a1Torque = sdfClone->Get<double>("a1TorqueConstant");
370+
auto a2Torque = sdfClone->Get<double>("a2TorqueConstant");
371+
auto a3Torque = sdfClone->Get<double>("a3TorqueConstant");
372+
373+
this->dataPtr->TorquePolynomial = {a0Torque, a1Torque, a2Torque, a3Torque};
374+
}
375+
else
376+
{
377+
gzerr << "Please specify the thrust and torque polynomial coefficients.\n";
378+
}
379+
380+
// Check for optional higher order coefficients
381+
if(auto a4Thrust = sdfClone->GetElement("a4ThrustConstant"))
382+
this->dataPtr->ThrustPolynomial.push_back(a4Thrust->Get<double>());
383+
if(auto a5Thrust = sdfClone->GetElement("a5ThrustConstant"))
384+
this->dataPtr->ThrustPolynomial.push_back(a5Thrust->Get<double>());
385+
if(auto a4Torque = sdfClone->GetElement("a4TorqueConstant"))
386+
this->dataPtr->TorquePolynomial.push_back(a4Torque->Get<double>());
387+
if(auto a5Torque = sdfClone->GetElement("a5TorqueConstant"))
388+
this->dataPtr->TorquePolynomial.push_back(a5Torque->Get<double>());
389+
}
334390
else
335391
{
336-
gzerr << "Please only use 'velocity', 'position' or "
337-
"'force' as motorType.\n";
392+
gzerr << "Please only use 'velocity', 'position', "
393+
"'force' or 'force_polynomial' as motorType.\n";
338394
}
339395
}
340396
else
@@ -399,8 +455,8 @@ void MulticopterMotorModel::PreUpdate(const UpdateInfo &_info,
399455
if (_info.dt < std::chrono::steady_clock::duration::zero())
400456
{
401457
gzwarn << "Detected jump back in time ["
402-
<< std::chrono::duration_cast<std::chrono::seconds>(_info.dt).count()
403-
<< "s]. System may not work properly." << std::endl;
458+
<< std::chrono::duration<double>(_info.dt).count()
459+
<< "s]. System may not work properly." << std::endl;
404460
}
405461

406462
// If the joint or links haven't been identified yet, look for them
@@ -411,7 +467,8 @@ void MulticopterMotorModel::PreUpdate(const UpdateInfo &_info,
411467

412468
const auto parentLinkName = _ecm.Component<components::ParentLinkName>(
413469
this->dataPtr->jointEntity);
414-
this->dataPtr->parentLinkName = parentLinkName->Data();
470+
if (parentLinkName)
471+
this->dataPtr->parentLinkName = parentLinkName->Data();
415472
}
416473

417474
if (this->dataPtr->linkEntity == kNullEntity)
@@ -523,15 +580,16 @@ void MulticopterMotorModelPrivate::UpdateForcesAndMoments(
523580

524581
if (msg.has_value())
525582
{
526-
if (this->actuatorNumber > msg->velocity_size() - 1)
583+
if (this->actuatorNumber > msg->velocity_size() - 1 ||
584+
this->actuatorNumber > msg->normalized_size() - 1)
527585
{
528586
gzerr << "You tried to access index " << this->actuatorNumber
529-
<< " of the Actuator velocity array which is of size "
530-
<< msg->velocity_size() << std::endl;
587+
<< " of the Actuator array which is of size "
588+
<< std::max(msg->velocity_size(), msg->normalized_size()) << std::endl;
531589
return;
532590
}
533591

534-
if (this->motorType == MotorType::kVelocity)
592+
if (this->motorType == MotorType::kVelocity || this->motorType == MotorType::kForcePolynomial)
535593
{
536594
this->refMotorInput = std::min(
537595
static_cast<double>(msg->velocity(this->actuatorNumber)),
@@ -558,6 +616,32 @@ void MulticopterMotorModelPrivate::UpdateForcesAndMoments(
558616
// joint_->SetForce(0, this->refMotorInput);
559617
break;
560618
}
619+
case (MotorType::kForcePolynomial):
620+
{
621+
sim::Link link(this->linkEntity);
622+
const auto worldPose = link.WorldPose(_ecm);
623+
using Vector3 = math::Vector3d;
624+
625+
// Compute thrust
626+
double thrust = 0.0;
627+
for (unsigned int i = 0; i < this->ThrustPolynomial.size(); i++) {
628+
thrust += this->ThrustPolynomial[i] * std::pow(this->refMotorInput, i);
629+
}
630+
link.AddWorldForce(_ecm, worldPose->Rot().RotateVector(Vector3(0, 0, thrust)));
631+
632+
// Compute torques
633+
double torque = 0.0;
634+
for (unsigned int i = 0; i < this->TorquePolynomial.size(); i++) {
635+
torque += this->TorquePolynomial[i] * std::pow(this->refMotorInput, i);
636+
}
637+
link.AddWorldForce(_ecm, worldPose->Rot().RotateVector(Vector3(0, 0, torque)));
638+
639+
// Set joint velocities
640+
const auto jointVelCmd = _ecm.Component<components::JointVelocityCmd>(
641+
this->jointEntity);
642+
*jointVelCmd = components::JointVelocityCmd({this->refMotorInput / this->rotorVelocitySlowdownSim});
643+
break;
644+
}
561645
default: // MotorType::kVelocity
562646
{
563647
const auto jointVelocity = _ecm.Component<components::JointVelocity>(
@@ -698,7 +782,3 @@ GZ_ADD_PLUGIN(MulticopterMotorModel,
698782

699783
GZ_ADD_PLUGIN_ALIAS(MulticopterMotorModel,
700784
"gz::sim::systems::MulticopterMotorModel")
701-
702-
// TODO(CH3): Deprecated, remove on version 8
703-
GZ_ADD_PLUGIN_ALIAS(MulticopterMotorModel,
704-
"ignition::gazebo::systems::MulticopterMotorModel")

0 commit comments

Comments
 (0)