diff --git a/src/hello_wolf/processor_range_bearing.cpp b/src/hello_wolf/processor_range_bearing.cpp index 0974d8b0d99d4e6d883ab238427f42dfc2fad390..3991741d59036ae0fb476e0edbca531866ff5d78 100644 --- a/src/hello_wolf/processor_range_bearing.cpp +++ b/src/hello_wolf/processor_range_bearing.cpp @@ -22,6 +22,18 @@ ProcessorRangeBearing::ProcessorRangeBearing(const SensorRangeBearingPtr _sensor void ProcessorRangeBearing::process(CaptureBasePtr _capture) { + + if ( !kf_pack_buffer_.empty() ) + { + // Select using incoming_ptr + KFPackPtr pack = kf_pack_buffer_.selectPack( _capture->getTimeStamp(), time_tolerance_ ); + + if (pack!=nullptr) + keyFrameCallback(pack->key_frame,pack->time_tolerance); + + kf_pack_buffer_.removeUpTo( _capture->getTimeStamp() ); + } + CaptureRangeBearingPtr capture = std::static_pointer_cast<CaptureRangeBearing>(_capture); // 1. get KF -- we assume a KF is available to hold this _capture (checked in assert below) diff --git a/src/problem.cpp b/src/problem.cpp index e801e145080c655d08a7db25feb8c2649881b9d3..fa7ef55ef2c4590a07c429cd2829dbd072243535 100644 --- a/src/problem.cpp +++ b/src/problem.cpp @@ -334,7 +334,7 @@ void Problem::keyFrameCallback(FrameBasePtr _keyframe_ptr, ProcessorBasePtr _pro for (auto sensor : hardware_ptr_->getSensorList()) for (auto processor : sensor->getProcessorList()) if (processor && (processor != _processor_ptr) ) - processor->keyFrameCallback(_keyframe_ptr, _time_tolerance); + processor->keyFrameCallbackNew(_keyframe_ptr, _time_tolerance); } LandmarkBasePtr Problem::addLandmark(LandmarkBasePtr _lmk_ptr) diff --git a/src/processor_base.cpp b/src/processor_base.cpp index 430ed8ad9d4cda1b154c9e4ab331e1863a30d702..18701229dab299956def1c10bddfac5030ae0e3c 100644 --- a/src/processor_base.cpp +++ b/src/processor_base.cpp @@ -45,6 +45,13 @@ FrameBasePtr ProcessorBase::emplaceFrame(FrameType _type, CaptureBasePtr _captur return new_frame_ptr; } +void ProcessorBase::keyFrameCallbackNew(FrameBasePtr _keyframe_ptr, const Scalar& _time_tol_other) +{ + if (_keyframe_ptr != nullptr) + kf_pack_buffer_.add(_keyframe_ptr,_time_tol_other); + +} + void ProcessorBase::remove() { if (!is_removing_) @@ -67,9 +74,9 @@ void ProcessorBase::remove() } } -void KFPackBuffer::removeUpTo(const KFPackPtr& _pack) +void KFPackBuffer::removeUpTo(const TimeStamp& _time_stamp) { - KFPackBuffer::Iterator post = container_.upper_bound(_pack->key_frame->getTimeStamp()); + KFPackBuffer::Iterator post = container_.upper_bound(_time_stamp); container_.erase(container_.begin(), post); // erasing by range } diff --git a/src/processor_base.h b/src/processor_base.h index dc6eaee20c1e01d6b9af17b3a290f959cbc6d24c..50c78c5a6687f913dea780d0d2cae5123fda8117 100644 --- a/src/processor_base.h +++ b/src/processor_base.h @@ -43,7 +43,7 @@ class KFPackBuffer void add(const FrameBasePtr& _key_frame, const Scalar& _time_tolerance); - void removeUpTo(const KFPackPtr& _pack); + void removeUpTo(const TimeStamp& _time_stamp); bool checkTimeTolerance(const TimeStamp& _time_stamp1, const Scalar& _time_tolerance1, const TimeStamp& _time_stamp2, const Scalar& _time_tolerance2); @@ -121,6 +121,8 @@ class ProcessorBase : public NodeBase, public std::enable_shared_from_this<Proce virtual bool keyFrameCallback(FrameBasePtr _keyframe_ptr, const Scalar& _time_tolerance) = 0; + void keyFrameCallbackNew(FrameBasePtr _keyframe_ptr, const Scalar& _time_tol_other); + SensorBasePtr getSensorPtr(); const SensorBasePtr getSensorPtr() const; void setSensorPtr(SensorBasePtr _sen_ptr){sensor_ptr_ = _sen_ptr;} @@ -131,7 +133,10 @@ class ProcessorBase : public NodeBase, public std::enable_shared_from_this<Proce protected: unsigned int processor_id_; + Scalar time_tolerance_; ///< self time tolerance for adding a capture into a frame + + KFPackBuffer kf_pack_buffer_; }; } diff --git a/src/processor_motion.cpp b/src/processor_motion.cpp index a8e94f2432955dc03d0068f8f9f46a4079d7cb78..168f96b7120a50668d3307d24c5da06ed32345f1 100644 --- a/src/processor_motion.cpp +++ b/src/processor_motion.cpp @@ -42,103 +42,128 @@ void ProcessorMotion::process(CaptureBasePtr _incoming_ptr) { if (_incoming_ptr == nullptr) { - WOLF_ERROR("Process got a nullptr !"); + WOLF_ERROR("Received capture is nullptr."); return; } - if (status_ == IDLE) - { - TimeStamp t0 = _incoming_ptr->getTimeStamp(); + if ( !kf_pack_buffer_.empty() ) + { + KFPackPtr pack; + + // Select using last_ptr + if (last_ptr_ != nullptr) + { + pack = kf_pack_buffer_.selectPack( last_ptr_->getTimeStamp(), time_tolerance_ ); + if (pack!=nullptr) + { + keyFrameCallback(pack->key_frame,pack->time_tolerance); + kf_pack_buffer_.removeUpTo( last_ptr_->getTimeStamp() ); + } + } + + // Select using incoming_ptr + pack = kf_pack_buffer_.selectPack( incoming_ptr_->getTimeStamp(), time_tolerance_ ); + if (pack!=nullptr) + { + keyFrameCallback(pack->key_frame,pack->time_tolerance); + kf_pack_buffer_.removeUpTo( incoming_ptr_->getTimeStamp() ); + } + } - if (origin_ptr_ == nullptr) - { - auto frm = getProblem()->getTrajectoryPtr()->closestKeyFrameToTimeStamp(t0); - if (frm && fabs(frm->getTimeStamp() - t0) < time_tolerance_) - { - std::cout << "PM: join KF" << std::endl; - // Join existing KF - setOrigin(frm); - } - else - { - // Create new KF for origin - std::cout << "PM: make KF" << std::endl; - VectorXs x0 = getProblem()->zeroState(); - setOrigin(x0, t0); - } - } - status_ = RUNNING; - } - incoming_ptr_ = std::static_pointer_cast<CaptureMotion>(_incoming_ptr); + if (status_ == IDLE) + { + TimeStamp t0 = _incoming_ptr->getTimeStamp(); + + if (origin_ptr_ == nullptr) + { + auto frm = getProblem()->getTrajectoryPtr()->closestKeyFrameToTimeStamp(t0); + if (frm && fabs(frm->getTimeStamp() - t0) < time_tolerance_) + { + std::cout << "PM: join KF" << std::endl; + // Join existing KF + setOrigin(frm); + } + else + { + // Create new KF for origin + std::cout << "PM: make KF" << std::endl; + VectorXs x0 = getProblem()->zeroState(); + setOrigin(x0, t0); + } + } + status_ = RUNNING; + } - /// @todo Anything else to do ? - if (incoming_ptr_ == nullptr) return; + incoming_ptr_ = std::static_pointer_cast<CaptureMotion>(_incoming_ptr); - preProcess(); + /// @todo Anything else to do ? + if (incoming_ptr_ == nullptr) return; - // integrate data - integrateOneStep(); + preProcess(); - // Update state and time stamps - last_ptr_->setTimeStamp(incoming_ptr_->getTimeStamp()); - last_ptr_->getFramePtr()->setTimeStamp(last_ptr_->getTimeStamp()); - last_ptr_->getFramePtr()->setState(getCurrentState()); + // integrate data + integrateOneStep(); - if (voteForKeyFrame() && permittedKeyFrame()) - { - // Set the frame of last_ptr as key - auto key_frame_ptr = last_ptr_->getFramePtr(); - key_frame_ptr->setState(getCurrentState()); - key_frame_ptr->setTimeStamp(getCurrentTimeStamp()); - key_frame_ptr->setKey(); - - // create motion feature and add it to the key_capture - auto key_feature_ptr = emplaceFeature(last_ptr_); - - // create motion constraint and link it to parent feature and other frame (which is origin's frame) - auto ctr_ptr = emplaceConstraint(key_feature_ptr, origin_ptr_); - - // create a new frame - auto new_frame_ptr = getProblem()->emplaceFrame(NON_KEY_FRAME, - getCurrentState(), - getCurrentTimeStamp()); - // create a new capture - auto new_capture_ptr = emplaceCapture(new_frame_ptr, - getSensorPtr(), - key_frame_ptr->getTimeStamp(), - Eigen::VectorXs::Zero(data_size_), - Eigen::MatrixXs::Zero(data_size_, data_size_), - last_ptr_->getCalibration(), - last_ptr_->getCalibration(), - key_frame_ptr); - // reset the new buffer - new_capture_ptr->getBuffer().get().clear(); - new_capture_ptr->getBuffer().get().push_back( motionZero(key_frame_ptr->getTimeStamp()) ) ; - - // reset integrals - delta_ = deltaZero(); - delta_cov_ . setZero(); - delta_integrated_ = deltaZero(); - delta_integrated_cov_ . setZero(); - jacobian_calib_ . setZero(); - - // reset processor origin to the new keyframe's capture - origin_ptr_ = last_ptr_; - last_ptr_ = new_capture_ptr; - - // reset derived things - resetDerived(); - - // callback to other processors - getProblem()->keyFrameCallback(key_frame_ptr, shared_from_this(), time_tolerance_); - } + // Update state and time stamps + last_ptr_->setTimeStamp(incoming_ptr_->getTimeStamp()); + last_ptr_->getFramePtr()->setTimeStamp(last_ptr_->getTimeStamp()); + last_ptr_->getFramePtr()->setState(getCurrentState()); + + if (voteForKeyFrame() && permittedKeyFrame()) + { + // Set the frame of last_ptr as key + auto key_frame_ptr = last_ptr_->getFramePtr(); + key_frame_ptr->setState(getCurrentState()); + key_frame_ptr->setTimeStamp(getCurrentTimeStamp()); + key_frame_ptr->setKey(); + + // create motion feature and add it to the key_capture + auto key_feature_ptr = emplaceFeature(last_ptr_); + + // create motion constraint and link it to parent feature and other frame (which is origin's frame) + auto ctr_ptr = emplaceConstraint(key_feature_ptr, origin_ptr_); + + // create a new frame + auto new_frame_ptr = getProblem()->emplaceFrame(NON_KEY_FRAME, + getCurrentState(), + getCurrentTimeStamp()); + // create a new capture + auto new_capture_ptr = emplaceCapture(new_frame_ptr, + getSensorPtr(), + key_frame_ptr->getTimeStamp(), + Eigen::VectorXs::Zero(data_size_), + Eigen::MatrixXs::Zero(data_size_, data_size_), + last_ptr_->getCalibration(), + last_ptr_->getCalibration(), + key_frame_ptr); + // reset the new buffer + new_capture_ptr->getBuffer().get().clear(); + new_capture_ptr->getBuffer().get().push_back( motionZero(key_frame_ptr->getTimeStamp()) ) ; + + // reset integrals + delta_ = deltaZero(); + delta_cov_ . setZero(); + delta_integrated_ = deltaZero(); + delta_integrated_cov_ . setZero(); + jacobian_calib_ . setZero(); + + // reset processor origin to the new keyframe's capture + origin_ptr_ = last_ptr_; + last_ptr_ = new_capture_ptr; + + // reset derived things + resetDerived(); + + // callback to other processors + getProblem()->keyFrameCallback(key_frame_ptr, shared_from_this(), time_tolerance_); + } - postProcess(); + postProcess(); - // clear incoming just in case - incoming_ptr_ = nullptr; // This line is not really needed, but it makes things clearer. + // clear incoming just in case + incoming_ptr_ = nullptr; // This line is not really needed, but it makes things clearer. } void ProcessorMotion::getState(const TimeStamp& _ts, Eigen::VectorXs& _x) diff --git a/src/processor_tracker.cpp b/src/processor_tracker.cpp index cc2b764e11126f549d8910ae60f838eacc0fb5fe..2a8de624180b28be6568ab3bc364d5bd7f533155 100644 --- a/src/processor_tracker.cpp +++ b/src/processor_tracker.cpp @@ -28,6 +28,60 @@ ProcessorTracker::~ProcessorTracker() void ProcessorTracker::process(CaptureBasePtr const _incoming_ptr) { + if (_incoming_ptr == nullptr) + { + WOLF_ERROR("Received capture is nullptr."); + return; + } + + WOLF_TRACE(""); + + if ( !kf_pack_buffer_.empty() ) + { + KFPackPtr pack; + + WOLF_TRACE(""); + + // Select using last_ptr + if (last_ptr_ != nullptr) + { + + WOLF_TRACE(""); + + pack = kf_pack_buffer_.selectPack( last_ptr_->getTimeStamp(), time_tolerance_ ); + if (pack!=nullptr) + { + + WOLF_TRACE(""); + + keyFrameCallback(pack->key_frame,pack->time_tolerance); + + WOLF_TRACE(""); + + kf_pack_buffer_.removeUpTo( last_ptr_->getTimeStamp() ); + } + } + + WOLF_TRACE(""); + + // Select using incoming_ptr + pack = kf_pack_buffer_.selectPack( incoming_ptr_->getTimeStamp(), time_tolerance_ ); + + WOLF_TRACE(""); + + if (pack!=nullptr) + { + WOLF_TRACE(""); + + keyFrameCallback(pack->key_frame,pack->time_tolerance); + + WOLF_TRACE(""); + + kf_pack_buffer_.removeUpTo( incoming_ptr_->getTimeStamp() ); + } + } + + WOLF_TRACE(""); using std::abs; @@ -35,6 +89,8 @@ void ProcessorTracker::process(CaptureBasePtr const _incoming_ptr) preProcess(); + WOLF_TRACE(""); + // FIRST TIME if (origin_ptr_ == nullptr && last_ptr_ == nullptr) { diff --git a/src/test/gtest_processor_base.cpp b/src/test/gtest_processor_base.cpp index 3750689edb419bec46bbcd7a05f6ab6829dd030d..7f6633eafc224d2ad78e5748ab2d104d6cac7c5d 100644 --- a/src/test/gtest_processor_base.cpp +++ b/src/test/gtest_processor_base.cpp @@ -5,13 +5,20 @@ * Author: asantamaria */ - +//Wolf #include "utils_gtest.h" -#include "processor_base.h" +#include "processor_odom_2D.h" +#include "sensor_odom_2D.h" + +#include "processor_tracker_feature_dummy.h" +#include "capture_void.h" + +#include "problem.h" // STL #include <iterator> +#include <iostream> using namespace wolf; using namespace Eigen; @@ -148,7 +155,7 @@ TEST_F(KFPackBufferTest, removeUpTo) // it should remove f20 and f10, thus size should be 1 after removal // Specifically, only f21 should remain KFPackPtr pack20 = std::make_shared<KFPack>(f20,tt20); - kfpackbuffer.removeUpTo( pack20 ); + kfpackbuffer.removeUpTo( pack20->key_frame->getTimeStamp() ); ASSERT_EQ(kfpackbuffer.size(),1); ASSERT_TRUE(kfpackbuffer.selectPack(f10->getTimeStamp(),tt10)==nullptr); ASSERT_TRUE(kfpackbuffer.selectPack(f20->getTimeStamp(),tt20)==nullptr); @@ -160,12 +167,85 @@ TEST_F(KFPackBufferTest, removeUpTo) ASSERT_EQ(kfpackbuffer.size(),2); FrameBasePtr f22 = std::make_shared<FrameBase>(TimeStamp(22),nullptr,nullptr,nullptr); KFPackPtr pack22 = std::make_shared<KFPack>(f22,5); - kfpackbuffer.removeUpTo( pack22 ); + kfpackbuffer.removeUpTo( pack22->key_frame->getTimeStamp() ); ASSERT_EQ(kfpackbuffer.size(),1); ASSERT_TRUE(kfpackbuffer.selectPack(f21->getTimeStamp(),tt21)==nullptr); ASSERT_TRUE(kfpackbuffer.selectPack(f28->getTimeStamp(),tt28)!=nullptr); } + +TEST(ProcessorBase, KeyFrameCallback) +{ + + using namespace wolf; + using std::shared_ptr; + using std::make_shared; + using std::static_pointer_cast; + using Eigen::Vector2s; + + // Wolf problem + ProblemPtr problem = Problem::create("PO 2D"); + + // Install tracker (sensor and processor) + SensorBasePtr sen_tracker = make_shared<SensorBase>("FEATURE", std::make_shared<StateBlock>(Eigen::VectorXs::Zero(2)), + std::make_shared<StateBlock>(Eigen::VectorXs::Zero(1)), + std::make_shared<StateBlock>(Eigen::VectorXs::Zero(2)), 2); + shared_ptr<ProcessorTrackerFeatureDummy> proc_tracker = make_shared<ProcessorTrackerFeatureDummy>(7, 4); + + problem->addSensor(sen_tracker); + sen_tracker->addProcessor(proc_tracker); + + // Install odometer (sensor and processor) + SensorBasePtr sen_odo = problem->installSensor("ODOM 2D", "odometer", Vector3s(0,0,0), ""); + ProcessorParamsOdom2DPtr proc_odo_params = make_shared<ProcessorParamsOdom2D>(); + ProcessorBasePtr prc_odo = problem->installProcessor("ODOM 2D", "odometer", sen_odo, proc_odo_params); + prc_odo->setTimeTolerance(0.01); + + std::cout << "sensor & processor created and added to wolf problem" << std::endl; + + // Sequence to test KeyFrame creations (callback calls) + + // initialize + TimeStamp t(0.0); + Vector3s x(0,0,0); + Matrix3s P = Matrix3s::Identity() * 0.1; + problem->setPrior(x, P, t); // KF1 + + CaptureOdom2DPtr capture_odo = make_shared<CaptureOdom2D>(t, sen_odo, Vector2s(0.5,0)); + + for (size_t ii=0; ii<10; ii++ ) + { + WOLF_DEBUG("iter:",ii," ts: ", t); + + // Move + t = t+0.01; + capture_odo->setTimeStamp(t); + sen_odo->process(capture_odo); + + WOLF_DEBUG("iter:",ii," ts: ", t); + + t = t+0.01; + capture_odo->setTimeStamp(t); + sen_odo->process(capture_odo); + + WOLF_DEBUG("iter:",ii," ts: ", t); + + t = t+0.01; + capture_odo->setTimeStamp(t); + sen_odo->process(capture_odo); + + WOLF_DEBUG("iter:",ii," ts: ", t); + + // Track + proc_tracker->process(make_shared<CaptureVoid>(t, sen_tracker)); + + WOLF_DEBUG("iter:",ii," ts: ", t); + } + + // Print WOLF info + problem->print(2); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);