diff --git a/src/processor/processor_loop_closure.cpp b/src/processor/processor_loop_closure.cpp index bf70934d9e820fd910ee7de744b6896b261ae1f4..2c66b5019e3588854cf43cdd0c1006a698f2ce8c 100644 --- a/src/processor/processor_loop_closure.cpp +++ b/src/processor/processor_loop_closure.cpp @@ -20,13 +20,19 @@ void ProcessorLoopClosure::processCapture(CaptureBasePtr _capture) * 3. Otherwise -> store capture (Note that more than one processor can be emplacing frames, so an older frame can arrive later than this one) */ + WOLF_INFO("ProcessorLoopClosure::processCapture capture ", _capture->id()); + // CASE 1: if (_capture->getFrame()) { + WOLF_INFO("CASE 1"); + process(_capture->getFrame(), _capture); // remove the frame and older frames buffer_pack_kf_.removeUpTo(_capture->getFrame()->getTimeStamp()); + + return; } // Search for any stored frame within time tolerance of capture @@ -35,16 +41,20 @@ void ProcessorLoopClosure::processCapture(CaptureBasePtr _capture) // CASE 2: if (frame_pack) { + WOLF_INFO("CASE 2"); + _capture->link(frame_pack->key_frame); process(frame_pack->key_frame, _capture); // remove the frame and older frames buffer_pack_kf_.removeUpTo(frame_pack->key_frame->getTimeStamp()); + + return; } // CASE 3: - else - buffer_capture_.add(_capture->getTimeStamp(), _capture); + WOLF_INFO("CASE 3"); + buffer_capture_.add(_capture->getTimeStamp(), _capture); } void ProcessorLoopClosure::processKeyFrame(FrameBasePtr _frame, const double& _time_tolerance) @@ -56,14 +66,20 @@ void ProcessorLoopClosure::processKeyFrame(FrameBasePtr _frame, const double& _t * 4. Otherwise: The frame is not compatible with any stored capture -> discard frame */ + WOLF_INFO("ProcessorLoopClosure::processKeyFrame frame ", _frame->id()); + // CASE 1: auto cap = _frame->getCaptureOf(getSensor()); if (cap) { + WOLF_INFO("CASE 1"); + process(_frame, cap); // remove the capture (if stored) buffer_capture_.getContainer().erase(cap->getTimeStamp()); + + return; } // Search for any stored capture within time tolerance of frame @@ -72,6 +88,10 @@ void ProcessorLoopClosure::processKeyFrame(FrameBasePtr _frame, const double& _t // CASE 2: if (capture) { + WOLF_INFO("CASE 2"); + + capture->link(_frame); + process(_frame, capture); // remove the capture (if stored) @@ -79,30 +99,43 @@ void ProcessorLoopClosure::processKeyFrame(FrameBasePtr _frame, const double& _t // remove old captures (10s of old captures are kept in case frames arrives unordered) buffer_capture_.removeUpTo(_frame->getTimeStamp() - 10); + + return; } // CASE 3: - else if (buffer_capture_.selectLastAfter(_frame->getTimeStamp(), params_->time_tolerance) == nullptr) + if (buffer_capture_.selectLastAfter(_frame->getTimeStamp(), params_->time_tolerance) == nullptr) { + WOLF_INFO("CASE 3"); + // store frame buffer_pack_kf_.add(_frame, _time_tolerance); + + return; } // CASE 4: + WOLF_INFO("CASE 4"); // nothing (discard frame) } void ProcessorLoopClosure::process(FrameBasePtr _frame, CaptureBasePtr _capture) { + WOLF_INFO("ProcessorLoopClosure::process frame ", _frame->id(), " capture ", _capture->id()); assert(_capture->getFrame() == _frame && "ProcessorLoopClosure::process _capture not linked to _frame"); // Detect and emplace features + WOLF_INFO("emplacing features..."); emplaceFeatures(_capture); // Vote for loop closure search if (voteFindLoopClosures(_capture)) { + WOLF_INFO("finding loop closures..."); + // Find loop closures auto cap_lc_list = findLoopClosures(_capture); + WOLF_INFO(cap_lc_list.size(), " loop closures found"); + // Emplace factors for each LC if validated for (auto cap_lc : cap_lc_list) if (validateLoopClosure(cap_lc, _capture)) diff --git a/test/gtest_processor_loop_closure.cpp b/test/gtest_processor_loop_closure.cpp index 2cd9fe39c0142018dcf88fe558e492b38518671c..aec8092de22eaee6a4577b43d8b7a10f8528582b 100644 --- a/test/gtest_processor_loop_closure.cpp +++ b/test/gtest_processor_loop_closure.cpp @@ -13,60 +13,311 @@ using namespace wolf; using namespace Eigen; -// Wolf problem -ProblemPtr problem = Problem::create("PO", 2); +class ProcessorLoopClosureTest : public testing::Test +{ + protected: + // Wolf problem + ProblemPtr problem = Problem::create("PO", 2); + SensorBasePtr sensor; + ProcessorLoopClosureDummyPtr processor; + + virtual void SetUp() + { + // Emplace sensor + sensor = SensorBase::emplace<SensorBase>(problem->getHardware(), + "SensorBase", + std::make_shared<StateBlock>(Vector2d::Zero()), + std::make_shared<StateBlock>(Vector1d::Zero()), + nullptr, + 2); + + // Emplace processor + ParamsProcessorLoopClosurePtr params = std::make_shared<ParamsProcessorLoopClosure>(); + params->time_tolerance = 0.5; + processor = ProcessorBase::emplace<ProcessorLoopClosureDummy>(sensor, + params); + } -ProcessorLoopClosureDummyPtr proc_lc; + FrameBasePtr emplaceFrame(TimeStamp ts, const Vector3d& x) + { + // new frame + return problem->emplaceFrame(ts, x); + } -void setup() + CaptureBasePtr emplaceCapture(FrameBasePtr frame) + { + // new capture + return CaptureBase::emplace<CaptureBase>(frame, + "CaptureBase", + frame->getTimeStamp(), + sensor); + } + + CaptureBasePtr createCapture(TimeStamp ts) + { + // new capture + return std::make_shared<CaptureBase>("CaptureBase", + ts, + sensor); + } +}; + +TEST_F(ProcessorLoopClosureTest, installProcessor) { - // Emplace sensor - auto sens_lc = SensorBase::emplace<SensorBase>(problem->getHardware(), - "SensorBase", - std::make_shared<StateBlock>(Vector2d::Zero()), - std::make_shared<StateBlock>(Vector1d::Zero()), - nullptr, - 2); - - // Emplace processor - ParamsProcessorLoopClosurePtr params = std::make_shared<ParamsProcessorLoopClosure>(); - proc_lc = ProcessorBase::emplace<ProcessorLoopClosureDummy>(sens_lc, params); + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 0); +} + +TEST_F(ProcessorLoopClosureTest, frame_stored) +{ + // new frame + auto frm1 = emplaceFrame(1, Vector3d::Zero()); + + // keyframecallback + problem->keyFrameCallback(frm1, nullptr, 0.5); + + EXPECT_EQ(processor->getNStoredFrames(), 1); + EXPECT_EQ(processor->getNStoredCaptures(), 0); } -TEST(ProcessorLoopClosure, installProcessor) +TEST_F(ProcessorLoopClosureTest, capture_stored) { - setup(); + // new capture + auto cap1 = createCapture(1); + + // captureCallback + processor->captureCallback(cap1); - EXPECT_EQ(proc_lc->getNStoredFrames(), 0); - EXPECT_EQ(proc_lc->getNStoredCaptures(), 0); + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 1); } -TEST(ProcessorLoopClosure, frame_stored) +TEST_F(ProcessorLoopClosureTest, captureCallbackCase1) { - setup(); + // emplace frame and capture + auto frm1 = emplaceFrame(1, Vector3d::Zero()); + auto cap1 = emplaceCapture(frm1); + + // captureCallback + processor->captureCallback(cap1); + + EXPECT_EQ(cap1->getFeatureList().size(), 1); // capture processed by the processor + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 0); +} +TEST_F(ProcessorLoopClosureTest, captureCallbackCase2) +{ // new frame - auto fr1 = problem->emplaceFrame(0, Vector3d::Zero()); + auto frm1 = emplaceFrame(1, Vector3d::Zero()); + + // new capture + auto cap1 = createCapture(1); // keyframecallback - problem->keyFrameCallback(fr1, nullptr, 1); + problem->keyFrameCallback(frm1, nullptr, 0.5); - EXPECT_EQ(proc_lc->getNStoredFrames(), 1); - EXPECT_EQ(proc_lc->getNStoredCaptures(), 0); + // captureCallback + processor->captureCallback(cap1); + + EXPECT_EQ(cap1->getFrame(), frm1); // capture processed by the processor + EXPECT_EQ(cap1->getFeatureList().size(), 1); // capture processed by the processor + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 0); } -TEST(ProcessorLoopClosure, capture_stored) +TEST_F(ProcessorLoopClosureTest, captureCallbackCase3) { - setup(); + // new frame + auto frm1 = emplaceFrame(1, Vector3d::Zero()); + // new capture + auto cap1 = createCapture(2); + + // keyframecallback + problem->keyFrameCallback(frm1, nullptr, 0.5); + + // captureCallback + processor->captureCallback(cap1); + + EXPECT_TRUE(cap1->getFrame() == nullptr); + EXPECT_EQ(cap1->getFeatureList().size(), 0); + EXPECT_EQ(processor->getNStoredFrames(), 1); + EXPECT_EQ(processor->getNStoredCaptures(), 1); +} +TEST_F(ProcessorLoopClosureTest, keyFrameCallbackCase1) +{ + // emplace frame and capture + auto frm1 = emplaceFrame(1, Vector3d::Zero()); + auto cap1 = emplaceCapture(frm1); + + // keyframecallback + problem->keyFrameCallback(frm1, nullptr, 0.5); + + EXPECT_EQ(cap1->getFeatureList().size(), 1); // capture processed by the processor + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 0); +} + +TEST_F(ProcessorLoopClosureTest, keyFrameCallbackCase2) +{ + // new frame + auto frm1 = emplaceFrame(1, Vector3d::Zero()); // new capture - auto cap1 = std::make_shared<CaptureBase>("CaptureBase", 0, nullptr); + auto cap1 = createCapture(1); + + // captureCallback + processor->captureCallback(cap1); // keyframecallback - proc_lc->captureCallback(cap1); + problem->keyFrameCallback(frm1, nullptr, 0.5); + + EXPECT_EQ(cap1->getFrame(), frm1); // capture processed by the processor + EXPECT_EQ(cap1->getFeatureList().size(), 1); // capture processed by the processor + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 0); +} + +TEST_F(ProcessorLoopClosureTest, keyFrameCallbackCase3) +{ + // new frame + auto frm1 = emplaceFrame(2, Vector3d::Zero()); + // new capture + auto cap1 = createCapture(1); + + // captureCallback + processor->captureCallback(cap1); + + // keyframecallback + problem->keyFrameCallback(frm1, nullptr, 0.5); + + EXPECT_TRUE(cap1->getFrame() == nullptr); + EXPECT_EQ(cap1->getFeatureList().size(), 0); + EXPECT_EQ(processor->getNStoredFrames(), 1); + EXPECT_EQ(processor->getNStoredCaptures(), 1); +} + +TEST_F(ProcessorLoopClosureTest, keyFrameCallbackCase4) +{ + // new frame + auto frm1 = emplaceFrame(1, Vector3d::Zero()); + // new capture + auto cap1 = createCapture(2); + + // captureCallback + processor->captureCallback(cap1); + + // keyframecallback + problem->keyFrameCallback(frm1, nullptr, 0.5); + + EXPECT_TRUE(cap1->getFrame() == nullptr); + EXPECT_EQ(cap1->getFeatureList().size(), 0); + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 1); +} + +TEST_F(ProcessorLoopClosureTest, captureCallbackMatch) +{ + // new frame + auto frm1 = emplaceFrame(1, Vector3d::Zero()); + auto frm2 = emplaceFrame(2, Vector3d::Zero()); + auto frm3 = emplaceFrame(3, Vector3d::Zero()); + auto frm4 = emplaceFrame(4, Vector3d::Zero()); + auto frm5 = emplaceFrame(5, Vector3d::Zero()); + // new captures + auto cap4 = createCapture(4); + + // keyframecallback + problem->keyFrameCallback(frm1, nullptr, 0.5); + problem->keyFrameCallback(frm2, nullptr, 0.5); + problem->keyFrameCallback(frm3, nullptr, 0.5); + problem->keyFrameCallback(frm4, nullptr, 0.5); + problem->keyFrameCallback(frm5, nullptr, 0.5); + + // captureCallback + processor->captureCallback(cap4); + + EXPECT_EQ(frm1->getCaptureList().size(), 0); + EXPECT_EQ(frm2->getCaptureList().size(), 0); + EXPECT_EQ(frm3->getCaptureList().size(), 0); + EXPECT_EQ(frm4->getCaptureList().size(), 1); + EXPECT_EQ(frm5->getCaptureList().size(), 0); + + EXPECT_TRUE(cap4->getFrame() == frm4); + EXPECT_EQ(cap4->getFeatureList().size(), 1); + + EXPECT_EQ(processor->getNStoredFrames(), 1); // all oldest frames are removed from buffer + EXPECT_EQ(processor->getNStoredCaptures(), 0); +} + +TEST_F(ProcessorLoopClosureTest, keyFrameCallbackMatch) +{ + // new frame + auto frm2 = emplaceFrame(2, Vector3d::Zero()); + // new captures + auto cap1 = createCapture(1); + auto cap2 = createCapture(2); + auto cap3 = createCapture(3); + auto cap4 = createCapture(4); + auto cap5 = createCapture(5); + + // captureCallback + processor->captureCallback(cap1); + processor->captureCallback(cap2); + processor->captureCallback(cap3); + processor->captureCallback(cap4); + processor->captureCallback(cap5); + + // keyframecallback + problem->keyFrameCallback(frm2, nullptr, 0.5); + + EXPECT_TRUE(cap1->getFrame() == nullptr); + EXPECT_TRUE(cap2->getFrame() == frm2); + EXPECT_TRUE(cap3->getFrame() == nullptr); + EXPECT_TRUE(cap4->getFrame() == nullptr); + EXPECT_TRUE(cap5->getFrame() == nullptr); + + EXPECT_EQ(cap1->getFeatureList().size(), 0); + EXPECT_EQ(cap2->getFeatureList().size(), 1); + EXPECT_EQ(cap3->getFeatureList().size(), 0); + EXPECT_EQ(cap4->getFeatureList().size(), 0); + EXPECT_EQ(cap5->getFeatureList().size(), 0); + + EXPECT_EQ(processor->getNStoredFrames(), 0); + EXPECT_EQ(processor->getNStoredCaptures(), 4); +} + +TEST_F(ProcessorLoopClosureTest, emplaceFactors) +{ + // emplace frame and capture + auto cap1 = emplaceCapture(emplaceFrame(1, Vector3d::Zero())); + processor->captureCallback(cap1); + + auto cap2 = emplaceCapture(emplaceFrame(2, Vector3d::Ones())); + processor->captureCallback(cap2); + + auto cap3 = emplaceCapture(emplaceFrame(3, 2*Vector3d::Ones())); + processor->captureCallback(cap3); + + auto cap4 = emplaceCapture(emplaceFrame(4, Vector3d::Zero())); + processor->captureCallback(cap4); + + EXPECT_EQ(cap1->getFrame()->getConstrainedByList().size(), 1); + EXPECT_EQ(cap2->getFrame()->getConstrainedByList().size(), 0); + EXPECT_EQ(cap3->getFrame()->getConstrainedByList().size(), 0); + EXPECT_EQ(cap4->getFrame()->getConstrainedByList().size(), 0); + + EXPECT_EQ(cap1->getFeatureList().size(), 1); + EXPECT_EQ(cap2->getFeatureList().size(), 1); + EXPECT_EQ(cap3->getFeatureList().size(), 1); + EXPECT_EQ(cap4->getFeatureList().size(), 1); + + EXPECT_EQ(cap1->getFeatureList().front()->getFactorList().size(), 0); + EXPECT_EQ(cap2->getFeatureList().front()->getFactorList().size(), 0); + EXPECT_EQ(cap3->getFeatureList().front()->getFactorList().size(), 0); + EXPECT_EQ(cap4->getFeatureList().front()->getFactorList().size(), 1); - EXPECT_EQ(proc_lc->getNStoredFrames(), 0); - EXPECT_EQ(proc_lc->getNStoredCaptures(), 1); + EXPECT_EQ(cap1->getFrame()->getConstrainedByList().front(), cap4->getFeatureList().front()->getFactorList().front()); } int main(int argc, char **argv)