diff --git a/include/core/common/factory.h b/include/core/common/factory.h index 1510362e65c0a457f8982eec5e8e5cc130798668..c9aa0cd1980d55577179a07be2c6c819055d2b4b 100644 --- a/include/core/common/factory.h +++ b/include/core/common/factory.h @@ -25,42 +25,68 @@ namespace wolf /** \brief Singleton template factory * - * This class implements a generic factory as a singleton. - * - * > IMPORTANT: This template factory can be used to construct many different objects except: - * > - Objects deriving from SensorBase --> see FactorySensor - * > - Objects deriving from ProcessorBase --> see FactoryProcessor - * > - * > The reason for this is that the two cases above need a more elaborated API than the one in this template class. + * This template class implements a generic factory as a singleton. * * \param TypeBase base type of all the objects created by the factory - * \param TypeInput type of the input argument. Typical cases are std::string for file names, and YAML::Node for YAML nodes. + * \param TypeInput variadic type for the input arguments. + * + * ### Template specialization * * - The class is templatized on the class of the produced objects, __TypeBase__. * The produced objects are always of a class deriving from TypeBase. - * The returned data is always a pointer to TypeBase. + * The returned data is always a shared pointer to TypeBase. * * For example, you may use as __TypeBase__ the following types: * - LandmarkBase: the Factory creates landmarks deriving from LandmarkBase and returns base pointers ````LandmarkBasePtr```` to them * - ParamsSensorBase: the Factory creates intrinsic parameters deriving from ParamsSensorBase and returns base pointers ````ParamsSensorBasePtr```` to them * - XxxBase: the Factory creates objects deriving from XxxBase and returns pointers ````XxxBasePtr```` to them. * - * - The class in also templatized on the type of the input parameter of the creator, __TypeInput__: - * - ````std::string```` is used when the input parameter is a file name from which to read data (typically a YAML file). - * - ````YAML::Node```` is used when the input parameter is a YAML node with structured data. + * - The class is variadic-templatized on the types of the input parameter of the creator, __TypeInput__: + * - ````std::string```` is used when the input parameter is a file name from which to read data (typically a YAML file). + * - ````std::string, wolf::ParamsServer``` is used when nodes are build from parameters in the ParamsServer + * - ````YAML::Node```` is used when the input parameter is a YAML node with structured data. + * - Any other variadic list of inputs is possible. + * + * - Examples of specific factories existing in Wolf are: + * \code + * + * // FactorySensor + * typedef Factory<SensorBase, // Type of objects to be returned: SensorBasePtr + * const std::string&, // Type of the sensor: name of the derived sensor class + * const Eigen::VectorXd&, // Sensor extrinsics + * const ParamsSensorBasePtr> // Sensor parameters + * FactorySensor; + * + * // FactoryProcessor + * typedef Factory<ProcessorBase, // Type of object returned: ProcessorBasePtr + * const std::string&, // Type of the processor: name of the derived processor class + * const ParamsProcessorBasePtr> // Parameters for creating the processor + * FactoryProcessor; + * + * // AutoConfProcessorFactory + * typedef Factory<ProcessorBase, // Type of object returned: ProcessorBasePtr + * const std::string&, // Type of the processor: name of the derived processor class + * const ParamsServer> // Parameters for creating the processor + * AutoConfProcessorFactory; + * + * // Landmarks from YAML + * typedef Factory<LandmarkBase, // Type of node created: LandmarkBasePtr + * const YAML::Node&> // YAML node with the lmk params + * LandmarkFactory; + * \endcode * * ### Operation of the factory * * #### Rationale * - * This factory can create objects of classes deriving from TypeBase. + * Once specialized, this factory can create objects of classes deriving from TypeBase. * * > For example, if you want to make a Landmark factory, set TypeBase = LandmarkBase.\n * > Then, the factory will create specific landmarks deriving from LandmarkBase.\n * > The specific type of landmark (e.g. LandmarkCorner2d, LandmarkAHP, LandmarkPolyline2d, etc) is indicated by a string that we call TYPE in this documentation. * * Specific object creation is invoked by the method ````create(TYPE, params ... )````, where - * - the TYPE of object to create is identified with a string + * - the TYPE of object to create is identified with a string. This string matches the name of the derived class. * - the params may be provided in different forms -- see TypeInput. * * The methods to create specific objects are called __creators__. @@ -75,23 +101,31 @@ namespace wolf * - Examples: Write and register a landmark creator for LandmarkPolyline2d. * * #### Define correct TYPE names - * The rule to make new TYPE strings unique is that you skip the generic 'Type' prefix from your class name, - * and you build a string in CAPITALS with space separators, e.g.: - * - ParamsSensorCamera -> ````"CAMERA"```` - * - ParamsSensorLaser2d -> ````"LASER 2d"```` - * - LandmarkPolyline2d -> ````"POLYLINE 2d"```` + * We use a std::string with literally the same text as the derived class name, e.g.: + * - ParamsSensorCamera -> ````"ParamsSensorCamera"```` + * - SensorCamera -> ````"SensorCamera"```` + * - ParamsSensorLaser2d -> ````"ParamsSensorLaser2d"```` + * - SensorLaser2d -> ````"SensorLaser2d"```` + * - LandmarkPolyline2d -> ````"LandmarkPolyline2d"```` * - etc. * * #### Access the factory + * * The first thing to know is that we have defined typedefs for the templates that we are using. For example: * * \code - * typedef Factory<ParamsSensorBase, std::string> FactoryParamsSensor; - * typedef Factory<ParamsProcessorBase, std::string> FactoryParamsProcessor; - * typedef Factory<LandmarkBase, YAML::Node> FactoryLandmark; + * typedef Factory<ParamsSensorBase, std::string> FactoryParamsSensor; + * typedef Factory<ParamsProcessorBase, std::string> FactoryParamsProcessor; + * typedef Factory<LandmarkBase, YAML::Node> FactoryLandmark; + * typedef Factory<SensorBase, + * const std::string&, + * const Eigen::VectorXd&, + * const ParamsSensorBasePtr> FactorySensor; + * * \endcode * - * Second to know, the Factory class is a <a href="http://stackoverflow.com/questions/1008019/c-singleton-design-pattern#1008289">singleton</a>: it can only exist once in your application. + * Second to know, the Factory class is a <a href="http://stackoverflow.com/questions/1008019/c-singleton-design-pattern#1008289">singleton</a>: + * it can only exist once in your application. * To access it, use the static method get(), * * \code @@ -103,10 +137,11 @@ namespace wolf * You can then call the methods you like, e.g. to create a landmark, you use: * * \code - * FactoryLandmark::get().create(...); // see below for creating objects ... + * FactoryLandmark::get().create(TYPE, args...); // see below for creating objects ... * \endcode * * #### Write creator methods (in your derived object classes) + * * The method LandmarkPolyline2d::create(...) exists in the LandmarkPolyline2d class as a static method. * All these ````XxxXxx::create()```` methods need to have exactly the same API, regardless of the object type. * The API puts into play the two template parameters: @@ -117,13 +152,13 @@ namespace wolf * * This API includes an element of type TypeInput, which might be either a std::string, or a YAML::node: * - ````std::string```` is used to indicate the name of a configuration file. These files are usually YAML files containing configuration data to create your object. - * - ````YAML::Node```` is used to access parts of a YAML file already encoded as nodes, such as when loading landmarks from a SLAM map stored as a YAML file. + * - ````YAML::Node```` is used to access parts of a YAML file already encoded as nodes, such as when loading landmarks from a SLAM map stored as a YAML file. * * Two examples: * * \code * static ParamsSensorBasePtr create(const std::string& _intrinsics_dot_yaml) - * static LandmarkBasePtr create(const YAML::Node& _lmk_yaml_node) + * static LandmarkBasePtr create(const YAML::Node& _lmk_yaml_node) * \endcode * * See further down for an implementation example. @@ -137,7 +172,7 @@ namespace wolf * that knows how to create your specific object, e.g.: * * \code - * FactoryLandmark::get().registerCreator("POLYLINE 2d", LandmarkPolyline2d::create); + * FactoryLandmark::get().registerCreator("LandmarkPolyline2d", LandmarkPolyline2d::create); * \endcode * * #### Automatic registration @@ -145,7 +180,7 @@ namespace wolf * For example, in sensor_camera_yaml.cpp we find the line: * * \code - * const bool registered_camera_intr = FactoryParamsSensor::get().registerCreator("CAMERA", createParamsSensorCamera); + * const bool registered_camera_intr = FactoryParamsSensor::get().registerCreator("ParamsSensorCamera", createParamsSensorCamera); * \endcode * * which is a static invocation (i.e., it is placed at global scope outside of the ParamsSensorCamera class). @@ -156,7 +191,7 @@ namespace wolf * The method unregisterCreator() unregisters the ObjectXxx::create() method. It only needs to be passed the string of the object type. * * \code - * Factory<MyTypeBase, MyTypeInput>::get().unregisterCreator("CAMERA"); + * Factory<MyTypeBase, MyTypeInput>::get().unregisterCreator("ParamsSensorCamera"); * \endcode * * #### Create objects using the factory @@ -166,13 +201,13 @@ namespace wolf * To create e.g. a LandmarkPolyline2d from a YAML node you type: * * \code - * LandmarkBasePtr lmk_ptr = Factory<LandmarkBasePtr, YAML::Node>::get().create("POLYLINE 2d", lmk_yaml_node); + * LandmarkBasePtr lmk_ptr = Factory<LandmarkBasePtr, YAML::Node>::get().create("LandmarkPolyline2d", lmk_yaml_node); * \endcode * * or even better, make use of the convenient typedefs: * * \code - * LandmarkBasePtr lmk_ptr = FactoryLandmark::get().create("POLYLINE 2d", lmk_yaml_node); + * LandmarkBasePtr lmk_ptr = FactoryLandmark::get().create("LandmarkPolyline2d", lmk_yaml_node); * \endcode * * ### Examples @@ -197,7 +232,7 @@ namespace wolf * } * * // Create a new landmark - * LandmarkBasePtr lmk_ptr = new LandmarkPolyline2d(points, first_defined, last_defined, first_id); + * LandmarkBasePtr lmk_ptr = std::make_shared<LandmarkPolyline2d>(points, first_defined, last_defined, first_id); * lmk_ptr->setId(id); * * return lmk_ptr; @@ -212,7 +247,7 @@ namespace wolf * // Register landmark creator (put the register code inside an unnamed namespace): * namespace * { - * const bool registered_lmk_polyline_2d = FactoryLandmark::get().registerCreator("POLYLINE 2d", LandmarkPolyline2d::create); + * const bool registered_lmk_polyline_2d = FactoryLandmark::get().registerCreator("LandmarkPolyline2d", LandmarkPolyline2d::create); * } * * \endcode @@ -234,12 +269,16 @@ namespace wolf template<class TypeBase, typename... TypeInput> class Factory { + private: typedef std::shared_ptr<TypeBase> TypeBasePtr; + public: // example of creator callback (see typedefs below) typedef TypeBasePtr (*CreatorCallback)(TypeInput... _input); + private: typedef std::map<std::string, CreatorCallback> CallbackMap; + public: bool registerCreator(const std::string& _type, CreatorCallback createFn); bool unregisterCreator(const std::string& _type); diff --git a/include/core/common/wolf.h b/include/core/common/wolf.h index 319b2df0bf0d0ed464ca8a55647a63594a1bb4a3..229e10f95dd8f7506846fe75536a85c6604ae521 100644 --- a/include/core/common/wolf.h +++ b/include/core/common/wolf.h @@ -29,15 +29,6 @@ namespace wolf { -/** - * \brief scalar type for the Wolf project - * - * Do NEVER forget to use Wolf scalar definitions when you code!!! - * - * Do NEVER use plain Eigen scalar types!!! (This is redundant with the above. But just to make sure you behave!) - */ -//typedef double double; - /** * \brief Vector and Matrices size type for the Wolf project * diff --git a/include/core/processor/factory_processor.h b/include/core/processor/factory_processor.h index 5fc2f8f14ccffc52653c6f43fa0c07fdb03be825..9d101848eb54966e970a48d08416548a259b7154 100644 --- a/include/core/processor/factory_processor.h +++ b/include/core/processor/factory_processor.h @@ -27,143 +27,92 @@ namespace wolf * * Specific object creation is invoked by create(TYPE, params), and the TYPE of processor is identified with a string. * For example, the following processor types are implemented, - * - "ODOM 3d" for ProcessorOdom3d - * - "ODOM 2d" for ProcessorOdom2d - * - "GPS" for ProcessorGPS - * - * The rule to make new TYPE strings unique is that you skip the prefix 'Processor' from your class name, - * and you build a string in CAPITALS with space separators. - * - ProcessorImageFeature -> ````"IMAGE"```` - * - ProcessorLaser2d -> ````"LASER 2d"```` - * - etc. - * - * The methods to create specific processors are called __creators__. - * Creators must be registered to the factory before they can be invoked for processor creation. - * - * This documentation shows you how to: - * - Access the Factory - * - Register and unregister creators - * - Create processors - * - Write a processor creator for ProcessorOdom2d (example). - * - * #### Accessing the Factory - * The FactoryProcessor class is a singleton: it can only exist once in your application. - * To obtain an instance of it, use the static method get(), - * - * \code - * FactoryProcessor::get() - * \endcode + * - "ProcessorOdom2d" for ProcessorOdom2d + * - "ProcessorOdom3d" for ProcessorOdom3d + * - "ProcessorDiffDrive" for ProcessorDiffDrive * - * You can then call the methods you like, e.g. to create a processor, you type: - * - * \code - * FactoryProcessor::get().create(...); // see below for creating processors ... - * \endcode - * - * #### Registering processor creators - * Prior to invoking the creation of a processor of a particular type, - * you must register the creator for this type into the factory. + * among others. * - * Registering processor creators into the factory is done through registerCreator(). - * You provide a processor type string (above), and a pointer to a static method - * that knows how to create your specific processor, e.g.: + * Find general Factory documentation in class Factory: + * - Access the factory + * - Register/unregister creators + * - Invoke object creation * - * \code - * FactoryProcessor::get().registerCreator("ProcessorOdom2d", ProcessorOdom2d::create); - * \endcode * - * The method ProcessorOdom2d::create() exists in the ProcessorOdom2d class as a static method. - * All these ProcessorXxx::create() methods need to have exactly the same API, regardless of the processor type. - * This API includes a processor name, and a pointer to a base struct of parameters, ParamsProcessorBasePtr, - * that can be derived for each derived processor. + * This documentation shows you how to use the FactoryProcessor specifically: + * - Write a processor creator + * - Create processors * - * Here is an example of ProcessorOdom2d::create() extracted from processor_odom_2d.h: + * #### Write processor creators + * Processor creators have the following API: * * \code - * static ProcessorBasePtr create(const std::string& _name, ParamsProcessorBasePtr _params) - * { - * // cast _params to good type - * ParamsProcessorOdom2d* params = (ParamsProcessorOdom2d*)_params; - * - * ProcessorBasePtr prc = new ProcessorOdom2d(params); - * prc->setName(_name); // pass the name to the created ProcessorOdom2d. - * return prc; - * } + * static ProcessorBasePtr create(const std::string& _name, ParamsProcessorBasePtr _params_processor); * \endcode * - * #### Achieving automatic registration - * Currently, registering is performed in each specific ProcessorXxxx source file, processor_xxxx.cpp. - * For example, in processor_odom_2d.cpp we find the line: + * They follow the general implementation shown below: * * \code - * const bool registered_odom_2d = FactoryProcessor::get().registerCreator("ProcessorOdom2d", ProcessorOdom2d::create); - * \endcode + * static ProcessorBasePtr create(const std::string& _unique_name, ParamsProcessorBasePtr _params_processor) + * { + * // cast processor parameters to good type --- example: ParamsProcessorOdom3d + * auto params_processor_ptr = std::static_pointer_cast<ParamsProcessorOdom3d>(_params_processor); * - * which is a static invocation (i.e., it is placed at global scope outside of the ProcessorOdom2d class). - * Therefore, at application level, all processors that have a .cpp file compiled are automatically registered. + * // Do create the Processor object --- example: ProcessorOdom3d + * auto prc_ptr = std::make_shared<ProcessorOdom3d>(params_processor_ptr); * - * #### Unregister processor creators - * The method unregisterCreator() unregisters the ProcessorXxx::create() method. - * It only needs to be passed the string of the processor type. + * // Complete the processor setup with a unique name identifying the processor + * prc_ptr->setName(_unique_name); * - * \code - * FactoryProcessor::get().unregisterCreator("ProcessorOdom2d"); + * return prc_ptr; + * } * \endcode * * #### Creating processors - * Prior to invoking the creation of a processor of a particular type, + * Note: Prior to invoking the creation of a processor of a particular type, * you must register the creator for this type into the factory. * * To create a ProcessorOdom2d, you type: * * \code - * FactoryProcessor::get().create("ProcessorOdom2d", "main odometry", params_ptr); + * auto prc_odom2d_ptr = FactoryProcessor::get().create("ProcessorOdom2d", "main odometry", params_ptr); * \endcode * - * #### Example 1 : using the Factories alone + * #### Example 1 : Create a sensor and its processor * We provide the necessary steps to create a processor of class ProcessorOdom2d in our application, * and bind it to a SensorOdom2d: * * \code - * #include "core/sensor/sensor_odom_2d.h" // provides SensorOdom2d and FactorySensor + * #include "core/sensor/sensor_odom_2d.h" // provides SensorOdom2d and FactorySensor * #include "core/processor/processor_odom_2d.h" // provides ProcessorOdom2d and FactoryProcessor * * // Note: SensorOdom2d::create() is already registered, automatically. * // Note: ProcessorOdom2d::create() is already registered, automatically. * * // First create the sensor (See FactorySensor for details) - * SensorBasePtr sensor_ptr = FactorySensor::get().create ( "FactorOdom2d" , "Main odometer" , extrinsics , &intrinsics ); + * SensorBasePtr sensor_ptr = FactorySensor::get().create ( "SensorOdom2d" , "Main odometer" , extrinsics , &intrinsics ); * - * // To create a odometry integrator, provide a type="ODOM 2d", a name="main odometry", and a pointer to the parameters struct: + * // To create a odometry integrator, provide a type="ProcessorOdom2d", a name="main odometry", and a pointer to the parameters struct: * - * ParamsProcessorOdom2d params({...}); // fill in the derived struct (note: ProcessorOdom2d actually has no input params) + * auto params = make_shared<ParamsProcessorOdom2d>({...}); // fill in the derived struct (note: ProcessorOdom2d actually has no input params) * * ProcessorBasePtr processor_ptr = - * FactoryProcessor::get().create ( "ProcessorOdom2d" , "main odometry" , ¶ms ); + * FactoryProcessor::get().create ( "ProcessorOdom2d" , "main odometry" , params ); * * // Bind processor to sensor * sensor_ptr->addProcessor(processor_ptr); * \endcode * - * #### Example 2: Using the helper API in class Problem - * The WOLF uppermost node, Problem, makes the creation of sensors and processors, and the binding between them, even simpler. - * - * The creation is basically replicating the factories' API. The binding is accomplished by passing the sensor name to the Processor installer. - * - * The example 1 above can be accomplished as follows (we obviated for simplicity all the parameter creation), - * - * \code - * #include "core/sensor/sensor_odom_2d.h" - * #include "core/processor/processor_odom_2d.h" - * #include "core/problem/problem.h" - * - * Problem problem(FRM_PO_2d); - * problem.installSensor ( "SensorOdom2d" , "Main odometer" , extrinsics , &intrinsics ); - * problem.installProcessor ( "ProcessorOdom2d" , "Odometry" , "Main odometer" , ¶ms ); - * \endcode - * - * You can also check the code in the example file ````src/examples/test_wolf_factories.cpp````. */ +typedef Factory<ProcessorBase, + const std::string&, + const ParamsProcessorBasePtr> FactoryProcessor; +template<> +inline std::string FactoryProcessor::getClass() const +{ + return "FactoryProcessor"; +} + // ParamsProcessor factory struct ParamsProcessorBase; @@ -175,16 +124,6 @@ inline std::string FactoryParamsProcessor::getClass() const return "FactoryParamsProcessor"; } -// Processor factory -typedef Factory<ProcessorBase, - const std::string&, - const ParamsProcessorBasePtr> FactoryProcessor; -template<> -inline std::string FactoryProcessor::getClass() const -{ - return "FactoryProcessor"; -} - #define WOLF_REGISTER_PROCESSOR(ProcessorType) \ namespace{ const bool WOLF_UNUSED ProcessorType##Registered = \ wolf::FactoryProcessor::get().registerCreator(#ProcessorType, ProcessorType::create); } \ diff --git a/include/core/sensor/factory_sensor.h b/include/core/sensor/factory_sensor.h index ff0574aaadc6a52015a6c9e4f076a1ec64d968c0..3e787dd56cf710390cce53ed81d3b3800b2445b6 100644 --- a/include/core/sensor/factory_sensor.h +++ b/include/core/sensor/factory_sensor.h @@ -25,81 +25,51 @@ namespace wolf * * This factory can create objects of classes deriving from SensorBase. * - * Specific object creation is invoked by ````create(TYPE, params ... )````, and the TYPE of sensor is identified with a string. + * Specific object creation is invoked by `create(TYPE, params ... )`, and the TYPE of sensor is identified with a string. * Currently, the following sensor types are implemented, - * - "CAMERA" for SensorCamera - * - "ODOM 2d" for SensorOdom2d - * - "GPS FIX" for SensorGPSFix + * - "SensorOdom2d" for SensorOdom2d + * - "SensorOdom3d" for SensorOdom3d + * - "SensorDiffDrive" for SensorDiffDrive + * - "SensorCamera" for SensorCamera // in plugin 'vision' + * - "SensorLaser2d" for SensorLaser2d // in plugin 'laser' * - * The rule to make new TYPE strings unique is that you skip the prefix 'Sensor' from your class name, - * and you build a string in CAPITALS with space separators, e.g.: - * - SensorCamera -> ````"CAMERA"```` - * - SensorLaser2d -> ````"LASER 2d"```` - * - etc. + * among others. * - * The methods to create specific sensors are called __creators__. - * Creators must be registered to the factory before they can be invoked for sensor creation. - * - * This documentation shows you how to: + * Find general Factory documentation in class Factory: * - Access the factory - * - Register and unregister creators - * - Create sensors - * - Write a sensor creator for SensorCamera (example). - * - * #### Accessing the factory - * The FactorySensor class is a <a href="http://stackoverflow.com/questions/1008019/c-singleton-design-pattern#1008289">singleton</a>: it can only exist once in your application. - * To obtain an instance of it, use the static method get(), + * - Register/unregister creators + * - Invoke object creation * - * \code - * FactorySensor::get() - * \endcode + * This documentation shows you how to use the FactorySensor specifically: + * - Write sensor creators. + * - Create sensors * - * You can then call the methods you like, e.g. to create a sensor, you type: + * #### Write sensor creators + * Sensor creators have the following API: * * \code - * FactorySensor::get().create(...); // see below for creating sensors ... + * static SensorBasePtr create(const std::string& _name, Eigen::VectorXd& _params_extrinsics, ParamsSensorBasePtr _params_sensor); * \endcode * - * #### Registering sensor creators - * Prior to invoking the creation of a sensor of a particular type, - * you must register the creator for this type into the factory. - * - * Registering sensor creators into the factory is done through registerCreator(). - * You provide a sensor type string (above), and a pointer to a static method - * that knows how to create your specific sensor, e.g.: + * They follow the general implementation shown below: * * \code - * FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); - * \endcode - * - * The method SensorCamera::create() exists in the SensorCamera class as a static method. - * All these ````SensorXxx::create()```` methods need to have exactly the same API, regardless of the sensor type. - * This API includes a sensor name, a vector of extrinsic parameters, - * and a pointer to a base struct of intrinsic parameters, ParamsSensorBasePtr, - * that can be derived for each derived sensor: - * - * \code - * static SensorBasePtr create(const std::string& _name, Eigen::VectorXd& _extrinsics_pq, ParamsSensorBasePtr _intrinsics) - * \endcode - * - * See further down for an implementation example. - * - * #### Achieving automatic registration - * Currently, registering is performed in each specific SensorXxxx source file, sensor_xxxx.cpp. - * For example, in sensor_camera.cpp we find the line: + * static SensorBasePtr create(const std::string& _unique_name, Eigen::VectorXd& _params_extrinsics, ParamsSensorBasePtr _params_sensor) + * { + * // check extrinsics vector --- example: 3D pose + * assert(_params_extrinsics.size() == 7 && "Bad extrinsics vector length. Should be 7 for 3d."); * - * \code - * const bool registered_camera = FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); - * \endcode + * // cast sensor parameters to good type --- example: ParamsSensorCamera + * auto intrinsics_ptr = std::static_pointer_cast<ParamsSensorCamera>(_params_sensor); * - * which is a static invocation (i.e., it is placed at global scope outside of the SensorCamera class). - * Therefore, at application level, all sensors that have a .cpp file compiled are automatically registered. + * // Do create the Sensor object --- example: SensorCamera + * auto sen_ptr = std::make_shared<SensorCamera>(_extrinsics_pq, intrinsics_ptr); * - * #### Unregistering sensor creators - * The method unregisterCreator() unregisters the SensorXxx::create() method. It only needs to be passed the string of the sensor type. + * // Complete the sensor setup with a unique name identifying the sensor + * sen_ptr->setName(_unique_name); * - * \code - * FactorySensor::get().unregisterCreator("CAMERA"); + * return sen_ptr; + * } * \endcode * * #### Creating sensors @@ -109,7 +79,7 @@ namespace wolf * To create e.g. a SensorCamera, you type: * * \code - * FactorySensor::get().create("CAMERA", "Front-left camera", extrinsics, intrinsics_ptr); + * auto camera_ptr = FactorySensor::get().create("SensorCamera", "Front-left camera", params_extrinsics, params_camera); * \endcode * * where ABSOLUTELY ALL input parameters are important. In particular, the sensor name "Front-left camera" will be used to identify this camera @@ -120,20 +90,21 @@ namespace wolf * - FactoryProcessor: to create processors that will be bound to sensors. * - Problem::installSensor() : to install sensors in WOLF Problem. * - * #### Example 1: writing a specific sensor creator + * #### Example 1: writing a SensorCamera creator * Here is an example of SensorCamera::create() extracted from sensor_camera.cpp: * * \code - * static SensorBasePtr create(const std::string& _name, Eigen::VectorXd& _extrinsics_pq, ParamsSensorBasePtr _intrinsics) + * static SensorBasePtr create(const std::string& _unique_name, Eigen::VectorXd& _extrinsics_pq, ParamsSensorBasePtr _intrinsics) * { * // check extrinsics vector * assert(_extrinsics_pq.size() == 7 && "Bad extrinsics vector length. Should be 7 for 3d."); * * // cast instrinsics to good type - * ParamsSensorCamera* intrinsics_ptr = (ParamsSensorCamera*) _intrinsics; + * auto intrinsics_ptr = std::static_pointer_cast<ParamsSensorCamera>(_intrinsics); * * // Do create the SensorCamera object, and complete its setup - * SensorCamera* sen_ptr = new SensorCamera(_extrinsics_pq, intrinsics_ptr); + * auto sen_ptr = std::make_shared<SensorCamera>(_extrinsics_pq, intrinsics_ptr); + * * sen_ptr->setName(_unique_name); * * return sen_ptr; @@ -148,14 +119,14 @@ namespace wolf * Put the code either at global scope (you must define a dummy variable for this), * \code * namespace { - * const bool registered_camera = FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); + * const bool registered_camera = FactorySensor::get().registerCreator("SensorCamera", SensorCamera::create); * } * main () { ... } * \endcode * or inside your main(), where a direct call is possible: * \code * main () { - * FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); + * FactorySensor::get().registerCreator("SensorCamera", SensorCamera::create); * ... * } * \endcode @@ -164,7 +135,7 @@ namespace wolf * Put the code at the last line of the sensor_xxx.cpp file, * \code * namespace { - * const bool registered_camera = FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); + * const bool registered_camera = FactorySensor::get().registerCreator("SensorCamera", SensorCamera::create); * } * \endcode * Automatic registration is recommended in wolf, and implemented in the classes shipped with it. @@ -174,8 +145,8 @@ namespace wolf * We finally provide the necessary steps to create a sensor of class SensorCamera in our application: * * \code - * #include "factory_sensor.h" - * #include "core/sensor/sensor_camera.h" // provides SensorCamera + * #include "core/sensor/factory_sensor.h" + * #include "vision/sensor/sensor_camera.h" // provides SensorCamera * * // Note: SensorCamera::create() is already registered, automatically. * @@ -183,43 +154,43 @@ namespace wolf * int main() { * * // To create a camera, provide: - * // a type = "CAMERA", + * // a type = "SensorCamera", * // a name = "Front-left camera", * // an extrinsics vector, and * // a pointer to the intrinsics struct: * - * Eigen::VectorXd extrinsics_1(7); // give it some values... - * ParamsSensorCamera intrinsics_1({...}); // see FactoryParamsSensor to fill in the derived struct + * Eigen::VectorXd extrinsics_1(7); // give it some values... + * + * // Create a pointer to the struct of sensor parameters stored in a YAML file ( see FactoryParamsSensor ) + * ParamsSensorCameraPtr intrinsics_1 = + * FactoryParamsSensor::get().create("ParamsSensorCamera", + * camera_1.yaml); * * SensorBasePtr camera_1_ptr = - * FactorySensor::get().create ( "CAMERA" , "Front-left camera" , extrinsics_1 , &intrinsics_1 ); + * FactorySensor::get().create ( "SensorCamera" , + * "Front-left camera" , + * extrinsics_1 , + * intrinsics_1 ); * * // A second camera... with a different name! * - * Eigen::VectorXd extrinsics_2(7); - * ParamsSensorCamera intrinsics_2({...}); + * Eigen::VectorXd extrinsics_2(7); + * + * ParamsSensorCameraPtr intrinsics_2 = + * FactoryParamsSensor::get().create("ParamsSensorCamera", + * camera_2.yaml); * * SensorBasePtr camera_2_ptr = - * FactorySensor::get().create( "CAMERA" , "Front-right camera" , extrinsics_2 , &intrinsics_2 ); + * FactorySensor::get().create( "SensorCamera" , + * "Front-right camera" , + * extrinsics_2 , + * intrinsics_2 ); * * return 0; * } * \endcode * - * You can also check the code in the example file ````src/examples/test_wolf_factories.cpp````. */ - -// ParamsSensor factory -struct ParamsSensorBase; -typedef Factory<ParamsSensorBase, - const std::string&> FactoryParamsSensor; -template<> -inline std::string FactoryParamsSensor::getClass() const -{ - return "FactoryParamsSensor"; -} - -// Sensor factory typedef Factory<SensorBase, const std::string&, const Eigen::VectorXd&, @@ -231,6 +202,16 @@ inline std::string FactorySensor::getClass() const return "FactorySensor"; } +// ParamsSensor factory +struct ParamsSensorBase; +typedef Factory<ParamsSensorBase, + const std::string&> FactoryParamsSensor; +template<> +inline std::string FactoryParamsSensor::getClass() const +{ + return "FactoryParamsSensor"; +} + #define WOLF_REGISTER_SENSOR(SensorType) \ namespace{ const bool WOLF_UNUSED SensorType##Registered = \ FactorySensor::get().registerCreator(#SensorType, SensorType::create); } \ diff --git a/include/core/solver/factory_solver.h b/include/core/solver/factory_solver.h index e3cc58a4a0b6d269026312f9c946a490d1045923..fc2031cdb245ee6364aa9de3bb38e834812e22a7 100644 --- a/include/core/solver/factory_solver.h +++ b/include/core/solver/factory_solver.h @@ -21,192 +21,8 @@ struct ParamsSensorBase; namespace wolf { -/** \brief Sensor factory class +/** \brief Solver factory class * - * This factory can create objects of classes deriving from SensorBase. - * - * Specific object creation is invoked by ````create(TYPE, params ... )````, and the TYPE of sensor is identified with a string. - * Currently, the following sensor types are implemented, - * - "CAMERA" for SensorCamera - * - "ODOM 2d" for SensorOdom2d - * - "GPS FIX" for SensorGPSFix - * - * The rule to make new TYPE strings unique is that you skip the prefix 'Sensor' from your class name, - * and you build a string in CAPITALS with space separators, e.g.: - * - SensorCamera -> ````"CAMERA"```` - * - SensorLaser2d -> ````"LASER 2d"```` - * - etc. - * - * The methods to create specific sensors are called __creators__. - * Creators must be registered to the factory before they can be invoked for sensor creation. - * - * This documentation shows you how to: - * - Access the factory - * - Register and unregister creators - * - Create sensors - * - Write a sensor creator for SensorCamera (example). - * - * #### Accessing the factory - * The FactorySensor class is a <a href="http://stackoverflow.com/questions/1008019/c-singleton-design-pattern#1008289">singleton</a>: it can only exist once in your application. - * To obtain an instance of it, use the static method get(), - * - * \code - * FactorySensor::get() - * \endcode - * - * You can then call the methods you like, e.g. to create a sensor, you type: - * - * \code - * FactorySensor::get().create(...); // see below for creating sensors ... - * \endcode - * - * #### Registering sensor creators - * Prior to invoking the creation of a sensor of a particular type, - * you must register the creator for this type into the factory. - * - * Registering sensor creators into the factory is done through registerCreator(). - * You provide a sensor type string (above), and a pointer to a static method - * that knows how to create your specific sensor, e.g.: - * - * \code - * FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); - * \endcode - * - * The method SensorCamera::create() exists in the SensorCamera class as a static method. - * All these ````SensorXxx::create()```` methods need to have exactly the same API, regardless of the sensor type. - * This API includes a sensor name, a vector of extrinsic parameters, - * and a pointer to a base struct of intrinsic parameters, ParamsSensorBasePtr, - * that can be derived for each derived sensor: - * - * \code - * static SensorBasePtr create(const std::string& _name, Eigen::VectorXd& _extrinsics_pq, ParamsSensorBasePtr _intrinsics) - * \endcode - * - * See further down for an implementation example. - * - * #### Achieving automatic registration - * Currently, registering is performed in each specific SensorXxxx source file, sensor_xxxx.cpp. - * For example, in sensor_camera.cpp we find the line: - * - * \code - * const bool registered_camera = FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); - * \endcode - * - * which is a static invocation (i.e., it is placed at global scope outside of the SensorCamera class). - * Therefore, at application level, all sensors that have a .cpp file compiled are automatically registered. - * - * #### Unregistering sensor creators - * The method unregisterCreator() unregisters the SensorXxx::create() method. It only needs to be passed the string of the sensor type. - * - * \code - * FactorySensor::get().unregisterCreator("CAMERA"); - * \endcode - * - * #### Creating sensors - * Note: Prior to invoking the creation of a sensor of a particular type, - * you must register the creator for this type into the factory. - * - * To create e.g. a SensorCamera, you type: - * - * \code - * FactorySensor::get().create("CAMERA", "Front-left camera", extrinsics, intrinsics_ptr); - * \endcode - * - * where ABSOLUTELY ALL input parameters are important. In particular, the sensor name "Front-left camera" will be used to identify this camera - * and to assign it the appropriate processors. DO NOT USE IT WITH DUMMY PARAMETERS! - * - * #### See also - * - FactoryParamsSensor: to create intrinsic structs deriving from ParamsSensorBase directly from YAML files. - * - FactoryProcessor: to create processors that will be bound to sensors. - * - Problem::installSensor() : to install sensors in WOLF Problem. - * - * #### Example 1: writing a specific sensor creator - * Here is an example of SensorCamera::create() extracted from sensor_camera.cpp: - * - * \code - * static SensorBasePtr create(const std::string& _name, Eigen::VectorXd& _extrinsics_pq, ParamsSensorBasePtr _intrinsics) - * { - * // check extrinsics vector - * assert(_extrinsics_pq.size() == 7 && "Bad extrinsics vector length. Should be 7 for 3d."); - * - * // cast instrinsics to good type - * ParamsSensorCamera* intrinsics_ptr = (ParamsSensorCamera*) _intrinsics; - * - * // Do create the SensorCamera object, and complete its setup - * SensorCamera* sen_ptr = new SensorCamera(_extrinsics_pq, intrinsics_ptr); - * sen_ptr->setName(_unique_name); - * - * return sen_ptr; - * } - * \endcode - * - * #### Example 2: registering a sensor creator into the factory - * Registration can be done manually or automatically. It involves the call to static functions. - * It is advisable to put these calls within unnamed namespaces. - * - * - __Manual registration__: you control registration at application level. - * Put the code either at global scope (you must define a dummy variable for this), - * \code - * namespace { - * const bool registered_camera = FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); - * } - * main () { ... } - * \endcode - * or inside your main(), where a direct call is possible: - * \code - * main () { - * FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); - * ... - * } - * \endcode - * - * - __Automatic registration__: registration is performed at library level. - * Put the code at the last line of the sensor_xxx.cpp file, - * \code - * namespace { - * const bool registered_camera = FactorySensor::get().registerCreator("CAMERA", SensorCamera::create); - * } - * \endcode - * Automatic registration is recommended in wolf, and implemented in the classes shipped with it. - * You are free to comment out these lines and place them wherever you consider it more convenient for your design. - * - * #### Example 2: creating sensors - * We finally provide the necessary steps to create a sensor of class SensorCamera in our application: - * - * \code - * #include "factory_sensor.h" - * #include "sensor_camera.h" // provides SensorCamera - * - * // Note: SensorCamera::create() is already registered, automatically. - * - * using namespace wolf; - * int main() { - * - * // To create a camera, provide: - * // a type = "CAMERA", - * // a name = "Front-left camera", - * // an extrinsics vector, and - * // a pointer to the intrinsics struct: - * - * Eigen::VectorXd extrinsics_1(7); // give it some values... - * ParamsSensorCamera intrinsics_1({...}); // see FactoryParamsSensor to fill in the derived struct - * - * SensorBasePtr camera_1_ptr = - * FactorySensor::get().create ( "CAMERA" , "Front-left camera" , extrinsics_1 , &intrinsics_1 ); - * - * // A second camera... with a different name! - * - * Eigen::VectorXd extrinsics_2(7); - * ParamsSensorCamera intrinsics_2({...}); - * - * SensorBasePtr camera_2_ptr = - * FactorySensor::get().create( "CAMERA" , "Front-right camera" , extrinsics_2 , &intrinsics_2 ); - * - * return 0; - * } - * \endcode - * - * You can also check the code in the example file ````src/examples/test_wolf_factories.cpp````. */ typedef Factory<SolverManager, diff --git a/include/core/state_block/factory_state_block.h b/include/core/state_block/factory_state_block.h index c353121b724011b993942d5a567166963a7440d0..0ffb0f23bbd9d18896c68f01b2c3f48183f39cc7 100644 --- a/include/core/state_block/factory_state_block.h +++ b/include/core/state_block/factory_state_block.h @@ -1,8 +1,8 @@ /* - * factory_state_block.h + * \file factory_state_block.h * * Created on: Apr 27, 2020 - * Author: jsola + * \author: jsola */ #ifndef STATE_BLOCK_FACTORY_STATE_BLOCK_H_ @@ -14,13 +14,107 @@ namespace wolf { -// State blocks factory +/** \brief StateBlock factory class + * + * This factory can create objects of class StateBlock and classes deriving from StateBlock. + * + * Specific object creation is invoked by create(TYPE, state, fixed), + * and the TYPE of state block is identified with a string. + * For example, the following processor types are implemented, + * - "StateBlock" for StateBlock + * - "StateQuaternion" for StateQuaternion + * - "StateAngle" for StateAngle + * - "StateHomogeneous3d" for StateHomogeneous3d + * + * The factory also creates state blocks according to the block key used in to identify state blocks in each Wolf node. + * These keys are single-letter strings. The following letters are implemented + * - "O" for 2d orientation, creates StateAngle + * - "O" for 3d orientation, creates StateQuaternion + * - "H" crestes StateHomogeneous3d + * + * Any other letter creates the base StateBlock. + * + * Find general Factory documentation in class Factory: + * - Access the factory + * - Register/unregister creators + * - Invoke object creation + * + * This documentation shows you how to use the FactoryStateBlock specifically: + * - Write a state block creator + * - Create state blocks + * + * #### Write state block creators + * StateBlock creators have the following API: + * + * \code + * static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed); + * \endcode + * + * They follow the general implementation shown below: + * + * \code + * static StateBlockPtr create(const Eigen::VectorXd& _state, bool _fixed) + * { + * return std::make_shared<StateBlockDerived>(_state, _fixed); + * } + * \endcode + * + * #### Creating processors + * Note: Prior to invoking the creation of a processor of a derived type, + * you must register the creator for this type into the factory. + * + * Note: State blocks of the base type do not need to be registered. + * + * To create a StateQuaternion, you type: + * + * \code + * auto sq_ptr = FactoryStateBlock::get().create("StateQuaternion", Vector4d(1,0,0,0), false); + * \endcode + * + * If your problem has dimension 3 (e.g. is 3D), you can use the key "O" to create a StateQuaternion, + * + * \code + * auto sq_ptr = FactoryStateBlock::get().create("O", Vector4d(1,0,0,0), false); + * \endcode + * + * However if your problem has dimension 2 (e.g. is 2D), the key "O" will create a StateAngle, + * + * \code + * auto sa_ptr = FactoryStateBlock::get().create("O", Vector1d(angle_in_radians), false); + * \endcode + * + * Note: It is an error to provide state vectors of the wrong size (4 for 3D orientation, 1 for 2D). + * + * To create StateBlocks to store 2D position and velocity, you type: + * + * \code + * auto sp2_ptr = FactoryStateBlock::get().create("StateBlock", Vector2d(1,2), false); + * auto sv2_ptr = FactoryStateBlock::get().create("StateBlock", Vector2d(1,2), false); + * \endcode + * + * To create StateBlocks to store 2D position and velocity, you can also use the key letters: + * + * \code + * auto sp2_ptr = FactoryStateBlock::get().create("P", Vector2d(1,2), false); + * auto sv2_ptr = FactoryStateBlock::get().create("V", Vector2d(1,2), false); + * \endcode + * + * To create StateBlocks to store 3D position and velocity, you type: + * + * \code + * auto sp3_ptr = FactoryStateBlock::get().create("P", Vector3d(1,2,3), false); + * auto sv3_ptr = FactoryStateBlock::get().create("V", Vector3d(1,2,3), false); + * \endcode + * + * Note: for base state blocks, the size is determined by the size of the provided vector parameter `VectorXd& _state`. + */ typedef Factory<StateBlock, const Eigen::VectorXd&, bool> FactoryStateBlock; template<> inline std::string FactoryStateBlock::getClass() const { return "FactoryStateBlock"; } + template<> inline StateBlockPtr FactoryStateBlock::create(const std::string& _type, const Eigen::VectorXd& _state, bool _fixed) {