-
cont-integration authoredcont-integration authored
problem.cpp 49.16 KiB
// WOLF - Copyright (C) 2020,2021,2022,2023,2024
// Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
// Authors: Joan Solà Ortega (jsola@iri.upc.edu) and
// Joan Vallvé Navarro (jvallve@iri.upc.edu)
// All rights reserved.
//
// This file is part of WOLF: http://www.iri.upc.edu/wolf
// WOLF is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// wolf includes
#include "core/problem/problem.h"
#include "core/internal/config.h"
#include "core/map/factory_map.h"
#include "core/sensor/factory_sensor.h"
#include "core/processor/factory_processor.h"
#include "core/capture/capture_void.h"
#include "core/factor/factor_block_absolute.h"
#include "core/factor/factor_quaternion_absolute.h"
#include "core/state_block/factory_state_block.h"
#include "core/state_block/state_quaternion.h"
#include "core/state_block/state_angle.h"
#include "core/tree_manager/factory_tree_manager.h"
#include "core/tree_manager/tree_manager_base.h"
#include "core/utils/loader.h"
#include "core/utils/loader_utils.h"
// yaml-schema-cpp
#include "yaml-schema-cpp/yaml_server.hpp"
#include "yaml-schema-cpp/yaml_schema.hpp"
namespace wolf
{
Problem::Problem(SizeEigen _dim, const TypeComposite& _frame_types, LoaderPtr _loader)
: hardware_ptr_(std::make_shared<HardwareBase>()),
trajectory_ptr_(std::make_shared<TrajectoryBase>()),
map_ptr_(std::make_shared<MapBase>()),
tree_manager_(nullptr),
motion_provider_map_(),
dim_(_dim),
frame_keys_(),
// reseved state types (P -> position, O -> orientation, V -> velocity)
frame_types_({{'P', (dim_ == 2 ? "StatePoint2d" : "StatePoint3d")},
{'O', (dim_ == 2 ? "StateAngle" : "StateQuaternion")},
{'V', (dim_ == 2 ? "StateVector2d" : "StateVector3d")}}),
first_frame_values_(),
first_frame_priors_(),
first_frame_status_(0),
transformed_(false),
loader_(_loader)
{
if (not loader_) loader_ = std::make_shared<Loader>();
// append provided state types
addFrameTypes(_frame_types);
}
void Problem::setup()
{
hardware_ptr_->setProblem(shared_from_this());
trajectory_ptr_->setProblem(shared_from_this());
if (map_ptr_) map_ptr_->setProblem(shared_from_this());
}
ProblemPtr Problem::create(SizeEigen _dim, const TypeComposite& _frame_types, LoaderPtr _loader)
{
ProblemPtr p(
new Problem(_dim, _frame_types, _loader)); // We use `new` and not `make_shared` since the Problem constructor
// is private and cannot be passed to `make_shared`.
p->setup();
return p->shared_from_this();
}
ProblemPtr Problem::autoSetup(const std::string& _input_yaml_file,
std::vector<std::string> _schema_folders,
LoaderPtr _loader,
bool _skip_yaml_validation)
{
// Load YAML node: yaml schema server only used to flatten "follows", not validation
WOLF_DEBUG("Loading YAML file...");
yaml_schema_cpp::YamlServer server = yaml_schema_cpp::YamlServer({}, _input_yaml_file);
// Call autoSetup via YAML node
return Problem::autoSetup(server.getNode(), _schema_folders, _loader, _skip_yaml_validation);
}
ProblemPtr Problem::autoSetup(YAML::Node _param_node,
std::vector<std::string> _schema_folders,
LoaderPtr _loader,
bool _skip_yaml_validation)
{
// VALIDATION ===============================================================================
if (not _skip_yaml_validation)
{
// Create YAML server
yaml_schema_cpp::YamlServer server = yaml_schema_cpp::YamlServer(_schema_folders);
server.setYaml(_param_node);
// SCHEMA FOLDERS (optional _schema_folders specify folders where to search schemas before installed ones)
// Search and load plugins to get the installed schema folders registered
if (not _loader) _loader = std::make_shared<Loader>();
searchAndLoadPlugins(server.getNode(), _loader);
// Add the installed schema folders after optional input folders
server.addFolderSchema(FolderRegistry::getRegisteredFolders());
// Validate against schema
WOLF_DEBUG("Validating YAML file...");
if (not server.applySchema("Problem.schema"))
{
WOLF_ERROR(server.getLog());
return nullptr;
}
// Validate against specific schema 2D or 3D
bool is2D = server.getNode()["problem"]["dimension"].as<int>() == 2;
if (not server.applySchema(is2D ? "Problem2d.schema" : "Problem3d.schema"))
{
WOLF_ERROR(server.getLog());
return nullptr;
}
WOLF_DEBUG("YAML file validated with ",
is2D ? "Problem2d.schema" : "Problem3d.schema",
"! Node after validation:\n",
server.getNode());
// recover modified node
_param_node = server.getNode();
}
// PROBLEM ===============================================================================
// dimension
WOLF_DEBUG("Loading problem parameters...");
YAML::Node problem_node = _param_node["problem"];
int dim = problem_node["dimension"].as<int>();
// Create problem
WOLF_DEBUG("Creating problem...");
auto problem = Problem::create(dim, {}, _loader);
// First frame
WOLF_DEBUG("Setting first frame parameters...");
problem->setFirstFrameOptions(problem_node["first_frame"]);
// Tree manager
WOLF_DEBUG("Creating tree manager...");
YAML::Node tree_manager_node = problem_node["tree_manager"];
std::string tree_manager_type = tree_manager_node["type"].as<std::string>();
if (tree_manager_type != "none" and tree_manager_type != "None" and tree_manager_type != "NONE")
{
if (not FactoryTreeManagerNode::isCreatorRegistered(tree_manager_type))
_loader->loadPlugin(tree_manager_node["plugin"].as<std::string>());
problem->setTreeManager(FactoryTreeManagerNode::create(tree_manager_type, tree_manager_node, {}));
}
// SENSORS ===============================================================================
// load plugin if sensor is not registered
WOLF_DEBUG("Installing sensors...");
for (auto sensor_n : _param_node["sensors"]) problem->installSensor(sensor_n);
// PROCESSORS ===============================================================================
// load plugin if processor is not registered
WOLF_DEBUG("Installing processors...");
for (auto processor_n : _param_node["processors"]) problem->installProcessor(processor_n);
// MAP (optional) ===============================================================================
// Default MapBase
WOLF_DEBUG("Creating map...");
// load plugin if map is not registered
if (not FactoryMapNode::isCreatorRegistered(_param_node["map"]["type"].as<std::string>()))
_loader->loadPlugin(_param_node["map"]["plugin"].as<std::string>());
auto map = FactoryMapNode::create(_param_node["map"]["type"].as<std::string>(), _param_node["map"], {});
map->setProblem(problem);
problem->setMap(map);
// Done
return problem;
}
Problem::~Problem()
{
// WOLF_DEBUG("destructed -P");
}
SensorBasePtr Problem::installSensor(const YAML::Node& _sensor_node, std::vector<std::string> _schema_folders)
{
// (optionally) Validate YAML node
if (not _schema_folders.empty())
{
// Search and load plugins to get the installed schema folders registered
searchAndLoadPlugins(_sensor_node, loader_);
// Add the installed schema folders after optional input _schema_folders
FolderRegistry::appendRegisteredFolders(_schema_folders);
// Validate against TypeAndPlugin (check if it has 'type' and 'plugin')
// NOTE: Validation with the correponding schema is done in the creator
auto server = yaml_schema_cpp::YamlServer(_schema_folders);
server.setYaml(_sensor_node);
if (not server.applySchema("TypeAndPlugin"))
{
WOLF_ERROR(server.getLog());
throw std::runtime_error("Sensor could not be created! Missing type or plugin");
}
}
// Load plugin if factory not registered
auto sensor_type = _sensor_node["type"].as<std::string>();
if (not FactorySensorNode::isCreatorRegistered(sensor_type))
{
loader_->loadPlugin(_sensor_node["plugin"].as<std::string>());
if (not FactorySensorNode::isCreatorRegistered(sensor_type))
throw std::runtime_error("Sensor creator not registered after loading its plugin.");
}
// Create sensor
SensorBasePtr sen_ptr = FactorySensorNode::create(sensor_type, _sensor_node, _schema_folders);
if (not sen_ptr) throw std::runtime_error("Sensor could not be created.");
// Link
sen_ptr->link(getHardware());
return sen_ptr;
}
SensorBasePtr Problem::installSensor(const std::string& _params_yaml_filename,
std::vector<std::string> _schema_folders)
{
if (not file_exists(_params_yaml_filename))
throw std::runtime_error("Cannot install sensor: provided params YAML file: '" + _params_yaml_filename +
"' does not exist.");
// Load yaml
auto server = yaml_schema_cpp::YamlServer(_schema_folders, _params_yaml_filename);
auto sensor_node = server.getNode();
// Search and load plugins to get the installed schema folders registered
searchAndLoadPlugins(sensor_node, loader_);
// Add the installed schema folders after optional input _schema_folders
server.addFolderSchema(FolderRegistry::getRegisteredFolders());
_schema_folders = server.getFolderSchema();
// Validate against TypeAndPlugin (check if it has 'type' and 'plugin')
// NOTE: Validation with the correponding schema is done in the creator
if (not server.applySchema("TypeAndPlugin"))
{
WOLF_ERROR(server.getLog());
throw std::runtime_error("Sensor could not be created! Missing type or plugin in the yaml");
}
// Factory not registered
auto sensor_type = sensor_node["type"].as<std::string>();
if (not FactorySensorFile::isCreatorRegistered(sensor_type))
throw std::runtime_error("Creator of sensor '" + sensor_type + "' not registered.");
// Create sensor (includes validation of derived schema)
SensorBasePtr sen_ptr = FactorySensorFile::create(sensor_type, _params_yaml_filename, _schema_folders);
if (not sen_ptr) throw std::runtime_error("Sensor could not be created.");
// Link
sen_ptr->link(getHardware());
return sen_ptr;
}
ProcessorBasePtr Problem::installProcessor(const YAML::Node& _processor_node, std::vector<std::string> _schema_folders)
{
// (optionally) Validate YAML node
if (not _schema_folders.empty())
{
// Search and load plugins to get the installed schema folders registered
searchAndLoadPlugins(_processor_node, loader_);
// Add the installed schema folders after optional input _schema_folders
FolderRegistry::appendRegisteredFolders(_schema_folders);
// Validate against ProcessorBaseWithSensor (check if it has 'type' and 'plugin')
// NOTE: Validation with the correponding schema is done in the creator
auto server = yaml_schema_cpp::YamlServer(_schema_folders);
server.setYaml(_processor_node);
if (not server.applySchema("ProcessorBaseWithSensor"))
{
WOLF_ERROR(server.getLog());
throw std::runtime_error("Processor could not be created!");
}
}
// Find sensor
auto corresponding_sensor_name = _processor_node["sensor_name"].as<std::string>();
SensorBasePtr sen_ptr = findSensor(corresponding_sensor_name);
if (sen_ptr == nullptr)
throw std::runtime_error("Cannot bind processor. Reason: Sensor '" + corresponding_sensor_name +
"' not found. Check sensor name, it must match in sensor and processor!");
// Load plugin if factory not registered
auto processor_type = _processor_node["type"].as<std::string>();
if (not FactoryProcessorNode::isCreatorRegistered(processor_type))
{
loader_->loadPlugin(_processor_node["plugin"].as<std::string>());
if (not FactoryProcessorNode::isCreatorRegistered(processor_type))
throw std::runtime_error("Processor creator not registered.");
}
// Create processor (with derived class validation)
ProcessorBasePtr prc_ptr = FactoryProcessorNode::create(processor_type, _processor_node, _schema_folders);
if (not prc_ptr) throw std::runtime_error("Processor could not be created.");
// Dimension check
if (prc_ptr->getDimCompatible() != 0 and prc_ptr->getDimCompatible() != this->getDim())
throw std::runtime_error("Processor not compatible with the Problem dimension.");
// Link
prc_ptr->configure(sen_ptr);
prc_ptr->link(sen_ptr);
return prc_ptr;
}
ProcessorBasePtr Problem::installProcessor(SensorBasePtr _sensor_corresponding,
const std::string& _params_yaml_filename,
std::vector<std::string> _schema_folders)
{
if (not file_exists(_params_yaml_filename))
throw std::runtime_error("Cannot install processor: parameters' YAML file does not exist: " +
_params_yaml_filename);
// Load yaml
auto server = yaml_schema_cpp::YamlServer(_schema_folders, _params_yaml_filename);
auto processor_node = server.getNode();
// Search and load plugins to get the installed schema folders registered
searchAndLoadPlugins(processor_node, loader_);
// Add the installed schema folders after optional input _schema_folders
server.addFolderSchema(FolderRegistry::getRegisteredFolders());
_schema_folders = server.getFolderSchema();
// Validate against TypeAndPlugin (check if it has 'type' and 'plugin')
// NOTE: Validation with the correponding schema is done in the creator
if (not server.applySchema("TypeAndPlugin"))
{
WOLF_ERROR(server.getLog());
throw std::runtime_error("Processor could not be created.");
}
// Creator registered
auto prc_type = processor_node["type"].as<std::string>();
if (not FactoryProcessorNode::isCreatorRegistered(prc_type))
throw std::runtime_error("Processor creator not registered.");
// Find sensor
if (_sensor_corresponding == nullptr) throw std::runtime_error("Cannot bind processor. Reason: Sensor nullptr!");
// Create processor (includes validating with derived schema)
ProcessorBasePtr prc_ptr = FactoryProcessorNode::create(prc_type, processor_node, _schema_folders);
if (not prc_ptr) throw std::runtime_error("Processor could not be created.");
// Dimension check
if (prc_ptr->getDimCompatible() != 0 and prc_ptr->getDimCompatible() != this->getDim())
throw std::runtime_error("Processor not compatible with the Problem dimension.");
// Add processor
prc_ptr->configure(_sensor_corresponding);
prc_ptr->link(_sensor_corresponding);
return prc_ptr;
}
ProcessorBasePtr Problem::installProcessor(const std::string& _params_yaml_filename,
std::vector<std::string> _schema_folders)
{
if (not file_exists(_params_yaml_filename))
throw std::runtime_error("Cannot install processor: parameters' YAML file does not exist: " +
_params_yaml_filename);
// Load yaml
auto server = yaml_schema_cpp::YamlServer(_schema_folders, _params_yaml_filename);
// Search and load plugins to get the installed schema folders registered
searchAndLoadPlugins(server.getNode(), loader_);
// Add the installed schema folders after optional input _schema_folders
server.addFolderSchema(FolderRegistry::getRegisteredFolders());
_schema_folders = server.getFolderSchema();
// Validate against ProcessorBase and derived type
// NOTE: Validation with the correponding schema is done in the creator
if (not server.applySchema("ProcessorBaseWithSensor"))
{
WOLF_ERROR(server.getLog());
throw std::runtime_error("Processor could not be created.");
}
auto processor_node = server.getNode();
// Creator not registered
auto prc_type = processor_node["type"].as<std::string>();
if (not FactoryProcessorNode::isCreatorRegistered(prc_type))
throw std::runtime_error("Processor creator not registered: " + prc_type);
// Find sensor
auto corresponding_sensor_name = processor_node["sensor_name"].as<std::string>();
SensorBasePtr sen_ptr = findSensor(corresponding_sensor_name);
if (sen_ptr == nullptr)
throw std::runtime_error("Cannot bind processor. Reason: Sensor '" + corresponding_sensor_name +
"' not found. Check sensor name, it must match in sensor and processor!");
// Create processor (includes validating with derived schema)
ProcessorBasePtr prc_ptr = FactoryProcessorNode::create(prc_type, processor_node, _schema_folders);
if (not prc_ptr) throw std::runtime_error("Processor could not be created.");
// Dimension check
if (prc_ptr->getDimCompatible() != 0 and prc_ptr->getDimCompatible() != this->getDim())
throw std::runtime_error("Processor not compatible with the Problem dimension.");
// Add processor
prc_ptr->configure(sen_ptr);
prc_ptr->link(sen_ptr);
return prc_ptr;
}
SensorBaseConstPtr Problem::findSensor(const std::string& _sensor_name) const
{
return getHardware()->getSensor(_sensor_name);
}
SensorBasePtr Problem::findSensor(const std::string& _sensor_name)
{
return getHardware()->getSensor(_sensor_name);
}
ProcessorBaseConstPtr Problem::findProcessor(const std::string& _processor_name) const
{
for (const auto& sensor : getHardware()->getSensorList())
for (const auto& processor : sensor->getProcessorList())
if (processor->getName() == _processor_name) return processor;
return nullptr;
}
ProcessorBasePtr Problem::findProcessor(const std::string& _processor_name)
{
for (const auto& sensor : getHardware()->getSensorList())
for (const auto& processor : sensor->getProcessorList())
if (processor->getName() == _processor_name) return processor;
return nullptr;
}
FrameBasePtr Problem::emplaceFrame(const TimeStamp& _time_stamp,
const TypeComposite& _frame_types,
const VectorComposite& _frame_state,
const PriorComposite& _frame_priors)
{
// check input _frame_specs
checkTypeComposite(_frame_types);
// add the types of the keys that are not in frame_types_ (also checks that are consistent)
addFrameTypes(_frame_types);
// add the keys that are not in frame_keys_
addFrameKeys(_frame_state.getKeys());
return FrameBase::emplace<FrameBase>(getTrajectory(), _time_stamp, _frame_types, _frame_state, _frame_priors);
}
FrameBasePtr Problem::emplaceFrame(const TimeStamp& _time_stamp, const StateKeys& _frame_keys)
{
if (not frame_types_.has(_frame_keys))
{
throw std::runtime_error("Problem::emplaceFrame: any unknown type, asked for " + _frame_keys +
" but problem only know the types of " + frame_types_.getKeys());
}
auto state = getState(_time_stamp, _frame_keys);
return emplaceFrame(_time_stamp, getFrameTypes(state.getKeys()), state);
}
FrameBasePtr Problem::emplaceFrame(const TimeStamp& _time_stamp, const VectorComposite& _frame_state)
{
if (not frame_types_.has(_frame_state.getKeys()))
{
throw std::runtime_error("Problem::emplaceFrame: any unknown type, given vectors of " +
_frame_state.getKeys() + " but problem only know the types of " +
frame_types_.getKeys());
}
return Problem::emplaceFrame(_time_stamp, getFrameTypes(_frame_state.getKeys()), _frame_state);
}
FrameBasePtr Problem::emplaceFrame(const TimeStamp& _time_stamp,
const StateKeys& _frame_keys,
const Eigen::VectorXd& _frame_state)
{
if (not frame_types_.has(_frame_keys))
{
throw std::runtime_error("Problem::emplaceFrame: any unknown type, given vectors of " + _frame_keys +
" but problem only know the types of " + frame_types_.getKeys());
}
VectorComposite vec_composite;
int next_state_index = 0;
for (auto key : _frame_keys)
{
auto state_size = FactoryStateBlockIdentityVector::create(frame_types_.at(key)).size();
if (_frame_state.size() < next_state_index + state_size)
{
throw std::runtime_error("Problem::emplaceFrame: the provided vector _frame_state is too short");
}
vec_composite[key] = _frame_state.segment(next_state_index, state_size);
next_state_index += state_size;
}
if (_frame_state.size() != next_state_index)
{
throw std::runtime_error("Problem::emplaceFrame: the provided vector _frame_state is too long");
}
return Problem::emplaceFrame(_time_stamp, getFrameTypes(vec_composite.getKeys()), vec_composite);
}
TimeStamp Problem::getTimeStamp() const
{
TimeStamp ts = TimeStamp::Invalid();
for (auto prc_pair : motion_provider_map_)
if (prc_pair.second->getTimeStamp().ok())
if ((not ts.ok()) or prc_pair.second->getTimeStamp() > ts) ts = prc_pair.second->getTimeStamp();
if (not ts.ok())
{
auto last_kf = trajectory_ptr_->getLastFrame();
if (last_kf) ts = last_kf->getTimeStamp(); // Use last estimated frame's state
}
WOLF_DEBUG_COND(not ts.ok(), "Problem has nowhere to find a valid timestamp!");
return ts;
}
VectorComposite Problem::getState(const StateKeys& _keys) const
{
StateKeys keys = (_keys == "" ? getFrameKeys() : _keys);
VectorComposite state;
// compose the states of all MotionProvider processors (ordered by user-defined priority) into one only state
for (auto prc_pair : motion_provider_map_)
{
auto prc_state = prc_pair.second->getState(keys);
// transfer processor vector blocks to problem state
for (auto pair_key_vec : prc_state)
{
if (not state.has(
pair_key_vec.first)) // Only write once. This gives priority to processors with more priority
{
state.insert(pair_key_vec);
}
}
// If all keys are filled return
if (state.has(keys))
{
return state;
}
}
// check for empty blocks and fill them with the last KF, with the prior, or with zeros in the worst case
VectorComposite state_last;
auto last_kf = trajectory_ptr_->getLastFrame();
if (last_kf)
state_last = last_kf->getState(keys);
else if (not first_frame_values_.empty())
state_last = first_frame_values_;
for (auto key : keys)
{
if (state.has(key) == 0)
{
auto state_last_it = state_last.find(key);
if (state_last_it != state_last.end())
state.emplace(key, state_last_it->second);
else
state.emplace(key, stateZero(std::string(1, key)).at(key));
}
}
return state;
}
VectorComposite Problem::getState(const TimeStamp& _ts, const StateKeys& _structure) const
{
StateKeys structure = (_structure == "" ? getFrameKeys() : _structure);
VectorComposite state;
// compose the states of all MotionProvider processors (ordered by user-defined priority) into one only state
for (auto prc_pair : motion_provider_map_)
{
auto prc_state = prc_pair.second->getState(_ts, structure);
// transfer processor vector blocks to problem state
for (auto pair_key_vec : prc_state)
{
if (state.count(pair_key_vec.first) ==
0) // Only write once. This gives priority to processors with more priority
state.insert(pair_key_vec);
}
// If all keys are filled return
if (state.size() == structure.size())
{
return state;
}
}
// check for empty blocks and fill them with the closest KF to ts, with the prior, or with zeros in the worst case
VectorComposite state_last;
auto last_kf = trajectory_ptr_->closestFrameToTimeStamp(_ts);
if (last_kf)
state_last = last_kf->getState(structure);
else if (not first_frame_values_.empty())
state_last = first_frame_values_;
for (auto key : structure)
{
if (state.count(key) == 0)
{
auto state_last_it = state_last.find(key);
if (state_last_it != state_last.end())
state.emplace(key, state_last_it->second);
else
state.emplace(key, stateZero(std::string(1, key)).at(key));
}
}
return state;
}
VectorComposite Problem::getOdometry(const StateKeys& _structure) const
{
StateKeys structure = (_structure == "" ? getFrameKeys() : _structure);
VectorComposite odom_state;
// compose the states of all MotionProvider processors (ordered by user-defined priority) into one only state
for (auto prc_pair : motion_provider_map_)
{
auto prc_state = prc_pair.second->getOdometry();
// transfer processor vector blocks to problem state
for (auto pair_key_vec : prc_state)
{
if (odom_state.count(pair_key_vec.first) ==
0) // Only write once. This gives priority to processors with more priority
odom_state.insert(pair_key_vec);
}
}
// check for empty blocks and fill them with the prior, or with zeros in the worst case
for (auto key : structure)
{
if (odom_state.count(key) == 0)
{
auto state_last_it = first_frame_values_.find(key);
if (state_last_it != first_frame_values_.end())
odom_state.emplace(key, state_last_it->second);
else
odom_state.emplace(key, stateZero(std::string(1, key)).at(key));
}
}
return odom_state;
}
SizeEigen Problem::getDim() const
{
return dim_;
}
StateKeys Problem::getFrameKeys() const
{
return frame_keys_;
}
TypeComposite Problem::getFrameTypes(StateKeys _keys) const
{
if (_keys == "") return frame_types_(frame_keys_);
if (not frame_types_.has(_keys))
throw std::runtime_error("Problem::getFrameTypes: frame_types_ does not have any of the required keys");
TypeComposite types;
for (auto key : _keys) types.emplace(key, frame_types_.at(key));
return types;
}
bool Problem::hasFrameKeys(StateKeys _keys) const
{
for (auto input_key : _keys)
if (frame_keys_.find(input_key) == std::string::npos) return false;
return true;
}
void Problem::addFrameKeys(StateKeys _keys)
{
for (auto input_key : _keys)
if (frame_keys_.find(input_key) == std::string::npos) frame_keys_.push_back(input_key);
}
void Problem::addFrameTypes(const TypeComposite& _types)
{
for (auto pair : _types)
if (not frame_types_.has(pair.first)) // new key not found in problem structure -> append!
frame_types_.emplace(pair.first, pair.second);
else
{
WOLF_DEBUG_COND(frame_types_.at(pair.first) == pair.second,
"Type already in 'frame_types_' doing nothing.");
if (frame_types_.at(pair.first) != pair.second)
{
WOLF_ERROR("Trying to append type '",
pair.second,
"' for key '",
pair.first,
"' but it is already set as '",
frame_types_.at(pair.first),
"'");
throw std::runtime_error("Problem::addFrameTypes: Different type already defined in this key.");
}
}
// add also keys
addFrameKeys(_types.getKeys());
}
void Problem::setTreeManager(TreeManagerBasePtr _gm)
{
if (tree_manager_) tree_manager_->setProblem(nullptr);
tree_manager_ = _gm;
if (tree_manager_) tree_manager_->setProblem(shared_from_this());
}
void Problem::addMotionProvider(MotionProviderPtr _motion_provider_ptr)
{
// Check if is state getter
if (not _motion_provider_ptr->isStateGetter())
{
WOLF_WARN("adding a MotionProvider processor with state_provider=false. Not adding this processor");
return;
}
// check duplicated priority
while (motion_provider_map_.count(_motion_provider_ptr->getOrder()) == 1)
{
WOLF_ERROR("Adding a MotionProvider processor with priority = ",
_motion_provider_ptr->getOrder(),
" which is already taken. Trying to add it with priority = ",
_motion_provider_ptr->getOrder() + 1);
_motion_provider_ptr->setOrder(_motion_provider_ptr->getOrder() + 1);
}
// add to map ordered by priority
motion_provider_map_.emplace(_motion_provider_ptr->getOrder(), _motion_provider_ptr);
// check consistent types
for (auto new_key : _motion_provider_ptr->getStateTypes().getKeys())
if (frame_types_.has(new_key) and frame_types_[new_key] != _motion_provider_ptr->getStateTypes().at(new_key))
{
throw std::runtime_error("Problem::addMotionProvider: inconsistent type for key " +
std::string(1, new_key) + " previously registered as " + frame_types_[new_key] +
" and now as " + _motion_provider_ptr->getStateTypes().at(new_key));
}
// Grow TypeComposite storing information about the types of the StateBlocks corresponding to each key
addFrameTypes(_motion_provider_ptr->getStateTypes());
}
void Problem::removeMotionProvider(MotionProviderPtr proc)
{
WOLF_WARN_COND(motion_provider_map_.count(proc->getOrder()) == 0, "Missing processor");
motion_provider_map_.erase(proc->getOrder());
// rebuild frame structure with remaining motion processors
frame_keys_.clear();
for (auto pm : motion_provider_map_) addFrameKeys(pm.second->getStateTypes().getKeys());
}
VectorComposite Problem::stateZero(TypeComposite _types) const
{
if (_types.empty()) _types = frame_types_(frame_keys_);
VectorComposite state;
for (auto&& type_pair : _types)
{
state.emplace(type_pair.first, FactoryStateBlockIdentityVector::create(type_pair.second));
}
return state;
}
VectorComposite Problem::stateZero(const StateKeys& _keys) const
{
if (not frame_types_.has(_keys))
throw std::runtime_error("Problem::stateZero any unknown type... asked for " + _keys +
" but only have types for " + frame_types_.getKeys());
StateKeys keys = (_keys == "" ? getFrameKeys() : _keys);
return stateZero(getFrameTypes(_keys));
}
bool Problem::permitKeyFrame(ProcessorBaseConstPtr _processor_ptr) const
{
// This should implement a black list of processors that have forbidden keyframe creation
// This decision is made at problem level, not at processor configuration level.
// If you want to configure a processor for not creating keyframes, see Processor::voting_active_ and its
// accessors.
// Currently allowing all processors to vote:
return true;
}
void Problem::keyFrameCallback(FrameBasePtr _keyframe_ptr, ProcessorBasePtr _processor_ptr)
{
WOLF_DEBUG_COND(_processor_ptr != nullptr,
_processor_ptr->getType(),
" - ",
_processor_ptr->getName(),
": KF",
_keyframe_ptr->id(),
" Callback emitted with ts = ",
_keyframe_ptr->getTimeStamp());
WOLF_DEBUG_COND(_processor_ptr == nullptr,
"External callback: KF",
_keyframe_ptr->id(),
" Callback emitted with ts = ",
_keyframe_ptr->getTimeStamp());
// pause processor profiling
if (_processor_ptr) _processor_ptr->stopCaptureProfiling();
for (auto sensor : hardware_ptr_->getSensorList())
for (auto processor : sensor->getProcessorList())
if (processor && (processor != _processor_ptr)) processor->keyFrameCallback(_keyframe_ptr);
// notify tree manager
if (tree_manager_) tree_manager_->keyFrameCallback(_keyframe_ptr);
// resume processor profiling
if (_processor_ptr) _processor_ptr->startCaptureProfiling();
}
StateBlockPtr Problem::notifyStateBlock(StateBlockPtr _state_ptr, Notification _noti)
{
std::lock_guard<std::mutex> lock(mut_notifications_);
// std::cout << "Problem::addStateBlockPtr " << _state_ptr << std::endl;
// Check if there is already a notification for this state block
auto notification_it = state_block_notification_map_.find(_state_ptr);
// exsiting notification for this state block
if (notification_it != state_block_notification_map_.end())
{
// duplicated notification -> warning
WOLF_WARN_COND(notification_it->second == _noti, "This notification has been already notified");
// opposite notification -> cancell out eachother
if (notification_it->second != _noti) state_block_notification_map_.erase(notification_it);
}
// Add notification
else
state_block_notification_map_[_state_ptr] = _noti;
return _state_ptr;
}
bool Problem::getStateBlockNotification(const StateBlockPtr& sb_ptr, Notification& notif) const
{
std::lock_guard<std::mutex> lock(mut_notifications_);
if (state_block_notification_map_.find(sb_ptr) == state_block_notification_map_.end()) return false;
notif = state_block_notification_map_.at(sb_ptr);
return true;
}
FactorBasePtr Problem::notifyFactor(FactorBasePtr _factor_ptr, Notification _noti)
{
std::lock_guard<std::mutex> lock(mut_notifications_);
// std::cout << "Problem::addFactorPtr " << _factor_ptr->id() << std::endl;
// Check if there is already the same notification for this factor
auto notification_it = factor_notification_map_.find(_factor_ptr);
// exsiting notification for this factor
if (notification_it != factor_notification_map_.end())
{
// duplicated notification
if (notification_it->second == _noti)
{
WOLF_WARN("This notification has been already notified");
}
// opposite notification -> cancell out eachother
else
{
factor_notification_map_.erase(notification_it);
}
}
// Add notification
else
factor_notification_map_[_factor_ptr] = _noti;
return _factor_ptr;
}
bool Problem::getFactorNotification(const FactorBasePtr& fac_ptr, Notification& notif) const
{
std::lock_guard<std::mutex> lock(mut_notifications_);
if (factor_notification_map_.find(fac_ptr) == factor_notification_map_.end()) return false;
notif = factor_notification_map_.at(fac_ptr);
return true;
}
void Problem::clearCovariance()
{
std::lock_guard<std::mutex> lock(mut_covariances_);
covariances_.clear();
}
void Problem::addCovarianceBlock(StateBlockPtr _state1, StateBlockPtr _state2, const Eigen::MatrixXd& _cov)
{
assert(_state1->getLocalSize() == (unsigned int)_cov.rows() && "wrong covariance block size");
assert(_state2->getLocalSize() == (unsigned int)_cov.cols() && "wrong covariance block size");
std::lock_guard<std::mutex> lock(mut_covariances_);
covariances_[std::pair<StateBlockPtr, StateBlockPtr>(_state1, _state2)] = _cov;
}
void Problem::addCovarianceBlock(StateBlockPtr _state1, const Eigen::MatrixXd& _cov)
{
assert(_state1->getLocalSize() == (unsigned int)_cov.rows() && "wrong covariance block size");
assert(_state1->getLocalSize() == (unsigned int)_cov.cols() && "wrong covariance block size");
std::lock_guard<std::mutex> lock(mut_covariances_);
covariances_[std::make_pair(_state1, _state1)] = _cov;
}
bool Problem::getCovarianceBlock(StateBlockConstPtr _state1,
StateBlockConstPtr _state2,
Eigen::MatrixXd& _cov,
const int _row,
const int _col) const
{
// std::cout << "entire cov to be filled:" << std::endl << _cov << std::endl;
// std::cout << "_row " << _row << std::endl;
// std::cout << "_col " << _col << std::endl;
// std::cout << "_state1 tangent space size: " << _state1->getLocalSize() << std::endl;
// std::cout << "_state2 tangent space size: " << _state2->getLocalSize() << std::endl;
// std::cout << "part of cov to be filled:" << std::endl << _cov.block(_row, _col, _state1->getLocalSize(),
// _state2->getLocalSize()) << std::endl; if (covariances_.find(std::pair<StateBlockPtr,
// StateBlockPtr>(_state1, _state2)) != covariances_.end())
// std::cout << "stored cov" << std::endl << covariances_[std::pair<StateBlockPtr, StateBlockPtr>(_state1,
// _state2)] << std::endl;
// else if (covariances_.find(std::pair<StateBlockPtr, StateBlockPtr>(_state2, _state1)) != covariances_.end())
// std::cout << "stored cov" << std::endl << covariances_[std::pair<StateBlockPtr, StateBlockPtr>(_state2,
// _state1)].transpose() << std::endl;
assert(_row + _state1->getLocalSize() <= _cov.rows() && _col + _state2->getLocalSize() <= _cov.cols() &&
"Problem::getCovarianceBlock: Bad matrix covariance size!");
std::lock_guard<std::mutex> lock(mut_covariances_);
if (covariances_.find(std::pair<StateBlockConstPtr, StateBlockConstPtr>(_state1, _state2)) != covariances_.end())
_cov.block(_row, _col, _state1->getLocalSize(), _state2->getLocalSize()) =
covariances_.at(std::pair<StateBlockConstPtr, StateBlockConstPtr>(_state1, _state2));
else if (covariances_.find(std::pair<StateBlockConstPtr, StateBlockConstPtr>(_state2, _state1)) !=
covariances_.end())
_cov.block(_row, _col, _state1->getLocalSize(), _state2->getLocalSize()) =
covariances_.at(std::pair<StateBlockConstPtr, StateBlockConstPtr>(_state2, _state1)).transpose();
else
{
WOLF_DEBUG("Could not find the requested covariance block.");
return false;
}
return true;
}
bool Problem::getCovarianceBlock(std::map<StateBlockConstPtr, unsigned int> _sb_2_idx, Eigen::MatrixXd& _cov) const
{
std::lock_guard<std::mutex> lock(mut_covariances_);
// fill _cov
for (auto it1 = _sb_2_idx.begin(); it1 != _sb_2_idx.end(); it1++)
for (auto it2 = it1; it2 != _sb_2_idx.end(); it2++)
{
auto sb1 = it1->first;
auto sb2 = it2->first;
std::pair<StateBlockConstPtr, StateBlockConstPtr> pair_12(sb1, sb2);
std::pair<StateBlockConstPtr, StateBlockConstPtr> pair_21(sb2, sb1);
// search st1 & st2
if (covariances_.find(pair_12) != covariances_.end())
{
assert(_sb_2_idx[sb1] + sb1->getLocalSize() <= _cov.rows() &&
_sb_2_idx[sb2] + sb2->getLocalSize() <= _cov.cols() &&
"Problem::getCovarianceBlock: Bad matrix covariance size!");
assert(_sb_2_idx[sb2] + sb2->getLocalSize() <= _cov.rows() &&
_sb_2_idx[sb1] + sb1->getLocalSize() <= _cov.cols() &&
"Problem::getCovarianceBlock: Bad matrix covariance size!");
_cov.block(_sb_2_idx[sb1], _sb_2_idx[sb2], sb1->getLocalSize(), sb2->getLocalSize()) =
covariances_.at(pair_12);
_cov.block(_sb_2_idx[sb2], _sb_2_idx[sb1], sb2->getLocalSize(), sb1->getLocalSize()) =
covariances_.at(pair_12).transpose();
}
else if (covariances_.find(pair_21) != covariances_.end())
{
assert(_sb_2_idx[sb1] + sb1->getLocalSize() <= _cov.rows() &&
_sb_2_idx[sb2] + sb2->getLocalSize() <= _cov.cols() &&
"Problem::getCovarianceBlock: Bad matrix covariance size!");
assert(_sb_2_idx[sb2] + sb2->getLocalSize() <= _cov.rows() &&
_sb_2_idx[sb1] + sb1->getLocalSize() <= _cov.cols() &&
"Problem::getCovarianceBlock: Bad matrix covariance size!");
_cov.block(_sb_2_idx[sb1], _sb_2_idx[sb2], sb1->getLocalSize(), sb2->getLocalSize()) =
covariances_.at(pair_21).transpose();
_cov.block(_sb_2_idx[sb2], _sb_2_idx[sb1], sb2->getLocalSize(), sb1->getLocalSize()) =
covariances_.at(pair_21);
}
else
return false;
}
return true;
}
bool Problem::getCovarianceBlock(StateBlockConstPtr _state, Eigen::MatrixXd& _cov, const int _row_and_col) const
{
return getCovarianceBlock(_state, _state, _cov, _row_and_col, _row_and_col);
}
bool Problem::getCovariance(NodeStateBlocksConstPtr _has_blocks,
const StateKeys& _keys,
Eigen::MatrixXd& _covariance) const
{
if (not _has_blocks)
{
WOLF_WARN("Called with a nullptr");
return false;
}
if (not _has_blocks->has(_keys))
{
WOLF_WARN("Does not have the keys ", _keys, ". Available keys: ", _has_blocks->getKeys());
return false;
}
bool success(true);
// resizing
SizeEigen sz = _has_blocks->getLocalSize();
_covariance.resize(sz, sz);
// filling covariance
int i = 0, j = 0;
for (auto key_i : _keys)
{
auto sb_i = _has_blocks->getStateBlock(key_i);
j = 0;
for (auto key_j : _keys)
{
auto sb_j = _has_blocks->getStateBlock(key_j);
success = success && getCovarianceBlock(sb_i, sb_j, _covariance, i, j);
j += sb_j->getLocalSize();
}
i += sb_i->getLocalSize();
}
return success;
}
bool Problem::getLastFrameCovariance(const StateKeys& _keys, Eigen::MatrixXd& cov) const
{
return getCovariance(getLastFrame(), _keys, cov);
}
MapBaseConstPtr Problem::getMap() const
{
return map_ptr_;
}
MapBasePtr Problem::getMap()
{
return map_ptr_;
}
void Problem::setMap(MapBasePtr _map_ptr)
{
map_ptr_ = _map_ptr;
}
TrajectoryBaseConstPtr Problem::getTrajectory() const
{
return trajectory_ptr_;
}
TrajectoryBasePtr Problem::getTrajectory()
{
return trajectory_ptr_;
}
HardwareBaseConstPtr Problem::getHardware() const
{
return hardware_ptr_;
}
HardwareBasePtr Problem::getHardware()
{
return hardware_ptr_;
}
FrameBaseConstPtr Problem::getLastFrame() const
{
return trajectory_ptr_->getLastFrame();
}
FrameBasePtr Problem::getLastFrame()
{
return trajectory_ptr_->getLastFrame();
}
FrameBaseConstPtr Problem::closestFrameToTimeStamp(const TimeStamp& _ts) const
{
return trajectory_ptr_->closestFrameToTimeStamp(_ts);
}
FrameBasePtr Problem::closestFrameToTimeStamp(const TimeStamp& _ts)
{
return trajectory_ptr_->closestFrameToTimeStamp(_ts);
}
void Problem::setFirstFrameOptions(const YAML::Node& _first_frame_node)
{
setFirstFrameOptions(TypeComposite(_first_frame_node, "type", false),
VectorComposite(_first_frame_node, "value", true),
PriorComposite(_first_frame_node, "prior", true));
}
void Problem::setFirstFrameOptions(const TypeComposite& _first_frame_types,
const VectorComposite& _first_frame_values,
const PriorComposite& _first_frame_priors)
{
if (isFirstFrameApplied())
{
throw std::runtime_error("prior options have already been applied");
}
WOLF_WARN_COND(first_frame_status_ == 1, "Overriding prior options.");
// add the types of the keys that are not in frame_types_
addFrameTypes(_first_frame_types);
first_frame_values_ = _first_frame_values;
first_frame_priors_ = _first_frame_priors;
first_frame_status_ = 1; // options set
}
FrameBasePtr Problem::applyFirstFrameOptions(const TimeStamp& _ts)
{
if (isFirstFrameApplied())
{
throw std::runtime_error("applyFirstFrameOptions can be called once!");
}
FrameBasePtr first_frame(nullptr);
// prior options set
if (first_frame_status_ == 1)
{
// Create first frame with the prior options
first_frame =
emplaceFrame(_ts, frame_types_(first_frame_values_.getKeys()), first_frame_values_, first_frame_priors_);
// notify all processors
keyFrameCallback(first_frame, nullptr);
}
// first frame status: APPLIED
first_frame_status_ = 2;
return first_frame;
}
FrameBasePtr Problem::emplaceFirstFrame(const TimeStamp& _ts, const YAML::Node& _first_frame_node)
{
setFirstFrameOptions(_first_frame_node);
return applyFirstFrameOptions(_ts);
}
FrameBasePtr Problem::emplaceFirstFrame(const TimeStamp& _ts,
const TypeComposite& _frame_types,
const VectorComposite& _frame_state,
const PriorComposite& _frame_priors)
{
setFirstFrameOptions(_frame_types, _frame_state, _frame_priors);
return applyFirstFrameOptions(_ts);
}
bool Problem::saveMap(const std::string& _filename_dot_yaml, const std::string& _map_name)
{
return getMap()->save(_filename_dot_yaml, _map_name);
}
void Problem::print(int _depth, bool _factored_by, bool _metric, bool _state_blocks, std::ostream& _stream) const
{
_stream << std::endl;
_stream << "Wolf tree status ------------------------" << std::endl;
getHardware()->print(_depth, _factored_by, _metric, _state_blocks, _stream, "");
getTrajectory()->print(_depth, _factored_by, _metric, _state_blocks, _stream, "");
getMap()->print(_depth, _factored_by, _metric, _state_blocks, _stream, "");
_stream << "-----------------------------------------" << std::endl;
_stream << std::endl;
}
bool Problem::check(bool _verbose, std::ostream& _stream) const
{
CheckLog log(true, "");
log.explanation_ = "## WOLF::problem inconsistencies list \n ---------------------------------- \n";
std::string tabs = "";
if (_verbose) _stream << std::endl;
if (_verbose) _stream << "Wolf tree integrity ---------------------" << std::endl;
auto P = shared_from_this();
if (_verbose)
{
_stream << "Prb @ " << P << std::endl;
}
// ------------------------
// HARDWARE branch
// ------------------------
hardware_ptr_->check(log, _verbose, _stream, tabs);
// ------------------------
// TRAJECTORY branch
// ------------------------
trajectory_ptr_->check(log, _verbose, _stream, tabs);
// ------------------------
// MAP branch
// ------------------------
map_ptr_->check(log, _verbose, _stream, tabs);
if (_verbose)
_stream << "--------------------------- Wolf tree " << (log.is_consistent_ ? " OK" : "Not OK !!") << std::endl;
if (_verbose) _stream << std::endl;
if (_verbose and not log.is_consistent_) _stream << log.explanation_ << std::endl;
return log.is_consistent_;
}
bool Problem::check(int _verbose_level) const
{
return check((_verbose_level > 0), std::cout);
}
void Problem::perturb(double amplitude)
{
// Sensors
for (auto S : getHardware()->getSensorList()) S->perturb(amplitude);
// Frames and Captures
for (auto F : getTrajectory()->getFrameMap())
{
F.second->perturb(amplitude);
for (auto& C : F.second->getCaptureList()) C->perturb(amplitude);
}
// Landmarks
for (auto L : getMap()->getLandmarkList()) L->perturb(amplitude);
}
void Problem::transform(const VectorComposite& _transformation)
{
std::lock_guard<std::mutex> lock_transform(mut_transform_); // Protect especially from SolverBase::update()
transformation_ = _transformation;
transformed_.store(true);
// Sensors
for (auto S : getHardware()->getSensorList()) S->transform(_transformation);
// Frames and Captures
for (auto F : getTrajectory()->getFrameMap())
{
F.second->transform(_transformation);
for (auto& C : F.second->getCaptureList()) C->transform(_transformation);
}
// Landmarks
for (auto L : getMap()->getLandmarkList()) L->transform(_transformation);
}
bool Problem::isTransformed() const
{
return transformed_.load();
}
void Problem::resetTransformed()
{
transformed_.store(false);
}
VectorComposite Problem::getTransformation() const
{
std::lock_guard<std::mutex> lock(mut_transform_);
return transformation_;
}
} // namespace wolf