@@ -110,13 +110,15 @@ using namespace systems;
110110namespace turning_direction {
111111static const int kCcw = 1 ;
112112static const int kCw = -1 ;
113+ static const int kBidirectional = 1 ;
113114} // namespace turning_direction
114115
115116// / \brief Type of input command to motor.
116117enum class MotorType {
117118 kVelocity ,
118119 kPosition ,
119- kForce
120+ kForce ,
121+ kForcePolynomial
120122};
121123
122124class 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
699783GZ_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