Program Listing for File ndt_cell.hpp

Return to documentation for file (include/beluga/sensor/data/ndt_cell.hpp)

// Copyright 2024 Ekumen, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BELUGA_SENSOR_DATA_NDT_CELL_HPP
#define BELUGA_SENSOR_DATA_NDT_CELL_HPP

#include <ostream>
#include <type_traits>

#include <Eigen/Core>

#include <range/v3/view/zip.hpp>

#include <sophus/common.hpp>
#include <sophus/se2.hpp>
#include <sophus/se3.hpp>
#include <sophus/so2.hpp>

#include "beluga/eigen_compatibility.hpp"

namespace beluga {

template <int NDim, typename Scalar = double>
struct NDTCell {
  static_assert(std::is_floating_point_v<Scalar>, "Scalar template parameter should be a floating point.");
  static constexpr int num_dim = NDim;
  using scalar_type = Scalar;
  Eigen::Vector<Scalar, NDim> mean;
  Eigen::Matrix<Scalar, NDim, NDim> covariance;

  [[nodiscard]] double likelihood_at(const NDTCell& measurement, double d1 = 1.0, double d2 = 1.0) const {
    const Eigen::Vector<Scalar, NDim> error = measurement.mean - mean;
    const double rhs =
        std::exp((-d2 / 2.0) * error.transpose() * (measurement.covariance + covariance).inverse() * error);
    return d1 * rhs;
  }

  friend std::ostream& operator<<(std::ostream& os, const NDTCell& cell) {
    os << "Mean \n" << cell.mean.transpose() << " \n\nCovariance: \n" << cell.covariance;
    return os;
  }

  friend NDTCell operator*(const Sophus::SE2<scalar_type>& tf, const NDTCell& ndt_cell) {
    static_assert(num_dim == 2, "Cannot transform a non 2D NDT Cell with a SE2 transform.");
    const Eigen::Vector2d uij = tf * ndt_cell.mean;
    const Eigen::Matrix2Xd cov = tf.so2().matrix() * ndt_cell.covariance * tf.so2().matrix().transpose();
    return NDTCell{uij, cov};
  }

  friend NDTCell operator*(const Sophus::SE3<scalar_type>& tf, const NDTCell& ndt_cell) {
    static_assert(num_dim == 3, "Cannot transform a non 3D NDT Cell with a SE3 transform.");
    const Eigen::Vector3d uij = tf * ndt_cell.mean;
    const Eigen::Matrix3Xd cov = tf.so3().matrix() * ndt_cell.covariance * tf.so3().matrix().transpose();
    return NDTCell{uij, cov};
  }
};

using NDTCell2d = NDTCell<2, double>;
using NDTCell2f = NDTCell<2, float>;
using NDTCell3d = NDTCell<3, double>;
using NDTCell3f = NDTCell<3, float>;

}  // namespace beluga

#endif