SpinnakerCamera.cpp
Go to the documentation of this file.
1 /*
2 This code was developed by the National Robotics Engineering Center (NREC), part of the Robotics Institute at Carnegie
3 Mellon University.
4 Its development was funded by DARPA under the LS3 program and submitted for public release on June 7th, 2012.
5 Release was granted on August, 21st 2012 with Distribution Statement "A" (Approved for Public Release, Distribution
6 Unlimited).
7 
8 This software is released under a BSD license:
9 
10 Copyright (c) 2012, Carnegie Mellon University. All rights reserved.
11 Copyright (c) 2018, Clearpath Robotics, Inc., All rights reserved.
12 
13 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
14 following conditions are met:
15 
16 Redistributions of source code must retain the above copyright notice, this list of conditions and the following
17 disclaimer.
18 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
19 disclaimer in the documentation and/or other materials provided with the distribution.
20 Neither the name of the Carnegie Mellon University nor the names of its contributors may be used to endorse or promote
21 products derived from this software without specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
23 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 
31 /*-*-C++-*-*/
44 
45 #include <iostream>
46 #include <sstream>
47 #include <typeinfo>
48 #include <string>
49 
50 #include <ros/ros.h>
51 
53 {
55  : serial_(0)
56  , system_(Spinnaker::System::GetInstance())
57  , camList_(system_->GetCameras())
58  , pCam_(static_cast<int>(NULL)) // Hack to suppress compiler warning. Spinnaker has only one contructor which takes
59  // an int
60  , camera_(static_cast<int>(NULL))
61  , captureRunning_(false)
62 {
63  unsigned int num_cameras = camList_.GetSize();
64  ROS_INFO_STREAM_ONCE("[SpinnakerCamera]: Number of cameras detected: " << num_cameras);
65 }
66 
68 {
69  // @note ebretl Destructors of camList_ and system_ handle teardown
70 }
71 
72 void SpinnakerCamera::setNewConfiguration(const spinnaker_camera_driver::SpinnakerConfig& config, const uint32_t& level)
73 {
74  // Check if camera is connected
75  if (!pCam_)
76  {
78  }
79 
80  // Activate mutex to prevent us from grabbing images during this time
81  std::lock_guard<std::mutex> scopedLock(mutex_);
82 
83  if (level >= LEVEL_RECONFIGURE_STOP)
84  {
85  ROS_DEBUG("SpinnakerCamera::setNewConfiguration: Reconfigure Stop.");
86  bool capture_was_running = captureRunning_;
87  start(); // For some reason some params only work after aquisition has be started once.
88  stop();
89  camera_->setNewConfiguration(config, level);
90  if (capture_was_running)
91  start();
92  }
93  else
94  {
95  camera_->setNewConfiguration(config, level);
96  }
97 } // end setNewConfiguration
98 
99 void SpinnakerCamera::setGain(const float& gain)
100 {
101  if (camera_)
102  camera_->setGain(gain);
103 }
104 
106 {
107  if (camera_)
108  return camera_->getHeightMax();
109  else
110  return 0;
111 }
112 
114 {
115  if (camera_)
116  return camera_->getWidthMax();
117  else
118  return 0;
119 }
120 
121 Spinnaker::GenApi::CNodePtr SpinnakerCamera::readProperty(const Spinnaker::GenICam::gcstring property_name)
122 {
123  if (camera_)
124  {
125  return camera_->readProperty(property_name);
126  }
127  else
128  {
129  return 0;
130  }
131 }
132 
134 {
135  if (!pCam_)
136  {
137  // If we have a specific camera to connect to (specified by a serial number)
138  if (serial_ != 0)
139  {
140  const auto serial_string = std::to_string(serial_);
141 
142  try
143  {
144  pCam_ = camList_.GetBySerial(serial_string);
145  }
146  catch (const Spinnaker::Exception& e)
147  {
148  throw std::runtime_error("[SpinnakerCamera::connect] Could not find camera with serial number " +
149  serial_string + ". Is that camera plugged in? Error: " + std::string(e.what()));
150  }
151  }
152  else
153  {
154  // Connect to any camera (the first)
155  try
156  {
157  pCam_ = camList_.GetByIndex(0);
158  }
159  catch (const Spinnaker::Exception& e)
160  {
161  throw std::runtime_error("[SpinnakerCamera::connect] Failed to get first connected camera. Error: " +
162  std::string(e.what()));
163  }
164  }
165  if (!pCam_ || !pCam_->IsValid())
166  {
167  throw std::runtime_error("[SpinnakerCamera::connect] Failed to obtain camera reference.");
168  }
169 
170  try
171  {
172  // Check Device type and save serial for reconnecting
173  Spinnaker::GenApi::INodeMap& genTLNodeMap = pCam_->GetTLDeviceNodeMap();
174 
175  if (serial_ == 0)
176  {
177  Spinnaker::GenApi::CStringPtr serial_ptr =
178  static_cast<Spinnaker::GenApi::CStringPtr>(genTLNodeMap.GetNode("DeviceID"));
179  if (IsAvailable(serial_ptr) && IsReadable(serial_ptr))
180  {
181  serial_ = atoi(serial_ptr->GetValue().c_str());
182  ROS_INFO("[SpinnakerCamera::connect]: Using Serial: %i", serial_);
183  }
184  else
185  {
186  throw std::runtime_error("[SpinnakerCamera::connect]: Unable to determine serial number.");
187  }
188  }
189 
190  Spinnaker::GenApi::CEnumerationPtr device_type_ptr =
191  static_cast<Spinnaker::GenApi::CEnumerationPtr>(genTLNodeMap.GetNode("DeviceType"));
192 
193  if (IsAvailable(device_type_ptr) && IsReadable(device_type_ptr))
194  {
195  ROS_INFO_STREAM("[SpinnakerCamera::connect]: Detected device type: " << device_type_ptr->ToString());
196 
197  if (device_type_ptr->GetCurrentEntry() == device_type_ptr->GetEntryByName("U3V"))
198  {
199  Spinnaker::GenApi::CEnumerationPtr device_speed_ptr =
200  static_cast<Spinnaker::GenApi::CEnumerationPtr>(genTLNodeMap.GetNode("DeviceCurrentSpeed"));
201  if (IsAvailable(device_speed_ptr) && IsReadable(device_speed_ptr))
202  {
203  if (device_speed_ptr->GetCurrentEntry() != device_speed_ptr->GetEntryByName("SuperSpeed"))
204  ROS_ERROR_STREAM("[SpinnakerCamera::connect]: U3V Device not running at Super-Speed. Check Cables! ");
205  }
206  }
207  // TODO(mhosmar): - check if interface is GigE and connect to GigE cam
208  }
209  }
210  catch (const Spinnaker::Exception& e)
211  {
212  throw std::runtime_error("[SpinnakerCamera::connect] Failed to determine device info with error: " +
213  std::string(e.what()));
214  }
215 
216  try
217  {
218  // Initialize Camera
219  pCam_->Init();
220 
221  // Retrieve GenICam nodemap
222  node_map_ = &pCam_->GetNodeMap();
223 
224  // detect model and set camera_ accordingly;
225  Spinnaker::GenApi::CStringPtr model_name = node_map_->GetNode("DeviceModelName");
226  std::string model_name_str(model_name->ToString());
227 
228  ROS_INFO("[SpinnakerCamera::connect]: Camera model name: %s", model_name_str.c_str());
229  if (model_name_str.find("Blackfly S") != std::string::npos)
230  camera_.reset(new Camera(node_map_));
231  else if (model_name_str.find("Chameleon3") != std::string::npos)
232  camera_.reset(new Cm3(node_map_));
233  else if (model_name_str.find("Grasshopper3") != std::string::npos)
234  camera_.reset(new Gh3(node_map_));
235  else
236  {
237  camera_.reset(new Camera(node_map_));
238  ROS_WARN("SpinnakerCamera::connect: Could not detect camera model name.");
239  }
240 
241  // Configure chunk data - Enable Metadata
242  // SpinnakerCamera::ConfigureChunkData(*node_map_);
243  }
244  catch (const Spinnaker::Exception& e)
245  {
246  throw std::runtime_error("[SpinnakerCamera::connect] Failed to connect to camera. Error: " +
247  std::string(e.what()));
248  }
249  catch (const std::runtime_error& e)
250  {
251  throw std::runtime_error("[SpinnakerCamera::connect] Failed to configure chunk data. Error: " +
252  std::string(e.what()));
253  }
254  }
255 
256  // TODO(mhosmar): Get camera info to check if camera is running in color or mono mode
257  /*
258  CameraInfo cInfo;
259  error = cam_.GetCameraInfo(&cInfo);
260  SpinnakerCamera::handleError("SpinnakerCamera::connect Failed to get camera info.", error);
261  isColor_ = cInfo.isColorCamera;
262  */
263 }
264 
266 {
267  std::lock_guard<std::mutex> scopedLock(mutex_);
268  captureRunning_ = false;
269  try
270  {
271  // Check if camera is connected
272  if (pCam_)
273  {
274  pCam_->DeInit();
275  pCam_ = static_cast<int>(NULL);
276  camList_.RemoveBySerial(std::to_string(serial_));
277  }
278  Spinnaker::CameraList temp_list = system_->GetCameras();
279  camList_.Append(temp_list);
280  }
281  catch (const Spinnaker::Exception& e)
282  {
283  throw std::runtime_error("[SpinnakerCamera::disconnect] Failed to disconnect camera with error: " +
284  std::string(e.what()));
285  }
286 }
287 
289 {
290  try
291  {
292  // Check if camera is connected
293  if (pCam_ && !captureRunning_)
294  {
295  // Start capturing images
296  pCam_->BeginAcquisition();
297  captureRunning_ = true;
298  }
299  }
300  catch (const Spinnaker::Exception& e)
301  {
302  throw std::runtime_error("[SpinnakerCamera::start] Failed to start capture with error: " + std::string(e.what()));
303  }
304 }
305 
307 {
308  if (pCam_ && captureRunning_)
309  {
310  // Stop capturing images
311  try
312  {
313  captureRunning_ = false;
314  pCam_->EndAcquisition();
315  }
316  catch (const Spinnaker::Exception& e)
317  {
318  throw std::runtime_error("[SpinnakerCamera::stop] Failed to stop capture with error: " + std::string(e.what()));
319  }
320  }
321 }
322 
323 void SpinnakerCamera::grabImage(sensor_msgs::Image* image, const std::string& frame_id)
324 {
325  std::lock_guard<std::mutex> scopedLock(mutex_);
326 
327  // Check if Camera is connected and Running
328  if (pCam_ && captureRunning_)
329  {
330  // Handle "Image Retrieval" Exception
331  try
332  {
333  Spinnaker::ImagePtr image_ptr = pCam_->GetNextImage(timeout_);
334  // std::string format(image_ptr->GetPixelFormatName());
335  // std::printf("\033[100m format: %s \n", format.c_str());
336 
337  if (image_ptr->IsIncomplete())
338  {
339  throw std::runtime_error("[SpinnakerCamera::grabImage] Image received from camera " + std::to_string(serial_) +
340  " is incomplete.");
341  }
342  else
343  {
344  // Set Image Time Stamp
345  image->header.stamp.sec = image_ptr->GetTimeStamp() * 1e-9;
346  image->header.stamp.nsec = image_ptr->GetTimeStamp();
347 
348  // Check the bits per pixel.
349  size_t bitsPerPixel = image_ptr->GetBitsPerPixel();
350 
351  // --------------------------------------------------
352  // Set the image encoding
353  std::string imageEncoding = sensor_msgs::image_encodings::MONO8;
354 
355  Spinnaker::GenApi::CEnumerationPtr color_filter_ptr =
356  static_cast<Spinnaker::GenApi::CEnumerationPtr>(node_map_->GetNode("PixelColorFilter"));
357 
358  Spinnaker::GenICam::gcstring color_filter_str = color_filter_ptr->ToString();
359  Spinnaker::GenICam::gcstring bayer_rg_str = "BayerRG";
360  Spinnaker::GenICam::gcstring bayer_gr_str = "BayerGR";
361  Spinnaker::GenICam::gcstring bayer_gb_str = "BayerGB";
362  Spinnaker::GenICam::gcstring bayer_bg_str = "BayerBG";
363 
364  // if(isColor_ && bayer_format != NONE)
365  if (color_filter_ptr->GetCurrentEntry() != color_filter_ptr->GetEntryByName("None"))
366  {
367  if (bitsPerPixel == 16)
368  {
369  // 16 Bits per Pixel
370  if (color_filter_str.compare(bayer_rg_str) == 0)
371  {
373  }
374  else if (color_filter_str.compare(bayer_gr_str) == 0)
375  {
377  }
378  else if (color_filter_str.compare(bayer_gb_str) == 0)
379  {
381  }
382  else if (color_filter_str.compare(bayer_bg_str) == 0)
383  {
385  }
386  else
387  {
388  throw std::runtime_error("[SpinnakerCamera::grabImage] Bayer format not recognized for 16-bit format.");
389  }
390  }
391  else
392  {
393  // 8 Bits per Pixel
394  if (color_filter_str.compare(bayer_rg_str) == 0)
395  {
397  }
398  else if (color_filter_str.compare(bayer_gr_str) == 0)
399  {
401  }
402  else if (color_filter_str.compare(bayer_gb_str) == 0)
403  {
405  }
406  else if (color_filter_str.compare(bayer_bg_str) == 0)
407  {
409  }
410  else
411  {
412  throw std::runtime_error("[SpinnakerCamera::grabImage] Bayer format not recognized for 8-bit format.");
413  }
414  }
415  }
416  else // Mono camera or in pixel binned mode.
417  {
418  if (bitsPerPixel == 16)
419  {
420  imageEncoding = sensor_msgs::image_encodings::MONO16;
421  }
422  else if (bitsPerPixel == 24)
423  {
424  imageEncoding = sensor_msgs::image_encodings::RGB8;
425  }
426  else
427  {
428  imageEncoding = sensor_msgs::image_encodings::MONO8;
429  }
430  }
431 
432  int width = image_ptr->GetWidth();
433  int height = image_ptr->GetHeight();
434  int stride = image_ptr->GetStride();
435 
436  // ROS_INFO_ONCE("\033[93m wxh: (%d, %d), stride: %d \n", width, height, stride);
437  fillImage(*image, imageEncoding, height, width, stride, image_ptr->GetData());
438  image->header.frame_id = frame_id;
439  } // end else
440  }
441  catch (const Spinnaker::Exception& e)
442  {
443  throw std::runtime_error("[SpinnakerCamera::grabImage] Failed to retrieve buffer with error: " +
444  std::string(e.what()));
445  }
446  }
447  else if (pCam_)
448  {
449  throw CameraNotRunningException("[SpinnakerCamera::grabImage] Camera is currently not running. Please start "
450  "capturing frames first.");
451  }
452  else
453  {
454  throw std::runtime_error("[SpinnakerCamera::grabImage] Not connected to the camera.");
455  }
456 } // end grabImage
457 
458 void SpinnakerCamera::setTimeout(const double& timeout)
459 {
460  timeout_ = static_cast<uint64_t>(std::round(timeout * 1000));
461 }
462 void SpinnakerCamera::setDesiredCamera(const uint32_t& id)
463 {
464  serial_ = id;
465 }
466 
467 void SpinnakerCamera::ConfigureChunkData(const Spinnaker::GenApi::INodeMap& nodeMap)
468 {
469  ROS_INFO_STREAM("*** CONFIGURING CHUNK DATA ***");
470  try
471  {
472  // Activate chunk mode
473  //
474  // *** NOTES ***
475  // Once enabled, chunk data will be available at the end of the payload
476  // of every image captured until it is disabled. Chunk data can also be
477  // retrieved from the nodemap.
478  //
479  Spinnaker::GenApi::CBooleanPtr ptrChunkModeActive = nodeMap.GetNode("ChunkModeActive");
480  if (!Spinnaker::GenApi::IsAvailable(ptrChunkModeActive) || !Spinnaker::GenApi::IsWritable(ptrChunkModeActive))
481  {
482  throw std::runtime_error("Unable to activate chunk mode. Aborting...");
483  }
484  ptrChunkModeActive->SetValue(true);
485  ROS_INFO_STREAM_ONCE("Chunk mode activated...");
486 
487  // Enable all types of chunk data
488  //
489  // *** NOTES ***
490  // Enabling chunk data requires working with nodes: "ChunkSelector"
491  // is an enumeration selector node and "ChunkEnable" is a boolean. It
492  // requires retrieving the selector node (which is of enumeration node
493  // type), selecting the entry of the chunk data to be enabled, retrieving
494  // the corresponding boolean, and setting it to true.
495  //
496  // In this example, all chunk data is enabled, so these steps are
497  // performed in a loop. Once this is complete, chunk mode still needs to
498  // be activated.
499  //
500  Spinnaker::GenApi::NodeList_t entries;
501  // Retrieve the selector node
502  Spinnaker::GenApi::CEnumerationPtr ptrChunkSelector = nodeMap.GetNode("ChunkSelector");
503  if (!Spinnaker::GenApi::IsAvailable(ptrChunkSelector) || !Spinnaker::GenApi::IsReadable(ptrChunkSelector))
504  {
505  throw std::runtime_error("Unable to retrieve chunk selector. Aborting...");
506  }
507  // Retrieve entries
508  ptrChunkSelector->GetEntries(entries);
509 
510  ROS_INFO_STREAM("Enabling entries...");
511 
512  for (unsigned int i = 0; i < entries.size(); i++)
513  {
514  // Select entry to be enabled
515  Spinnaker::GenApi::CEnumEntryPtr ptrChunkSelectorEntry = entries.at(i);
516  // Go to next node if problem occurs
517  if (!Spinnaker::GenApi::IsAvailable(ptrChunkSelectorEntry) ||
518  !Spinnaker::GenApi::IsReadable(ptrChunkSelectorEntry))
519  {
520  continue;
521  }
522  ptrChunkSelector->SetIntValue(ptrChunkSelectorEntry->GetValue());
523 
524  ROS_INFO_STREAM("\t" << ptrChunkSelectorEntry->GetSymbolic() << ": ");
525  // Retrieve corresponding boolean
526  Spinnaker::GenApi::CBooleanPtr ptrChunkEnable = nodeMap.GetNode("ChunkEnable");
527  // Enable the boolean, thus enabling the corresponding chunk data
528  if (!Spinnaker::GenApi::IsAvailable(ptrChunkEnable))
529  {
530  ROS_INFO("Node not available");
531  }
532  else if (ptrChunkEnable->GetValue())
533  {
534  ROS_INFO("Enabled");
535  }
536  else if (Spinnaker::GenApi::IsWritable(ptrChunkEnable))
537  {
538  ptrChunkEnable->SetValue(true);
539  ROS_INFO("Enabled");
540  }
541  else
542  {
543  ROS_INFO("Node not writable");
544  }
545  }
546  }
547  catch (const Spinnaker::Exception& e)
548  {
549  throw std::runtime_error(e.what());
550  }
551 }
552 } // namespace spinnaker_camera_driver
const std::string BAYER_GRBG8
void grabImage(sensor_msgs::Image *image, const std::string &frame_id)
Loads the raw data from the cameras buffer.
std::mutex mutex_
A mutex to make sure that we don&#39;t try to grabImages while reconfiguring or vice versa.
void disconnect()
Disconnects from the camera.
const std::string BAYER_GRBG16
void connect()
Function that connects to a specified camera.
const std::string BAYER_RGGB16
void ConfigureChunkData(const Spinnaker::GenApi::INodeMap &nodeMap)
#define ROS_WARN(...)
const std::string BAYER_GBRG8
const std::string BAYER_GBRG16
const std::string BAYER_BGGR16
void start()
Starts the camera loading data into its buffer.
#define ROS_INFO(...)
void setTimeout(const double &timeout)
Will set grabImage timeout for the camera.
const std::string MONO16
Spinnaker::GenApi::CNodePtr readProperty(const Spinnaker::GenICam::gcstring property_name)
void setDesiredCamera(const uint32_t &id)
Used to set the serial number for the camera you wish to connect to.
#define ROS_INFO_STREAM_ONCE(args)
const std::string BAYER_BGGR8
#define ROS_INFO_STREAM(args)
Spinnaker::GenApi::INodeMap * node_map_
Interface to Point Grey cameras.
const std::string BAYER_RGGB8
uint32_t serial_
A variable to hold the serial number of the desired camera.
#define ROS_ERROR_STREAM(args)
void stop()
Stops the camera loading data into its buffer.
void setNewConfiguration(const spinnaker_camera_driver::SpinnakerConfig &config, const uint32_t &level)
Function that allows reconfiguration of the camera.
#define ROS_DEBUG(...)


spinnaker_camera_driver
Author(s): Chad Rockey
autogenerated on Sun Feb 14 2021 03:47:26