ign_ros_control_plugin.cpp
Go to the documentation of this file.
1 // Copyright 2022 The ros_control team.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <ignition/gazebo/components/Joint.hh>
16 #include <ignition/gazebo/components/JointType.hh>
17 #include <ignition/gazebo/components/Name.hh>
18 #include <ignition/gazebo/components/ParentEntity.hh>
19 #include <ignition/gazebo/components/World.hh>
20 #include <ignition/gazebo/Model.hh>
21 
22 #include <ignition/plugin/Register.hh>
23 
24 #include <map>
25 #include <memory>
26 #include <string>
27 #include <utility>
28 #include <vector>
29 
30 #include <ros/ros.h>
31 
35 
37 
38 #include <urdf/model.h>
39 
42 
43 namespace ign_ros_control
44 {
47 {
48 public:
49 
50  IgnitionROSControlPluginPrivate(const std::string& robot_namespace,
51  const std::string& robot_description
52  )
53  : model_nh_(robot_namespace)
54  , robot_description_(robot_description)
55  {
56  async_ros_spin_.reset(new ros::AsyncSpinner(0)); // will use a thread for each CPU core
57  async_ros_spin_->start();
58  }
59 
61  {
62  async_ros_spin_->stop();
63  }
64 
66  std::string getURDF(std::string param_name) const;
67 
68  bool parseTransmissionsFromURDF(const std::string &urdf_string);
69 
77  std::map<std::string, ignition::gazebo::Entity> GetEnabledJoints(
78  const ignition::gazebo::Entity & _entity,
79  ignition::gazebo::EntityComponentManager & _ecm) const;
80 
81  // Transmissions in this plugin's scope
82  std::vector<transmission_interface::TransmissionInfo> transmissions_;
83 
85  ignition::gazebo::Entity entity_;
86 
89 
91  std::shared_ptr<pluginlib::ClassLoader<
94 
96  std::shared_ptr<controller_manager::ControllerManager>
98 
101  robot_hw_{nullptr};
102 
105 
107  ignition::gazebo::EntityComponentManager * ecm_{nullptr};
108 
109  // Node Handles
110  ros::NodeHandle model_nh_; // namespaces to robot name
111 
113  std::string robot_description_ = "robot_description";
114 
116  std::shared_ptr<ros::AsyncSpinner> async_ros_spin_;
117 };
118 
120 std::map<std::string, ignition::gazebo::Entity>
122  const ignition::gazebo::Entity & _entity,
123  ignition::gazebo::EntityComponentManager & _ecm) const
124 {
125  std::map<std::string, ignition::gazebo::Entity> output;
126 
127  std::vector<std::string> enabledJoints;
128 
129  // Get all available joints
130  auto jointEntities = _ecm.ChildrenByComponents(_entity, ignition::gazebo::components::Joint());
131 
132  // Iterate over all joints and verify whether they can be enabled or not
133  for (const auto & jointEntity : jointEntities) {
134  const auto jointName = _ecm.Component<ignition::gazebo::components::Name>(
135  jointEntity)->Data();
136 
137  // Make sure the joint type is supported, i.e. it has exactly one
138  // actuated axis
139  const auto * jointType = _ecm.Component<ignition::gazebo::components::JointType>(jointEntity);
140  switch (jointType->Data()) {
141  case sdf::JointType::PRISMATIC:
142  case sdf::JointType::REVOLUTE:
143  case sdf::JointType::CONTINUOUS:
144  case sdf::JointType::GEARBOX:
145  {
146  // Supported joint type
147  break;
148  }
149  case sdf::JointType::FIXED:
150  {
151  ROS_INFO(
152  "[Ignition ROS Control] Fixed joint [%s] (Entity=%lu)] is skipped",
153  jointName.c_str(), jointEntity);
154  continue;
155  }
156  case sdf::JointType::REVOLUTE2:
157  case sdf::JointType::SCREW:
158  case sdf::JointType::BALL:
159  case sdf::JointType::UNIVERSAL:
160  {
161  ROS_WARN(
162  "[Ignition ROS Control] Joint [%s] (Entity=%lu)] is of unsupported type."
163  " Only joints with a single axis are supported.",
164  jointName.c_str(), jointEntity);
165  continue;
166  }
167  default:
168  {
169  ROS_WARN(
170  "[Ignition ROS Control] Joint [%s] (Entity=%lu)] is of unknown type",
171  jointName.c_str(), jointEntity);
172  continue;
173  }
174  }
175  output[jointName] = jointEntity;
176  }
177 
178  return output;
179 }
180 
182 std::string IgnitionROSControlPluginPrivate::getURDF(std::string param_name) const
183 {
184  std::string urdf_string;
185 
186  // search and wait for robot_description on param server
187  while (urdf_string.empty())
188  {
189  std::string search_param_name;
190  if (model_nh_.searchParam(param_name, search_param_name))
191  {
192  ROS_INFO_ONCE("[Ignition ROS Control] ign_ros_control_plugin is waiting for model"
193  " URDF in parameter [%s] on the ROS param server.", search_param_name.c_str());
194 
195  model_nh_.getParam(search_param_name, urdf_string);
196  }
197  else
198  {
199  ROS_INFO_ONCE("[Ignition ROS Control] ign_ros_control_plugin is waiting for model"
200  " URDF in parameter [%s] on the ROS param server.", robot_description_.c_str());
201 
202  model_nh_.getParam(param_name, urdf_string);
203  }
204 
205  std::this_thread::sleep_for(std::chrono::microseconds(100000));
206  }
207  ROS_INFO_STREAM("[Ignition ROS Control] Recieved urdf from param server, parsing...");
208 
209  return urdf_string;
210 }
211 
212 // Get Transmissions from the URDF
214 {
216  return true;
217 }
218 
221  : dataPtr(nullptr)
222 {
223 }
224 
227 {
228 }
229 
232  const ignition::gazebo::Entity & _entity,
233  const std::shared_ptr<const sdf::Element> & _sdf,
234  ignition::gazebo::EntityComponentManager & _ecm,
235  ignition::gazebo::EventManager &)
236 {
237  // Get namespace for nodehandle
238  std::string robot_namespace, robot_description, robot_hw_sim_type_str;
239  int update_rate;
240  if(_sdf->HasElement("robotNamespace"))
241  {
242  robot_namespace = _sdf->Get<std::string>("robotNamespace");
243  }
244  else
245  {
246  robot_namespace = ""; // default
247  }
248  // Get robot_description ROS param name
249  if (_sdf->HasElement("robotParam"))
250  {
251  robot_description = _sdf->Get<std::string>("robotParam");
252  }
253  else
254  {
255  robot_description = "robot_description";
256  }
257  // Get the robot simulation interface type
258  if(_sdf->HasElement("robotSimType"))
259  {
260  robot_hw_sim_type_str = _sdf->Get<std::string>("robotSimType");
261  }
262  else
263  {
264  robot_hw_sim_type_str = "ign_ros_control/IgnitionSystem";
265  ROS_DEBUG_STREAM("[Ignition ROS Control] Using default plugin for RobotHWSim (none specified in URDF/SDF)\""<<robot_hw_sim_type_str<<"\"");
266  }
267  if(_sdf->HasElement("updateRate"))
268  {
269  update_rate = _sdf->Get<int>("updateRate");
270  }
271  else
272  {
273  ROS_ERROR("[Ignition ROS Control] No updateRate defined in the sdf file.");
274  return;
275  }
276 
277  // setup ros related
278  std::string ros_node_name = "ign_ros_control_plugin";
279  int argc = 1;
280  char* arg0 = strdup(ros_node_name.c_str());
281  char* argv[] = {arg0, 0};
282  if (!ros::isInitialized())
283  ros::init(argc,argv,ros_node_name,ros::init_options::NoSigintHandler);
284  else
285  ROS_ERROR("[Ignition ROS Control] Something other than this ign_ros_control_plugin started ros::init(...).");
286 
287  dataPtr = std::make_unique<IgnitionROSControlPluginPrivate>(robot_namespace,robot_description);
288 
289  // Make sure the controller is attached to a valid model
290  const auto model = ignition::gazebo::Model(_entity);
291  if (!model.Valid(_ecm)) {
292  ROS_ERROR(
293  "[Ignition ROS Control] Failed to initialize because [%s] (Entity=%lu)] is not a model."
294  "Please make sure that Ignition ROS Control is attached to a valid model.",
295  model.Name(_ecm).c_str(), _entity);
296  return;
297  }
298 
299  ROS_DEBUG_STREAM("[Ignition ROS Control] Setting up controller for [" <<
300  model.Name(_ecm) << "] (Entity=" << _entity << ")].");
301 
302  // Get list of enabled joints
303  auto enabledJoints = this->dataPtr->GetEnabledJoints(
304  _entity,
305  _ecm);
306 
307  if (enabledJoints.size() == 0) {
308  ROS_DEBUG_STREAM("[Ignition ROS Control] There are no available Joints.");
309  return;
310  }
311 
312  // Read urdf from ros parameter server then
313  // setup actuators and mechanism control node.
314  // This call will block if ROS is not properly initialized.
315  const std::string urdf_string = this->dataPtr->getURDF(robot_description);
316  if (!this->dataPtr->parseTransmissionsFromURDF(urdf_string))
317  {
318  ROS_ERROR("[Ignition ROS Control] Error parsing URDF in gazebo_ros_control plugin, plugin not active.\n");
319  return;
320  }
321 
322  // Load the RobotHWSim abstraction to interface the controllers with the gazebo model
323  try
324  {
325  this->dataPtr->robot_hw_sim_loader_.reset
327  ("ign_ros_control",
328  "ign_ros_control::IgnitionSystemInterface"));
329 
330  this->dataPtr->robot_hw_ = this->dataPtr->robot_hw_sim_loader_->createInstance(robot_hw_sim_type_str);
331  //urdf::Model urdf_model;
332  //const urdf::Model *const urdf_model_ptr = urdf_model.initString(urdf_string) ? &urdf_model : nullptr;
333 
334  if(!this->dataPtr->robot_hw_->initSim(this->dataPtr->model_nh_, enabledJoints, _ecm, this->dataPtr->transmissions_, update_rate))
335  {
336  ROS_FATAL("[Ignition ROS Control] Could not initialize robot simulation interface.");
337  return;
338  }
339 
340  // Create the controller manager
341  ROS_DEBUG_STREAM("[Ignition ROS Control] Loading controller_manager.");
342  this->dataPtr->controller_manager_.reset
343  (new controller_manager::ControllerManager(this->dataPtr->robot_hw_.get(), this->dataPtr->model_nh_));
344 
345 
346  }
348  {
349  ROS_FATAL_STREAM("[Ignition ROS Control] Failed to create robot simulation interface loader: "<<ex.what());
350  }
351 
352  this->dataPtr->control_period_ = ros::Duration(ros::Rate(update_rate));
353 
354  this->dataPtr->entity_ = _entity;
355 }
356 
359  const ignition::gazebo::UpdateInfo & _info,
360  ignition::gazebo::EntityComponentManager & /*_ecm*/)
361 {
362  static bool warned{false};
363  if (!warned) {
364  double dt = std::chrono::duration_cast<std::chrono::duration<double>>( _info.dt ).count(); // Secs
365  ros::Duration gazebo_period(dt);
366 
367  // Check the period against the simulation period
368  if (this->dataPtr->control_period_.toSec() < dt) {
370  "[Ignition ROS Control] Desired controller update period (" << this->dataPtr->control_period_.toSec() <<
371  " s) is faster than the gazebo simulation period (" <<
372  gazebo_period.toSec() << " s).");
373  } else if (this->dataPtr->control_period_.toSec() > gazebo_period.toSec()) {
375  "[Ignition ROS Control] Desired controller update period (" << this->dataPtr->control_period_.toSec() <<
376  " s) is slower than the gazebo simulation period (" <<
377  gazebo_period.toSec() << " s).");
378  }
379  warned = true;
380  }
381 
382  // Always set commands on joints, otherwise at low control frequencies the joints tremble
383  // as they are updated at a fraction of gazebo sim time
384  this->dataPtr->robot_hw_->write();
385 }
386 
389  const ignition::gazebo::UpdateInfo & _info,
390  const ignition::gazebo::EntityComponentManager & /*_ecm*/)
391 {
392 
393  // Get the simulation time and period
394  double sim_time = std::chrono::duration_cast<std::chrono::duration<double>>( _info.simTime ).count(); // Secs
395 
396  ros::Time sim_time_ros(sim_time);
397  ros::Duration sim_period(sim_time_ros.toSec() - this->dataPtr->last_update_sim_time_ros_.toSec());
398 
399  if (sim_period.toSec() >= this->dataPtr->control_period_.toSec()) {
400  this->dataPtr->last_update_sim_time_ros_ = sim_time_ros;
401  this->dataPtr->robot_hw_->read();
402  this->dataPtr->controller_manager_->update(sim_time_ros, sim_period);
403  }
404 
405 }
406 } // namespace ign_ros_control
407 
408 IGNITION_ADD_PLUGIN(
410  ignition::gazebo::System,
411  ign_ros_control::IgnitionROSControlPlugin::ISystemConfigure,
412  ign_ros_control::IgnitionROSControlPlugin::ISystemPreUpdate,
413  ign_ros_control::IgnitionROSControlPlugin::ISystemPostUpdate)
controller_manager::ControllerManager
transmission_interface::TransmissionParser::parse
static bool parse(const std::string &urdf_string, std::vector< TransmissionInfo > &transmissions)
ign_ros_control::IgnitionROSControlPlugin::~IgnitionROSControlPlugin
~IgnitionROSControlPlugin() override
Destructor.
Definition: ign_ros_control_plugin.cpp:226
ign_ros_control::IgnitionROSControlPlugin::PostUpdate
void PostUpdate(const ignition::gazebo::UpdateInfo &_info, const ignition::gazebo::EntityComponentManager &_ecm) override
Definition: ign_ros_control_plugin.cpp:388
ign_ros_control::IgnitionROSControlPluginPrivate::robot_description_
std::string robot_description_
Robot description.
Definition: ign_ros_control_plugin.cpp:113
ROS_ERROR_STREAM
#define ROS_ERROR_STREAM(args)
boost::shared_ptr< ign_ros_control::IgnitionSystemInterface >
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
ign_ros_control
Definition: ign_ros_control_plugin.hpp:22
ign_ros_control::IgnitionROSControlPluginPrivate::model_nh_
ros::NodeHandle model_nh_
Definition: ign_ros_control_plugin.cpp:110
ros::NodeHandle::getParam
bool getParam(const std::string &key, bool &b) const
ROS_FATAL_STREAM
#define ROS_FATAL_STREAM(args)
ros.h
ign_ros_control::IgnitionROSControlPlugin::IgnitionROSControlPlugin
IgnitionROSControlPlugin()
Constructor.
Definition: ign_ros_control_plugin.cpp:220
ros::AsyncSpinner
TimeBase< Time, Duration >::toSec
double toSec() const
ign_ros_control::IgnitionROSControlPluginPrivate::robot_hw_
boost::shared_ptr< ign_ros_control::IgnitionSystemInterface > robot_hw_
Robot hardware.
Definition: ign_ros_control_plugin.cpp:101
ign_ros_control::IgnitionROSControlPlugin::Configure
void Configure(const ignition::gazebo::Entity &_entity, const std::shared_ptr< const sdf::Element > &_sdf, ignition::gazebo::EntityComponentManager &_ecm, ignition::gazebo::EventManager &_eventMgr) override
Definition: ign_ros_control_plugin.cpp:231
ROS_DEBUG_STREAM
#define ROS_DEBUG_STREAM(args)
ign_ros_control::IgnitionROSControlPluginPrivate::GetEnabledJoints
std::map< std::string, ignition::gazebo::Entity > GetEnabledJoints(const ignition::gazebo::Entity &_entity, ignition::gazebo::EntityComponentManager &_ecm) const
Get a list of enabled, unique, 1-axis joints of the model. If no joint names are specified in the plu...
Definition: ign_ros_control_plugin.cpp:121
transmission_parser.h
controller_manager.h
ign_ros_control::IgnitionROSControlPluginPrivate::getURDF
std::string getURDF(std::string param_name) const
Get the URDF XML from the parameter server.
Definition: ign_ros_control_plugin.cpp:182
ign_ros_control::IgnitionROSControlPlugin::PreUpdate
void PreUpdate(const ignition::gazebo::UpdateInfo &_info, ignition::gazebo::EntityComponentManager &_ecm) override
Definition: ign_ros_control_plugin.cpp:358
ROS_WARN_STREAM
#define ROS_WARN_STREAM(args)
ign_system.hpp
ign_ros_control::IgnitionROSControlPluginPrivate::~IgnitionROSControlPluginPrivate
~IgnitionROSControlPluginPrivate()
Definition: ign_ros_control_plugin.cpp:60
ign_ros_control::IgnitionROSControlPluginPrivate::controller_manager_
std::shared_ptr< controller_manager::ControllerManager > controller_manager_
Controller manager.
Definition: ign_ros_control_plugin.cpp:97
ign_ros_control::IgnitionROSControlPluginPrivate::parseTransmissionsFromURDF
bool parseTransmissionsFromURDF(const std::string &urdf_string)
Definition: ign_ros_control_plugin.cpp:213
ign_ros_control::IgnitionROSControlPluginPrivate::robot_hw_sim_loader_
std::shared_ptr< pluginlib::ClassLoader< ign_ros_control::IgnitionSystemInterface > > robot_hw_sim_loader_
Interface loader.
Definition: ign_ros_control_plugin.cpp:93
ign_ros_control::IgnitionROSControlPluginPrivate::transmissions_
std::vector< transmission_interface::TransmissionInfo > transmissions_
Definition: ign_ros_control_plugin.cpp:82
ign_ros_control::IgnitionROSControlPlugin::dataPtr
std::unique_ptr< IgnitionROSControlPluginPrivate > dataPtr
Private data pointer.
Definition: ign_ros_control_plugin.hpp:60
ROS_INFO_ONCE
#define ROS_INFO_ONCE(...)
model.h
ros::NodeHandle::searchParam
bool searchParam(const std::string &key, std::string &result) const
ros::isInitialized
ROSCPP_DECL bool isInitialized()
ign_ros_control::IgnitionROSControlPluginPrivate::entity_
ignition::gazebo::Entity entity_
Entity ID for sensor within Gazebo.
Definition: ign_ros_control_plugin.cpp:85
ign_ros_control::IgnitionROSControlPluginPrivate::async_ros_spin_
std::shared_ptr< ros::AsyncSpinner > async_ros_spin_
ROS comm.
Definition: ign_ros_control_plugin.cpp:116
ROS_WARN
#define ROS_WARN(...)
pluginlib::ClassLoader
class_loader.hpp
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
ROS_FATAL
#define ROS_FATAL(...)
ign_ros_control::IgnitionSystemInterface
Definition: ign_system_interface.hpp:37
ign_ros_control::IgnitionROSControlPluginPrivate
Definition: ign_ros_control_plugin.cpp:46
ign_ros_control::IgnitionROSControlPluginPrivate::last_update_sim_time_ros_
ros::Time last_update_sim_time_ros_
Last time the update method was called.
Definition: ign_ros_control_plugin.cpp:104
ros::Time
ign_ros_control::IgnitionROSControlPluginPrivate::control_period_
ros::Duration control_period_
Timing.
Definition: ign_ros_control_plugin.cpp:88
ign_ros_control_plugin.hpp
ign_ros_control::IgnitionROSControlPluginPrivate::IgnitionROSControlPluginPrivate
IgnitionROSControlPluginPrivate(const std::string &robot_namespace, const std::string &robot_description)
Definition: ign_ros_control_plugin.cpp:50
ROS_ERROR
#define ROS_ERROR(...)
ign_ros_control::IgnitionROSControlPlugin
Definition: ign_ros_control_plugin.hpp:27
pluginlib::LibraryLoadException
ros::Rate
DurationBase< Duration >::toSec
double toSec() const
ign_ros_control::IgnitionROSControlPluginPrivate::ecm_
ignition::gazebo::EntityComponentManager * ecm_
ECM pointer.
Definition: ign_ros_control_plugin.cpp:107
ROS_INFO
#define ROS_INFO(...)
ros::Duration
ros::init_options::NoSigintHandler
NoSigintHandler
interface_resources.h
ros::NodeHandle


ign_ros_control
Author(s): Gennaro Raiola
autogenerated on Sun Aug 14 2022 02:23:53