3 #include <sensor_msgs/CameraInfo.h>
4 #include <sensor_msgs/Image.h>
6 #include <mujoco/mujoco.h>
16 mjp_defaultPlugin(&plugin);
18 plugin.name =
"MujocoRosUtils::ImagePublisher";
19 plugin.capabilityflags |= mjPLUGIN_SENSOR;
21 const char * attributes[] = {
"frame_id",
"color_topic_name",
"depth_topic_name",
"info_topic_name",
"height",
22 "width",
"publish_rate"};
24 plugin.nattribute =
sizeof(attributes) /
sizeof(attributes[0]);
25 plugin.attributes = attributes;
27 plugin.nstate = +[](
const mjModel *,
31 plugin.nsensordata = +[](
const mjModel *,
36 plugin.needstage = mjSTAGE_VEL;
38 plugin.init = +[](
const mjModel * m, mjData * d,
int plugin_id)
45 d->plugin_data[plugin_id] =
reinterpret_cast<uintptr_t
>(plugin_instance);
49 plugin.destroy = +[](mjData * d,
int plugin_id)
51 auto * plugin_instance =
reinterpret_cast<class
ImagePublisher *
>(d->plugin_data[plugin_id]);
52 plugin_instance->
free();
53 delete reinterpret_cast<ImagePublisher *
>(d->plugin_data[plugin_id]);
54 d->plugin_data[plugin_id] = 0;
57 plugin.
reset = +[](
const mjModel * m,
double *,
58 void * plugin_data,
int plugin_id)
60 auto * plugin_instance =
reinterpret_cast<class
ImagePublisher *
>(plugin_data);
61 plugin_instance->
reset(m, plugin_id);
64 plugin.compute = +[](
const mjModel * m, mjData * d,
int plugin_id,
int
67 auto * plugin_instance =
reinterpret_cast<class
ImagePublisher *
>(d->plugin_data[plugin_id]);
68 plugin_instance->
compute(m, d, plugin_id);
71 mjp_registerPlugin(&plugin);
77 const char * frame_id_char = mj_getPluginConfig(m, plugin_id,
"frame_id");
78 std::string frame_id =
"";
79 if(strlen(frame_id_char) > 0)
81 frame_id = std::string(frame_id_char);
85 const char * color_topic_name_char = mj_getPluginConfig(m, plugin_id,
"color_topic_name");
86 std::string color_topic_name =
"";
87 if(strlen(color_topic_name_char) > 0)
89 color_topic_name = std::string(color_topic_name_char);
93 const char * depth_topic_name_char = mj_getPluginConfig(m, plugin_id,
"depth_topic_name");
94 std::string depth_topic_name =
"";
95 if(strlen(depth_topic_name_char) > 0)
97 depth_topic_name = std::string(depth_topic_name_char);
101 const char * info_topic_name_char = mj_getPluginConfig(m, plugin_id,
"info_topic_name");
102 std::string info_topic_name =
"";
103 if(strlen(info_topic_name_char) > 0)
105 info_topic_name = std::string(info_topic_name_char);
109 const char * height_char = mj_getPluginConfig(m, plugin_id,
"height");
111 if(strlen(height_char) > 0)
113 height =
static_cast<int>(strtol(height_char,
nullptr, 10));
117 mju_error(
"[ImagePublisher] `height` must be positive.");
122 const char * width_char = mj_getPluginConfig(m, plugin_id,
"width");
124 if(strlen(width_char) > 0)
126 width =
static_cast<int>(strtol(width_char,
nullptr, 10));
130 mju_error(
"[ImagePublisher] `width` must be positive.");
135 const char * publish_rate_char = mj_getPluginConfig(m, plugin_id,
"publish_rate");
136 mjtNum publish_rate = 30.0;
137 if(strlen(publish_rate_char) > 0)
139 publish_rate = strtod(publish_rate_char,
nullptr);
141 if(publish_rate <= 0)
143 mju_error(
"[ImagePublisher] `publish_rate` must be positive.");
149 for(; sensor_id < m->nsensor; sensor_id++)
151 if(m->sensor_type[sensor_id] == mjSENS_PLUGIN && m->sensor_plugin[sensor_id] == plugin_id)
156 if(sensor_id == m->nsensor)
158 mju_error(
"[ImagePublisher] Plugin not found in sensors.");
161 if(m->sensor_objtype[sensor_id] != mjOBJ_CAMERA)
163 mju_error(
"[ImagePublisher] Plugin must be attached to a camera.");
167 std::cout <<
"[ImagePublisher] Create." << std::endl;
169 return new ImagePublisher(m, d, sensor_id, frame_id, color_topic_name, depth_topic_name, info_topic_name, height,
170 width, publish_rate);
176 const std::string & frame_id,
177 std::string color_topic_name,
178 std::string depth_topic_name,
179 std::string info_topic_name,
183 : sensor_id_(sensor_id), camera_id_(m->sensor_objid[sensor_id]), frame_id_(frame_id),
184 publish_skip_(std::max(static_cast<int>(1.0 / (publish_rate * m->opt.timestep)), 1)), viewport_({0, 0, width, height})
186 std::string camera_name = std::string(mj_id2name(m, mjOBJ_CAMERA, camera_id_));
187 if(frame_id_.empty())
189 frame_id_ = camera_name;
191 if(color_topic_name.empty())
193 color_topic_name =
"mujoco/" + camera_name +
"/color";
195 if(depth_topic_name.empty())
197 depth_topic_name =
"mujoco/" + camera_name +
"/depth";
199 if(info_topic_name.empty())
201 info_topic_name =
"mujoco/" + camera_name +
"/camera_info";
207 mju_error(
"[ImagePublisher] Could not initialize GLFW.");
211 glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
212 glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);
213 window_ = glfwCreateWindow(viewport_.width, viewport_.height,
"MujocoRosUtils::ImagePublisher",
nullptr,
nullptr);
216 mju_error(
"[ImagePublisher] Could not create GLFW window.");
221 glfwMakeContextCurrent(window_);
224 mjv_defaultCamera(&camera_);
225 mjv_defaultOption(&option_);
226 mjv_defaultScene(&scene_);
227 mjr_defaultContext(&context_);
230 mjv_makeScene(m, &scene_, 1000);
231 mjr_makeContext(m, &context_, mjFONTSCALE_100);
234 camera_.type = mjCAMERA_FIXED;
235 camera_.fixedcamid = camera_id_;
237 mjr_setBuffer(mjFB_OFFSCREEN, &context_);
238 if(context_.currentBuffer != mjFB_OFFSCREEN)
240 mju_error(
"[ImagePublisher] Offscreen rendering not supported, using default/window framebuffer.");
244 size_t color_buffer_size =
sizeof(
unsigned char) * 3 * viewport_.width * viewport_.height;
245 size_t depth_buffer_size =
sizeof(
float) * viewport_.width * viewport_.height;
246 color_buffer_ =
static_cast<unsigned char *
>(std::malloc(color_buffer_size));
247 depth_buffer_ =
static_cast<float *
>(std::malloc(depth_buffer_size));
248 color_buffer_flipped_ =
static_cast<unsigned char *
>(std::malloc(color_buffer_size));
249 depth_buffer_flipped_ =
static_cast<float *
>(std::malloc(depth_buffer_size));
253 char ** argv =
nullptr;
254 if(!ros::isInitialized())
256 ros::init(argc, argv,
"mujoco_ros", ros::init_options::NoSigintHandler);
259 nh_ = std::make_shared<ros::NodeHandle>();
260 color_pub_ = nh_->advertise<sensor_msgs::Image>(color_topic_name, 1);
261 depth_pub_ = nh_->advertise<sensor_msgs::Image>(depth_topic_name, 1);
262 info_pub_ = nh_->advertise<sensor_msgs::CameraInfo>(info_topic_name, 1);
282 glfwMakeContextCurrent(
window_);
294 float near =
static_cast<float>(m->vis.map.znear * m->stat.extent);
295 float far =
static_cast<float>(m->vis.map.zfar * m->stat.extent);
298 for(
int h = 0; h <
viewport_.height; h++)
307 for(
int c = 0; c < 3; c++)
316 ros::Time stamp_now = ros::Time::now();
318 sensor_msgs::Image color_msg;
319 color_msg.header.stamp = stamp_now;
323 color_msg.encoding =
"rgb8";
324 color_msg.is_bigendian = 0;
325 color_msg.step =
static_cast<unsigned int>(
sizeof(
unsigned char) * 3 *
viewport_.width);
326 color_msg.data.resize(
sizeof(
unsigned char) * 3 *
viewport_.width *
viewport_.height);
331 sensor_msgs::Image depth_msg;
332 depth_msg.header.stamp = stamp_now;
336 depth_msg.encoding =
"32FC1";
337 depth_msg.is_bigendian = 0;
338 depth_msg.step =
static_cast<unsigned int>(
sizeof(float) *
viewport_.width);
343 sensor_msgs::CameraInfo info_msg;
344 info_msg.header.stamp = stamp_now;
348 info_msg.distortion_model =
"plumb_bob";
349 info_msg.D.resize(5, 0.0);
350 info_msg.K.fill(0.0);
351 info_msg.R.fill(0.0);
352 info_msg.P.fill(0.0);
353 double focal_scaling = (1.0 / std::tan((m->cam_fovy[
camera_id_] * M_PI / 180.0) / 2.0)) *
viewport_.height / 2.0;
354 info_msg.K[0] = info_msg.P[0] = focal_scaling;
355 info_msg.K[2] = info_msg.P[2] =
static_cast<double>(
viewport_.width) / 2.0;
356 info_msg.K[4] = info_msg.P[5] = focal_scaling;
357 info_msg.K[5] = info_msg.P[6] =
static_cast<double>(
viewport_.height) / 2.0;
358 info_msg.K[8] = info_msg.P[10] = 1.0;