diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c99a3e22087bd637b9d4de70dec55977cd422a95
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+#Ignore build, bin and lib folders
+bin/
+build/
+lib/
+.settings/language.settings.xml
+.project
+.cproject
diff --git a/CMakeLists.txt b/CMakeLists.txt
index feda53e6d5abcbbf77308df98e9dfd423563e2db..26e07801dedb4ad9366e4251a1ff1721bb2e591a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,7 @@ PROJECT(laser_scan_utils)
 SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
 SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
 SET(CMAKE_INSTALL_PREFIX /usr/local)
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules")
 
 IF (NOT CMAKE_BUILD_TYPE)
   #SET(CMAKE_BUILD_TYPE "DEBUG")
diff --git a/cmake_modules/Findcsm.cmake b/cmake_modules/Findcsm.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..938022e920cddb287f773480596ffe497d8a0306
--- /dev/null
+++ b/cmake_modules/Findcsm.cmake
@@ -0,0 +1,64 @@
+FIND_PATH(
+    csm_INCLUDE_DIR
+    NAMES algos.h
+    PATHS /usr/local/include/csm)
+IF(csm_INCLUDE_DIR)
+  MESSAGE("Found csm include dirs: ${csm_INCLUDE_DIR}")
+ELSE(csm_INCLUDE_DIR)
+  MESSAGE("Couldn't find csm include dirs")
+ENDIF(csm_INCLUDE_DIR)
+
+FIND_LIBRARY(
+    csm_LIBRARY
+    NAMES libcsm.so libcsm.dylib
+    PATHS /usr/local/lib)
+IF(csm_LIBRARY)
+  MESSAGE("Found csm lib: ${csm_LIBRARY}")
+ELSE(csm_LIBRARY)
+  MESSAGE("Couldn't find csm lib")
+ENDIF(csm_LIBRARY)
+
+IF (csm_INCLUDE_DIR AND csm_LIBRARY)
+   SET(csm_FOUND TRUE)
+ ELSE(csm_INCLUDE_DIR AND csm_LIBRARY)
+   set(csm_FOUND FALSE)
+ENDIF (csm_INCLUDE_DIR AND csm_LIBRARY)
+
+IF (csm_FOUND)
+   IF (NOT csm_FIND_QUIETLY)
+      MESSAGE(STATUS "Found csm: ${csm_LIBRARY}")
+   ENDIF (NOT csm_FIND_QUIETLY)
+ELSE (csm_FOUND)
+   IF (csm_FIND_REQUIRED)
+      MESSAGE(FATAL_ERROR "Could not find csm")
+   ENDIF (csm_FIND_REQUIRED)
+ENDIF (csm_FOUND)
+
+
+macro(csm_report_not_found REASON_MSG)
+  set(csm_FOUND FALSE)
+  unset(csm_INCLUDE_DIR)
+  unset(csm_LIBRARIES)
+
+  # Reset the CMake module path to its state when this script was called.
+  set(CMAKE_MODULE_PATH ${CALLERS_CMAKE_MODULE_PATH})
+
+  # Note <package>_FIND_[REQUIRED/QUIETLY] variables defined by
+  # FindPackage() use the camelcase library name, not uppercase.
+  if (csm_FIND_QUIETLY)
+    message(STATUS "Failed to find csm- " ${REASON_MSG} ${ARGN})
+  else (csm_FIND_REQUIRED)
+    message(FATAL_ERROR "Failed to find csm - " ${REASON_MSG} ${ARGN})
+  else()
+    # Neither QUIETLY nor REQUIRED, use SEND_ERROR which emits an error
+    # that prevents generation, but continues configuration.
+    message(SEND_ERROR "Failed to find csm - " ${REASON_MSG} ${ARGN})
+  endif ()
+  return()
+endmacro(csm_report_not_found)
+
+if(NOT csm_FOUND)
+  csm_report_not_found("Something went wrong while setting up csm.")
+endif(NOT csm_FOUND)
+# Set the include directories for csm (itself).
+set(csm_FOUND TRUE)
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a0fbd34c48eb9ab048f3bb5faf1442f014bda7e6..ee3f8e7da535c1964752088000607a9485bb91b1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,7 +2,7 @@
 MESSAGE("Starting laser_scan_utils CMakeLists ...")
 CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 
-#find dependencies. 
+#find dependencies.
 FIND_PACKAGE(faramotics QUIET) #faramotics is only required for some tests
 IF(faramotics_FOUND)
 	#FIND_PACKAGE(GLUT REQUIRED)
@@ -10,8 +10,12 @@ IF(faramotics_FOUND)
     MESSAGE("Faramotics Library FOUND: Tests requiring it will be built.")
 ENDIF(faramotics_FOUND)
 
+FIND_PACKAGE(csm QUIET)
 #include directories
 INCLUDE_DIRECTORIES(.)
+IF(csm_FOUND)
+    INCLUDE_DIRECTORIES(${csm_INCLUDE_DIR})
+ENDIF(csm_FOUND)
 IF(Ceres_FOUND)
     INCLUDE_DIRECTORIES(${CERES_INCLUDE_DIRS})
 ENDIF(Ceres_FOUND)
@@ -22,7 +26,7 @@ ENDIF(faramotics_FOUND)
 #headers
 SET(HDRS_BASE
     laser_scan_utils.h)
-    
+
 SET(HDRS
     corner_finder.h
     corner_finder_inscribed_angle.h
@@ -38,7 +42,12 @@ SET(HDRS
     line_segment.h
     point_set.h
     polyline.h
-    scan_segment.h)
+    scan_segment.h
+    )
+  IF(csm_FOUND)
+    SET(HDRS ${HDRS}
+      icp.h)
+  ENDIF(csm_FOUND)
 
 #sources
 SET(SRCS
@@ -56,26 +65,37 @@ SET(SRCS
     line_segment.cpp
     point_set.cpp
     polyline.cpp
-    scan_segment.cpp)
-    
+    scan_segment.cpp
+    )
+  IF(csm_FOUND)
+    SET(SRCS ${SRCS}
+      icp.cpp)
+  ENDIF(csm_FOUND)
 # create the shared library
 ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
+target_link_libraries(${PROJECT_NAME} ${csm_LIBRARY})
 
 #install library
 INSTALL(TARGETS ${PROJECT_NAME}
       RUNTIME DESTINATION bin
       LIBRARY DESTINATION lib/iri-algorithms
       ARCHIVE DESTINATION lib/iri-algorithms)
-        
-#install headers         
-INSTALL(FILES ${HDRS_BASE} 
+
+#install headers
+INSTALL(FILES ${HDRS_BASE}
       DESTINATION include/iri-algorithms/laser_scan_utils)
-INSTALL(FILES ${HDRS} 
+INSTALL(FILES ${HDRS}
       DESTINATION include/iri-algorithms/laser_scan_utils)
 
 #install Find*.cmake
 INSTALL(FILES ../Findlaser_scan_utils.cmake DESTINATION ${CMAKE_ROOT}/Modules/)
 
 #Build examples & tests
-MESSAGE("Building tests.")
-ADD_SUBDIRECTORY(test)
+IF(NOT BUILD_TESTS)
+  OPTION(BUILD_TESTS "Build Unit tests" ON)
+ENDIF(NOT BUILD_TESTS)
+
+IF(BUILD_TESTS)
+  MESSAGE("Building tests.")
+  ADD_SUBDIRECTORY(test)
+ENDIF(BUILD_TESTS)
diff --git a/src/icp.cpp b/src/icp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c137f9bfdb6095e689b646496a64bfa2ac3e1fbf
--- /dev/null
+++ b/src/icp.cpp
@@ -0,0 +1,142 @@
+#include "icp.h"
+using namespace laserscanutils;
+unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
+std::mt19937 generator (seed);
+std::uniform_real_distribution<double> dis(0.0, 1.0);
+
+class LDWrapper {
+public:
+    LDP laser_data;
+    LDWrapper(const LaserScan& scan, const LaserScanParams& scan_params)
+    {
+        int num_rays = scan.ranges_raw_.size();
+        laser_data = ld_alloc_new(num_rays);
+
+        int i = 0;
+        for(auto it : scan.ranges_raw_){
+            laser_data->theta[i] = scan_params.angle_min_ + i*scan_params.angle_step_;
+
+            if(scan_params.range_min_ <= it and it <= scan_params.range_max_){
+                laser_data->readings[i] = it;
+                laser_data->valid[i] = 1;
+            }else{
+                laser_data->readings[i] = NAN;
+                laser_data->valid[i] = 0;
+            }
+            laser_data->cluster[i] = -1;
+            ++i;
+        }
+
+        // for(int i = 0; i < num_rays; ++i){
+        //     laser_data->theta[i] = laser_data->theta[i] * 0.0175;
+        // }
+        laser_data->min_theta = laser_data->theta[0];
+        laser_data->max_theta = laser_data->theta[num_rays-1];
+
+        laser_data->odometry[0] = 0.0;
+        laser_data->odometry[1] = 0.0;
+        laser_data->odometry[2] = 0.0;
+
+        laser_data->true_pose[0] = 0.0;
+        laser_data->true_pose[1] = 0.0;
+        laser_data->true_pose[2] = 0.0;
+    }
+    ~LDWrapper(){
+        ld_free(laser_data);
+    }
+};
+
+ICP::ICP()
+{
+
+}
+
+ICP::~ICP()
+{
+
+}
+
+icpOutput ICP::align(const LaserScan &_last_ls, const LaserScan &_origin_ls, const LaserScanParams& scan_params, const icpParams &icp_params, Eigen::Vector3s &_last_transf)
+{
+    // Uncomment to enable debug messages from the CSM library
+    // sm_debug_write(true);
+
+    LDWrapper last   = LDWrapper(_last_ls, scan_params);
+    LDWrapper origin = LDWrapper(_origin_ls, scan_params);
+
+    int num_rays = _last_ls.ranges_raw_.size();
+
+    sm_params csm_input{};
+    sm_result csm_output{};
+
+    csm_input.min_reading   = scan_params.range_min_;
+    csm_input.max_reading   = scan_params.range_max_;
+    csm_input.sigma         = scan_params.range_std_dev_;
+
+    csm_input.laser_ref     = origin.laser_data;
+    csm_input.laser_sens    = last.laser_data;
+
+    csm_input.first_guess[0] = _last_transf(0);
+    csm_input.first_guess[1] = _last_transf(1);
+    csm_input.first_guess[2] = _last_transf(2);
+
+    csm_input.use_point_to_line_distance = icp_params.use_point_to_line_distance;
+    csm_input.max_correspondence_dist   = icp_params.max_correspondence_dist;
+    csm_input.max_iterations            = icp_params.max_iterations;
+    csm_input.use_corr_tricks           = icp_params.use_corr_tricks;
+    csm_input.outliers_maxPerc          = icp_params.outliers_maxPerc;
+    csm_input.outliers_adaptive_order   = icp_params.outliers_adaptive_order;
+    csm_input.outliers_adaptive_mult    = icp_params.outliers_adaptive_mult;
+
+
+    csm_input.do_compute_covariance     = 1;
+
+    sm_icp(&csm_input, &csm_output);
+
+    icpOutput result{};
+    result.nvalid = csm_output.nvalid;
+    result.valid  = csm_output.valid;
+    result.error  = csm_output.error;
+
+    if (result.valid == 1)
+    {
+        result.res_transf(0) = csm_output.x[0];
+        result.res_transf(1) = csm_output.x[1];
+        result.res_transf(2) = csm_output.x[2];
+
+        for(int i = 0; i < 3; ++i)
+            for(int j = 0; j < 3; ++j)
+                result.res_covar(i,j) =
+                        //gsl_matrix_get(csm_output.cov_x_m, i, j);                 // NOT COMPILING
+                        csm_output.cov_x_m->data[i * csm_output.cov_x_m->tda + j];  // This does the same
+    }
+    else
+    {
+        std::cout << "ICP valid != 1, providing first guess transformation and identity covariance\n";
+        result.res_transf = _last_transf;
+        result.res_covar = Eigen::Matrix3s::Identity();
+    }
+
+    // std::cout << "Number of valid correspondences: " << csm_output.nvalid << '\n';
+    // std::cout << "Number of iterations: " << csm_output.iterations << '\n';
+    // std::cout << "Error: " << csm_output.error << '\n';
+
+    return result;
+}
+
+void ICP::printLaserData(LDP &laser_data)
+{
+    std::cout << "Laser Reading: " << laser_data->readings[0] << '\n';
+}
+
+void ICP::printTwoLaserData(sm_params &params)
+{
+
+    for (int ii=0; ii<params.laser_ref->nrays-1; ++ii)
+    {
+        std::cout << "Theta: " << params.laser_ref->theta[ii] << "; Readings: "
+                    << params.laser_ref->readings[ii] << "; " << params.laser_sens->readings[ii]
+                    << '\n';
+    }
+
+}
diff --git a/src/icp.h b/src/icp.h
new file mode 100644
index 0000000000000000000000000000000000000000..d6345fc992ea5657445b02f2fdd04b5d614eacbb
--- /dev/null
+++ b/src/icp.h
@@ -0,0 +1,44 @@
+#ifndef ICP_H_
+#define ICP_H_
+
+#include <chrono>
+#include <random>
+#include "laser_scan.h"
+#include <csm/csm_all.h>
+// using namespace CSM;
+
+namespace laserscanutils{
+
+struct icpOutput{
+    int valid; /** 1 if the result is valid */
+    Eigen::Vector3s res_transf;
+    Eigen::Matrix3s res_covar;
+    int nvalid;     /** Number of valid correspondence in the end */
+    double error;   	/** Total correspondence error */
+};
+
+struct icpParams{
+    int use_point_to_line_distance;
+    int max_correspondence_dist;
+    int max_iterations;
+    int use_corr_tricks;
+    double outliers_maxPerc;
+    double outliers_adaptive_order;
+    double outliers_adaptive_mult;
+};
+
+class ICP
+{
+    public:
+        ICP();
+        ~ICP();
+
+    static icpOutput align(const LaserScan &_last_ls, const LaserScan &_reference_ls, const LaserScanParams& scan_params, const icpParams &icp_params, Eigen::Vector3s &_last_transf);
+
+    static void printTwoLaserData(sm_params &params);
+    static void printLaserData(LDP &laser_data);
+};
+
+}
+
+#endif
diff --git a/src/point_set.cpp b/src/point_set.cpp
index 3f797b3e0eb30c1feae0b20ba4191e07f88fa4b4..aed39e414586b444e7bfc805a85ceb1c041c6801 100644
--- a/src/point_set.cpp
+++ b/src/point_set.cpp
@@ -82,7 +82,7 @@ void PointSet::computeRadius()
     radius_ = sqrt(dd2max);
 }
 
-void PointSet::computeBoundingBox(const double & _clearance)
+void PointSet::computeBoundingBox(const ScalarT & _clearance)
 {
     double cxx, cyy, cxy; //variance and covariance terms
     Eigen::MatrixXd points_o, points_c; //points wrt origin, points wrt cov eigen vectors