o3d3xx_node.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 Love Park Robotics, LLC
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distribted on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include <cstdint>
00018 #include <functional>
00019 #include <memory>
00020 #include <mutex>
00021 #include <sstream>
00022 #include <stdexcept>
00023 #include <string>
00024 #include <vector>
00025 #include <cv_bridge/cv_bridge.h>
00026 #include <image_transport/image_transport.h>
00027 #include <opencv2/opencv.hpp>
00028 #include <pcl_conversions/pcl_conversions.h>
00029 #include <pcl_ros/point_cloud.h>
00030 #include <ros/ros.h>
00031 #include <sensor_msgs/Image.h>
00032 #include <sensor_msgs/image_encodings.h>
00033 #include <o3d3xx_camera.h>
00034 #include <o3d3xx_framegrabber.h>
00035 #include <o3d3xx_image.h>
00036 #include <o3d3xx/Config.h>
00037 #include <o3d3xx/Dump.h>
00038 #include <o3d3xx/GetVersion.h>
00039 #include <o3d3xx/Rm.h>
00040 #include <o3d3xx/Extrinsics.h>
00041 #include <o3d3xx/Trigger.h>
00042 
00043 class O3D3xxNode
00044 {
00045 public:
00046   O3D3xxNode()
00047     : schema_mask_(o3d3xx::DEFAULT_SCHEMA_MASK),
00048       timeout_millis_(500),
00049       timeout_tolerance_secs_(5.0),
00050       publish_viz_images_(false),
00051       assume_sw_triggered_(false),
00052       spinner_(new ros::AsyncSpinner(1))
00053   {
00054     std::string camera_ip;
00055     int xmlrpc_port;
00056     std::string password;
00057     int schema_mask;
00058     std::string frame_id_base;
00059 
00060     ros::NodeHandle nh; // public
00061     ros::NodeHandle np("~"); // private
00062 
00063     np.param("ip", camera_ip, o3d3xx::DEFAULT_IP);
00064     np.param("xmlrpc_port", xmlrpc_port, (int) o3d3xx::DEFAULT_XMLRPC_PORT);
00065     np.param("password", password, o3d3xx::DEFAULT_PASSWORD);
00066     np.param("schema_mask", schema_mask, (int) o3d3xx::DEFAULT_SCHEMA_MASK);
00067     np.param("timeout_millis", this->timeout_millis_, 500);
00068     np.param("timeout_tolerance_secs", this->timeout_tolerance_secs_, 5.0);
00069     np.param("publish_viz_images", this->publish_viz_images_, false);
00070     np.param("assume_sw_triggered", this->assume_sw_triggered_, false);
00071     np.param("frame_id_base", frame_id_base,
00072              std::string(ros::this_node::getName()).substr(1));
00073 
00074     this->schema_mask_ = static_cast<std::uint16_t>(schema_mask);
00075 
00076     this->frame_id_ = frame_id_base + "_link";
00077     this->optical_frame_id_ = frame_id_base + "_optical_link";
00078 
00079     //-----------------------------------------
00080     // Instantiate the camera and frame-grabber
00081     //-----------------------------------------
00082     this->cam_ =
00083       std::make_shared<o3d3xx::Camera>(camera_ip, xmlrpc_port, password);
00084 
00085     // NOTE: we initially only want to stream in the unit vectors, we switch
00086     // to the requested mask, *after* we publish the unit vectors at least
00087     // once.
00088     this->fg_ =
00089       std::make_shared<o3d3xx::FrameGrabber>(this->cam_, o3d3xx::IMG_UVEC);
00090 
00091     //----------------------
00092     // Published topics
00093     //----------------------
00094     this->cloud_pub_ =
00095       nh.advertise<pcl::PointCloud<o3d3xx::PointT> >("cloud", 1);
00096 
00097     image_transport::ImageTransport it(nh);
00098     this->depth_pub_ = it.advertise("depth", 1);
00099     this->depth_viz_pub_ = it.advertise("depth_viz", 1);
00100     this->amplitude_pub_ = it.advertise("amplitude", 1);
00101     this->raw_amplitude_pub_ = it.advertise("raw_amplitude", 1);
00102     this->conf_pub_ = it.advertise("confidence", 1);
00103     this->good_bad_pub_ = it.advertise("good_bad_pixels", 1);
00104     this->xyz_image_pub_ = it.advertise("xyz_image", 1);
00105 
00106     // NOTE: not using ImageTransport here ... having issues with the
00107     // latching. I need to investigate further. A "normal" publisher seems to
00108     // work.
00109     this->uvec_pub_ =
00110       nh.advertise<sensor_msgs::Image>("unit_vectors", 1, true);
00111 
00112     this->extrinsics_pub_ = nh.advertise<o3d3xx::Extrinsics>("extrinsics", 1);
00113 
00114     //----------------------
00115     // Advertised services
00116     //----------------------
00117     this->version_srv_ =
00118       nh.advertiseService<o3d3xx::GetVersion::Request,
00119                           o3d3xx::GetVersion::Response>
00120       ("GetVersion", std::bind(&O3D3xxNode::GetVersion, this,
00121         std::placeholders::_1,
00122         std::placeholders::_2));
00123 
00124     this->dump_srv_ =
00125       nh.advertiseService<o3d3xx::Dump::Request, o3d3xx::Dump::Response>
00126       ("Dump", std::bind(&O3D3xxNode::Dump, this,
00127                           std::placeholders::_1,
00128                           std::placeholders::_2));
00129 
00130     this->config_srv_ =
00131       nh.advertiseService<o3d3xx::Config::Request, o3d3xx::Config::Response>
00132       ("Config", std::bind(&O3D3xxNode::Config, this,
00133                             std::placeholders::_1,
00134                             std::placeholders::_2));
00135 
00136     this->rm_srv_ =
00137       nh.advertiseService<o3d3xx::Rm::Request, o3d3xx::Rm::Response>
00138       ("Rm", std::bind(&O3D3xxNode::Rm, this,
00139                         std::placeholders::_1,
00140                         std::placeholders::_2));
00141 
00142     this->trigger_srv_ =
00143       nh.advertiseService<o3d3xx::Trigger::Request, o3d3xx::Trigger::Response>
00144       ("Trigger", std::bind(&O3D3xxNode::Trigger, this,
00145                             std::placeholders::_1,
00146                             std::placeholders::_2));
00147   }
00148 
00152   void Run()
00153   {
00154     std::unique_lock<std::mutex> fg_lock(this->fg_mutex_, std::defer_lock);
00155     this->spinner_->start();
00156 
00157     o3d3xx::ImageBuffer::Ptr buff =
00158       std::make_shared<o3d3xx::ImageBuffer>();
00159 
00160     pcl::PointCloud<o3d3xx::PointT>::Ptr
00161       cloud(new pcl::PointCloud<o3d3xx::PointT>());
00162 
00163     cv::Mat confidence_img;
00164     cv::Mat depth_img;
00165     cv::Mat depth_viz_img;
00166     double min, max;
00167 
00168     std::vector<float> extrinsics(6);
00169 
00170     ros::Time last_frame = ros::Time::now();
00171 
00172     bool got_uvec = false;
00173 
00174     while (ros::ok())
00175     {
00176       fg_lock.lock();
00177       if (!this->fg_->WaitForFrame(buff.get(), this->timeout_millis_))
00178       {
00179         fg_lock.unlock();
00180         if (! this->assume_sw_triggered_)
00181           {
00182             ROS_WARN("Timeout waiting for camera!");
00183           }
00184         else
00185           {
00186             ros::Duration(.001).sleep();
00187           }
00188 
00189         if ((ros::Time::now() - last_frame).toSec() >
00190             this->timeout_tolerance_secs_)
00191           {
00192             ROS_WARN("Attempting to restart frame grabber...");
00193             fg_lock.lock();
00194             if (got_uvec)
00195               {
00196                 this->fg_.reset(new o3d3xx::FrameGrabber(this->cam_,
00197                                                          this->schema_mask_));
00198               }
00199             else
00200               {
00201                 this->fg_.reset(new o3d3xx::FrameGrabber(this->cam_,
00202                                                          o3d3xx::IMG_UVEC));
00203               }
00204             fg_lock.unlock();
00205             last_frame = ros::Time::now();
00206           }
00207         continue;
00208       }
00209       fg_lock.unlock();
00210 
00211       std_msgs::Header head = std_msgs::Header();
00212       head.stamp = ros::Time::now();
00213       head.frame_id = this->frame_id_;
00214       last_frame = head.stamp;
00215 
00216       std_msgs::Header optical_head = std_msgs::Header();
00217       optical_head.stamp = head.stamp;
00218       optical_head.frame_id = this->optical_frame_id_;
00219 
00220       // publish unit vectors once on a latched topic, then re-initialize the
00221       // framegrabber with the user's requested schema mask
00222       if (! got_uvec)
00223         {
00224           sensor_msgs::ImagePtr uvec_msg =
00225             cv_bridge::CvImage(optical_head,
00226                                sensor_msgs::image_encodings::TYPE_32FC3,
00227                                buff->UnitVectors()).toImageMsg();
00228           this->uvec_pub_.publish(uvec_msg);
00229 
00230           ROS_INFO("Got unit vectors, restarting framegrabber with mask: %d",
00231                    (int) this->schema_mask_);
00232 
00233           fg_lock.lock();
00234           got_uvec = true;
00235           this->fg_.reset(new o3d3xx::FrameGrabber(this->cam_,
00236                                                    this->schema_mask_));
00237           fg_lock.unlock();
00238 
00239           continue;
00240         }
00241 
00242       // boost::shared_ptr vs std::shared_ptr forces us to make this copy :(
00243       pcl::copyPointCloud(*(buff->Cloud().get()), *cloud);
00244       cloud->header = pcl_conversions::toPCL(head);
00245       this->cloud_pub_.publish(cloud);
00246 
00247       depth_img = buff->DepthImage();
00248       sensor_msgs::ImagePtr depth =
00249         cv_bridge::CvImage(optical_head,
00250                            "mono16",
00251                            depth_img).toImageMsg();
00252       this->depth_pub_.publish(depth);
00253 
00254       sensor_msgs::ImagePtr amplitude =
00255         cv_bridge::CvImage(optical_head,
00256                            "mono16",
00257                            buff->AmplitudeImage()).toImageMsg();
00258       this->amplitude_pub_.publish(amplitude);
00259 
00260       sensor_msgs::ImagePtr raw_amplitude =
00261         cv_bridge::CvImage(optical_head,
00262                            "mono16",
00263                            buff->RawAmplitudeImage()).toImageMsg();
00264       this->raw_amplitude_pub_.publish(raw_amplitude);
00265 
00266       confidence_img = buff->ConfidenceImage();
00267       sensor_msgs::ImagePtr confidence =
00268         cv_bridge::CvImage(optical_head,
00269                            "mono8",
00270                            confidence_img).toImageMsg();
00271       this->conf_pub_.publish(confidence);
00272 
00273       sensor_msgs::ImagePtr xyz_image_msg =
00274         cv_bridge::CvImage(head,
00275                            sensor_msgs::image_encodings::TYPE_16SC3,
00276                            buff->XYZImage()).toImageMsg();
00277       this->xyz_image_pub_.publish(xyz_image_msg);
00278 
00279       extrinsics = buff->Extrinsics();
00280       o3d3xx::Extrinsics extrinsics_msg;
00281       extrinsics_msg.header = optical_head;
00282       try
00283         {
00284           extrinsics_msg.tx = extrinsics.at(0);
00285           extrinsics_msg.ty = extrinsics.at(1);
00286           extrinsics_msg.tz = extrinsics.at(2);
00287           extrinsics_msg.rot_x = extrinsics.at(3);
00288           extrinsics_msg.rot_y = extrinsics.at(4);
00289           extrinsics_msg.rot_z = extrinsics.at(5);
00290         }
00291       catch (const std::out_of_range& ex)
00292         {
00293           ROS_WARN("out-of-range error fetching extrinsics");
00294         }
00295       this->extrinsics_pub_.publish(extrinsics_msg);
00296 
00297       if (this->publish_viz_images_)
00298       {
00299         // depth image with better colormap
00300         cv::minMaxIdx(depth_img, &min, &max);
00301         cv::convertScaleAbs(depth_img, depth_viz_img, 255 / max);
00302         cv::applyColorMap(depth_viz_img, depth_viz_img, cv::COLORMAP_JET);
00303         sensor_msgs::ImagePtr depth_viz =
00304           cv_bridge::CvImage(optical_head,
00305                              "bgr8",
00306                              depth_viz_img).toImageMsg();
00307         this->depth_viz_pub_.publish(depth_viz);
00308 
00309         // show good vs bad pixels as binary image
00310         cv::Mat good_bad_map = cv::Mat::ones(confidence_img.rows,
00311                                              confidence_img.cols,
00312                                              CV_8UC1);
00313         cv::bitwise_and(confidence_img, good_bad_map,
00314                         good_bad_map);
00315         good_bad_map *= 255;
00316         sensor_msgs::ImagePtr good_bad =
00317           cv_bridge::CvImage(optical_head,
00318                              "mono8",
00319                              good_bad_map).toImageMsg();
00320         this->good_bad_pub_.publish(good_bad);
00321       }
00322     }
00323   }
00324 
00331   bool GetVersion(o3d3xx::GetVersion::Request &req,
00332                   o3d3xx::GetVersion::Response &res)
00333   {
00334     int major, minor, patch;
00335     o3d3xx::version(&major, &minor, &patch);
00336 
00337     std::ostringstream ss;
00338     ss << O3D3XX_LIBRARY_NAME
00339        << ": " << major << "." << minor << "." << patch;
00340 
00341     res.version = ss.str();
00342     return true;
00343   }
00344 
00352   bool Trigger(o3d3xx::Trigger::Request &req,
00353                o3d3xx::Trigger::Response &res)
00354   {
00355     std::lock_guard<std::mutex> lock(this->fg_mutex_);
00356     res.status = 0;
00357 
00358     try
00359       {
00360         this->fg_->SWTrigger();
00361       }
00362     catch (const o3d3xx::error_t& ex)
00363       {
00364         res.status = ex.code();
00365       }
00366 
00367     return true;
00368   }
00369 
00377   bool Dump(o3d3xx::Dump::Request &req,
00378             o3d3xx::Dump::Response &res)
00379   {
00380     std::lock_guard<std::mutex> lock(this->fg_mutex_);
00381     res.status = 0;
00382 
00383     try
00384     {
00385       res.config = this->cam_->ToJSON();
00386     }
00387     catch (const o3d3xx::error_t& ex)
00388     {
00389       res.status = ex.code();
00390     }
00391 
00392     this->fg_.reset(new o3d3xx::FrameGrabber(this->cam_, this->schema_mask_));
00393     return true;
00394   }
00395 
00407   bool Config(o3d3xx::Config::Request &req,
00408               o3d3xx::Config::Response &res)
00409   {
00410     std::lock_guard<std::mutex> lock(this->fg_mutex_);
00411     res.status = 0;
00412     res.msg = "OK";
00413 
00414     try
00415     {
00416       this->cam_->FromJSON(req.json);
00417     }
00418     catch (const o3d3xx::error_t& ex)
00419     {
00420       res.status = ex.code();
00421       res.msg = ex.what();
00422     }
00423     catch (const std::exception& std_ex)
00424     {
00425       res.status = -1;
00426       res.msg = std_ex.what();
00427     }
00428 
00429     this->fg_.reset(new o3d3xx::FrameGrabber(this->cam_, this->schema_mask_));
00430     return true;
00431   }
00432 
00439   bool Rm(o3d3xx::Rm::Request &req,
00440           o3d3xx::Rm::Response &res)
00441   {
00442     std::lock_guard<std::mutex> lock(this->fg_mutex_);
00443     res.status = 0;
00444     res.msg = "OK";
00445 
00446     try
00447     {
00448       if (req.index > 0)
00449       {
00450         this->cam_->RequestSession();
00451         this->cam_->SetOperatingMode(o3d3xx::Camera::operating_mode::EDIT);
00452         o3d3xx::DeviceConfig::Ptr dev = this->cam_->GetDeviceConfig();
00453 
00454         if (dev->ActiveApplication() != req.index)
00455         {
00456           this->cam_->DeleteApplication(req.index);
00457         }
00458         else
00459         {
00460           res.status = -1;
00461           res.msg = std::string("Cannot delete active application!");
00462         }
00463       }
00464     }
00465     catch (const o3d3xx::error_t& ex)
00466     {
00467       res.status = ex.code();
00468       res.msg = ex.what();
00469     }
00470     catch (const std::exception& std_ex)
00471     {
00472       res.status = -1;
00473       res.msg = std_ex.what();
00474     }
00475 
00476     this->cam_->CancelSession(); // <-- OK to do this here
00477     this->fg_.reset(new o3d3xx::FrameGrabber(this->cam_, this->schema_mask_));
00478     return true;
00479   }
00480 
00481 
00482 private:
00483   std::uint16_t schema_mask_;
00484   int timeout_millis_;
00485   double timeout_tolerance_secs_;
00486   bool publish_viz_images_;
00487   bool assume_sw_triggered_;
00488   std::unique_ptr<ros::AsyncSpinner> spinner_;
00489   o3d3xx::Camera::Ptr cam_;
00490   o3d3xx::FrameGrabber::Ptr fg_;
00491   std::mutex fg_mutex_;
00492 
00493   std::string frame_id_;
00494   std::string optical_frame_id_;
00495 
00496   ros::Publisher cloud_pub_;
00497   ros::Publisher uvec_pub_;
00498   ros::Publisher extrinsics_pub_;
00499   image_transport::Publisher depth_pub_;
00500   image_transport::Publisher depth_viz_pub_;
00501   image_transport::Publisher amplitude_pub_;
00502   image_transport::Publisher raw_amplitude_pub_;
00503   image_transport::Publisher conf_pub_;
00504   image_transport::Publisher good_bad_pub_;
00505   image_transport::Publisher xyz_image_pub_;
00506 
00507   ros::ServiceServer version_srv_;
00508   ros::ServiceServer dump_srv_;
00509   ros::ServiceServer config_srv_;
00510   ros::ServiceServer rm_srv_;
00511   ros::ServiceServer trigger_srv_;
00512 
00513 }; // end: class O3D3xxNode
00514 
00515 int main(int argc, char **argv)
00516 {
00517   o3d3xx::Logging::Init();
00518   ros::init(argc, argv, "o3d3xx");
00519   O3D3xxNode().Run();
00520   return 0;
00521 }


o3d3xx
Author(s): Tom Panzarella
autogenerated on Thu Jun 6 2019 18:17:40