Skip to content

Commit 6cafb13

Browse files
Added framework for trigger management. To be tested on real hardware
1 parent 0c7aab6 commit 6cafb13

File tree

3 files changed

+217
-7
lines changed

3 files changed

+217
-7
lines changed

spinnaker_camera_driver/include/spinnaker_camera_driver/SpinnakerCamera.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ class SpinnakerCamera
130130
*/
131131
void stop();
132132

133+
void trigger();
134+
133135
/*!
134136
* \brief Loads the raw data from the cameras buffer.
135137
*
@@ -167,6 +169,8 @@ class SpinnakerCamera
167169
bool readableProperty(const Spinnaker::GenICam::gcstring property_name);
168170
Spinnaker::GenApi::CNodePtr readProperty(const Spinnaker::GenICam::gcstring property_name);
169171

172+
void configureTrigger(bool enable, bool software);
173+
170174
uint32_t getSerial()
171175
{
172176
return serial_;

spinnaker_camera_driver/src/SpinnakerCamera.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,32 @@ void SpinnakerCamera::disconnect()
303303
}
304304
}
305305

306+
void SpinnakerCamera::trigger()
307+
{
308+
try
309+
{
310+
// Check if camera is connected
311+
if (pCam_ && captureRunning_)
312+
{
313+
// Execute software trigger
314+
Spinnaker::GenApi::CCommandPtr ptrSoftwareTriggerCommand = node_map_->GetNode("TriggerSoftware");
315+
if (!Spinnaker::GenApi::IsAvailable(ptrSoftwareTriggerCommand) ||
316+
!Spinnaker::GenApi::IsWritable(ptrSoftwareTriggerCommand))
317+
{
318+
throw std::runtime_error("[SpinnakerCamera::trigger] Unable to execute trigger ");
319+
}
320+
321+
ptrSoftwareTriggerCommand->Execute();
322+
323+
324+
}
325+
}
326+
catch (const Spinnaker::Exception& e)
327+
{
328+
throw std::runtime_error("[SpinnakerCamera::trigger] capture is not running ");
329+
}
330+
}
331+
306332
void SpinnakerCamera::start()
307333
{
308334
try
@@ -338,6 +364,138 @@ void SpinnakerCamera::stop()
338364
}
339365
}
340366

367+
// This function configures the camera to use a trigger. First, trigger mode is
368+
// set to off in order to select the trigger source. Once the trigger source
369+
// has been selected, trigger mode is then enabled, which has the camera
370+
// capture only a single image upon the execution of the chosen trigger.
371+
void SpinnakerCamera::configureTrigger(bool enable, bool software)
372+
{
373+
try
374+
{
375+
//
376+
// Ensure trigger mode off
377+
//
378+
// *** NOTES ***
379+
// The trigger must be disabled in order to configure whether the source
380+
// is software or hardware.
381+
//
382+
Spinnaker::GenApi::CEnumerationPtr ptrTriggerMode = node_map_->GetNode("TriggerMode");
383+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerMode) ||
384+
!Spinnaker::GenApi::IsReadable(ptrTriggerMode))
385+
{
386+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to disable trigger mode (node retrieval)");
387+
}
388+
389+
Spinnaker::GenApi::CEnumEntryPtr ptrTriggerModeOff = ptrTriggerMode->GetEntryByName("Off");
390+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerModeOff) ||
391+
!Spinnaker::GenApi::IsReadable(ptrTriggerModeOff))
392+
{
393+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to disable trigger mode (enum entry retrieval)");
394+
}
395+
396+
ptrTriggerMode->SetIntValue(ptrTriggerModeOff->GetValue());
397+
398+
if (!enable) {
399+
ROS_INFO("Trigger mode disabled");
400+
return;
401+
}
402+
ROS_INFO("Trigger mode disabled for configuration...");
403+
404+
//
405+
// Set TriggerSelector to FrameStart
406+
//
407+
// *** NOTES ***
408+
// For this example, the trigger selector should be set to frame start.
409+
// This is the default for most cameras.
410+
//
411+
Spinnaker::GenApi::CEnumerationPtr ptrTriggerSelector = node_map_->GetNode("TriggerSelector");
412+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerSelector) ||
413+
!Spinnaker::GenApi::IsWritable(ptrTriggerSelector))
414+
{
415+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to set trigger selector (node retrieval)");
416+
}
417+
418+
Spinnaker::GenApi::CEnumEntryPtr ptrTriggerSelectorFrameStart = ptrTriggerSelector->GetEntryByName("FrameStart");
419+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerSelectorFrameStart) ||
420+
!Spinnaker::GenApi::IsReadable(ptrTriggerSelectorFrameStart))
421+
{
422+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to set trigger selector (enum entry retrieval)");
423+
}
424+
425+
ptrTriggerSelector->SetIntValue(ptrTriggerSelectorFrameStart->GetValue());
426+
427+
ROS_INFO("Trigger selector set to frame start...");
428+
429+
//
430+
// Select trigger source
431+
//
432+
// *** NOTES ***
433+
// The trigger source must be set to hardware or software while trigger
434+
// mode is off.
435+
//
436+
Spinnaker::GenApi::CEnumerationPtr ptrTriggerSource = node_map_->GetNode("TriggerSource");
437+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerSource) ||
438+
!Spinnaker::GenApi::IsWritable(ptrTriggerSource))
439+
{
440+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to set trigger mode (node retrieval)");
441+
}
442+
443+
if (software) {
444+
// Set trigger mode to software
445+
Spinnaker::GenApi::CEnumEntryPtr ptrTriggerSourceSoftware = ptrTriggerSource->GetEntryByName("Software");
446+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerSourceSoftware) ||
447+
!Spinnaker::GenApi::IsReadable(ptrTriggerSourceSoftware))
448+
{
449+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to set trigger mode (enum entry retrieval)");
450+
}
451+
452+
ptrTriggerSource->SetIntValue(ptrTriggerSourceSoftware->GetValue());
453+
454+
ROS_INFO("Trigger source set to software.");
455+
} else {
456+
// Set trigger mode to hardware ('Line0')
457+
Spinnaker::GenApi::CEnumEntryPtr ptrTriggerSourceHardware = ptrTriggerSource->GetEntryByName("Line0");
458+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerSourceHardware) ||
459+
!Spinnaker::GenApi::IsReadable(ptrTriggerSourceHardware))
460+
{
461+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to set trigger mode (enum entry retrieval)");
462+
}
463+
464+
ptrTriggerSource->SetIntValue(ptrTriggerSourceHardware->GetValue());
465+
466+
ROS_INFO("Trigger source set to hardware.");
467+
}
468+
469+
//
470+
// Turn trigger mode on
471+
//
472+
// *** LATER ***
473+
// Once the appropriate trigger source has been set, turn trigger mode
474+
// on in order to retrieve images using the trigger.
475+
//
476+
477+
Spinnaker::GenApi::CEnumEntryPtr ptrTriggerModeOn = ptrTriggerMode->GetEntryByName("On");
478+
if (!Spinnaker::GenApi::IsAvailable(ptrTriggerModeOn) ||
479+
!Spinnaker::GenApi::IsReadable(ptrTriggerModeOn))
480+
{
481+
throw std::runtime_error("[SpinnakerCamera::configureTrigger] Unable to enable trigger mode (enum entry retrieval)");
482+
}
483+
484+
ptrTriggerMode->SetIntValue(ptrTriggerModeOn->GetValue());
485+
486+
// NOTE: Blackfly and Flea3 GEV cameras need 1 second delay after trigger mode is turned on
487+
488+
ROS_INFO("Trigger mode turned back on...");
489+
}
490+
catch (Spinnaker::Exception& e)
491+
{
492+
throw std::runtime_error(std::string("[SpinnakerCamera::configureTrigger] ") + e.what());
493+
}
494+
495+
}
496+
497+
498+
341499
void SpinnakerCamera::grabImage(sensor_msgs::Image* image, const std::string& frame_id)
342500
{
343501
std::lock_guard<std::mutex> scopedLock(mutex_);
@@ -382,6 +540,9 @@ void SpinnakerCamera::grabImage(sensor_msgs::Image* image, const std::string& fr
382540
Spinnaker::GenICam::gcstring bayer_gr_str = "BayerGR";
383541
Spinnaker::GenICam::gcstring bayer_gb_str = "BayerGB";
384542
Spinnaker::GenICam::gcstring bayer_bg_str = "BayerBG";
543+
ROS_WARN_STREAM_ONCE("[SpinnakerCamera::grabImage] camera "
544+
<< std::to_string(serial_)
545+
<< " pixel color filter is " << color_filter_str);
385546

386547

387548
// if(isColor_ && bayer_format != NONE)
@@ -481,6 +642,7 @@ void SpinnakerCamera::setTimeout(const double& timeout)
481642
{
482643
timeout_ = static_cast<uint64_t>(std::round(timeout * 1000));
483644
}
645+
484646
void SpinnakerCamera::setDesiredCamera(const uint32_t& id)
485647
{
486648
serial_ = id;

spinnaker_camera_driver/src/nodelet.cpp

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
303303
pnh.param<bool>("auto_packet_size", auto_packet_size_, true);
304304
pnh.param<int>("packet_delay", packet_delay_, 4000);
305305
pnh.param<int>("throughput_limit", throughput_limit_, 125000000);
306+
pnh.param<bool>("trigger", trigger_, false);
307+
pnh.param<bool>("sw_trigger", sw_trigger_, false);
308+
pnh.param<double>("max_trigger_delay", max_trigger_delay_, 0.2);
306309

307310
// TODO(mhosmar): Set GigE parameters:
308311
// spinnaker_.setGigEParameters(auto_packet_size_, packet_size_, packet_delay_);
@@ -356,6 +359,13 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
356359
pnh.param<double>("min_acceptable_delay", min_acceptable, 0.0);
357360
double max_acceptable; // The maximum publishing delay (in seconds) before warning.
358361
pnh.param<double>("max_acceptable_delay", max_acceptable, 0.2);
362+
363+
if (trigger_) {
364+
trig_sub_ = pnh.subscribe("trigger", 1,
365+
&spinnaker_camera_driver::SpinnakerCameraNodelet::triggerCallback, this);
366+
}
367+
368+
359369
ros::SubscriberStatusCallback cb2 = boost::bind(&SpinnakerCameraNodelet::connectCb, this);
360370
pub_.reset(new diagnostic_updater::DiagnosedPublisher<wfov_camera_msgs::WFOVImage>(
361371
nh.advertise<wfov_camera_msgs::WFOVImage>("image", queue_size, cb2, cb2),
@@ -519,6 +529,7 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
519529
NODELET_DEBUG("Connected to camera.");
520530

521531
// Set last configuration, forcing the reconfigure level to stop
532+
ROS_INFO("Nodelet: throughput limit to %d",throughput_limit_);
522533
spinnaker_.setThroughputLimit(throughput_limit_);
523534
spinnaker_.setNewConfiguration(config_, SpinnakerCamera::LEVEL_RECONFIGURE_STOP);
524535

@@ -530,6 +541,11 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
530541

531542
NODELET_DEBUG_ONCE("Setting timeout to: %f.", timeout);
532543
spinnaker_.setTimeout(timeout);
544+
545+
NODELET_DEBUG_ONCE("Configuring trigger: %s %s", trigger_?"enabled":"disabled",
546+
sw_trigger_?"software":"hardware");
547+
spinnaker_.configureTrigger(trigger_,sw_trigger_);
548+
533549
}
534550
catch (const std::runtime_error& e)
535551
{
@@ -539,8 +555,7 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
539555
// Subscribe to gain and white balance changes
540556
{
541557
std::lock_guard<std::mutex> scopedLock(connect_mutex_);
542-
sub_ =
543-
getMTNodeHandle().subscribe("image_exposure_sequence", 10,
558+
sub_ = getMTNodeHandle().subscribe("image_exposure_sequence", 10,
544559
&spinnaker_camera_driver::SpinnakerCameraNodelet::gainWBCallback, this);
545560
}
546561
state = CONNECTED;
@@ -586,6 +601,20 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
586601
NODELET_DEBUG_ONCE("Starting a new grab from camera with serial {%d}.", spinnaker_.getSerial());
587602
spinnaker_.grabImage(&wfov_image->image, frame_id_);
588603

604+
ros::Time time = ros::Time::now() + ros::Duration(config_.time_offset);
605+
wfov_image->header.stamp = time;
606+
wfov_image->image.header.stamp = time;
607+
if (trigger_) {
608+
double trigger_delay = (wfov_image->header.stamp - trigger_header_.stamp).toSec();
609+
if (trigger_delay > max_trigger_delay_) {
610+
NODELET_ERROR("Rejecting image not attached to trigger (age %.3fs max %.3f)",trigger_delay,max_trigger_delay_);
611+
break;
612+
}
613+
// replacing timestamp with trigger stamp
614+
wfov_image->header.stamp = trigger_header_.stamp;
615+
wfov_image->image.header.stamp = trigger_header_.stamp;
616+
}
617+
589618
// Set other values
590619
wfov_image->header.frame_id = frame_id_;
591620

@@ -595,9 +624,6 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
595624

596625
// wfov_image->temperature = spinnaker_.getCameraTemperature();
597626

598-
ros::Time time = ros::Time::now() + ros::Duration(config_.time_offset);
599-
wfov_image->header.stamp = time;
600-
wfov_image->image.header.stamp = time;
601627

602628
// Set the CameraInfo message
603629
ci_.reset(new sensor_msgs::CameraInfo(cinfo_->getCameraInfo()));
@@ -614,8 +640,11 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
614640

615641
wfov_image->info = *ci_;
616642

617-
// Publish the full message
618-
pub_->publish(wfov_image);
643+
if (pub_->getPublisher().getNumSubscribers() > 0)
644+
{
645+
// Publish the full message
646+
pub_->publish(wfov_image);
647+
}
619648

620649
// Publish the message using standard image transport
621650
if (it_pub_.getNumSubscribers() > 0)
@@ -667,6 +696,14 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
667696
}
668697
}
669698

699+
void triggerCallback(const std_msgs::Header& msg)
700+
{
701+
trigger_header_ = msg;
702+
if (sw_trigger_) {
703+
spinnaker_.trigger();
704+
} // else we just store the timestamp and assume the camera is hardware triggered
705+
}
706+
670707
/* Class Fields */
671708
std::shared_ptr<dynamic_reconfigure::Server<spinnaker_camera_driver::SpinnakerConfig> > srv_; ///< Needed to
672709
/// initialize
@@ -685,6 +722,7 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
685722
/// constructor
686723
/// requirements
687724
ros::Subscriber sub_; ///< Subscriber for gain and white balance changes.
725+
ros::Subscriber trig_sub_; ///< Subscriber for gain and white balance changes.
688726

689727
std::mutex connect_mutex_;
690728

@@ -724,6 +762,12 @@ class SpinnakerCameraNodelet : public nodelet::Nodelet
724762
/// GigE throughput limit:
725763
int throughput_limit_;
726764

765+
// Data for software trigger management
766+
std_msgs::Header trigger_header_;
767+
bool trigger_;
768+
bool sw_trigger_;
769+
double max_trigger_delay_;
770+
727771
/// Configuration:
728772
spinnaker_camera_driver::SpinnakerConfig config_;
729773
};

0 commit comments

Comments
 (0)