From 5e1449f3cbdc86376e68c1e6a222d2ea386270f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:47:32 +0000 Subject: [PATCH] Update documentation Triggered by commit 08e682fe0795952ad01fc991979fe3056a27c29a. Co-authored-by: hidmic --- .buildinfo | 2 +- _sources/guides/extending-beluga.md | 410 +++++++++++++++- about/contact.html | 4 +- about/rationale.html | 4 +- concepts/architecture.html | 4 +- concepts/design-principles.html | 4 +- concepts/key-concepts.html | 4 +- genindex.html | 4 +- getting-started/installation.html | 4 +- getting-started/quickstart.html | 4 +- guides/benchmarking-beluga.html | 4 +- guides/extending-beluga.html | 458 +++++++++++++++++- guides/profiling-beluga.html | 4 +- guides/using-beluga-amcl.html | 4 +- index.html | 4 +- .../html/3d__embedding_8hpp_source.html | 4 +- .../reference/html/DenseGrid2Page.html | 4 +- .../reference/html/LaserScanPage.html | 4 +- .../reference/html/LinearGrid2Page.html | 4 +- .../reference/html/MotionModelPage.html | 4 +- .../reference/html/OccupancyGrid2Page.html | 4 +- .../reference/html/ParticlePage.html | 4 +- .../reference/html/PointCloudPage.html | 4 +- .../html/RandomStateDistributionPage.html | 4 +- .../reference/html/RegularGridPage.html | 4 +- .../reference/html/SensorModelPage.html | 4 +- .../reference/html/SparsePointCloudPage.html | 4 +- .../reference/html/actions_8hpp.html | 4 +- .../reference/html/actions_8hpp_source.html | 4 +- .../reference/html/algorithm_8hpp.html | 4 +- .../reference/html/algorithm_8hpp_source.html | 4 +- .../html/amcl__core_8hpp_source.html | 4 +- .../generated/reference/html/annotated.html | 4 +- .../reference/html/annotated_classes.html | 4 +- .../generated/reference/html/assign_8hpp.html | 4 +- .../reference/html/assign_8hpp_source.html | 4 +- .../reference/html/beam__model_8hpp.html | 4 +- .../html/beam__model_8hpp_source.html | 4 +- .../html/bearing__sensor__model_8hpp.html | 4 +- .../bearing__sensor__model_8hpp_source.html | 4 +- .../generated/reference/html/beluga_8hpp.html | 4 +- .../reference/html/beluga_8hpp_source.html | 4 +- .../reference/html/bresenham_8hpp.html | 4 +- .../reference/html/bresenham_8hpp_source.html | 4 +- .../reference/html/ciabatta_8hpp_source.html | 4 +- .../reference/html/circular__array_8hpp.html | 4 +- .../html/circular__array_8hpp_source.html | 4 +- .../generated/reference/html/citelist.html | 4 +- .../html/classbeluga_1_1Amcl-members.html | 4 +- .../reference/html/classbeluga_1_1Amcl.html | 4 +- ...classbeluga_1_1BaseDenseGrid2-members.html | 4 +- .../html/classbeluga_1_1BaseDenseGrid2.html | 4 +- .../classbeluga_1_1BaseLaserScan-members.html | 4 +- .../html/classbeluga_1_1BaseLaserScan.html | 4 +- ...lassbeluga_1_1BaseLinearGrid2-members.html | 4 +- .../html/classbeluga_1_1BaseLinearGrid2.html | 4 +- ...sbeluga_1_1BaseOccupancyGrid2-members.html | 4 +- .../classbeluga_1_1BaseOccupancyGrid2.html | 4 +- .../html/classbeluga_1_1BasePointCloud.html | 4 +- ...lassbeluga_1_1BaseRegularGrid-members.html | 4 +- .../html/classbeluga_1_1BaseRegularGrid.html | 4 +- .../classbeluga_1_1BaseSparsePointCloud.html | 4 +- ...lassbeluga_1_1BeamSensorModel-members.html | 4 +- .../html/classbeluga_1_1BeamSensorModel.html | 4 +- ...sbeluga_1_1BearingSensorModel-members.html | 4 +- .../classbeluga_1_1BearingSensorModel.html | 4 +- .../classbeluga_1_1Bresenham2i-members.html | 4 +- .../html/classbeluga_1_1Bresenham2i.html | 4 +- ...beluga_1_1Bresenham2i_1_1Line-members.html | 4 +- .../classbeluga_1_1Bresenham2i_1_1Line.html | 4 +- ...esenham2i_1_1Line_1_1iterator-members.html | 4 +- ...ga_1_1Bresenham2i_1_1Line_1_1iterator.html | 4 +- .../classbeluga_1_1CircularArray-members.html | 4 +- .../html/classbeluga_1_1CircularArray.html | 4 +- ...uga_1_1DifferentialDriveModel-members.html | 4 +- ...classbeluga_1_1DifferentialDriveModel.html | 4 +- ...ssbeluga_1_1ExponentialFilter-members.html | 4 +- .../classbeluga_1_1ExponentialFilter.html | 4 +- ...assbeluga_1_1IndexingIterator-members.html | 4 +- .../html/classbeluga_1_1IndexingIterator.html | 4 +- .../classbeluga_1_1LandmarkMap-members.html | 4 +- .../html/classbeluga_1_1LandmarkMap.html | 4 +- ...beluga_1_1LandmarkSensorModel-members.html | 4 +- .../classbeluga_1_1LandmarkSensorModel.html | 4 +- ...eluga_1_1LikelihoodFieldModel-members.html | 4 +- .../classbeluga_1_1LikelihoodFieldModel.html | 4 +- ...ultivariateNormalDistribution-members.html | 4 +- ...uga_1_1MultivariateNormalDistribution.html | 4 +- ...ariateNormalDistributionParam-members.html | 4 +- ..._1MultivariateNormalDistributionParam.html | 4 +- ...ga_1_1MultivariateUniformDistribution.html | 4 +- ..._01Eigen_1_1AlignedBox2d_01_4-members.html | 4 +- ...1SE2d_00_01Eigen_1_1AlignedBox2d_01_4.html | 4 +- ...1SE2d_00_01OccupancyGrid_01_4-members.html | 4 +- ...ophus_1_1SE2d_00_01OccupancyGrid_01_4.html | 4 +- ..._01Eigen_1_1AlignedBox3d_01_4-members.html | 4 +- ...1SE3d_00_01Eigen_1_1AlignedBox3d_01_4.html | 4 +- ...classbeluga_1_1NDTSensorModel-members.html | 4 +- .../html/classbeluga_1_1NDTSensorModel.html | 4 +- .../html/classbeluga_1_1Numeric-members.html | 4 +- .../html/classbeluga_1_1Numeric.html | 4 +- ..._1_1OmnidirectionalDriveModel-members.html | 4 +- ...ssbeluga_1_1OmnidirectionalDriveModel.html | 4 +- ...beluga_1_1ParticleClusterizer-members.html | 4 +- .../classbeluga_1_1ParticleClusterizer.html | 4 +- .../html/classbeluga_1_1Ray2d-members.html | 4 +- .../reference/html/classbeluga_1_1Ray2d.html | 4 +- ...lassbeluga_1_1SparseValueGrid-members.html | 4 +- .../html/classbeluga_1_1SparseValueGrid.html | 4 +- ...lassbeluga_1_1StationaryModel-members.html | 4 +- .../html/classbeluga_1_1StationaryModel.html | 4 +- ...nRecoveryProbabilityEstimator-members.html | 4 +- ..._1_1ThrunRecoveryProbabilityEstimator.html | 4 +- .../html/classbeluga_1_1TupleContainer.html | 4 +- ...ple_3_01Types_8_8_8_01_4_01_4-members.html | 4 +- ...td_1_1tuple_3_01Types_8_8_8_01_4_01_4.html | 4 +- .../html/classbeluga_1_1TupleVector.html | 4 +- .../classbeluga_1_1ValueGrid2-members.html | 4 +- .../html/classbeluga_1_1ValueGrid2.html | 4 +- ...Sophus_1_1SE2d_00_01void_01_4-members.html | 4 +- ...ash_3_01Sophus_1_1SE2d_00_01void_01_4.html | 4 +- ...Sophus_1_1SE3d_00_01void_01_4-members.html | 4 +- ...ash_3_01Sophus_1_1SE3d_00_01void_01_4.html | 4 +- ...td_1_7c7412bd4d4b17eb2e661212dfcf682d.html | 4 +- ...td_1_d633e0a7fa009222653d9c0b95a8689b.html | 4 +- ..._01st82aee1c4aa004416efdc2dd54a3ad958.html | 4 +- ..._01stba9d91ade2df69fd366eb7c564df40a1.html | 4 +- .../generated/reference/html/classes.html | 4 +- .../html/cluster__based__estimation_8hpp.html | 4 +- ...luster__based__estimation_8hpp_source.html | 4 +- .../reference/html/containers_8hpp.html | 4 +- .../html/containers_8hpp_source.html | 4 +- .../reference/html/dense__grid_8hpp.html | 4 +- .../html/dense__grid_8hpp_source.html | 4 +- .../html/differential__drive__model_8hpp.html | 4 +- ...ifferential__drive__model_8hpp_source.html | 4 +- .../reference/html/dir_000002_000003.html | 4 +- .../reference/html/dir_000002_000006.html | 4 +- .../reference/html/dir_000002_000008.html | 4 +- .../reference/html/dir_000002_000010.html | 4 +- .../reference/html/dir_000002_000011.html | 4 +- .../reference/html/dir_000002_000013.html | 4 +- .../reference/html/dir_000003_000014.html | 4 +- .../reference/html/dir_000003_000017.html | 4 +- .../reference/html/dir_000004_000005.html | 4 +- .../reference/html/dir_000004_000009.html | 4 +- .../reference/html/dir_000004_000014.html | 4 +- .../reference/html/dir_000004_000017.html | 4 +- .../reference/html/dir_000006_000016.html | 4 +- .../reference/html/dir_000006_000017.html | 4 +- .../reference/html/dir_000008_000014.html | 4 +- .../reference/html/dir_000009_000004.html | 4 +- .../reference/html/dir_000010_000011.html | 4 +- .../reference/html/dir_000011_000003.html | 4 +- .../reference/html/dir_000011_000004.html | 4 +- .../reference/html/dir_000011_000012.html | 4 +- .../reference/html/dir_000011_000015.html | 4 +- .../reference/html/dir_000012_000015.html | 4 +- .../reference/html/dir_000017_000014.html | 4 +- .../dir_0ae0216c5b903435324487dd13dfdff9.html | 4 +- .../dir_0ee0838bbed77bc71495b9458bcc4b6b.html | 4 +- .../dir_1ad523cfdb7a3d7d09cddf672d6cd478.html | 4 +- .../dir_2fab1df6832ac5fd5ad2dca443afcb3d.html | 4 +- .../dir_3245ff25b92019e7f89fc2115bc7867e.html | 4 +- .../dir_500caef060b471cfdd05b62a42e3142c.html | 4 +- .../dir_6311cd7a9afaa93eeee404de83766793.html | 4 +- .../dir_634c6b228f937c8a89fa81bc2f240134.html | 4 +- .../dir_7ddb7368080dbe1879dd7372dd1df3df.html | 4 +- .../dir_876c246173b27422a95ea0c3c06ba40d.html | 4 +- .../dir_8c67632ad66eb9bf379bf849f1aad5d2.html | 4 +- .../dir_8cfff6e4809896f1b09096e04b4d983e.html | 4 +- .../dir_a3553bb43d48af21167f3919d16bc6f3.html | 4 +- .../dir_a695b385a3d14b7fe3275ff1f0b9cead.html | 4 +- .../dir_b61f5b26177d4f0e7f42e2895ffd1c30.html | 4 +- .../dir_c03a81e29d2f9f088aa073fa974eabbc.html | 4 +- .../dir_d44c64559bbebec7f509842c48db8b23.html | 4 +- .../dir_eeee05555aca35b91744c960e0076d0e.html | 4 +- .../dir_fb5de34e8dcc24e05691267f91b0587f.html | 4 +- .../reference/html/distance__map_8hpp.html | 4 +- .../html/distance__map_8hpp_source.html | 4 +- .../html/effective__sample__size_8hpp.html | 4 +- .../effective__sample__size_8hpp_source.html | 4 +- .../eigen__compatibility_8hpp_source.html | 4 +- .../reference/html/elements_8hpp.html | 4 +- .../reference/html/elements_8hpp_source.html | 4 +- .../reference/html/epilogue_8hpp_source.html | 4 +- .../reference/html/estimation_8hpp.html | 4 +- .../html/estimation_8hpp_source.html | 4 +- .../reference/html/every__n_8hpp.html | 4 +- .../reference/html/every__n_8hpp_source.html | 4 +- .../html/exponential__filter_8hpp.html | 4 +- .../html/exponential__filter_8hpp_source.html | 4 +- .../generated/reference/html/files.html | 4 +- .../html/forward__like_8hpp_source.html | 4 +- .../generated/reference/html/functions.html | 4 +- .../reference/html/functions_all.html | 4 +- .../generated/reference/html/functions_b.html | 4 +- .../generated/reference/html/functions_c.html | 4 +- .../html/functions_class_members.html | 4 +- .../generated/reference/html/functions_d.html | 4 +- .../generated/reference/html/functions_e.html | 4 +- .../reference/html/functions_enum.html | 4 +- .../reference/html/functions_eval.html | 4 +- .../generated/reference/html/functions_f.html | 4 +- .../reference/html/functions_func.html | 4 +- .../reference/html/functions_func_b.html | 4 +- .../reference/html/functions_func_c.html | 4 +- .../reference/html/functions_func_d.html | 4 +- .../reference/html/functions_func_e.html | 4 +- .../reference/html/functions_func_f.html | 4 +- .../html/functions_func_functions.html | 4 +- .../reference/html/functions_func_h.html | 4 +- .../reference/html/functions_func_i.html | 4 +- .../reference/html/functions_func_l.html | 4 +- .../reference/html/functions_func_m.html | 4 +- .../reference/html/functions_func_n.html | 4 +- .../reference/html/functions_func_o.html | 4 +- .../reference/html/functions_func_p.html | 4 +- .../reference/html/functions_func_r.html | 4 +- .../reference/html/functions_func_s.html | 4 +- .../reference/html/functions_func_t.html | 4 +- .../reference/html/functions_func_u.html | 4 +- .../reference/html/functions_func_v.html | 4 +- .../reference/html/functions_func_w.html | 4 +- .../generated/reference/html/functions_h.html | 4 +- .../generated/reference/html/functions_i.html | 4 +- .../generated/reference/html/functions_k.html | 4 +- .../generated/reference/html/functions_l.html | 4 +- .../generated/reference/html/functions_m.html | 4 +- .../generated/reference/html/functions_n.html | 4 +- .../generated/reference/html/functions_o.html | 4 +- .../generated/reference/html/functions_p.html | 4 +- .../generated/reference/html/functions_r.html | 4 +- .../reference/html/functions_rela.html | 4 +- .../generated/reference/html/functions_s.html | 4 +- .../generated/reference/html/functions_t.html | 4 +- .../reference/html/functions_type.html | 4 +- .../generated/reference/html/functions_u.html | 4 +- .../generated/reference/html/functions_v.html | 4 +- .../reference/html/functions_vars.html | 4 +- .../generated/reference/html/functions_w.html | 4 +- .../generated/reference/html/functions_z.html | 4 +- .../reference/html/graph_legend.html | 4 +- .../generated/reference/html/hierarchy.html | 4 +- .../generated/reference/html/index.html | 4 +- .../html/indexing__iterator_8hpp.html | 4 +- .../html/indexing__iterator_8hpp_source.html | 4 +- .../generated/reference/html/inherits.html | 4 +- .../html/landmark__detection__types_8hpp.html | 4 +- ...andmark__detection__types_8hpp_source.html | 4 +- .../reference/html/landmark__map_8hpp.html | 4 +- .../html/landmark__map_8hpp_source.html | 4 +- .../html/landmark__sensor__model_8hpp.html | 4 +- .../landmark__sensor__model_8hpp_source.html | 4 +- .../reference/html/laser__scan_8hpp.html | 4 +- .../html/laser__scan_8hpp_source.html | 4 +- .../html/likelihood__field__model_8hpp.html | 4 +- .../likelihood__field__model_8hpp_source.html | 4 +- .../reference/html/linear__grid_8hpp.html | 4 +- .../html/linear__grid_8hpp_source.html | 4 +- .../generated/reference/html/motion_8hpp.html | 4 +- .../reference/html/motion_8hpp_source.html | 4 +- ...ltivariate__distribution__traits_8hpp.html | 4 +- ...ate__distribution__traits_8hpp_source.html | 4 +- ...ltivariate__normal__distribution_8hpp.html | 4 +- ...ate__normal__distribution_8hpp_source.html | 4 +- ...tivariate__uniform__distribution_8hpp.html | 4 +- ...te__uniform__distribution_8hpp_source.html | 4 +- .../reference/html/namespacebeluga.html | 4 +- .../reference/html/namespacemembers.html | 4 +- .../reference/html/namespacemembers_enum.html | 4 +- .../reference/html/namespacemembers_func.html | 4 +- .../namespacemembers_namespace_members.html | 4 +- .../reference/html/namespacemembers_type.html | 4 +- .../reference/html/namespacemembers_vars.html | 4 +- .../generated/reference/html/namespaces.html | 4 +- .../reference/html/namespaces_namespaces.html | 4 +- .../reference/html/ndt__cell_8hpp_source.html | 4 +- .../html/ndt__sensor__model_8hpp_source.html | 4 +- .../reference/html/normalize_8hpp.html | 4 +- .../reference/html/normalize_8hpp_source.html | 4 +- .../reference/html/occupancy__grid_8hpp.html | 4 +- .../html/occupancy__grid_8hpp_source.html | 4 +- .../omnidirectional__drive__model_8hpp.html | 4 +- ...directional__drive__model_8hpp_source.html | 4 +- .../html/on__effective__size__drop_8hpp.html | 4 +- ...on__effective__size__drop_8hpp_source.html | 4 +- .../reference/html/on__motion_8hpp.html | 4 +- .../html/on__motion_8hpp_source.html | 4 +- .../reference/html/overlay_8hpp.html | 4 +- .../reference/html/overlay_8hpp_source.html | 4 +- .../generated/reference/html/pages.html | 4 +- .../reference/html/particle__traits_8hpp.html | 4 +- .../html/particle__traits_8hpp_source.html | 4 +- .../reference/html/particles_8hpp.html | 4 +- .../reference/html/particles_8hpp_source.html | 4 +- .../reference/html/point__cloud_8hpp.html | 4 +- .../html/point__cloud_8hpp_source.html | 4 +- .../reference/html/policies_8hpp.html | 4 +- .../reference/html/policies_8hpp_source.html | 4 +- .../reference/html/policy_8hpp_source.html | 4 +- .../reference/html/primitives_8hpp.html | 4 +- .../html/primitives_8hpp_source.html | 4 +- .../reference/html/prologue_8hpp_source.html | 4 +- .../reference/html/propagate_8hpp.html | 4 +- .../reference/html/propagate_8hpp_source.html | 4 +- .../generated/reference/html/random_8hpp.html | 4 +- .../reference/html/random_8hpp_source.html | 4 +- .../html/random__intersperse_8hpp.html | 4 +- .../html/random__intersperse_8hpp_source.html | 4 +- .../reference/html/raycasting_8hpp.html | 4 +- .../html/raycasting_8hpp_source.html | 4 +- .../reference/html/regular__grid_8hpp.html | 4 +- .../html/regular__grid_8hpp_source.html | 4 +- .../reference/html/reweight_8hpp.html | 4 +- .../reference/html/reweight_8hpp_source.html | 4 +- .../generated/reference/html/sample_8hpp.html | 4 +- .../reference/html/sample_8hpp_source.html | 4 +- .../generated/reference/html/sensor_8hpp.html | 4 +- .../reference/html/sensor_8hpp_source.html | 4 +- .../reference/html/sophus__matchers_8hpp.html | 4 +- .../html/sophus__matchers_8hpp_source.html | 4 +- .../reference/html/sophus__printers_8hpp.html | 4 +- .../html/sophus__printers_8hpp_source.html | 4 +- .../html/sparse__point__cloud_8hpp.html | 4 +- .../sparse__point__cloud_8hpp_source.html | 4 +- .../html/sparse__value__grid_8hpp.html | 4 +- .../html/sparse__value__grid_8hpp_source.html | 4 +- .../reference/html/spatial__hash_8hpp.html | 4 +- .../html/spatial__hash_8hpp_source.html | 4 +- .../html/stationary__model_8hpp.html | 4 +- .../html/stationary__model_8hpp_source.html | 4 +- .../html/strongly__typed__numeric_8hpp.html | 4 +- .../strongly__typed__numeric_8hpp_source.html | 4 +- .../structbeluga_1_1AmclParams-members.html | 4 +- .../html/structbeluga_1_1AmclParams.html | 4 +- ...tructbeluga_1_1BeamModelParam-members.html | 4 +- .../html/structbeluga_1_1BeamModelParam.html | 4 +- ...ctbeluga_1_1BearingModelParam-members.html | 4 +- .../structbeluga_1_1BearingModelParam.html | 4 +- ...1Line_1_1iterator_1_1sentinel-members.html | 4 +- ...ham2i_1_1Line_1_1iterator_1_1sentinel.html | 4 +- ..._1DifferentialDriveModelParam-members.html | 4 +- ...beluga_1_1DifferentialDriveModelParam.html | 4 +- ...a_1_1LandmarkBearingDetection-members.html | 4 +- ...uctbeluga_1_1LandmarkBearingDetection.html | 4 +- ...tbeluga_1_1LandmarkModelParam-members.html | 4 +- .../structbeluga_1_1LandmarkModelParam.html | 4 +- ..._1_1LandmarkPositionDetection-members.html | 4 +- ...ctbeluga_1_1LandmarkPositionDetection.html | 4 +- ..._1_1LikelihoodFieldModelParam-members.html | 4 +- ...ctbeluga_1_1LikelihoodFieldModelParam.html | 4 +- .../html/structbeluga_1_1NDTCell-members.html | 4 +- .../html/structbeluga_1_1NDTCell.html | 4 +- ...structbeluga_1_1NDTModelParam-members.html | 4 +- .../html/structbeluga_1_1NDTModelParam.html | 4 +- ...mnidirectionalDriveModelParam-members.html | 4 +- ...uga_1_1OmnidirectionalDriveModelParam.html | 4 +- ...a_1_1ParticleClusterizerParam-members.html | 4 +- ...uctbeluga_1_1ParticleClusterizerParam.html | 4 +- ...tions_1_1detail_1_1assign__fn-members.html | 4 +- ...ga_1_1actions_1_1detail_1_1assign__fn.html | 4 +- ...detail_1_1propagate__base__fn-members.html | 4 +- ...ions_1_1detail_1_1propagate__base__fn.html | 4 +- ...ns_1_1detail_1_1propagate__fn-members.html | 4 +- ...1_1actions_1_1detail_1_1propagate__fn.html | 4 +- ...1detail_1_1reweight__base__fn-members.html | 4 +- ...tions_1_1detail_1_1reweight__base__fn.html | 4 +- ...ons_1_1detail_1_1reweight__fn-members.html | 4 +- ..._1_1actions_1_1detail_1_1reweight__fn.html | 4 +- ...erizer__detail_1_1ClusterCell-members.html | 4 +- ...1_1clusterizer__detail_1_1ClusterCell.html | 4 +- .../structbeluga_1_1common__tuple__type.html | 4 +- ...equence_3_01I_8_8_8_01_4_01_4-members.html | 4 +- ...index__sequence_3_01I_8_8_8_01_4_01_4.html | 4 +- .../structbeluga_1_1decay__tuple__like.html | 4 +- ...__t_3e4d97fe1d7c99b65756778192d27fe0a.html | 4 +- ...__t_3ee802d0cdb58b24411f7d7c175ec0540.html | 4 +- ...eluga_1_1detail_1_1CellHasher-members.html | 4 +- .../structbeluga_1_1detail_1_1CellHasher.html | 4 +- ..._1default__weighted__mean__fn-members.html | 4 +- ...detail_1_1default__weighted__mean__fn.html | 4 +- ...tail_1_1make__from__state__fn-members.html | 4 +- ...ga_1_1detail_1_1make__from__state__fn.html | 4 +- ...uctbeluga_1_1has__common__tuple__type.html | 4 +- ...__typ87797e8c898985f6b7323492f6753a83.html | 4 +- .../structbeluga_1_1has__single__element.html | 4 +- ...nable74bb370853b4969c8155f7c3b3c0dd64.html | 4 +- .../html/structbeluga_1_1is__tuple__like.html | 4 +- ...1_1multivariate__distribution__traits.html | 4 +- ...1_1is108ecb65e882d1fe2fc8b637bf46b34c.html | 4 +- ...1_1is14a8f4eaf89c5248a34012104455aef8.html | 4 +- ...1_1is7181fb16a0982e51ad03a3e45b8f3508.html | 4 +- ...1_1is72ce39e8fabe4098180348777969ad69.html | 4 +- ...1_1is7335572249efb8242f8ba7b16eaa7821.html | 4 +- ...1_1is789d3e70fc790b24150efc4abae3ee41.html | 4 +- ...1_1is8392abcab316d155fa772ee9c35c6c7b.html | 4 +- ...1_1iscd5fdf80de344db88138b1e69a58d58d.html | 4 +- ...1_1isce7b8f35afd806decbb40e1cebc9c5a6.html | 4 +- ...1_1ise7ac88d52952e6533296e162494fd0f1.html | 4 +- ...uctbeluga_1_1particle__traits-members.html | 4 +- .../structbeluga_1_1particle__traits.html | 4 +- ...ies_1_1detail_1_1every__n__fn-members.html | 4 +- ...1_1policies_1_1detail_1_1every__n__fn.html | 4 +- ...1_1detail_1_1every__n__policy-members.html | 4 +- ...olicies_1_1detail_1_1every__n__policy.html | 4 +- ...effective__size__drop__policy-members.html | 4 +- ..._1_1on__effective__size__drop__policy.html | 4 +- ...s_1_1detail_1_1on__motion__fn-members.html | 4 +- ...1policies_1_1detail_1_1on__motion__fn.html | 4 +- ...1detail_1_1on__motion__policy-members.html | 4 +- ...icies_1_1detail_1_1on__motion__policy.html | 4 +- ...1_1detail_1_1on__motion__policy__base.html | 4 +- ...s_1_1SE2_3_01Scalar_01_4_01_4-members.html | 4 +- ..._01Sophus_1_1SE2_3_01Scalar_01_4_01_4.html | 4 +- ...s_1_1SE3_3_01Scalar_01_4_01_4-members.html | 4 +- ..._01Sophus_1_1SE3_3_01Scalar_01_4_01_4.html | 4 +- .../html/structbeluga_1_1policy-members.html | 4 +- .../html/structbeluga_1_1policy.html | 4 +- .../structbeluga_1_1policy__base-members.html | 4 +- .../html/structbeluga_1_1policy__base.html | 4 +- .../html/structbeluga_1_1spatial__hash.html | 4 +- ...1_1state__detail_1_1state__fn-members.html | 4 +- ...tbeluga_1_1state__detail_1_1state__fn.html | 4 +- .../html/structbeluga_1_1tuple__index.html | 4 +- ...f__t_8b6a44f3e1dec7206a1db1c3f8ee23de.html | 4 +- ...__common__tuple__indirect__fn-members.html | 4 +- ...il_1_1as__common__tuple__indirect__fn.html | 4 +- ...il_1_1random__intersperse__fn-members.html | 4 +- ..._1_1detail_1_1random__intersperse__fn.html | 4 +- ..._1_1random__intersperse__view-members.html | 4 +- ..._1detail_1_1random__intersperse__view.html | 4 +- ...1_1detail_1_1sample__base__fn-members.html | 4 +- ..._1views_1_1detail_1_1sample__base__fn.html | 4 +- ...views_1_1detail_1_1sample__fn-members.html | 4 +- ...luga_1_1views_1_1detail_1_1sample__fn.html | 4 +- ...ews_1_1detail_1_1sample__view-members.html | 4 +- ...ga_1_1views_1_1detail_1_1sample__view.html | 4 +- ...etail_1_1take__while__kld__fn-members.html | 4 +- ...ews_1_1detail_1_1take__while__kld__fn.html | 4 +- ...1_1views_1_1detail_1_1zip__fn-members.html | 4 +- ...tbeluga_1_1views_1_1detail_1_1zip__fn.html | 4 +- ...1weight__detail_1_1weight__fn-members.html | 4 +- ...eluga_1_1weight__detail_1_1weight__fn.html | 4 +- ...1T_00_01PhantomType_01_4_01_4-members.html | 4 +- ...eric_3_01T_00_01PhantomType_01_4_01_4.html | 4 +- ...eric_3_01T_00_01PhantomType_01_4_01_4.html | 4 +- ...3_01T_00_01N_00_01F_01_4_01_4-members.html | 4 +- ...arArray_3_01T_00_01N_00_01F_01_4_01_4.html | 4 +- ...arArray_3_01T_00_01N_00_01F_01_4_01_4.html | 4 +- .../reference/html/take__evenly_8hpp.html | 4 +- .../html/take__evenly_8hpp_source.html | 4 +- .../reference/html/take__while__kld_8hpp.html | 4 +- .../html/take__while__kld_8hpp_source.html | 4 +- .../reference/html/testing_8hpp.html | 4 +- .../reference/html/testing_8hpp_source.html | 4 +- ...y__probability__estimator_8hpp_source.html | 4 +- .../reference/html/tuple__traits_8hpp.html | 4 +- .../html/tuple__traits_8hpp_source.html | 4 +- .../reference/html/tuple__vector_8hpp.html | 4 +- .../html/tuple__vector_8hpp_source.html | 4 +- .../reference/html/type__traits_8hpp.html | 4 +- .../html/type__traits_8hpp_source.html | 4 +- .../unscented__transform_8hpp_source.html | 4 +- .../reference/html/value__grid_8hpp.html | 4 +- .../html/value__grid_8hpp_source.html | 4 +- .../generated/reference/html/views_8hpp.html | 4 +- .../reference/html/views_8hpp_source.html | 4 +- .../generated/reference/html/zip_8hpp.html | 4 +- .../reference/html/zip_8hpp_source.html | 4 +- packages/beluga/docs/index.html | 4 +- .../reference/html/amcl__node_8hpp.html | 4 +- .../html/amcl__node_8hpp_source.html | 4 +- .../reference/html/amcl__nodelet_8hpp.html | 4 +- .../html/amcl__nodelet_8hpp_source.html | 4 +- .../generated/reference/html/annotated.html | 4 +- .../reference/html/annotated_classes.html | 4 +- ...classbeluga__amcl_1_1AmclNode-members.html | 4 +- .../html/classbeluga__amcl_1_1AmclNode.html | 4 +- ...ssbeluga__amcl_1_1AmclNodelet-members.html | 4 +- .../classbeluga__amcl_1_1AmclNodelet.html | 4 +- ...sbeluga__amcl_1_1BaseAMCLNode-members.html | 4 +- .../classbeluga__amcl_1_1BaseAMCLNode.html | 4 +- ...ssbeluga__amcl_1_1NdtAmclNode-members.html | 4 +- .../classbeluga__amcl_1_1NdtAmclNode.html | 4 +- ...beluga__amcl_1_1NdtAmclNode3D-members.html | 4 +- .../classbeluga__amcl_1_1NdtAmclNode3D.html | 4 +- .../generated/reference/html/classes.html | 4 +- .../dir_2fab1df6832ac5fd5ad2dca443afcb3d.html | 4 +- .../dir_d44c64559bbebec7f509842c48db8b23.html | 4 +- .../dir_f707c9b3691f4881939017e70f463c1f.html | 4 +- .../generated/reference/html/files.html | 4 +- .../generated/reference/html/functions.html | 4 +- .../html/functions_class_members.html | 4 +- .../reference/html/functions_func.html | 4 +- .../reference/html/functions_type.html | 4 +- .../reference/html/functions_vars.html | 4 +- .../reference/html/graph_legend.html | 4 +- .../generated/reference/html/hierarchy.html | 4 +- .../generated/reference/html/index.html | 4 +- .../generated/reference/html/inherits.html | 4 +- .../reference/html/ndt__amcl__node_8hpp.html | 4 +- .../html/ndt__amcl__node_8hpp_source.html | 4 +- .../html/ndt__amcl__node__3d_8hpp.html | 4 +- .../html/ndt__amcl__node__3d_8hpp_source.html | 4 +- .../generated/reference/html/pages.html | 4 +- .../html/ros2__common_8hpp_source.html | 4 +- packages/beluga_amcl/docs/index.html | 4 +- packages/beluga_amcl/docs/ros-reference.html | 4 +- packages/beluga_amcl/docs/ros2-reference.html | 4 +- .../generated/reference/html/amcl_8hpp.html | 4 +- .../reference/html/amcl_8hpp_source.html | 4 +- .../generated/reference/html/annotated.html | 4 +- .../reference/html/annotated_classes.html | 4 +- .../reference/html/beluga__ros_8hpp.html | 4 +- .../html/beluga__ros_8hpp_source.html | 4 +- .../generated/reference/html/citelist.html | 4 +- .../classbeluga__ros_1_1Amcl-members.html | 4 +- .../html/classbeluga__ros_1_1Amcl.html | 4 +- ...classbeluga__ros_1_1LaserScan-members.html | 4 +- .../html/classbeluga__ros_1_1LaserScan.html | 4 +- ...sbeluga__ros_1_1OccupancyGrid-members.html | 4 +- .../classbeluga__ros_1_1OccupancyGrid.html | 4 +- ...assbeluga__ros_1_1PointCloud3-members.html | 4 +- .../html/classbeluga__ros_1_1PointCloud3.html | 4 +- ...uga__ros_1_1SparsePointCloud3-members.html | 4 +- ...classbeluga__ros_1_1SparsePointCloud3.html | 4 +- .../generated/reference/html/classes.html | 4 +- .../dir_2fab1df6832ac5fd5ad2dca443afcb3d.html | 4 +- .../dir_724afa0a6cc96469a92ffa22ab208279.html | 4 +- .../dir_d44c64559bbebec7f509842c48db8b23.html | 4 +- .../generated/reference/html/files.html | 4 +- .../generated/reference/html/functions.html | 4 +- .../html/functions_class_members.html | 4 +- .../reference/html/functions_func.html | 4 +- .../reference/html/functions_type.html | 4 +- .../reference/html/functions_vars.html | 4 +- .../reference/html/graph_legend.html | 4 +- .../generated/reference/html/hierarchy.html | 4 +- .../generated/reference/html/index.html | 4 +- .../generated/reference/html/inherits.html | 4 +- .../reference/html/laser__scan_8hpp.html | 4 +- .../html/laser__scan_8hpp_source.html | 4 +- .../reference/html/messages_8hpp_source.html | 4 +- .../reference/html/namespaceSophus.html | 4 +- .../reference/html/namespacebeluga__ros.html | 4 +- .../html/namespacebeluga__ros_1_1msg.html | 4 +- .../reference/html/namespacemembers.html | 4 +- .../reference/html/namespacemembers_func.html | 4 +- .../namespacemembers_namespace_members.html | 4 +- .../generated/reference/html/namespaces.html | 4 +- .../reference/html/namespaces_namespaces.html | 4 +- .../reference/html/namespacetf2.html | 4 +- .../reference/html/occupancy__grid_8hpp.html | 4 +- .../html/occupancy__grid_8hpp_source.html | 4 +- .../generated/reference/html/pages.html | 4 +- .../reference/html/particle__cloud_8hpp.html | 4 +- .../html/particle__cloud_8hpp_source.html | 4 +- .../reference/html/point__cloud_8hpp.html | 4 +- .../html/point__cloud_8hpp_source.html | 4 +- .../html/sparse__point__cloud_8hpp.html | 4 +- .../sparse__point__cloud_8hpp_source.html | 4 +- ...ructbeluga__ros_1_1AmclParams-members.html | 4 +- .../html/structbeluga__ros_1_1AmclParams.html | 4 +- ...1OccupancyGrid_1_1ValueTraits-members.html | 4 +- ...__ros_1_1OccupancyGrid_1_1ValueTraits.html | 4 +- ...a__ros_1_1detail_1_1almost__equal__to.html | 4 +- ...s_1_1SE2_3_01Scalar_01_4_01_4-members.html | 4 +- ..._01Sophus_1_1SE2_3_01Scalar_01_4_01_4.html | 4 +- .../reference/html/tf2__eigen_8hpp.html | 4 +- .../html/tf2__eigen_8hpp_source.html | 4 +- .../reference/html/tf2__sophus_8hpp.html | 4 +- .../html/tf2__sophus_8hpp_source.html | 4 +- packages/beluga_ros/docs/index.html | 4 +- resources/bibliography.html | 4 +- roadmap/features.html | 4 +- roadmap/releases.html | 4 +- search.html | 4 +- searchindex.js | 2 +- tutorials/nav2-integration.html | 4 +- tutorials/particle-filtering.html | 4 +- 581 files changed, 2019 insertions(+), 1161 deletions(-) diff --git a/.buildinfo b/.buildinfo index 900ef4b0f..cd7c0a578 100644 --- a/.buildinfo +++ b/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file records the configuration used when building these files. When it is not found, a full rebuild will be done. -config: c491acfa49aa3eb40a4ba6dc2c3e9620 +config: 6ef5f10713d91accc92bc8e72b0efad5 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/_sources/guides/extending-beluga.md b/_sources/guides/extending-beluga.md index 231c09f60..61707efb6 100644 --- a/_sources/guides/extending-beluga.md +++ b/_sources/guides/extending-beluga.md @@ -1,3 +1,411 @@ # Extending Beluga -Coming soon! +## Prerequisites + +To effectively extend the Beluga library, you should be comfortable with: + +- C++ programming, especially template programming +- Probability and statistical estimation techniques + +:::{tip} +You may also want to take a look at the [design principles](../concepts/design-principles) and [architecture concepts](../concepts/architecture) that Beluga was built upon before jumping to code. +::: + +To set up a development environment, follow Beluga [installation instructions](../getting-started/installation) first. In particular, Beluga [development workflows](https://github.com/Ekumen-OS/beluga/blob/main/DEVELOPING.md) may come in handy when contributing back to the library. + +## Implementing Motion Models + +Motion models in Beluga define how a robot’s state evolves over time based on control inputs. These models are crucial for state prediction, allowing the particle filter to estimate the robot's future position as it moves through its environment. For a deeper understanding of these, you may revisit Beluga's [key concepts](../concepts/key-concepts). + +### Key Considerations + +When implementing a motion model, you need to ensure it meets a number of requirements: +- It must define a `state_type` that represents the particle's state, often as a pose using structures like `Sophus::SE2d`. +- It must specify the `control_type` it accepts, typically representing velocities or other actions influencing the state. +- It must be callable that accepts a control action and returns a function that predicts the next state based on a given control input. This returned function must satisfy the [`StateSamplingFunction` requirements](https://ekumen-os.github.io/beluga/packages/beluga/docs/_doxygen/generated/reference/html/MotionModelPage.html). + +### Example Code + +Below is a sample implementation of a velocity-based motion model that predicts the robot’s next state based on linear and angular velocities, with noise to account for uncertainty: + +```cpp +#include +#include +#include +#include + +namespace beluga { + +// Example of a simple velocity-based motion model using Sophus and Eigen. +class VelocityMotionModel { + public: + using state_type = Sophus::SE2d; // Represents the particle's state as SE(2) pose. + using control_type = Eigen::Vector2d; // Represents control input: [linear_velocity, angular_velocity]. + + // Constructor to initialize noise parameters for linear and angular velocities. + VelocityMotionModel(double linear_stddev, double angular_stddev) + : linear_noise_{0.0, linear_stddev}, angular_noise_{0.0, angular_stddev} {} + + // Callable function to produce a StateSamplingFunction based on a control input. + auto operator()(const control_type& control) const { + // Returns a lambda satisfying the StateSamplingFunction requirements. + return [this, control](const state_type& current_state, auto& rng) -> state_type { + // Sample noisy velocities using Gaussian distributions. + const double noisy_linear_velocity = control[0] + linear_noise_(rng); + const double noisy_angular_velocity = control[1] + angular_noise_(rng); + + // Predict the next state using noisy control and SE(2) transformations. + Sophus::SE2d next_state = current_state; + next_state.translation() += Eigen::Vector2d( + noisy_linear_velocity * std::cos(current_state.angle()), + noisy_linear_velocity * std::sin(current_state.angle())); + next_state.so2() *= Sophus::SO2d::exp(noisy_angular_velocity); + + return next_state; + }; + } + + private: + std::normal_distribution linear_noise_; // Noise for linear velocity. + std::normal_distribution angular_noise_; // Noise for angular velocity. +}; + +} // namespace beluga +``` + +Let's break down the code to highlight the implementation techniques: + +```cpp +#include +#include +#include +#include +``` + +We start by including the necessary headers: +- `` for pseudo-random noise generation. +- `Eigen` for vector and matrix operations. +- `Sophus` for handling Lie group elements like SE(2). + +```cpp +namespace beluga { + +// Example of a simple velocity-based motion model using Sophus and Eigen. +class VelocityMotionModel { + public: + using state_type = Sophus::SE2d; // Represents the particle's state as SE(2) pose. + using control_type = Eigen::Vector2d; // Represents control input: [linear_velocity, angular_velocity]. +``` + +Here, the `VelocityMotionModel` class is defined. `state_type` is specified as `Sophus::SE2d`, a common representation of the robot’s 2D pose, and `control_type` is defined as `Eigen::Vector2d`, consisting of linear and angular velocities. + +```cpp + // Constructor to initialize noise parameters for linear and angular velocities. + VelocityMotionModel(double linear_stddev, double angular_stddev) + : linear_noise_{0.0, linear_stddev}, angular_noise_{0.0, angular_stddev} {} +``` + +The constructor initializes the model with standard deviations for the linear and angular velocity noise. `linear_noise_` and `angular_noise_` are normal distributions centered at `0.0` with standard deviations specified by parameters. + +```cpp + // Callable function to produce a StateSamplingFunction based on a control input. + auto operator()(const control_type& control) const { + // Returns a lambda satisfying the StateSamplingFunction requirements. + return [this, control](const state_type& current_state, auto& rng) -> state_type { + // Sample noisy velocities using Gaussian distributions. + double noisy_linear_velocity = control[0] + sample_noise(linear_noise_, rng); + double noisy_angular_velocity = control[1] + sample_noise(angular_noise_, rng); +``` + +The `operator()` function returns a lambda. This lambda predicts the next state given a current state and a random number generator. + +```cpp + // Predict the next state using noisy control and SE(2) transformations. + Sophus::SE2d next_state = current_state; + next_state.translation() += Eigen::Vector2d( + noisy_linear_velocity * std::cos(current_state.angle()), + noisy_linear_velocity * std::sin(current_state.angle())); + next_state.so2() *= Sophus::SO2d::exp(noisy_angular_velocity); + + return next_state; + }; + } +``` +The translation is updated based on the noisy linear velocity and the current orientation. The orientation (`so2`) is updated using the exponential map to handle the angular update. + +### Next Steps + +For a real-world implementation, see [`beluga::OmnidirectionalDriveModel`](https://ekumen-os.github.io/beluga/packages/beluga/docs/_doxygen/generated/reference/html/classbeluga_1_1OmnidirectionalDriveModel.html)'s' implementation. + +## Implementing Sensor Models + +Sensor models in Beluga are responsible for assessing how likely it is that a given particle's state matches observed sensor data. By assigning weights to particles based on sensor data, sensor models play a crucial role in filtering out unlikely states and refining the robot's estimated position. For a deeper understanding of these, you may revisit Beluga's [key concepts](../concepts/key-concepts) too. + +### Key Considerations + +When implementing a sensor model in Beluga, you need to ensure it meets a number of requirements: + +- It must define a `state_type` that represents the particle's state, often as a pose using structures like `Sophus::SE2d`. +- It must specify a `weight_type`, typically a numerical type, representing the weight calculated for each particle. +- It must list a `measurement_type` as the format of the sensor data, and for this you may want to look at [sensor data abstractions]()https://ekumen-os.github.io/beluga/packages/beluga/docs/_doxygen/generated/reference/html/dir_876c246173b27422a95ea0c3c06ba40d.html). +- It must be a callable (`operator()`) that accepts a sensor measurement and returns a function compliant with the [`StateWeightingFunction` requirements](https://ekumen-os.github.io/beluga/packages/beluga/docs/_doxygen/generated/reference/html/SensorModelPage.html). This function calculates the weight of a particle’s state given the measurement. + +### Example Code + +Below is an example of a simple range sensor model. This model calculates the likelihood of each particle’s state based on sensor measurements of distance from the origin: + +```cpp +#include +#include +#include +#include +#include + +namespace beluga { + +// Example of a simple range-based sensor model. +class RangeSensorModel { + public: + using state_type = Sophus::SE2d; // Represents the particle's state as SE(2) pose. + using weight_type = double; // Particle weight, based on measurement likelihood. + using measurement_type = double; // Represents a distance measurement. + + // Constructor to initialize sensor noise parameters. + RangeSensorModel(double sensor_stddev) + : sensor_noise_{0.0, sensor_stddev} {} + + // Callable function to produce a StateWeightingFunction based on a measurement. + auto operator()(measurement_type measurement) const { + // Returns a lambda satisfying the StateWeightingFunction requirements. + return [this, measurement](const state_type& current_state) -> weight_type { + // Calculate the expected sensor position based on the robot's pose. + double expected_measurement = current_state.translation().norm(); + + // Calculate weight based on a Gaussian kernel. + return std::exp(-0.5 * std::pow((expected_measurement - measurement) / sensor_noise_.stddev(), 2)); + }; + } + + private: + std::normal_distribution sensor_noise_; // Noise for sensor measurement. +}; + +} // namespace beluga +``` + +Let's break down the code to highlight the implementation techniques: + +```cpp +#include +#include +#include +#include +#include +``` + +The required headers are included: +- `` for mathematical functions. +- `` for handling noise in measurements. +- `Eigen` and `Sophus` for linear algebra and pose handling. + +```cpp +namespace beluga { + +// Example of a simple range-based sensor model. +class RangeSensorModel { + public: + using state_type = Sophus::SE2d; // Represents the particle's state as SE(2) pose. + using weight_type = double; // Particle weight, based on measurement likelihood. + using measurement_type = double; // Represents a distance measurement. +``` + +Here, the `RangeSensorModel` class is defined. `state_type` is specified as `Sophus::SE2d`, representing the robot’s pose; `weight_type` is a `double`, indicating the computed weight for each particle; and `measurement_type` is `double`, representing the sensor measurement. + +```cpp + // Constructor to initialize sensor noise parameters. + RangeSensorModel(double sensor_stddev) + : sensor_noise_{0.0, sensor_stddev} {} +``` +The constructor initializes a normal distribution, `sensor_noise_`, to simulate measurement noise. This distribution is centered at `0.0` with a standard deviation defined by `sensor_stddev`. + +```cpp + // Callable function to produce a StateWeightingFunction based on a measurement. + auto operator()(measurement_type measurement) const { + // Returns a lambda satisfying the StateWeightingFunction requirements. + return [this, measurement](const state_type& current_state) -> weight_type { + // Calculate the expected sensor position based on the robot's pose. + double expected_measurement = current_state.translation().norm(); +``` +The `operator()` function returns a lambda. This lambda calculates the weight of a particle based on the error between the expected and actual measurements. + +```cpp + // Compute the distance between the expected and actual measurement. + double distance = (expected_measurement - measurement).norm(); + + // Calculate weight based on Gaussian likelihood. + double weight = std::exp(-0.5 * std::pow(distance / sensor_noise_.stddev(), 2)); + + return weight; + }; + } +``` +A Gaussian kernel is used to compute the weight, assigning higher values to states closer to the measurement. + +### Next steps + +For a real-world implementation, see [`beluga::LikelihoodFieldModel`](https://ekumen-os.github.io/beluga/packages/beluga/docs/_doxygen/generated/reference/html/classbeluga_1_1LikelihoodFieldModel.html)'s' implementation. + +## Implementing Estimation Algorithms + +Estimation algorithms in Beluga are responsible for synthesizing the most likely estimate of a robot's state from a set of weighted particles. Statistical location and dispersion measures such as sampled means and variances are typical. + +Estimation algorithms are implemented as functions that take a particle range as input. In this library, instead of implementing them as free functions, we implement them as [_niebloids_](https://en.cppreference.com/w/Template:cpp/ranges/niebloid) (global instances of function objects that disable ADL). While this section focuses on estimation algorithms, it is worth noting that niebloids are used more broadly across the library as a sane approach to customization. + +### Key Considerations +To build a robust estimation algorithm that integrates well with Beluga: + +- The algorithm must work with a set of particle states and their corresponding weights, which encode each particle's probability during estimation. +- The algorithm should be implemented using a niebloid construct. + +### Example Code + +The following example demonstrates how to implement a scalar median estimation algorithm using a niebloid: + +```cpp +#include +#include +#include +#include +#include +#include +#include + +namespace beluga { + +namespace detail { + +// Example of a niebloid for calculating the median of weighted scalar values. +struct weighted_median_fn { + // Operator to compute the median for scalar values. + template + auto operator()(Values&& values, Weights&& weights, Projection projection = {}) const { + using WeightType = std::decay_t>; + using ValueType = std::decay_t>>; + static_assert(ranges::input_range); + static_assert(ranges::input_range); + + // Zip values with weights and sort them based on the projected value. + std::vector> sorted_pairs; + for (auto&& [value, weight] : ranges::views::zip(values, weights)) { + sorted_pairs.emplace_back(projection(value), weight); + } + ranges::sort(sorted_pairs, std::less{}, [](const auto& pair) { return pair.first; }); + + // Find the median based on cumulative weights. + auto cumulative_weight = WeightType{0}; + auto half_total_weight = 0.5 * ranges::accumulate( + sorted_pairs, WeightType{0}, std::plus{}, + [](const auto& pair) { return pair.second; }); + + for (const auto& [projected_value, weight] : sorted_pairs) { + cumulative_weight += weight; + if (cumulative_weight >= half_total_weight) { + return projected_value; + } + } + + // Fallback: return the last value in case no median found (should not happen with correct weights). + return sorted_pairs.back().first; + } +}; + +} // namespace detail + +// A niebloid instance to compute the median. +inline constexpr detail::weighted_median_fn weighted_median; + +} // namespace beluga +``` + +Let's break down the code to highlight the implementation techniques: + +```cpp +#include +#include +#include +#include +#include +#include +``` + +We begin by including the required headers: +- `` and `` for sorting and accumulating operations. +- `` for zipping values and weights, sorting values, and summing weights. +- `` for storing the sorted pairs of values and weights. + +```cpp +namespace beluga { +namespace detail { +``` +The implementation is structured within the `beluga` and `detail` namespaces, consistent with the library’s organization. + +```cpp +// Example of a niebloid for calculating the median of weighted scalar values. +struct weighted_median_fn { + // Operator to compute the median for scalar values. + template + auto operator()(Values&& values, Weights&& weights, Projection projection = {}) const { + using WeightType = std::decay_t>; + using ValueType = std::decay_t>>; + static_assert(ranges::input_range); + static_assert(ranges::input_range); +``` +The `weighted_median_fn` struct is defined, focusing on the scalar median computation. The `operator()` function handles ranges of values and weights. Note the optional `projection`, allowing value transformation before processing. The result type of the projection may differ from the original values, requiring the use of `std::decay_t` to manage type adjustments. + +```cpp + // Zip values with weights and sort them based on the projected value. + std::vector> sorted_pairs; + for (auto&& [value, weight] : ranges::views::zip(values, weights)) { + sorted_pairs.emplace_back(projection(value), weight); + } + ranges::sort(sorted_pairs, std::less{}, [](const auto& pair) { return pair.first; }); +``` +Values and weights are zipped together. The projection is applied to each value, and they are stored with their weights in `sorted_pairs`. These pairs are then sorted based on the projected value using `std::sort`. + +```cpp + auto cumulative_weight = WeightType{0}; + auto half_total_weight = 0.5 * ranges::accumulate( + sorted_pairs, WeightType{0}, std::plus{}, + [](const auto& pair) { return pair.second; }); + + for (const auto& [projected_value, weight] : sorted_pairs) { + cumulative_weight += weight; + if (cumulative_weight >= half_total_weight) { + return projected_value; + } + } +``` +The median is calculated using cumulative weights. The algorithm iterates through the sorted pairs, summing the weights. When the cumulative weight exceeds half the total, the median value is identified. + +```cpp + // Fallback: return the last value in case no median found (should not happen with correct weights). + return sorted_pairs.back().first; + } +}; +``` +A fallback mechanism is provided for when and if no median is found due to an anomaly in the weights. + +```cpp +} // namespace detail + +// A niebloid instance to compute the median. +inline constexpr detail::weighted_mean_fn weighted_median; + +} // namespace beluga +``` + +The `weighted_median` niebloid is instantiated. + +### Next Steps + +For a real-world implementation, see [`beluga/algorithm/estimation.hpp`](https://ekumen-os.github.io/beluga/packages/beluga/docs/_doxygen/generated/reference/html/estimation_8hpp.html) content. diff --git a/about/contact.html b/about/contact.html index f42d848b8..396c4b895 100644 --- a/about/contact.html +++ b/about/contact.html @@ -59,7 +59,7 @@ - + @@ -421,7 +421,7 @@

Contact#< diff --git a/about/rationale.html b/about/rationale.html index aa85cdf1c..4e6e6a0b4 100644 --- a/about/rationale.html +++ b/about/rationale.html @@ -60,7 +60,7 @@ - + @@ -431,7 +431,7 @@

Rationale

- Last updated on 'Wed Oct 23 10:28:02 2024 -0300, 655b0d6'. + Last updated on 'Fri Oct 25 11:00:39 2024 -0300, 08e682f'.

diff --git a/concepts/architecture.html b/concepts/architecture.html index 03368d077..594db48c6 100644 --- a/concepts/architecture.html +++ b/concepts/architecture.html @@ -60,7 +60,7 @@ - + @@ -477,7 +477,7 @@

CRTP-based data adapters

- Last updated on 'Wed Oct 23 10:28:02 2024 -0300, 655b0d6'. + Last updated on 'Fri Oct 25 11:00:39 2024 -0300, 08e682f'.

diff --git a/concepts/design-principles.html b/concepts/design-principles.html index 54dff330c..25f43b84f 100644 --- a/concepts/design-principles.html +++ b/concepts/design-principles.html @@ -60,7 +60,7 @@ - + @@ -500,7 +500,7 @@

Memory aware

- Last updated on 'Wed Oct 23 10:28:02 2024 -0300, 655b0d6'. + Last updated on 'Fri Oct 25 11:00:39 2024 -0300, 08e682f'.

diff --git a/concepts/key-concepts.html b/concepts/key-concepts.html index c2796338e..60a5ed0f5 100644 --- a/concepts/key-concepts.html +++ b/concepts/key-concepts.html @@ -62,7 +62,7 @@ - + @@ -507,7 +507,7 @@

Monte Carlo Localization

- Last updated on 'Wed Oct 23 10:28:02 2024 -0300, 655b0d6'. + Last updated on 'Fri Oct 25 11:00:39 2024 -0300, 08e682f'.

diff --git a/genindex.html b/genindex.html index a15809951..996c5e2af 100644 --- a/genindex.html +++ b/genindex.html @@ -57,7 +57,7 @@ - + @@ -364,7 +364,7 @@

Index

diff --git a/getting-started/installation.html b/getting-started/installation.html index bd5eeee78..5e2aa3cf0 100644 --- a/getting-started/installation.html +++ b/getting-started/installation.html @@ -60,7 +60,7 @@ - + @@ -587,7 +587,7 @@

Build and source workspace

- Last updated on 'Wed Oct 23 10:28:02 2024 -0300, 655b0d6'. + Last updated on 'Fri Oct 25 11:00:39 2024 -0300, 08e682f'.

diff --git a/getting-started/quickstart.html b/getting-started/quickstart.html index 04a5c0889..4ff2aae24 100644 --- a/getting-started/quickstart.html +++ b/getting-started/quickstart.html @@ -60,7 +60,7 @@ - + @@ -605,7 +605,7 @@

Visualize Beluga output

- Last updated on 'Wed Oct 23 10:28:02 2024 -0300, 655b0d6'. + Last updated on 'Fri Oct 25 11:00:39 2024 -0300, 08e682f'.

diff --git a/guides/benchmarking-beluga.html b/guides/benchmarking-beluga.html index cbbb89b6c..c38810fbb 100644 --- a/guides/benchmarking-beluga.html +++ b/guides/benchmarking-beluga.html @@ -60,7 +60,7 @@ - + @@ -429,7 +429,7 @@

Benchmarking Beluga

- Last updated on 'Wed Oct 23 10:28:02 2024 -0300, 655b0d6'. + Last updated on 'Fri Oct 25 11:00:39 2024 -0300, 08e682f'.

diff --git a/guides/extending-beluga.html b/guides/extending-beluga.html index 09189106d..ec8e74783 100644 --- a/guides/extending-beluga.html +++ b/guides/extending-beluga.html @@ -60,7 +60,7 @@ - + @@ -335,7 +335,9 @@ - + @@ -351,6 +353,32 @@

Extending Beluga

@@ -362,7 +390,395 @@

Extending Beluga

Extending Beluga#

-

Coming soon!

+
+

Prerequisites#

+

To effectively extend the Beluga library, you should be comfortable with:

+
    +
  • C++ programming, especially template programming

  • +
  • Probability and statistical estimation techniques

  • +
+
+

Tip

+

You may also want to take a look at the design principles and architecture concepts that Beluga was built upon before jumping to code.

+
+

To set up a development environment, follow Beluga installation instructions first. In particular, Beluga development workflows may come in handy when contributing back to the library.

+
+
+

Implementing Motion Models#

+

Motion models in Beluga define how a robot’s state evolves over time based on control inputs. These models are crucial for state prediction, allowing the particle filter to estimate the robot’s future position as it moves through its environment. For a deeper understanding of these, you may revisit Beluga’s key concepts.

+
+

Key Considerations#

+

When implementing a motion model, you need to ensure it meets a number of requirements:

+
    +
  • It must define a state_type that represents the particle’s state, often as a pose using structures like Sophus::SE2d.

  • +
  • It must specify the control_type it accepts, typically representing velocities or other actions influencing the state.

  • +
  • It must be callable that accepts a control action and returns a function that predicts the next state based on a given control input. This returned function must satisfy the StateSamplingFunction requirements.

  • +
+
+
+

Example Code#

+

Below is a sample implementation of a velocity-based motion model that predicts the robot’s next state based on linear and angular velocities, with noise to account for uncertainty:

+
#include <random>
+#include <beluga/motion.hpp>
+#include <Eigen/Dense>
+#include <sophus/se2.hpp>
+
+namespace beluga {
+
+// Example of a simple velocity-based motion model using Sophus and Eigen.
+class VelocityMotionModel {
+ public:
+  using state_type = Sophus::SE2d;       // Represents the particle's state as SE(2) pose.
+  using control_type = Eigen::Vector2d;  // Represents control input: [linear_velocity, angular_velocity].
+
+  // Constructor to initialize noise parameters for linear and angular velocities.
+  VelocityMotionModel(double linear_stddev, double angular_stddev)
+      : linear_noise_{0.0, linear_stddev}, angular_noise_{0.0, angular_stddev} {}
+
+  // Callable function to produce a StateSamplingFunction based on a control input.
+  auto operator()(const control_type& control) const {
+    // Returns a lambda satisfying the StateSamplingFunction requirements.
+    return [this, control](const state_type& current_state, auto& rng) -> state_type {
+      // Sample noisy velocities using Gaussian distributions.
+      const double noisy_linear_velocity = control[0] + linear_noise_(rng);
+      const double noisy_angular_velocity = control[1] + angular_noise_(rng);
+
+      // Predict the next state using noisy control and SE(2) transformations.
+      Sophus::SE2d next_state = current_state;
+      next_state.translation() += Eigen::Vector2d(
+          noisy_linear_velocity * std::cos(current_state.angle()),
+          noisy_linear_velocity * std::sin(current_state.angle()));
+      next_state.so2() *= Sophus::SO2d::exp(noisy_angular_velocity);
+
+      return next_state;
+    };
+  }
+
+ private:
+  std::normal_distribution<double> linear_noise_;  // Noise for linear velocity.
+  std::normal_distribution<double> angular_noise_;  // Noise for angular velocity.
+};
+
+}  // namespace beluga
+
+
+

Let’s break down the code to highlight the implementation techniques:

+
#include <random>
+#include <beluga/motion.hpp>
+#include <Eigen/Dense>
+#include <sophus/se2.hpp>
+
+
+

We start by including the necessary headers:

+
    +
  • <random> for pseudo-random noise generation.

  • +
  • Eigen for vector and matrix operations.

  • +
  • Sophus for handling Lie group elements like SE(2).

  • +
+
namespace beluga {
+
+// Example of a simple velocity-based motion model using Sophus and Eigen.
+class VelocityMotionModel {
+ public:
+  using state_type = Sophus::SE2d;       // Represents the particle's state as SE(2) pose.
+  using control_type = Eigen::Vector2d;  // Represents control input: [linear_velocity, angular_velocity].
+
+
+

Here, the VelocityMotionModel class is defined. state_type is specified as Sophus::SE2d, a common representation of the robot’s 2D pose, and control_type is defined as Eigen::Vector2d, consisting of linear and angular velocities.

+
  // Constructor to initialize noise parameters for linear and angular velocities.
+  VelocityMotionModel(double linear_stddev, double angular_stddev)
+      : linear_noise_{0.0, linear_stddev}, angular_noise_{0.0, angular_stddev} {}
+
+
+

The constructor initializes the model with standard deviations for the linear and angular velocity noise. linear_noise_ and angular_noise_ are normal distributions centered at 0.0 with standard deviations specified by parameters.

+
  // Callable function to produce a StateSamplingFunction based on a control input.
+  auto operator()(const control_type& control) const {
+    // Returns a lambda satisfying the StateSamplingFunction requirements.
+    return [this, control](const state_type& current_state, auto& rng) -> state_type {
+      // Sample noisy velocities using Gaussian distributions.
+      double noisy_linear_velocity = control[0] + sample_noise(linear_noise_, rng);
+      double noisy_angular_velocity = control[1] + sample_noise(angular_noise_, rng);
+
+
+

The operator() function returns a lambda. This lambda predicts the next state given a current state and a random number generator.

+
      // Predict the next state using noisy control and SE(2) transformations.
+      Sophus::SE2d next_state = current_state;
+      next_state.translation() += Eigen::Vector2d(
+          noisy_linear_velocity * std::cos(current_state.angle()),
+          noisy_linear_velocity * std::sin(current_state.angle()));
+      next_state.so2() *= Sophus::SO2d::exp(noisy_angular_velocity);
+
+      return next_state;
+    };
+  }
+
+
+

The translation is updated based on the noisy linear velocity and the current orientation. The orientation (so2) is updated using the exponential map to handle the angular update.

+
+
+

Next Steps#

+

For a real-world implementation, see beluga::OmnidirectionalDriveModel’s’ implementation.

+
+
+
+

Implementing Sensor Models#

+

Sensor models in Beluga are responsible for assessing how likely it is that a given particle’s state matches observed sensor data. By assigning weights to particles based on sensor data, sensor models play a crucial role in filtering out unlikely states and refining the robot’s estimated position. For a deeper understanding of these, you may revisit Beluga’s key concepts too.

+
+

Key Considerations#

+

When implementing a sensor model in Beluga, you need to ensure it meets a number of requirements:

+
    +
  • It must define a state_type that represents the particle’s state, often as a pose using structures like Sophus::SE2d.

  • +
  • It must specify a weight_type, typically a numerical type, representing the weight calculated for each particle.

  • +
  • It must list a measurement_type as the format of the sensor data, and for this you may want to look at sensor data abstractionshttps://ekumen-os.github.io/beluga/packages/beluga/docs/_doxygen/generated/reference/html/dir_876c246173b27422a95ea0c3c06ba40d.html).

  • +
  • It must be a callable (operator()) that accepts a sensor measurement and returns a function compliant with the StateWeightingFunction requirements. This function calculates the weight of a particle’s state given the measurement.

  • +
+
+
+

Example Code#

+

Below is an example of a simple range sensor model. This model calculates the likelihood of each particle’s state based on sensor measurements of distance from the origin:

+
#include <cmath>
+#include <random>
+#include <beluga/sensor.hpp>
+#include <Eigen/Dense>
+#include <sophus/se2.hpp>
+
+namespace beluga {
+
+// Example of a simple range-based sensor model.
+class RangeSensorModel {
+ public:
+  using state_type = Sophus::SE2d;        // Represents the particle's state as SE(2) pose.
+  using weight_type = double;             // Particle weight, based on measurement likelihood.
+  using measurement_type = double;        // Represents a distance measurement.
+
+  // Constructor to initialize sensor noise parameters.
+  RangeSensorModel(double sensor_stddev)
+      : sensor_noise_{0.0, sensor_stddev} {}
+
+  // Callable function to produce a StateWeightingFunction based on a measurement.
+  auto operator()(measurement_type measurement) const {
+    // Returns a lambda satisfying the StateWeightingFunction requirements.
+    return [this, measurement](const state_type& current_state) -> weight_type {
+      // Calculate the expected sensor position based on the robot's pose.
+      double expected_measurement = current_state.translation().norm();
+
+      // Calculate weight based on a Gaussian kernel.
+      return std::exp(-0.5 * std::pow((expected_measurement - measurement) / sensor_noise_.stddev(), 2));
+    };
+  }
+
+ private:
+  std::normal_distribution<double> sensor_noise_;  // Noise for sensor measurement.
+};
+
+}  // namespace beluga
+
+
+

Let’s break down the code to highlight the implementation techniques:

+
#include <cmath>
+#include <random>
+#include <beluga/sensor.hpp>
+#include <Eigen/Dense>
+#include <sophus/se2.hpp>
+
+
+

The required headers are included:

+
    +
  • <cmath> for mathematical functions.

  • +
  • <random> for handling noise in measurements.

  • +
  • Eigen and Sophus for linear algebra and pose handling.

  • +
+
namespace beluga {
+
+// Example of a simple range-based sensor model.
+class RangeSensorModel {
+ public:
+  using state_type = Sophus::SE2d;        // Represents the particle's state as SE(2) pose.
+  using weight_type = double;             // Particle weight, based on measurement likelihood.
+  using measurement_type = double;        // Represents a distance measurement.
+
+
+

Here, the RangeSensorModel class is defined. state_type is specified as Sophus::SE2d, representing the robot’s pose; weight_type is a double, indicating the computed weight for each particle; and measurement_type is double, representing the sensor measurement.

+
  // Constructor to initialize sensor noise parameters.
+  RangeSensorModel(double sensor_stddev)
+      : sensor_noise_{0.0, sensor_stddev} {}
+
+
+

The constructor initializes a normal distribution, sensor_noise_, to simulate measurement noise. This distribution is centered at 0.0 with a standard deviation defined by sensor_stddev.

+
  // Callable function to produce a StateWeightingFunction based on a measurement.
+  auto operator()(measurement_type measurement) const {
+    // Returns a lambda satisfying the StateWeightingFunction requirements.
+    return [this, measurement](const state_type& current_state) -> weight_type {
+      // Calculate the expected sensor position based on the robot's pose.
+      double expected_measurement = current_state.translation().norm();
+
+
+

The operator() function returns a lambda. This lambda calculates the weight of a particle based on the error between the expected and actual measurements.

+
      // Compute the distance between the expected and actual measurement.
+      double distance = (expected_measurement - measurement).norm();
+
+      // Calculate weight based on Gaussian likelihood.
+      double weight = std::exp(-0.5 * std::pow(distance / sensor_noise_.stddev(), 2));
+
+      return weight;
+    };
+  }
+
+
+

A Gaussian kernel is used to compute the weight, assigning higher values to states closer to the measurement.

+
+
+

Next steps#

+

For a real-world implementation, see beluga::LikelihoodFieldModel’s’ implementation.

+
+
+
+

Implementing Estimation Algorithms#

+

Estimation algorithms in Beluga are responsible for synthesizing the most likely estimate of a robot’s state from a set of weighted particles. Statistical location and dispersion measures such as sampled means and variances are typical.

+

Estimation algorithms are implemented as functions that take a particle range as input. In this library, instead of implementing them as free functions, we implement them as niebloids (global instances of function objects that disable ADL). While this section focuses on estimation algorithms, it is worth noting that niebloids are used more broadly across the library as a sane approach to customization.

+
+

Key Considerations#

+

To build a robust estimation algorithm that integrates well with Beluga:

+
    +
  • The algorithm must work with a set of particle states and their corresponding weights, which encode each particle’s probability during estimation.

  • +
  • The algorithm should be implemented using a niebloid construct.

  • +
+
+
+

Example Code#

+

The following example demonstrates how to implement a scalar median estimation algorithm using a niebloid:

+
#include <algorithm>
+#include <numeric>
+#include <functional>
+#include <range/v3/algorithms/sort.hpp>
+#include <range/v3/numeric/accumulate.hpp>
+#include <range/v3/view/zip.hpp>
+#include <vector>
+
+namespace beluga {
+
+namespace detail {
+
+// Example of a niebloid for calculating the median of weighted scalar values.
+struct weighted_median_fn {
+  // Operator to compute the median for scalar values.
+  template <class Values, class Weights, class Projection = ranges::identity>
+  auto operator()(Values&& values, Weights&& weights, Projection projection = {}) const {
+    using WeightType = std::decay_t<ranges::range_value_t<Weights>>;
+    using ValueType = std::decay_t<std::invoke_result_t<Projection, ranges::range_value_t<Values>>>;
+    static_assert(ranges::input_range<Values>);
+    static_assert(ranges::input_range<Weights>);
+
+    // Zip values with weights and sort them based on the projected value.
+    std::vector<std::pair<ValueType, WeightType>> sorted_pairs;
+    for (auto&& [value, weight] : ranges::views::zip(values, weights)) {
+      sorted_pairs.emplace_back(projection(value), weight);
+    }
+    ranges::sort(sorted_pairs, std::less{}, [](const auto& pair) { return pair.first; });
+
+    // Find the median based on cumulative weights.
+    auto cumulative_weight = WeightType{0};
+    auto half_total_weight = 0.5 * ranges::accumulate(
+      sorted_pairs, WeightType{0}, std::plus{},
+      [](const auto& pair) { return pair.second; });
+
+    for (const auto& [projected_value, weight] : sorted_pairs) {
+      cumulative_weight += weight;
+      if (cumulative_weight >= half_total_weight) {
+        return projected_value;
+      }
+    }
+
+    // Fallback: return the last value in case no median found (should not happen with correct weights).
+    return sorted_pairs.back().first;
+  }
+};
+
+}  // namespace detail
+
+// A niebloid instance to compute the median.
+inline constexpr detail::weighted_median_fn weighted_median;
+
+}  // namespace beluga
+
+
+

Let’s break down the code to highlight the implementation techniques:

+
#include <algorithm>
+#include <numeric>
+#include <range/v3/algorithms/sort.hpp>
+#include <range/v3/numeric/accumulate.hpp>
+#include <range/v3/view/zip.hpp>
+#include <vector>
+
+
+

We begin by including the required headers:

+
    +
  • <algorithm> and <numeric> for sorting and accumulating operations.

  • +
  • <range/v3/*> for zipping values and weights, sorting values, and summing weights.

  • +
  • <vector> for storing the sorted pairs of values and weights.

  • +
+
namespace beluga {
+namespace detail {
+
+
+

The implementation is structured within the beluga and detail namespaces, consistent with the library’s organization.

+
// Example of a niebloid for calculating the median of weighted scalar values.
+struct weighted_median_fn {
+  // Operator to compute the median for scalar values.
+  template <class Values, class Weights, class Projection = ranges::identity>
+  auto operator()(Values&& values, Weights&& weights, Projection projection = {}) const {
+    using WeightType = std::decay_t<ranges::range_value_t<Weights>>;
+    using ValueType = std::decay_t<std::invoke_result_t<Projection, ranges::range_value_t<Values>>>;
+    static_assert(ranges::input_range<Values>);
+    static_assert(ranges::input_range<Weights>);
+
+
+

The weighted_median_fn struct is defined, focusing on the scalar median computation. The operator() function handles ranges of values and weights. Note the optional projection, allowing value transformation before processing. The result type of the projection may differ from the original values, requiring the use of std::decay_t to manage type adjustments.

+
    // Zip values with weights and sort them based on the projected value.
+    std::vector<std::pair<ValueType, WeightType>> sorted_pairs;
+    for (auto&& [value, weight] : ranges::views::zip(values, weights)) {
+      sorted_pairs.emplace_back(projection(value), weight);
+    }
+    ranges::sort(sorted_pairs, std::less{}, [](const auto& pair) { return pair.first; });
+
+
+

Values and weights are zipped together. The projection is applied to each value, and they are stored with their weights in sorted_pairs. These pairs are then sorted based on the projected value using std::sort.

+
    auto cumulative_weight = WeightType{0};
+    auto half_total_weight = 0.5 * ranges::accumulate(
+      sorted_pairs, WeightType{0}, std::plus{},
+      [](const auto& pair) { return pair.second; });
+
+    for (const auto& [projected_value, weight] : sorted_pairs) {
+      cumulative_weight += weight;
+      if (cumulative_weight >= half_total_weight) {
+        return projected_value;
+      }
+    }
+
+
+

The median is calculated using cumulative weights. The algorithm iterates through the sorted pairs, summing the weights. When the cumulative weight exceeds half the total, the median value is identified.

+
    // Fallback: return the last value in case no median found (should not happen with correct weights).
+    return sorted_pairs.back().first;
+  }
+};
+
+
+

A fallback mechanism is provided for when and if no median is found due to an anomaly in the weights.

+
}  // namespace detail
+
+// A niebloid instance to compute the median.
+inline constexpr detail::weighted_mean_fn weighted_median;
+
+}  // namespace beluga
+
+
+

The weighted_median niebloid is instantiated.

+
+
+

Next Steps#

+

For a real-world implementation, see beluga/algorithm/estimation.hpp content.

+
+
@@ -401,6 +817,40 @@

Extending Beluga + +