prosilica_nodelet.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
15 * * Neither the name of the Willow Garage nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *********************************************************************/
32 
33 #include <string>
34 #include <ros/ros.h>
35 #include <ros/console.h>
36 #include <nodelet/nodelet.h>
38 #include <dynamic_reconfigure/server.h>
39 #include <dynamic_reconfigure/SensorLevels.h>
44 #include <sensor_msgs/CameraInfo.h>
45 
46 #include <sensor_msgs/Image.h>
47 #include <sensor_msgs/CameraInfo.h>
48 #include <sensor_msgs/fill_image.h>
49 #include <sensor_msgs/SetCameraInfo.h>
50 
51 #include <boost/thread.hpp>
52 
53 #include <prosilica_camera/ProsilicaCameraConfig.h>
54 #include "prosilica/prosilica.h"
55 #include "prosilica/rolling_sum.h"
56 
57 bool prosilica_inited = false;//for if the nodelet is loaded multiple times in the same manager
58 int num_cameras = 0;
59 
61 {
62 
64 {
65 
66 public:
67 
69  {
71  init_thread_.interrupt();
72  init_thread_.join();
73 
74  if(camera_)
75  {
76  camera_->stop();
77  camera_.reset(); // must destroy Camera before calling prosilica::fini
78  }
79 
83 
84  --num_cameras;
85  if(num_cameras<=0)
86  {
88  prosilica_inited = false;
89  num_cameras = 0;
90  }
91 
92  NODELET_WARN("Unloaded prosilica camera with guid %s", hw_id_.c_str());
93  }
94 
97  count_(0),
104  {
105  ++num_cameras;
106  }
107 
108 private:
109 
111  boost::thread init_thread_;
113 
118 
119  sensor_msgs::Image img_;
120  sensor_msgs::CameraInfo cam_info_;
121 
122  std::string frame_id_;
123  unsigned long guid_;
124  std::string hw_id_;
125  std::string ip_address_;
129 
130  // Dynamic reconfigure parameters
131  double update_rate_;
134 
137  int count_;
138 
139  // Dynamic Reconfigure
140  prosilica_camera::ProsilicaCameraConfig last_config_;
141  boost::recursive_mutex config_mutex_;
142  typedef dynamic_reconfigure::Server<prosilica_camera::ProsilicaCameraConfig> ReconfigureServer;
144 
145  // State updater
147  {
153  }camera_state_;
154  std::string state_info_;
155  std::string intrinsics_;
156  static const int WINDOW_SIZE = 100; // remember previous 5s
161 
163 
164 
165  virtual void onInit()
166  {
170  init_thread_ = boost::thread(boost::bind(&ProsilicaNodelet::onInitImpl, this));
171 
172  }
173 
174  void onInitImpl()
175  {
178 
180  if(!prosilica_inited)
181  {
182  NODELET_INFO("Initializing prosilica GIGE API");
183  prosilica::init();
184  prosilica_inited = true;
185  }
186 
188  count_ = 0;
189  update_rate_=30;
190  NODELET_INFO("namespace: %s", pn.getNamespace().c_str());
191  pn.param<std::string>("frame_id", frame_id_, "/camera_optical_frame");
192  NODELET_INFO("Loaded param frame_id: %s", frame_id_.c_str());
193 
194  pn.param<std::string>("guid", hw_id_, "");
195  if(hw_id_ == "")
196  {
197  guid_ = 0;
198  }
199  else
200  {
201  guid_ = boost::lexical_cast<int>(hw_id_);
202  NODELET_INFO("Loaded param guid: %lu", guid_);
203  }
204 
205  pn.param<std::string>("ip_address", ip_address_, "");
206  NODELET_INFO("Loaded ip address: %s", ip_address_.c_str());
207 
208  pn.param<double>("open_camera_retry_period", open_camera_retry_period_, 3.);
209  NODELET_INFO("Retry period: %f", open_camera_retry_period_);
210 
211  // Setup updater
212  updater.add(getName().c_str(), this, &ProsilicaNodelet::getCurrentState);
213  NODELET_INFO("updated state");
214  // Setup periodic callback to get new data from the camera
215  update_timer_ = nh.createTimer(ros::Rate(update_rate_).expectedCycleTime(), &ProsilicaNodelet::updateCallback, this, false ,false);
216  update_timer_.stop();
217  NODELET_INFO("created update timer");
218  // Open camera
219  openCamera();
220 
221  // Advertise topics
222  ros::NodeHandle image_nh(nh);
223  image_transport::ImageTransport image_it(image_nh);
224  image_publisher_ = image_it.advertiseCamera("image_raw", 1);
225  poll_srv_ = polled_camera::advertise(nh, "request_image", &ProsilicaNodelet::pollCallback, this);
226  set_camera_info_srv_ = pn.advertiseService("set_camera_info", &ProsilicaNodelet::setCameraInfo, this);
227  trigger_sub_ = pn.subscribe(trig_timestamp_topic_, 1, &ProsilicaNodelet::syncInCallback, this);
228 
229  // Setup dynamic reconfigure server
230  reconfigure_server_.reset(new ReconfigureServer(config_mutex_, pn));
231  ReconfigureServer::CallbackType f = boost::bind(&ProsilicaNodelet::reconfigureCallback, this, _1, _2);
232  reconfigure_server_->setCallback(f);
233  }
234 
235  void openCamera()
236  {
237  while (!camera_)
238  {
239  boost::lock_guard<boost::recursive_mutex> scoped_lock(config_mutex_);
240  camera_state_ = OPENING;
241  try
242  {
243  if(guid_ != 0)
244  {
245  state_info_ = "Trying to load camera with guid " + hw_id_;
246  NODELET_INFO("%s", state_info_.c_str());
247  camera_ = boost::make_shared<prosilica::Camera>((unsigned long)guid_);
248  updater.setHardwareIDf("%d", guid_);
249  NODELET_INFO("Started Prosilica camera with guid \"%lu\"", guid_);
250 
251  }
252  else if(!ip_address_.empty())
253  {
254  state_info_ = "Trying to load camera with ipaddress: " + ip_address_;
255  NODELET_INFO("%s", state_info_.c_str());
256  camera_ = boost::make_shared<prosilica::Camera>(ip_address_.c_str());
257  guid_ = camera_->guid();
258  hw_id_ = boost::lexical_cast<std::string>(guid_);
259  updater.setHardwareIDf("%d", guid_);
260 
261  NODELET_INFO("Started Prosilica camera with guid \"%d\"", (int)camera_->guid());
262  }
263  else
264  {
265  updater.setHardwareID("unknown");
266  if(prosilica::numCameras()>0)
267  {
268  state_info_ = "Trying to load first camera found";
269  NODELET_INFO("%s", state_info_.c_str());
270  guid_ = prosilica::getGuid(0);
271  camera_ = boost::make_shared<prosilica::Camera>((unsigned long)guid_);
272  hw_id_ = boost::lexical_cast<std::string>(guid_);
273  updater.setHardwareIDf("%d", guid_);
274  NODELET_INFO("Started Prosilica camera with guid \"%d\"", (int)guid_);
275  }
276  else
277  {
278  throw std::runtime_error("Found no cameras on local subnet");
279  }
280  }
281 
282  }
283  catch (std::exception& e)
284  {
285  camera_state_ = CAMERA_NOT_FOUND;
286  std::stringstream err;
287  if (prosilica::numCameras() == 0)
288  {
289  err << "Found no cameras on local subnet";
290  }
291  else if (guid_ != 0)
292  {
293  err << "Unable to open prosilica camera with guid " << guid_ <<": "<<e.what();
294  }
295  else if (ip_address_ != "")
296  {
297  err << "Unable to open prosilica camera with ip address " << ip_address_ <<": "<<e.what();
298  }
299 
300  state_info_ = err.str();
301  NODELET_WARN("%s", state_info_.c_str());
302  if(prosilica::numCameras() > 0)
303  {
304  NODELET_INFO("Available cameras:\n%s", getAvailableCameras().c_str());
305  }
306  camera_.reset();
307 
308  }
309  updater.update();
310  boost::this_thread::sleep(boost::posix_time::seconds(open_camera_retry_period_));
311  }
312  loadIntrinsics();
313  start();
314  }
315 
316  std::string getAvailableCameras()
317  {
318  std::vector<prosilica::CameraInfo> cameras = prosilica::listCameras();
319  std::stringstream list;
320  for (unsigned int i = 0; i < cameras.size(); ++i)
321  {
322  list << cameras[i].serial << " - " <<cameras[i].name<< " - Unique ID = "<<cameras[i].guid<<" IP = "<<cameras[i].ip_address<<std::endl;
323  }
324  return list.str();
325  }
326 
328  {
329  try
330  {
331  camera_->setKillCallback(boost::bind(&ProsilicaNodelet::kill, this, _1));
332 
333  if(auto_adjust_stream_bytes_per_second_ && camera_->hasAttribute("StreamBytesPerSecond"))
334  camera_->setAttribute("StreamBytesPerSecond", (tPvUint32)(115000000/num_cameras));
335 
336  // Retrieve contents of user memory
337  std::string buffer(prosilica::Camera::USER_MEMORY_SIZE, '\0');
338  camera_->readUserMemory(&buffer[0], prosilica::Camera::USER_MEMORY_SIZE);
339 
340  PvAttrRangeUint32(camera_->handle(), "BinningX", &dummy, &max_binning_x);
341  PvAttrRangeUint32(camera_->handle(), "BinningY", &dummy, &max_binning_y);
342  PvAttrRangeUint32(camera_->handle(), "Width", &dummy, &sensor_width_);
343  PvAttrRangeUint32(camera_->handle(), "Height", &dummy, &sensor_height_);
344 
345 
346  // Parse calibration file
347  std::string camera_name;
348  if (camera_calibration_parsers::parseCalibrationIni(buffer, camera_name, cam_info_))
349  {
350  intrinsics_ = "Loaded calibration";
351  NODELET_INFO("Loaded calibration for camera '%s'", camera_name.c_str());
352  }
353  else
354  {
355  intrinsics_ = "Failed to load intrinsics from camera";
356  NODELET_WARN("Failed to load intrinsics from camera");
357  }
358  }
359  catch(std::exception &e)
360  {
361  camera_state_ = CAMERA_NOT_FOUND;
362  state_info_ = e.what();
363  }
364  }
365 
366  void start()
367  {
368  try
369  {
370  switch(trigger_mode_)
371  {
372  case prosilica::Software:
373  NODELET_INFO("starting camera %s in software trigger mode", hw_id_.c_str());
374  camera_->start(prosilica::Software, 1., prosilica::Continuous);
375  if(update_rate_ > 0)
376  {
377  update_timer_.setPeriod(ros::Rate(update_rate_).expectedCycleTime());
378  update_timer_.start();
379  }
380  break;
381  case prosilica::Freerun:
382  NODELET_INFO("starting camera %s in freerun trigger mode", hw_id_.c_str());
383  camera_->setFrameCallback(boost::bind(&ProsilicaNodelet::publishImage, this, _1));
384  camera_->start(prosilica::Freerun, 1., prosilica::Continuous);
385  break;
387  NODELET_INFO("starting camera %s in fixedrate trigger mode", hw_id_.c_str());
388  camera_->setFrameCallback(boost::bind(&ProsilicaNodelet::publishImage, this, _1));
389  camera_->start(prosilica::FixedRate, update_rate_, prosilica::Continuous);
390  break;
391  case prosilica::SyncIn1:
392  NODELET_INFO("starting camera %s in sync1 trigger mode", hw_id_.c_str());
393  camera_->setFrameCallback(boost::bind(&ProsilicaNodelet::publishImage, this, _1));
394  camera_->start(prosilica::SyncIn1, update_rate_, prosilica::Continuous);
395  break;
396  case prosilica::SyncIn2:
397  NODELET_INFO("starting camera %s in sync2 trigger mode", hw_id_.c_str());
398  camera_->setFrameCallback(boost::bind(&ProsilicaNodelet::publishImage, this, _1));
399  camera_->start(prosilica::SyncIn2, update_rate_, prosilica::Continuous);
400  break;
401  default:
402  break;
403  }
404  }
405  catch(std::exception &e)
406  {
407  camera_state_ = CAMERA_NOT_FOUND;
408  state_info_ = e.what();
409  }
410  }
411 
412  void stop()
413  {
414  update_timer_.stop();
415  if(!camera_)
416  return;
417  camera_->removeEvents();
418  camera_->stop();
419 
420  }
421 
422  void kill(unsigned long guid)
423  {
424  if(guid == guid_)
425  {
426  NODELET_WARN("[%s] got kill request for prosilica camera %lu",getName().c_str(), guid);
428  init_thread_.interrupt();
429  init_thread_.join();
430 
431  camera_state_ = CAMERA_NOT_FOUND;
432  state_info_ = "Prosilica camera " + hw_id_ + " disconnected";
433  NODELET_ERROR("%s", state_info_.c_str());
434  updater.update();
435  boost::lock_guard<boost::recursive_mutex> scoped_lock(config_mutex_);
436  stop();
437  camera_.reset();
438  init_thread_ = boost::thread(boost::bind(&ProsilicaNodelet::openCamera, this));
439  return;
440  }
441  }
442 
443  void publishImage(tPvFrame* frame)
444  {
445  publishImage(frame, ros::Time::now());
446  }
447 
448  void publishImage(tPvFrame* frame, ros::Time time)
449  {
450  camera_state_ = OK;
451  state_info_ = "Camera operating normally";
452  if (image_publisher_.getNumSubscribers() > 0)
453  {
454 
455  if (processFrame(frame, img_, cam_info_))
456  {
457  image_publisher_.publish(img_, cam_info_, time);
458  frames_dropped_acc_.add(0);
459  }
460  else
461  {
462  camera_state_ = FORMAT_ERROR;
463  state_info_ = "Unable to process frame";
465  frames_dropped_acc_.add(1);
466  }
468  frames_completed_acc_.add(1);
469  }
470  updater.update();
471  }
472 
473  void updateCallback(const ros::TimerEvent &event)
474  {
475  // Download the most recent data from the device
476  camera_state_ = OK;
477  state_info_ = "Camera operating normally";
478  if(image_publisher_.getNumSubscribers() > 0)
479  {
480  boost::lock_guard<boost::recursive_mutex> lock(config_mutex_);
481  try
482  {
483  tPvFrame* frame = NULL;
484  frame = camera_->grab(1000);
485  publishImage(frame, event.current_real);
486  }
487  catch(std::exception &e)
488  {
489  camera_state_ = ERROR;
490  state_info_ = e.what();
491  NODELET_ERROR("Unable to read from camera: %s", e.what());
493  frames_dropped_acc_.add(1);
494  updater.update();
495  return;
496  }
497  }
498  }
499 
500  void pollCallback(polled_camera::GetPolledImage::Request& req,
501  polled_camera::GetPolledImage::Response& rsp,
502  sensor_msgs::Image& image, sensor_msgs::CameraInfo& info)
503  {
504  if (trigger_mode_ != prosilica::Software)
505  {
506  rsp.success = false;
507  rsp.status_message = "Camera is not in software triggered mode";
508  return;
509  }
510 
511  last_config_.binning_x = req.binning_x;
512  last_config_.binning_y = req.binning_y;
513  last_config_.x_offset = req.roi.x_offset;
514  last_config_.y_offset = req.roi.y_offset;
515  last_config_.height = req.roi.height;
516  last_config_.width = req.roi.width;
517 
518  reconfigureCallback(last_config_, dynamic_reconfigure::SensorLevels::RECONFIGURE_RUNNING);
519 
520  try
521  {
522  tPvFrame* frame = NULL;
523  frame = camera_->grab(req.timeout.toSec()*100);
524  if (processFrame(frame, image, info))
525  {
526  image.header.stamp = info.header.stamp =rsp.stamp = ros::Time::now();
527  rsp.status_message = "Success";
528  rsp.success = true;
529  }
530  else
531  {
532  rsp.success = false;
533  rsp.status_message = "Failed to process image";
534  return;
535  }
536  }
537  catch(std::exception &e)
538  {
539  rsp.success = false;
540  std::stringstream err;
541  err<< "Failed to grab frame: "<<e.what();
542  rsp.status_message = err.str();
543  return;
544  }
545  }
546 
547  void syncInCallback (const std_msgs::HeaderConstPtr& msg)
548  {
549  if (trigger_mode_ != prosilica::Software)
550  {
551  camera_state_ = ERROR;
552  state_info_ = "Can not sync from topic trigger unless in Software Trigger mode";
553  NODELET_ERROR_ONCE("%s", state_info_.c_str());
554  return;
555  }
556  ros::TimerEvent e;
557  e.current_real = msg->stamp;
558  updateCallback(e);
559  }
560 
561  bool processFrame(tPvFrame* frame, sensor_msgs::Image &img, sensor_msgs::CameraInfo &cam_info)
562  {
564  if (frame==NULL || frame->Status != ePvErrSuccess)
565  return false;
566 
567  try
568  {
572  tPvUint32 binning_x = 1, binning_y = 1;
573  if (camera_->hasAttribute("BinningX"))
574  {
575  camera_->getAttribute("BinningX", binning_x);
576  camera_->getAttribute("BinningY", binning_y);
577  }
578 
579  // Binning averages bayer samples, so just call it mono8 in that case
580  if (frame->Format == ePvFmtBayer8 && (binning_x > 1 || binning_y > 1))
581  frame->Format = ePvFmtMono8;
582 
583  if (!frameToImage(frame, img))
584  return false;
585 
586  // Set the operational parameters in CameraInfo (binning, ROI)
587  cam_info.binning_x = binning_x;
588  cam_info.binning_y = binning_y;
589  // ROI in CameraInfo is in unbinned coordinates, need to scale up
590  cam_info.roi.x_offset = frame->RegionX * binning_x;
591  cam_info.roi.y_offset = frame->RegionY * binning_y;
592  cam_info.roi.height = frame->Height * binning_y;
593  cam_info.roi.width = frame->Width * binning_x;
594  cam_info.roi.do_rectify = (frame->Height != sensor_height_ / binning_y) ||
595  (frame->Width != sensor_width_ / binning_x);
596 
597  if(auto_adjust_stream_bytes_per_second_ && camera_->hasAttribute("StreamBytesPerSecond"))
598  camera_->setAttribute("StreamBytesPerSecond", (tPvUint32)(115000000/num_cameras));
599  }
600  catch(std::exception &e)
601  {
602  return false;
603  }
604 
605  count_++;
606  return true;
607  }
608 
609  bool frameToImage(tPvFrame* frame, sensor_msgs::Image &image)
610  {
611  // NOTE: 16-bit and Yuv formats not supported
612  static const char* BAYER_ENCODINGS[] = { "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8", "bayer_bggr8" };
613 
614  std::string encoding;
615  if (frame->Format == ePvFmtMono8) encoding = sensor_msgs::image_encodings::MONO8;
616  else if (frame->Format == ePvFmtBayer8) encoding = BAYER_ENCODINGS[frame->BayerPattern];
617  else if (frame->Format == ePvFmtRgb24) encoding = sensor_msgs::image_encodings::RGB8;
618  else if (frame->Format == ePvFmtBgr24) encoding = sensor_msgs::image_encodings::BGR8;
619  else if (frame->Format == ePvFmtRgba32) encoding = sensor_msgs::image_encodings::RGBA8;
620  else if (frame->Format == ePvFmtBgra32) encoding = sensor_msgs::image_encodings::BGRA8;
621  else {
622  NODELET_WARN("Received frame with unsupported pixel format %d", frame->Format);
623  return false;
624  }
625 
626  if(frame->ImageSize == 0 || frame->Height == 0)
627  return false;
628  uint32_t step = frame->ImageSize / frame->Height;
629  return sensor_msgs::fillImage(image, encoding, frame->Height, frame->Width, step, frame->ImageBuffer);
630  }
631 
632  bool setCameraInfo(sensor_msgs::SetCameraInfoRequest &req, sensor_msgs::SetCameraInfoResponse &rsp)
633  {
634  NODELET_INFO("New camera info received");
635  sensor_msgs::CameraInfo &info = req.camera_info;
636 
637  // Sanity check: the image dimensions should match the max resolution of the sensor.
638  if (info.width != sensor_width_ || info.height != sensor_height_)
639  {
640  rsp.success = false;
641  rsp.status_message = (boost::format("Camera_info resolution %ix%i does not match current video "
642  "setting, camera running at resolution %ix%i.")
643  % info.width % info.height % sensor_width_ % sensor_height_).str();
644  NODELET_ERROR("%s", rsp.status_message.c_str());
645  return true;
646  }
647 
648  stop();
649 
650  std::string cam_name = "prosilica";
651  cam_name += hw_id_;
652  std::stringstream ini_stream;
653  if (!camera_calibration_parsers::writeCalibrationIni(ini_stream, cam_name, info))
654  {
655  rsp.status_message = "Error formatting camera_info for storage.";
656  rsp.success = false;
657  }
658  else
659  {
660  std::string ini = ini_stream.str();
661  if (ini.size() > prosilica::Camera::USER_MEMORY_SIZE)
662  {
663  rsp.success = false;
664  rsp.status_message = "Unable to write camera_info to camera memory, exceeded storage capacity.";
665  }
666  else
667  {
668  try
669  {
670  camera_->writeUserMemory(ini.c_str(), ini.size());
671  cam_info_ = info;
672  rsp.success = true;
673  }
675  {
676  rsp.success = false;
677  rsp.status_message = e.what();
678  }
679  }
680  }
681  if (!rsp.success)
682  NODELET_ERROR("%s", rsp.status_message.c_str());
683 
684  start();
685 
686  return true;
687  }
688 
689  void reconfigureCallback(prosilica_camera::ProsilicaCameraConfig &config, uint32_t level)
690  {
691  NODELET_DEBUG("Reconfigure request received");
692 
693  if (level >= (uint32_t)dynamic_reconfigure::SensorLevels::RECONFIGURE_STOP)
694  stop();
695 
697  if (config.trigger_mode == "streaming")
698  {
699  trigger_mode_ = prosilica::Freerun;
700  update_rate_ = 1.; // make sure we get _something_
701  }
702  else if (config.trigger_mode == "syncin1")
703  {
704  trigger_mode_ = prosilica::SyncIn1;
705  update_rate_ = config.trig_rate;
706  }
707  else if (config.trigger_mode == "syncin2")
708  {
709  trigger_mode_ = prosilica::SyncIn2;
710  update_rate_ = config.trig_rate;
711  }
712  else if (config.trigger_mode == "fixedrate")
713  {
714  trigger_mode_ = prosilica::FixedRate;
715  update_rate_ = config.trig_rate;
716  }
717  else if (config.trigger_mode == "software")
718  {
719  trigger_mode_ = prosilica::Software;
720  update_rate_ = config.trig_rate;
721  }
722 
723  else if (config.trigger_mode == "polled")
724  {
725  trigger_mode_ = prosilica::Software;
726  update_rate_ = 0;
727  }
728  else if (config.trigger_mode == "triggered")
729  {
730  trigger_mode_ = prosilica::Software;
731  update_rate_ = 0;
732  }
733  else
734  {
735  NODELET_ERROR("Invalid trigger mode '%s' in reconfigure request", config.trigger_mode.c_str());
736  }
737 
738  if(config.trig_timestamp_topic != last_config_.trig_timestamp_topic)
739  {
740  trigger_sub_.shutdown();
741  trig_timestamp_topic_ = config.trig_timestamp_topic;
742  }
743 
744  if(!trigger_sub_ && config.trigger_mode == "triggered")
745  {
746  trigger_sub_ = ros::NodeHandle().subscribe(trig_timestamp_topic_, 1, &ProsilicaNodelet::syncInCallback, this);
747  }
748 
749 
750  // Exposure
751  if (config.auto_exposure)
752  {
753  camera_->setExposure(0, prosilica::Auto);
754  if (camera_->hasAttribute("ExposureAutoMax"))
755  {
756  tPvUint32 us = config.exposure_auto_max*1000000. + 0.5;
757  camera_->setAttribute("ExposureAutoMax", us);
758  }
759  if (camera_->hasAttribute("ExposureAutoTarget"))
760  camera_->setAttribute("ExposureAutoTarget", (tPvUint32)config.exposure_auto_target);
761  }
762  else
763  {
764  unsigned us = config.exposure*1000000. + 0.5;
765  camera_->setExposure(us, prosilica::Manual);
766  camera_->setAttribute("ExposureValue", (tPvUint32)us);
767  }
768 
769  // Gain
770  if (config.auto_gain)
771  {
772  if (camera_->hasAttribute("GainAutoMax"))
773  {
774  camera_->setGain(0, prosilica::Auto);
775  camera_->setAttribute("GainAutoMax", (tPvUint32)config.gain_auto_max);
776  camera_->setAttribute("GainAutoTarget", (tPvUint32)config.gain_auto_target);
777  }
778  else
779  {
780  tPvUint32 major, minor;
781  camera_->getAttribute("FirmwareVerMajor", major);
782  camera_->getAttribute("FirmwareVerMinor", minor);
783  NODELET_WARN("Auto gain not available for this camera. Auto gain is available "
784  "on firmware versions 1.36 and above. You are running version %u.%u.",
785  (unsigned)major, (unsigned)minor);
786  config.auto_gain = false;
787  }
788  }
789  else
790  {
791  camera_->setGain(config.gain, prosilica::Manual);
792  camera_->setAttribute("GainValue", (tPvUint32)config.gain);
793  }
794 
795  // White balance
796  if (config.auto_whitebalance)
797  {
798  if (camera_->hasAttribute("WhitebalMode"))
799  camera_->setWhiteBalance(0, 0, prosilica::Auto);
800  else
801  {
802  NODELET_WARN("Auto white balance not available for this camera.");
803  config.auto_whitebalance = false;
804  }
805  }
806  else
807  {
808  camera_->setWhiteBalance(config.whitebalance_blue, config.whitebalance_red, prosilica::Manual);
809  if (camera_->hasAttribute("WhitebalValueRed"))
810  camera_->setAttribute("WhitebalValueRed", (tPvUint32)config.whitebalance_red);
811  if (camera_->hasAttribute("WhitebalValueBlue"))
812  camera_->setAttribute("WhitebalValueBlue", (tPvUint32)config.whitebalance_blue);
813  }
814 
815  // Binning configuration
816  if (camera_->hasAttribute("BinningX"))
817  {
818  config.binning_x = std::min(config.binning_x, (int)max_binning_x);
819  config.binning_y = std::min(config.binning_y, (int)max_binning_y);
820 
821  camera_->setBinning(config.binning_x, config.binning_y);
822  }
823  else if (config.binning_x > 1 || config.binning_y > 1)
824  {
825  NODELET_WARN("Binning not available for this camera.");
826  config.binning_x = config.binning_y = 1;
827  }
828 
829  // Region of interest configuration
830  // Make sure ROI fits in image
831  config.x_offset = std::min(config.x_offset, (int)sensor_width_ - 1);
832  config.y_offset = std::min(config.y_offset, (int)sensor_height_ - 1);
833  config.width = std::min(config.width, (int)sensor_width_ - config.x_offset);
834  config.height = std::min(config.height, (int)sensor_height_ - config.y_offset);
835  // If width or height is 0, set it as large as possible
836  int width = config.width ? config.width : sensor_width_ - config.x_offset;
837  int height = config.height ? config.height : sensor_height_ - config.y_offset;
838 
839  // Adjust full-res ROI to binning ROI
841  int x_offset = config.x_offset / config.binning_x;
842  int y_offset = config.y_offset / config.binning_y;
843  unsigned int right_x = (config.x_offset + width + config.binning_x - 1) / config.binning_x;
844  unsigned int bottom_y = (config.y_offset + height + config.binning_y - 1) / config.binning_y;
845  // Rounding up is bad when at max resolution which is not divisible by the amount of binning
846  right_x = std::min(right_x, (unsigned)(sensor_width_ / config.binning_x));
847  bottom_y = std::min(bottom_y, (unsigned)(sensor_height_ / config.binning_y));
848  width = right_x - x_offset;
849  height = bottom_y - y_offset;
850 
851  camera_->setRoi(x_offset, y_offset, width, height);
852 
853  // TF frame
854  img_.header.frame_id = cam_info_.header.frame_id = config.frame_id;
855 
856  // Normally the node adjusts the bandwidth used by the camera during diagnostics, to use as
857  // much as possible without dropping packets. But this can create interference if two
858  // cameras are on the same switch, e.g. for stereo. So we allow the user to set the bandwidth
859  // directly.
860  auto_adjust_stream_bytes_per_second_ = config.auto_adjust_stream_bytes_per_second;
861  if (!auto_adjust_stream_bytes_per_second_)
862  camera_->setAttribute("StreamBytesPerSecond", (tPvUint32)config.stream_bytes_per_second);
863  else
864  camera_->setAttribute("StreamBytesPerSecond", (tPvUint32)(115000000/num_cameras));
865 
868  if (level >= (uint32_t)dynamic_reconfigure::SensorLevels::RECONFIGURE_STOP)
869  {
870  try
871  {
872  start();
873  }
874  catch(std::exception &e)
875  {
876  NODELET_ERROR("Invalid settings: %s", e.what());
877  config = last_config_;
878  }
879  }
880 
881  last_config_ = config;
882  }
883 
885  {
886  stat.add("Serial", guid_);
887  stat.add("Info", state_info_);
888  stat.add("Intrinsics", intrinsics_);
889  stat.add("Total frames dropped", frames_dropped_total_);
890  stat.add("Total frames", frames_completed_total_);
891 
892  if(frames_completed_total_>0)
893  {
894  stat.add("Total % frames dropped", 100.*(double)frames_dropped_total_/frames_completed_total_);
895  }
896  if(frames_completed_acc_.sum()>0)
897  {
898  stat.add("Recent % frames dropped", 100.*frames_dropped_acc_.sum()/frames_completed_acc_.sum());
899  }
900 
901  switch (camera_state_)
902  {
903  case OPENING:
904  stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "Opening camera");
905  break;
906  case OK:
907  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Camera operating normally");
908  break;
909  case CAMERA_NOT_FOUND:
910  stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "Can not find camera %d", guid_ );
911  stat.add("Available Cameras", getAvailableCameras());
912  break;
913  case FORMAT_ERROR:
914  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Problem retrieving frame");
915  break;
916  case ERROR:
917  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Camera has encountered an error");
918  break;
919  default:
920  break;
921  }
922  }
923 };
924 }
925 
928 
tPvErr Status
bool prosilica_inited
static bool fillImage(Image &image, const std::string &encoding_arg, uint32_t rows_arg, uint32_t cols_arg, uint32_t step_arg, const void *data_arg)
void add(T sample)
Definition: rolling_sum.h:17
void getCurrentState(diagnostic_updater::DiagnosticStatusWrapper &stat)
void publish(const sensor_msgs::Image &image, const sensor_msgs::CameraInfo &info) const
tPvErr PVDECL PvAttrRangeUint32(tPvHandle Camera, const char *Name, tPvUint32 *pMin, tPvUint32 *pMax)
#define NODELET_ERROR(...)
RollingSum< unsigned long > frames_completed_acc_
enum prosilica_camera::ProsilicaNodelet::CameraState camera_state_
#define NODELET_WARN(...)
f
void summary(unsigned char lvl, const std::string s)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ePvFmtBayer8
RollingSum< unsigned long > packets_missed_acc_
uint32_t getNumSubscribers() const
void setHardwareID(const std::string &hwid)
tPvBayerPattern BayerPattern
void init()
Definition: prosilica.cpp:88
ePvFmtMono8
void stop()
bool processFrame(tPvFrame *frame, sensor_msgs::Image &img, sensor_msgs::CameraInfo &cam_info)
void syncInCallback(const std_msgs::HeaderConstPtr &msg)
void setPeriod(const Duration &period, bool reset=true)
const std::string & getName() const
void start()
unsigned long Height
void add(const std::string &name, TaskFunction f)
tPvImageFormat Format
uint64_t getGuid(size_t i)
Definition: prosilica.cpp:104
unsigned long ImageSize
boost::shared_ptr< prosilica::Camera > camera_
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
ePvErrSuccess
boost::shared_ptr< ReconfigureServer > reconfigure_server_
void publishImage(tPvFrame *frame, ros::Time time)
ros::NodeHandle & getPrivateNodeHandle() const
void summaryf(unsigned char lvl, const char *format,...)
PLUGINLIB_EXPORT_CLASS(prosilica_camera::ProsilicaNodelet, nodelet::Nodelet)
void pollCallback(polled_camera::GetPolledImage::Request &req, polled_camera::GetPolledImage::Response &rsp, sensor_msgs::Image &image, sensor_msgs::CameraInfo &info)
PublicationServer advertise(ros::NodeHandle &nh, const std::string &service, const PublicationServer::DriverCallback &cb, const ros::VoidPtr &tracked_object=ros::VoidPtr())
CameraPublisher advertiseCamera(const std::string &base_topic, uint32_t queue_size, bool latch=false)
RollingSum< unsigned long > packets_received_acc_
static const size_t USER_MEMORY_SIZE
Data must have size <= USER_MEMORY_SIZE bytes.
Definition: prosilica.h:171
image_transport::CameraPublisher image_publisher_
ePvFmtBgr24
std::vector< CameraInfo > listCameras()
Definition: prosilica.cpp:112
size_t numCameras()
Definition: prosilica.cpp:98
ePvFmtRgb24
bool param(const std::string &param_name, T &param_val, const T &default_val) const
#define NODELET_ERROR_ONCE(...)
int num_cameras
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
const std::string & getNamespace() const
void fini()
Definition: prosilica.cpp:93
bool writeCalibrationIni(std::ostream &out, const std::string &camera_name, const sensor_msgs::CameraInfo &cam_info)
polled_camera::PublicationServer poll_srv_
ros::NodeHandle & getNodeHandle() const
unsigned long RegionX
unsigned long RegionY
void * ImageBuffer
bool setCameraInfo(sensor_msgs::SetCameraInfoRequest &req, sensor_msgs::SetCameraInfoResponse &rsp)
#define NODELET_INFO(...)
bool frameToImage(tPvFrame *frame, sensor_msgs::Image &image)
ePvFmtBgra32
dynamic_reconfigure::Server< prosilica_camera::ProsilicaCameraConfig > ReconfigureServer
diagnostic_updater::Updater updater
static Time now()
prosilica_camera::ProsilicaCameraConfig last_config_
bool parseCalibrationIni(const std::string &buffer, std::string &camera_name, sensor_msgs::CameraInfo &cam_info)
void add(const std::string &key, const T &val)
void setHardwareIDf(const char *format,...)
void updateCallback(const ros::TimerEvent &event)
#define NODELET_DEBUG(...)
RollingSum< unsigned long > frames_dropped_acc_
ePvFmtRgba32
unsigned long Width
void reconfigureCallback(prosilica_camera::ProsilicaCameraConfig &config, uint32_t level)


prosilica_camera
Author(s): Maintained by William Woodall - wwoodall@willowgarage.com, Contributions by Allison Thackston - allison.thackston@nasa.gov
autogenerated on Mon Jun 10 2019 14:20:19