Skip to content
Snippets Groups Projects
Commit 7e1c38d3 authored by Joan Solà Ortega's avatar Joan Solà Ortega
Browse files

Merge branch '299-factory-documentation' into 'devel'

Resolve "Factory documentation"

Closes #299

See merge request !350
parents 7d330cb7 14e3a97a
No related branches found
No related tags found
1 merge request!350Resolve "Factory documentation"
Pipeline #5603 passed
......@@ -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);
......
......@@ -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
*
......
......@@ -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" , &params );
* 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" , &params );
* \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); } \
......
......@@ -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); } \
......
......@@ -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,
......
/*
* 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)
{
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment