FP Perception 0.1.2
Loading...
Searching...
No Matches
perception_server.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <thread>
4#include <functional>
5#include <memory>
6#include <mutex>
7#include <condition_variable>
8#include <algorithm>
9
10#include <rclcpp/rclcpp.hpp>
11#include <pluginlib/class_loader.hpp>
12
13#include <opencv2/opencv.hpp>
14#include <cv_bridge/cv_bridge.hpp>
15
35
36#include <fp_perception_msgs/msg/perception_audio.hpp>
37#include <fp_perception_msgs/msg/perception_text.hpp>
38#include <fp_perception_msgs/srv/perception_speech.hpp>
39#include <fp_perception_msgs/srv/perception_transcribe.hpp>
40#include <fp_perception_msgs/srv/perception_sentiment.hpp>
41#include <fp_perception_msgs/srv/perception_image_analysis.hpp>
42
43namespace fp_perception
44{
45
46class PerceptionServer : public rclcpp::Node
47{
48public:
50 {
51 const char* parameter_name;
53 const char* driver_description;
55 };
56
57 using Audio = fp_perception_msgs::msg::PerceptionAudio;
58 using Speech = fp_perception_msgs::srv::PerceptionSpeech;
59 using Transcribe = fp_perception_msgs::srv::PerceptionTranscribe;
60 using Sentiment = fp_perception_msgs::srv::PerceptionSentiment;
61 using ImageAnalysis = fp_perception_msgs::srv::PerceptionImageAnalysis;
62
63 inline static constexpr DriverSpec kRosVisionDriverSpec{ "ros_vision_driver", "fp_perception::DefaultDriver",
64 "vision driver", "VisionSourceDriver" };
65 inline static constexpr DriverSpec kNonRosVisionDriverSpec{ "non_ros_vision_driver", "fp_perception::OpenCVDriver",
66 "vision driver", "VisionSourceDriver" };
67 inline static constexpr DriverSpec kMicrophoneDriverSpec{ "microphone_driver", "fp_perception::MicrophoneAudioDriver",
68 "microphone driver", "AudioSourceDriver" };
69 inline static constexpr DriverSpec kSpeakerDriverSpec{ "speaker_driver", "fp_perception::SpeakerAudioDriver",
70 "speaker driver", "AudioSinkDriver" };
71 inline static constexpr DriverSpec kTranscriptionDriverSpec{ "transcription_driver", "fp_perception::OpenAIDriver",
72 "transcription driver", "TranscriptionDriver" };
73 inline static constexpr DriverSpec kSpeechDriverSpec{ "speech_synthesis_driver", "fp_perception::OpenAISpeechDriver",
74 "speech synthesis driver", "SpeechSynthesisDriver" };
75 inline static constexpr DriverSpec kSentimentDriverSpec{ "sentiment_driver", "fp_perception::SentimentDriver",
76 "sentiment driver", "SentimentAnalysisDriver" };
77 inline static constexpr DriverSpec kImageAnalysisDriverSpec{ "image_analysis_driver",
78 "fp_perception::OpenAIImageAnalysisDriver",
79 "image analysis driver", "ImageAnalysisDriver" };
80
86 PerceptionServer(const rclcpp::NodeOptions& options = rclcpp::NodeOptions())
87 : Node("perception_server", options), driver_loader_("fp_perception", "fp_perception::DriverBase")
88 {
90 }
91
96
98 {
99 std::scoped_lock lock(initialization_mutex_);
100 if (initialized_)
101 return;
102
103 RCLCPP_INFO(this->get_logger(), "Initializing PerceptionServer...");
104 DriverManager driver_manager(shared_from_this(), driver_loader_);
105
106 // ROS Interface
109 load_drivers(driver_manager);
111
112 if (run_tests_)
113 run_tests();
114
116 audio_publisher_ = this->create_publisher<Audio>(audio_input_topic_, 10);
117
119 audio_subscriber_ = this->create_subscription<Audio>(
120 audio_output_topic_, 10, std::bind(&PerceptionServer::audioCallback, this, std::placeholders::_1));
121
123 transcription_ = this->create_service<Transcribe>(
125 std::bind(&PerceptionServer::transcribe_callback, this, std::placeholders::_1, std::placeholders::_2));
126
127 if (speech_enabled_)
129 this->create_service<Speech>(speech_service_name_, std::bind(&PerceptionServer::speech_callback, this,
130 std::placeholders::_1, std::placeholders::_2));
131
133 sentiment_service_ = this->create_service<Sentiment>(
135 std::bind(&PerceptionServer::sentiment_callback, this, std::placeholders::_1, std::placeholders::_2));
136
138 image_publisher_ = this->create_publisher<sensor_msgs::msg::Image>(vision_input_topic_, 10);
139
141 image_analysis_service_ = this->create_service<ImageAnalysis>(
143 std::bind(&PerceptionServer::image_analysis_callback, this, std::placeholders::_1, std::placeholders::_2));
144
146
147 initialized_ = true;
148 }
149
150protected:
152 {
153 deferred_initialize_timer_ = this->create_wall_timer(std::chrono::milliseconds(1), [this]() {
156
157 try
158 {
159 initialize();
160 }
161 catch (const std::exception& e)
162 {
163 RCLCPP_ERROR(this->get_logger(), "Deferred initialization failed: %s", e.what());
164 }
165 });
166 }
167
169 {
171 {
172 audio_publish_thread_ = std::thread([this]() { publishAudio(); });
173 }
174 }
175
177 {
179 microphone_driver_->deinitialize();
180
181 if (audio_publish_thread_.joinable())
183 }
184
186 {
187 this->declare_parameter("use_ros_vision_driver", false);
188 this->declare_parameter("use_non_ros_vision_driver", false);
189 this->declare_parameter("use_microphone_driver", false);
190 this->declare_parameter("use_speaker_driver", false);
191 this->declare_parameter("use_transcription_driver", false);
192 this->declare_parameter("use_sentiment_driver", false);
193 this->declare_parameter("use_speech_driver", false);
194 this->declare_parameter("use_image_analysis_driver", false);
195 this->declare_parameter("use_diagnostics", false);
196 this->declare_parameter("run_tests", false);
197
198 use_ros_vision_driver_ = this->get_parameter("use_ros_vision_driver").as_bool();
199 use_non_ros_vision_driver_ = this->get_parameter("use_non_ros_vision_driver").as_bool();
200 use_microphone_driver_ = this->get_parameter("use_microphone_driver").as_bool();
201 use_speaker_driver_ = this->get_parameter("use_speaker_driver").as_bool();
202 use_transcription_driver_ = this->get_parameter("use_transcription_driver").as_bool();
203 use_sentiment_driver_ = this->get_parameter("use_sentiment_driver").as_bool();
204 use_speech_driver_ = this->get_parameter("use_speech_driver").as_bool();
205 use_image_analysis_driver_ = this->get_parameter("use_image_analysis_driver").as_bool();
206 run_tests_ = this->get_parameter("run_tests").as_bool();
207 }
208
210 {
211 this->declare_parameter("interface.audio_input.topic", "perception/microphone");
212 this->declare_parameter("interface.audio_input.frame_id", "microphone_frame");
213 this->declare_parameter("interface.audio_input.publish", false);
214 this->declare_parameter("interface.audio_input.frequency", 10);
215 this->declare_parameter("interface.audio_input.audio_retention_window", 10);
216 this->declare_parameter("interface.audio_input.default_audio_request_window", 10);
217
218 this->declare_parameter("interface.audio_output.topic", "perception/speaker");
219 this->declare_parameter("interface.audio_output.subscribe", false);
220
221 this->declare_parameter("interface.vision_input.topic", "perception/camera");
222 this->declare_parameter("interface.vision_input.frame_id", "camera_frame");
223 this->declare_parameter("interface.vision_input.publish", false);
224 this->declare_parameter("interface.vision_input.frequency", 10);
225
226 this->declare_parameter("interface.transcription.service", "perception/transcription");
227 this->declare_parameter("interface.transcription.provide_service", false);
228
229 this->declare_parameter("interface.speech.service_name", "perception/speech");
230 this->declare_parameter("interface.speech.provide_service", false);
231
232 this->declare_parameter("interface.sentiment.service_name", "perception/sentiment_analysis");
233 this->declare_parameter("interface.sentiment.provide_service", false);
234
235 this->declare_parameter("interface.image_analysis.service_name", "perception/image_analysis");
236 this->declare_parameter("interface.image_analysis.provide_service", false);
237
238 audio_input_publish_ = this->get_parameter("interface.audio_input.publish").as_bool();
239 audio_input_topic_ = this->get_parameter("interface.audio_input.topic").as_string();
240 audio_input_frame_id_ = this->get_parameter("interface.audio_input.frame_id").as_string();
241 audio_input_frequency_ = this->get_parameter("interface.audio_input.frequency").as_int();
242 audio_input_retention_window_ = this->get_parameter("interface.audio_input.audio_retention_window").as_int();
243 default_audio_request_window_ = this->get_parameter("interface.audio_input.default_audio_request_window").as_int();
244
245 audio_output_subscribe_ = this->get_parameter("interface.audio_output.subscribe").as_bool();
246 audio_output_topic_ = this->get_parameter("interface.audio_output.topic").as_string();
247
248 vision_input_publish_ = this->get_parameter("interface.vision_input.publish").as_bool();
249 vision_input_topic_ = this->get_parameter("interface.vision_input.topic").as_string();
250 vision_input_frame_id_ = this->get_parameter("interface.vision_input.frame_id").as_string();
251 vision_input_frequency_ = this->get_parameter("interface.vision_input.frequency").as_int();
252
253 transcription_service_ = this->get_parameter("interface.transcription.service").as_string();
254 transcription_enabled_ = this->get_parameter("interface.transcription.provide_service").as_bool();
255
256 speech_service_name_ = this->get_parameter("interface.speech.service_name").as_string();
257 speech_enabled_ = this->get_parameter("interface.speech.provide_service").as_bool();
258
259 sentiment_service_name_ = this->get_parameter("interface.sentiment.service_name").as_string();
260 sentiment_enabled_ = this->get_parameter("interface.sentiment.provide_service").as_bool();
261
262 image_analysis_service_name_ = this->get_parameter("interface.image_analysis.service_name").as_string();
263 image_analysis_enabled_ = this->get_parameter("interface.image_analysis.provide_service").as_bool();
264
265 RCLCPP_INFO(this->get_logger(), "Audio input publish: %s", audio_input_publish_ ? "true" : "false");
266 RCLCPP_INFO(this->get_logger(), "Audio input topic: %s", audio_input_topic_.c_str());
267 RCLCPP_INFO(this->get_logger(), "Audio input frame_id: %s", audio_input_frame_id_.c_str());
268 RCLCPP_INFO(this->get_logger(), "Audio input frequency: %d", audio_input_frequency_);
269 RCLCPP_INFO(this->get_logger(), "Audio retention window: %d seconds", audio_input_retention_window_);
270 RCLCPP_INFO(this->get_logger(), "Default audio request window: %d seconds", default_audio_request_window_);
271
272 RCLCPP_INFO(this->get_logger(), "Audio output subscribe: %s", audio_output_subscribe_ ? "true" : "false");
273 RCLCPP_INFO(this->get_logger(), "Audio output topic: %s", audio_output_topic_.c_str());
274
275 RCLCPP_INFO(this->get_logger(), "Vision input publish: %s", vision_input_publish_ ? "true" : "false");
276 RCLCPP_INFO(this->get_logger(), "Vision input topic: %s", vision_input_topic_.c_str());
277 RCLCPP_INFO(this->get_logger(), "Vision input frame_id: %s", vision_input_frame_id_.c_str());
278 RCLCPP_INFO(this->get_logger(), "Vision input frequency: %d", vision_input_frequency_);
279
280 RCLCPP_INFO(this->get_logger(), "Transcription enabled: %s", transcription_enabled_ ? "true" : "false");
281 RCLCPP_INFO(this->get_logger(), "Transcription service: %s", transcription_service_.c_str());
282 RCLCPP_INFO(this->get_logger(), "Speech synthesis enabled: %s", speech_enabled_ ? "true" : "false");
283 RCLCPP_INFO(this->get_logger(), "Speech synthesis service name: %s", speech_service_name_.c_str());
284
285 RCLCPP_INFO(this->get_logger(), "Sentiment analysis enabled: %s", sentiment_enabled_ ? "true" : "false");
286 RCLCPP_INFO(this->get_logger(), "Sentiment analysis service name: %s", sentiment_service_name_.c_str());
287
288 RCLCPP_INFO(this->get_logger(), "Image analysis enabled: %s", image_analysis_enabled_ ? "true" : "false");
289 RCLCPP_INFO(this->get_logger(), "Image analysis service name: %s", image_analysis_service_name_.c_str());
290 }
291
292 template <typename DriverType>
293 std::shared_ptr<DriverType> load_driver_if_enabled(const bool enabled, const DriverSpec& spec,
294 const DriverManager& driver_manager)
295 {
296 if (!enabled)
297 return nullptr;
298
299 return driver_manager.loadDriver<DriverType>(spec.parameter_name, spec.default_plugin_name, spec.driver_description,
301 }
302
322
324 {
325 transcription_pipeline_ = std::make_unique<TranscriptionPipeline>(
327
328 speech_pipeline_ = std::make_unique<SpeechPipeline>(speech_driver_, speaker_driver_);
329
330 sentiment_pipeline_ = std::make_unique<SentimentPipeline>(
332 [this](const audio_buffer_request& request) { return read_public_audio(request); });
333
335 std::make_unique<ImageAnalysisPipeline>(image_analysis_driver_, ros_vision_driver_, non_ros_vision_driver_);
336 }
337
339 {
340 RCLCPP_INFO(this->get_logger(), "Running tests for loaded plugins...");
341
342 DriverManager::testDriver(this->get_logger(), ros_vision_driver_, "ros vision driver");
343 DriverManager::testDriver(this->get_logger(), non_ros_vision_driver_, "non-ros vision driver");
344 DriverManager::testDriver(this->get_logger(), microphone_driver_, "microphone driver");
345 DriverManager::testDriver(this->get_logger(), speaker_driver_, "speaker driver");
346 DriverManager::testDriver(this->get_logger(), transcription_driver_, "transcription driver");
347 DriverManager::testDriver(this->get_logger(), speech_driver_, "speech synthesis driver");
348 DriverManager::testDriver(this->get_logger(), sentiment_driver_, "sentiment driver");
349 DriverManager::testDriver(this->get_logger(), image_analysis_driver_, "image analysis driver");
350 }
351
352 virtual void publishAudio()
353 {
354 while (rclcpp::ok())
355 {
356 try
357 {
359
361 data = microphone_driver_->readChunk();
362
363 const auto audio_stamp = this->now();
364
365 if (use_microphone_driver_ && microphone_driver_ && !data.samples.empty())
367
369 {
370 // If publishing is enabled, publish the audio data
371 Audio msg;
372 msg.header.stamp = audio_stamp;
373 msg.header.frame_id = audio_input_frame_id_;
374 msg.sample_rate = data.sample_rate;
375 msg.channels = data.channels;
376 msg.chunk_size = data.chunk_size;
377 msg.chunk_count = data.chunk_count;
378 msg.samples = data.samples;
379
380 audio_publisher_->publish(msg);
381 }
382 }
383 catch (const std::exception& e)
384 {
385 if (rclcpp::ok())
386 RCLCPP_WARN(this->get_logger(), "Audio publish loop skipped a cycle: %s", e.what());
387 }
388
390 std::this_thread::sleep_for(std::chrono::milliseconds(int(1000 / std::max(1, audio_input_frequency_))));
391 }
392 }
393
395 {
396 while (rclcpp::ok())
397 {
399 {
400 auto frame = ros_vision_driver_->captureFrame();
401 auto msg = cv_bridge::CvImage(std_msgs::msg::Header(), "bgr8", frame.image).toImageMsg();
402 msg->header.stamp = frame.stamp;
403 msg->header.frame_id = frame.frame_id;
404 image_publisher_->publish(*msg);
405 }
406
408 {
409 auto frame = non_ros_vision_driver_->captureFrame();
410
411 // If publishing is enabled, publish the image
412 auto msg = cv_bridge::CvImage(std_msgs::msg::Header(), "bgr8", frame.image).toImageMsg();
413 msg->header.stamp = frame.stamp;
414 msg->header.frame_id = frame.frame_id;
415 image_publisher_->publish(*msg);
416 }
417
418 std::this_thread::sleep_for(std::chrono::milliseconds(int(1000 / vision_input_frequency_)));
419 }
420 }
421
430 {
432 speaker_driver_->play(data);
433 }
434
435 transcription_request make_transcription_request(const Transcribe::Request& request, std_msgs::msg::Header& header)
436 {
439 request.audio_request_window > 0 ? request.audio_request_window : default_audio_request_window_;
440 internal_request.use_device_audio = request.use_device_audio;
441 internal_request.use_device_audio_time_window = false;
442
443 if (!request.use_device_audio)
444 {
446 header = request.audio.header;
447 }
448 else if (request.audio.header.stamp.sec != 0 || request.audio.header.stamp.nanosec != 0)
449 {
450 internal_request.use_device_audio_time_window = true;
451 internal_request.device_audio_start_time = rclcpp::Time(request.audio.header.stamp);
452 header = request.audio.header;
453 }
454
455 return internal_request;
456 }
457
458 void write_transcription_response(const transcription_result& result, Transcribe::Response& response)
459 {
460 if (result.success)
461 {
462 response.transcription = result.text;
463 response.success = true;
464 return;
465 }
466
467 response.success = false;
468 response.transcription = result.error.empty() ? "No transcription result received." : result.error;
469 }
470
472 {
475 request.audio_request_window > 0 ? request.audio_request_window : default_audio_request_window_;
476 internal_request.use_device_audio = request.use_device_audio;
477 internal_request.use_device_audio_time_window = false;
478
479 if (!request.use_device_audio)
480 internal_request.text = request.text;
481 else if (request.header.stamp.sec != 0 || request.header.stamp.nanosec != 0)
482 {
483 internal_request.use_device_audio_time_window = true;
484 internal_request.device_audio_start_time = rclcpp::Time(request.header.stamp);
485 }
486
487 return internal_request;
488 }
489
490 void write_sentiment_response(const sentiment_result& result, Sentiment::Response& response)
491 {
492 response.analyzed_text = result.analyzed_text;
493
494 if (result.success)
495 {
496 response.label = result.label;
497 response.score = result.score;
498 return;
499 }
500
501 response.label = result.error.empty() ? "Error: No sentiment analysis result available" : result.error;
502 response.score = 0.0;
503 }
504
506 {
508 internal_request.use_device_vision = request.use_device_vision;
509 internal_request.prompt = request.prompt.empty() ? std::string("What's in this image?") : request.prompt;
510
511 if (!request.use_device_vision)
512 {
513 const auto& image_msg = request.image;
514 auto cv_ptr = cv_bridge::toCvCopy(image_msg, image_msg.encoding);
515 internal_request.frame.image = cv_ptr->image;
516 internal_request.frame.frame_id = image_msg.header.frame_id;
517 internal_request.frame.stamp = image_msg.header.stamp;
518 }
519
520 return internal_request;
521 }
522
523 void write_image_analysis_response(const image_analysis_result& result, ImageAnalysis::Response& response)
524 {
525 response.response =
526 result.success ? result.response : (result.error.empty() ? "No image analysis result received." : result.error);
527 }
528
537 void transcribe_callback(const std::shared_ptr<Transcribe::Request> request,
538 std::shared_ptr<Transcribe::Response> response)
539 {
540 RCLCPP_INFO(this->get_logger(), "Received transcription request.");
541
543 {
544 response->success = false;
545 response->transcription = "Transcription driver is not loaded.";
546 response->header.stamp = this->now();
547 response->header.frame_id = audio_input_frame_id_;
548 RCLCPP_ERROR(this->get_logger(), "%s", response->transcription.c_str());
549 return;
550 }
551
552 response->header.stamp = this->now();
553 response->header.frame_id = audio_input_frame_id_;
554 const auto transcription_request_data = make_transcription_request(*request, response->header);
555
557 {
558 std::lock_guard<std::mutex> lock(transcription_driver_mutex_);
559 try
560 {
562 }
563 catch (const std::exception& e)
564 {
565 response->success = false;
566 const std::string error_message = e.what();
567 if (error_message.find("public audio buffer") != std::string::npos ||
568 error_message.find("audio buffer") != std::string::npos ||
569 error_message.find("Timeout waiting for audio data") != std::string::npos)
570 {
571 response->transcription = std::string("Device audio not available: ") + error_message;
572 }
573 else
574 {
575 response->transcription = std::string("Transcription service error: ") + error_message;
576 }
577 RCLCPP_ERROR(this->get_logger(), "%s", response->transcription.c_str());
578 return;
579 }
580 }
581
582 RCLCPP_INFO(this->get_logger(), "Sending transcription response: success=%s transcription=\"%s\"",
583 transcription_result_data.success ? "true" : "false", transcription_result_data.text.c_str());
584
586 }
587
596 void speech_callback(const std::shared_ptr<Speech::Request> request, std::shared_ptr<Speech::Response> response)
597 {
598 RCLCPP_INFO(this->get_logger(), "Received speech request with text: %s", request->input.text.c_str());
599
601 const auto result = speech_pipeline_->run(text_data, request->use_device_audio);
602
603 if (result.success)
604 {
605 if (result.used_device_audio)
606 {
607 RCLCPP_INFO(this->get_logger(), "Using device audio for speech output.");
608 response->success = true;
609 }
610 else
611 {
612 RCLCPP_INFO(this->get_logger(), "Using external audio for speech output.");
613 response->audio = fp_perception::audio_data_to_msg(result.audio);
614 response->success = true;
615 }
616 }
617 else
618 {
619 RCLCPP_ERROR(this->get_logger(), "No speech synthesis result received.");
620 response->success = false;
621 }
622 }
623
633 void sentiment_callback(const std::shared_ptr<Sentiment::Request> request,
634 std::shared_ptr<Sentiment::Response> response)
635 {
637 {
638 response->label = "Error: sentiment driver not loaded";
639 response->score = 0.0;
640 RCLCPP_ERROR(this->get_logger(), "%s", response->label.c_str());
641 return;
642 }
643
645
647
648 try
649 {
652 }
653 catch (const std::exception& e)
654 {
655 response->label = std::string("Error: ") + e.what();
656 response->score = 0.0;
657 RCLCPP_ERROR(this->get_logger(), "%s", response->label.c_str());
658 return;
659 }
660
662
663 RCLCPP_INFO(this->get_logger(), "Sending sentiment response: label='%s' score=%.3f analyzed_text='%s'",
664 response->label.c_str(), response->score, response->analyzed_text.c_str());
665 }
666
668 {
669 duration_seconds = std::max(1, duration_seconds);
670
672
673 const int sample_rate = std::max(1, out.sample_rate);
674 const int channels = std::max(1, out.channels);
675 const size_t requested_samples =
676 static_cast<size_t>(sample_rate) * static_cast<size_t>(channels) * static_cast<size_t>(duration_seconds);
677 const size_t samples_to_copy = out.samples.size();
678 const double returned_seconds =
679 static_cast<double>(samples_to_copy) / static_cast<double>(std::max(1, sample_rate * channels));
681 const auto returned_duration = rclcpp::Duration::from_seconds(returned_seconds);
683
684 RCLCPP_INFO(this->get_logger(),
685 "Using latest device audio window: requested=%d seconds start=%.9f end=%.9f returned=%.3f seconds "
686 "sample_rate=%d channels=%d frames=%d",
687 duration_seconds, window_start_time.seconds(), buffer_end_time.seconds(), returned_seconds, sample_rate,
688 channels, out.chunk_size);
689
691 {
692 RCLCPP_WARN(this->get_logger(),
693 "Requested %d seconds of device audio, but only %.2f seconds are buffered. Transcribing latest "
694 "available audio.",
695 duration_seconds, returned_seconds);
696 }
697
698 return out;
699 }
700
702 {
703 const int duration_seconds = std::max(1, request.duration_seconds);
704
705 if (request.use_time_window)
706 {
707 const auto requested_end_time = request.start_time + rclcpp::Duration::from_seconds(duration_seconds);
708
709 RCLCPP_INFO(this->get_logger(),
710 "Waiting for timestamped device audio window: requested=[%.9f, %.9f] timeout=%d seconds",
711 request.start_time.seconds(), requested_end_time.seconds(), std::max(5, duration_seconds + 5));
712
713 public_audio_buffer_.waitForWindow(request.start_time, duration_seconds,
714 std::chrono::seconds(std::max(5, duration_seconds + 5)));
715
716 auto out = public_audio_buffer_.readWindow(request.start_time, duration_seconds);
719 const double returned_seconds =
720 static_cast<double>(out.chunk_size) / static_cast<double>(std::max(1, out.sample_rate));
721
722 if (out.samples.empty() || out.chunk_size <= 0)
723 {
724 RCLCPP_WARN(this->get_logger(),
725 "Requested timestamped device audio window has no overlap with the buffered range. Falling back to "
726 "latest %d seconds of buffered audio. requested=[%.9f, %.9f] buffered=[%.9f, %.9f]",
727 duration_seconds, request.start_time.seconds(), requested_end_time.seconds(),
728 buffered_start_time.seconds(), buffered_end_time.seconds());
729 return read_latest_public_audio(duration_seconds);
730 }
731
732 if (returned_seconds + 1e-6 < static_cast<double>(duration_seconds))
733 {
734 RCLCPP_WARN(this->get_logger(),
735 "Requested timestamped device audio window is only partially available. Transcribing %.3f seconds "
736 "of overlapping audio. requested=[%.9f, %.9f] buffered=[%.9f, %.9f]",
737 returned_seconds, request.start_time.seconds(), requested_end_time.seconds(),
738 buffered_start_time.seconds(), buffered_end_time.seconds());
739 }
740
741 RCLCPP_INFO(this->get_logger(),
742 "Timestamped device audio window ready: requested=[%.9f, %.9f] buffered=[%.9f, %.9f] returned=%.3f "
743 "seconds frames=%d",
744 request.start_time.seconds(), requested_end_time.seconds(), buffered_start_time.seconds(),
745 buffered_end_time.seconds(), returned_seconds, out.chunk_size);
746
747 return out;
748 }
749
750 return read_latest_public_audio(duration_seconds);
751 }
752
753 void image_analysis_callback(const std::shared_ptr<ImageAnalysis::Request> request,
754 std::shared_ptr<ImageAnalysis::Response> response)
755 {
756 RCLCPP_INFO(this->get_logger(), "Received image analysis request.");
757
759 {
760 response->response = "Image analysis driver is not loaded.";
761 RCLCPP_ERROR(this->get_logger(), "%s", response->response.c_str());
762 return;
763 }
764
766 try
767 {
769 }
770 catch (const std::exception& e)
771 {
772 response->response = std::string("Failed to acquire image: ") + e.what();
773 RCLCPP_ERROR(this->get_logger(), "%s", response->response.c_str());
774 return;
775 }
776
777 try
778 {
781 }
782 catch (const std::exception& e)
783 {
784 response->response = std::string("Image analysis driver error: ") + e.what();
785 RCLCPP_ERROR(this->get_logger(), "%s", response->response.c_str());
786 return;
787 }
788 }
789
792
801
803
806
807 // Audio inputtopic parameters
814
815 // Audio output topic parameters
818
819 // Vision input topic parameters
824
825 // Transcription service parameters
828
829 // Speech synthesis service parameters
832
833 // Sentiment analysis service parameters
836
839
841 pluginlib::ClassLoader<fp_perception::DriverBase> driver_loader_;
842
844 std::shared_ptr<fp_perception::VisionSourceDriver> ros_vision_driver_;
845
847 std::shared_ptr<fp_perception::VisionSourceDriver> non_ros_vision_driver_;
848
850 std::shared_ptr<fp_perception::AudioSourceDriver> microphone_driver_;
851
853 std::shared_ptr<fp_perception::AudioSinkDriver> speaker_driver_;
854
856 std::shared_ptr<fp_perception::TranscriptionDriver> transcription_driver_;
857
859 std::shared_ptr<fp_perception::SentimentAnalysisDriver> sentiment_driver_;
860
862 std::shared_ptr<fp_perception::SpeechSynthesisDriver> speech_driver_;
863
865 std::shared_ptr<fp_perception::ImageAnalysisDriver> image_analysis_driver_;
866
867 std::unique_ptr<TranscriptionPipeline> transcription_pipeline_;
868 std::unique_ptr<SpeechPipeline> speech_pipeline_;
869 std::unique_ptr<SentimentPipeline> sentiment_pipeline_;
870 std::unique_ptr<ImageAnalysisPipeline> image_analysis_pipeline_;
871
872 // Publisher for audio data
873 rclcpp::Publisher<Audio>::SharedPtr audio_publisher_;
874
875 // Subscriber for audio data
876 rclcpp::Subscription<Audio>::SharedPtr audio_subscriber_;
877
878 // Service for transcription
879 rclcpp::Service<Transcribe>::SharedPtr transcription_;
880
881 // Service for image analysis
882 rclcpp::Service<ImageAnalysis>::SharedPtr image_analysis_service_;
883
884 // Service for speech synthesis
885 rclcpp::Service<Speech>::SharedPtr speech_service_;
886
887 // Service for sentiment analysis
888 rclcpp::Service<Sentiment>::SharedPtr sentiment_service_;
889
890 // Publisher for vision data
891 rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr image_publisher_;
892
893 rclcpp::TimerBase::SharedPtr deferred_initialize_timer_;
895 bool initialized_ = false;
897};
898
899} // namespace fp_perception
Definition audio_buffer.hpp:19
rclcpp::Time endTime() const
Definition audio_buffer.hpp:273
rclcpp::Time startTime() const
Definition audio_buffer.hpp:267
audio_data readWindow(const rclcpp::Time &start_time, int duration_seconds)
Definition audio_buffer.hpp:176
void waitForWindow(const rclcpp::Time &start_time, int duration_seconds, std::chrono::seconds timeout=std::chrono::seconds(30))
Definition audio_buffer.hpp:226
audio_data readLatest(int duration_seconds)
Definition audio_buffer.hpp:146
void append(const audio_data &data, int max_duration_seconds)
Definition audio_buffer.hpp:24
Definition driver_manager.hpp:16
static void testDriver(const rclcpp::Logger &logger, const std::shared_ptr< DriverType > &driver, const std::string &driver_description)
Definition driver_manager.hpp:49
std::shared_ptr< DriverType > loadDriver(const std::string &parameter_name, const std::string &default_plugin_name, const std::string &driver_description, const std::string &interface_description) const
Definition driver_manager.hpp:24
Definition perception_server.hpp:47
void publishVideo()
Definition perception_server.hpp:394
int audio_input_retention_window_
Definition perception_server.hpp:812
void initialize_pipelines()
Definition perception_server.hpp:323
void write_image_analysis_response(const image_analysis_result &result, ImageAnalysis::Response &response)
Definition perception_server.hpp:523
std::unique_ptr< SpeechPipeline > speech_pipeline_
Definition perception_server.hpp:868
rclcpp::Subscription< Audio >::SharedPtr audio_subscriber_
Definition perception_server.hpp:876
std::mutex transcription_driver_mutex_
Definition perception_server.hpp:804
void audioCallback(const Audio &msg)
Set the latest audio data to the driver from topic subscription. This function is called when new aud...
Definition perception_server.hpp:429
std::string image_analysis_service_name_
Definition perception_server.hpp:838
bool sentiment_enabled_
Definition perception_server.hpp:834
void start_background_tasks()
Definition perception_server.hpp:168
fp_perception_msgs::srv::PerceptionImageAnalysis ImageAnalysis
Definition perception_server.hpp:61
static constexpr DriverSpec kSentimentDriverSpec
Definition perception_server.hpp:75
void write_transcription_response(const transcription_result &result, Transcribe::Response &response)
Definition perception_server.hpp:458
std::shared_ptr< fp_perception::TranscriptionDriver > transcription_driver_
Definition perception_server.hpp:856
static constexpr DriverSpec kNonRosVisionDriverSpec
Definition perception_server.hpp:65
bool initialized_
Definition perception_server.hpp:895
int vision_input_frequency_
Definition perception_server.hpp:823
std::shared_ptr< fp_perception::SpeechSynthesisDriver > speech_driver_
Definition perception_server.hpp:862
std::shared_ptr< fp_perception::ImageAnalysisDriver > image_analysis_driver_
Definition perception_server.hpp:865
rclcpp::Publisher< sensor_msgs::msg::Image >::SharedPtr image_publisher_
Definition perception_server.hpp:891
std::unique_ptr< TranscriptionPipeline > transcription_pipeline_
Definition perception_server.hpp:867
std::mutex sentiment_driver_mutex_
Definition perception_server.hpp:805
bool audio_input_publish_
Definition perception_server.hpp:808
std::shared_ptr< fp_perception::VisionSourceDriver > non_ros_vision_driver_
Definition perception_server.hpp:847
bool audio_output_subscribe_
Definition perception_server.hpp:817
bool speech_enabled_
Definition perception_server.hpp:830
virtual void publishAudio()
Definition perception_server.hpp:352
bool use_ros_vision_driver_
Definition perception_server.hpp:793
bool use_speech_driver_
Definition perception_server.hpp:799
pluginlib::ClassLoader< fp_perception::DriverBase > driver_loader_
Definition perception_server.hpp:841
std::shared_ptr< DriverType > load_driver_if_enabled(const bool enabled, const DriverSpec &spec, const DriverManager &driver_manager)
Definition perception_server.hpp:293
std::string audio_input_frame_id_
Definition perception_server.hpp:810
fp_perception_msgs::msg::PerceptionAudio Audio
Definition perception_server.hpp:57
int default_audio_request_window_
Definition perception_server.hpp:813
bool image_analysis_enabled_
Definition perception_server.hpp:837
void transcribe_callback(const std::shared_ptr< Transcribe::Request > request, std::shared_ptr< Transcribe::Response > response)
Service callback for transcription requests.
Definition perception_server.hpp:537
sentiment_request make_sentiment_request(const Sentiment::Request &request)
Definition perception_server.hpp:471
bool use_transcription_driver_
Definition perception_server.hpp:797
bool use_microphone_driver_
Definition perception_server.hpp:795
void run_tests()
Definition perception_server.hpp:338
fp_perception::audio_data read_latest_public_audio(int duration_seconds)
Definition perception_server.hpp:667
~PerceptionServer()
Definition perception_server.hpp:92
static constexpr DriverSpec kImageAnalysisDriverSpec
Definition perception_server.hpp:77
static constexpr DriverSpec kMicrophoneDriverSpec
Definition perception_server.hpp:67
std::string audio_output_topic_
Definition perception_server.hpp:816
fp_perception::audio_data read_public_audio(const audio_buffer_request &request)
Definition perception_server.hpp:701
bool use_sentiment_driver_
Definition perception_server.hpp:798
PerceptionServer(const rclcpp::NodeOptions &options=rclcpp::NodeOptions())
Construct a new Perception Server object.
Definition perception_server.hpp:86
std::shared_ptr< fp_perception::AudioSourceDriver > microphone_driver_
Definition perception_server.hpp:850
rclcpp::TimerBase::SharedPtr deferred_initialize_timer_
Definition perception_server.hpp:893
fp_perception_msgs::srv::PerceptionSpeech Speech
Definition perception_server.hpp:58
std::string vision_input_frame_id_
Definition perception_server.hpp:822
bool use_image_analysis_driver_
Definition perception_server.hpp:800
rclcpp::Service< Sentiment >::SharedPtr sentiment_service_
Definition perception_server.hpp:888
bool run_tests_
Definition perception_server.hpp:791
void configure_interfaces()
Definition perception_server.hpp:209
std::shared_ptr< fp_perception::AudioSinkDriver > speaker_driver_
Definition perception_server.hpp:853
rclcpp::Service< Speech >::SharedPtr speech_service_
Definition perception_server.hpp:885
bool transcription_enabled_
Definition perception_server.hpp:826
std::string audio_input_topic_
Definition perception_server.hpp:809
std::string transcription_service_
Definition perception_server.hpp:827
void stop_background_tasks()
Definition perception_server.hpp:176
std::unique_ptr< SentimentPipeline > sentiment_pipeline_
Definition perception_server.hpp:869
std::thread audio_publish_thread_
Definition perception_server.hpp:896
void write_sentiment_response(const sentiment_result &result, Sentiment::Response &response)
Definition perception_server.hpp:490
void image_analysis_callback(const std::shared_ptr< ImageAnalysis::Request > request, std::shared_ptr< ImageAnalysis::Response > response)
Definition perception_server.hpp:753
void configure_driver_selection()
Definition perception_server.hpp:185
std::string sentiment_service_name_
Definition perception_server.hpp:835
std::shared_ptr< fp_perception::SentimentAnalysisDriver > sentiment_driver_
Definition perception_server.hpp:859
bool use_non_ros_vision_driver_
Definition perception_server.hpp:794
fp_perception_msgs::srv::PerceptionSentiment Sentiment
Definition perception_server.hpp:60
std::mutex initialization_mutex_
Definition perception_server.hpp:894
void load_drivers(const DriverManager &driver_manager)
Definition perception_server.hpp:303
static constexpr DriverSpec kSpeechDriverSpec
Definition perception_server.hpp:73
rclcpp::Service< Transcribe >::SharedPtr transcription_
Definition perception_server.hpp:879
AudioBuffer public_audio_buffer_
Definition perception_server.hpp:802
static constexpr DriverSpec kSpeakerDriverSpec
Definition perception_server.hpp:69
static constexpr DriverSpec kRosVisionDriverSpec
Definition perception_server.hpp:63
fp_perception_msgs::srv::PerceptionTranscribe Transcribe
Definition perception_server.hpp:59
void speech_callback(const std::shared_ptr< Speech::Request > request, std::shared_ptr< Speech::Response > response)
Service callback for the speech service.
Definition perception_server.hpp:596
void schedule_deferred_initialization()
Definition perception_server.hpp:151
std::shared_ptr< fp_perception::VisionSourceDriver > ros_vision_driver_
Definition perception_server.hpp:844
transcription_request make_transcription_request(const Transcribe::Request &request, std_msgs::msg::Header &header)
Definition perception_server.hpp:435
int audio_input_frequency_
Definition perception_server.hpp:811
rclcpp::Service< ImageAnalysis >::SharedPtr image_analysis_service_
Definition perception_server.hpp:882
bool vision_input_publish_
Definition perception_server.hpp:820
std::string vision_input_topic_
Definition perception_server.hpp:821
std::unique_ptr< ImageAnalysisPipeline > image_analysis_pipeline_
Definition perception_server.hpp:870
image_analysis_request make_image_analysis_request(const ImageAnalysis::Request &request)
Definition perception_server.hpp:505
static constexpr DriverSpec kTranscriptionDriverSpec
Definition perception_server.hpp:71
void sentiment_callback(const std::shared_ptr< Sentiment::Request > request, std::shared_ptr< Sentiment::Response > response)
Callback function for the sentiment analysis service.
Definition perception_server.hpp:633
std::string speech_service_name_
Definition perception_server.hpp:831
rclcpp::Publisher< Audio >::SharedPtr audio_publisher_
Definition perception_server.hpp:873
bool use_speaker_driver_
Definition perception_server.hpp:796
void initialize()
Definition perception_server.hpp:97
Definition audio_buffer.hpp:16
text_data msg_to_text_data(const fp_perception_msgs::msg::PerceptionText &msg)
Definition structs.hpp:94
fp_perception_msgs::msg::PerceptionAudio audio_data_to_msg(const audio_data &data)
Convert fp_perception::audio_data to fp_perception_msgs::msg::PerceptionAudio message.
Definition structs.hpp:45
audio_data msg_to_audio_data(const fp_perception_msgs::msg::PerceptionAudio &msg)
Convert fp_perception_msgs::msg::PerceptionAudio message to audio_data.
Definition structs.hpp:67
Definition perception_server.hpp:50
const char * parameter_name
Definition perception_server.hpp:51
const char * default_plugin_name
Definition perception_server.hpp:52
const char * driver_description
Definition perception_server.hpp:53
const char * interface_description
Definition perception_server.hpp:54
Definition structs.hpp:26
rclcpp::Time start_time
Definition structs.hpp:29
Struct to hold audio data.
Definition structs.hpp:16
std::vector< int16_t > samples
Audio samples.
Definition structs.hpp:17
Definition structs.hpp:11
bool use_device_vision
Definition structs.hpp:14
Definition structs.hpp:18
Definition structs.hpp:11
int audio_request_window
Definition structs.hpp:14
Definition structs.hpp:20
Definition structs.hpp:33
Definition structs.hpp:13
int audio_request_window
Definition structs.hpp:16
Definition structs.hpp:22
bool success
Definition structs.hpp:24