mujoco_ros_utils
ActuatorCommand.cpp
Go to the documentation of this file.
1 #include "ActuatorCommand.h"
2 
3 #include <mujoco/mujoco.h>
4 
5 #include <iostream>
6 
7 namespace MujocoRosUtils
8 {
9 
11 {
12  mjpPlugin plugin;
13  mjp_defaultPlugin(&plugin);
14 
15  plugin.name = "MujocoRosUtils::ActuatorCommand";
16  // Allow plugins to be placed on either the body element or the actuator element
17  plugin.capabilityflags |= mjPLUGIN_ACTUATOR;
18  plugin.capabilityflags |= mjPLUGIN_PASSIVE;
19 
20  const char * attributes[] = {"actuator_name", "topic_name"};
21 
22  plugin.nattribute = sizeof(attributes) / sizeof(attributes[0]);
23  plugin.attributes = attributes;
24 
25  plugin.nstate = +[](const mjModel *, // m
26  int // plugin_id
27  ) { return 0; };
28 
29  plugin.nsensordata = +[](const mjModel *, // m
30  int, // plugin_id
31  int // sensor_id
32  ) { return 0; };
33 
34  plugin.needstage = mjSTAGE_VEL;
35 
36  plugin.init = +[](const mjModel * m, mjData * d, int plugin_id)
37  {
38  auto * plugin_instance = ActuatorCommand::Create(m, d, plugin_id);
39  if(!plugin_instance)
40  {
41  return -1;
42  }
43  d->plugin_data[plugin_id] = reinterpret_cast<uintptr_t>(plugin_instance);
44  return 0;
45  };
46 
47  plugin.destroy = +[](mjData * d, int plugin_id)
48  {
49  delete reinterpret_cast<ActuatorCommand *>(d->plugin_data[plugin_id]);
50  d->plugin_data[plugin_id] = 0;
51  };
52 
53  plugin.reset = +[](const mjModel * m, double *, // plugin_state
54  void * plugin_data, int plugin_id)
55  {
56  auto * plugin_instance = reinterpret_cast<class ActuatorCommand *>(plugin_data);
57  plugin_instance->reset(m, plugin_id);
58  };
59 
60  plugin.compute = +[](const mjModel * m, mjData * d, int plugin_id, int // capability_bit
61  )
62  {
63  auto * plugin_instance = reinterpret_cast<class ActuatorCommand *>(d->plugin_data[plugin_id]);
64  plugin_instance->compute(m, d, plugin_id);
65  };
66 
67  mjp_registerPlugin(&plugin);
68 }
69 
70 ActuatorCommand * ActuatorCommand::Create(const mjModel * m, mjData * d, int plugin_id)
71 {
72  // actuator_name
73  const char * actuator_name_char = mj_getPluginConfig(m, plugin_id, "actuator_name");
74  if(strlen(actuator_name_char) == 0)
75  {
76  mju_error("[ActuatorCommand] `actuator_name` is missing.");
77  return nullptr;
78  }
79  int actuator_id = 0;
80  for(; actuator_id < m->nu; actuator_id++)
81  {
82  if(strcmp(actuator_name_char, mj_id2name(m, mjOBJ_ACTUATOR, actuator_id)) == 0)
83  {
84  break;
85  }
86  }
87  if(actuator_id == m->nu)
88  {
89  mju_error("[ActuatorCommand] The actuator with the specified name not found.");
90  return nullptr;
91  }
92 
93  // topic_name
94  const char * topic_name_char = mj_getPluginConfig(m, plugin_id, "topic_name");
95  std::string topic_name = "";
96  if(strlen(topic_name_char) > 0)
97  {
98  topic_name = std::string(topic_name_char);
99  }
100 
101  std::cout << "[ActuatorCommand] Create." << std::endl;
102 
103  return new ActuatorCommand(m, d, actuator_id, topic_name);
104 }
105 
107  mjData *, // d
108  int actuator_id,
109  std::string topic_name)
110 : actuator_id_(actuator_id)
111 {
112  if(topic_name.empty())
113  {
114  std::string actuator_name = std::string(mj_id2name(m, mjOBJ_ACTUATOR, actuator_id));
115  topic_name = "mujoco/" + actuator_name;
116  }
117 
118  int argc = 0;
119  char ** argv = nullptr;
120  if(!rclcpp::ok())
121  {
122  rclcpp::init(argc, argv);
123  }
124  rclcpp::NodeOptions node_options;
125 
126  nh_ = rclcpp::Node::make_shared("actuator_command", node_options);
127  sub_ = nh_->create_subscription<std_msgs::msg::Float64>(
128  topic_name, 1, std::bind(&ActuatorCommand::callback, this, std::placeholders::_1));
129  // // Use a dedicated queue so as not to call callbacks of other modules
130  executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
131  executor_->add_node(nh_);
132 }
133 
134 void ActuatorCommand::reset(const mjModel *, // m
135  int // plugin_id
136 )
137 {
138 }
139 
140 void ActuatorCommand::compute(const mjModel *, // m
141  mjData * d,
142  int // plugin_id
143 )
144 {
145  // Call ROS callback
146  executor_->spin_once(std::chrono::seconds(0));
147 
148  // Set actuator command
149  if(!std::isnan(ctrl_))
150  {
151  d->ctrl[actuator_id_] = ctrl_;
152  ctrl_ = std::numeric_limits<mjtNum>::quiet_NaN();
153  }
154 }
155 
156 void ActuatorCommand::callback(const std_msgs::msg::Float64::SharedPtr msg)
157 {
158  ctrl_ = msg->data;
159 }
160 
161 } // namespace MujocoRosUtils
Plugin to send a command to an actuator via ROS topic.
static void RegisterPlugin()
Register plugin.
void reset(const mjModel *m, int plugin_id)
Reset.
rclcpp::executors::SingleThreadedExecutor::SharedPtr executor_
mjtNum ctrl_
Actuator command (NaN for no command)
rclcpp::Subscription< std_msgs::msg::Float64 >::SharedPtr sub_
void compute(const mjModel *m, mjData *d, int plugin_id)
Compute.
ActuatorCommand(ActuatorCommand &&)=default
Copy constructor.
rclcpp::Node::SharedPtr nh_
void callback(const std_msgs::msg::Float64::SharedPtr msg)
Constructor.
static ActuatorCommand * Create(const mjModel *m, mjData *d, int plugin_id)
Create an instance.