├── .gitignore ├── maps └── .gitkeep ├── src ├── lidar_mapping_node.cpp └── lidar_mapping.cpp ├── package.xml ├── launch ├── lidar_mapping.launch └── carla_ros_bridge_with_example_ego_vehicle.launch ├── CMakeLists.txt ├── README.md ├── config └── objects.json ├── include └── lidar_mapping.h └── rviz └── mapping.rviz /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /maps/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lidar_mapping_node.cpp: -------------------------------------------------------------------------------- 1 | #include "lidar_mapping.h" 2 | 3 | int main(int argc, char** argv) { 4 | ros::init(argc, argv, "lidar_mapping"); 5 | LidarMapping node; 6 | ros::spin(); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | lidar_mapping 4 | 1.0.0 5 | The lidar_mapping package 6 | Casper-Auto 7 | MIT 8 | 9 | catkin 10 | 11 | roscpp 12 | rospy 13 | geometry_msgs 14 | nav_msgs 15 | sensor_msgs 16 | tf 17 | 18 | -------------------------------------------------------------------------------- /launch/lidar_mapping.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(lidar_mapping) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | find_package(catkin REQUIRED COMPONENTS 8 | rospy 9 | roscpp 10 | std_msgs 11 | geometry_msgs 12 | nav_msgs 13 | sensor_msgs 14 | ) 15 | 16 | find_package(Eigen3 REQUIRED) 17 | find_package(PCL REQUIRED) 18 | 19 | catkin_package( 20 | CATKIN_DEPENDS 21 | ) 22 | 23 | install(DIRECTORY launch/ 24 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch 25 | ) 26 | 27 | include_directories( 28 | include 29 | ${catkin_INCLUDE_DIRS} 30 | ) 31 | 32 | include_directories(${EIGEN3_INCLUDE_DIR}) 33 | include_directories(${PCL_INCLUDE_DIRS}) 34 | 35 | link_directories(${PCL_LIBRARY_DIRS}) 36 | add_definitions(${PCL_DEFINITIONS}) 37 | 38 | add_executable(lidar_mapping 39 | src/lidar_mapping_node.cpp 40 | src/lidar_mapping.cpp 41 | ) 42 | 43 | target_link_libraries(lidar_mapping 44 | ${catkin_LIBRARIES} 45 | ${PCL_LIBRARIES} 46 | ) 47 | 48 | add_dependencies(lidar_mapping 49 | ${${PROJECT_NAME}_EXPORTED_TARGETS} 50 | ${catkin_EXPORTED_TARGETS} 51 | ) 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Carla Lidar Mapping 2 | 3 | Mapping with Ground-truth localization. 4 | 5 | ## Carla version 6 | 7 | The demo was tested with Carla 0.9.11, though the version does not matter for the mapping itself. Feel free to test it with other Carla versions. 8 | 9 | ## Motivation 10 | 11 | **Be short:** To purely observe how mapping works, without coupling with localization problems. 12 | 13 | Simultaneous Localization and Mapping (SLAM) is treated a complicated problem partially because we have to deal with two problems together and they are mutually dependent, which is like a "chicken and egg" problem. 14 | 15 | In fact, the two problems are not in the same scale considering the complexity. Why not start from the easier one, which is mapping? In reality, when we say mapping, it is always a different wording of SLAM, or LAM if we don't care about the real-time processing. With a simulator, it becomes possible to do mapping with perfect localization. In that case, we can find mapping is more or less just a problem of coordinate transformation. 16 | 17 | ## PCL Viewer 18 | 19 | Install: 20 | 21 | ``` 22 | sudo apt install pcl-tools 23 | ``` 24 | 25 | Use: 26 | 27 | ``` 28 | pcl_viewer my_map.pcd 29 | ``` 30 | -------------------------------------------------------------------------------- /launch/carla_ros_bridge_with_example_ego_vehicle.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /config/objects.json: -------------------------------------------------------------------------------- 1 | { 2 | "objects": 3 | [ 4 | { 5 | "type": "sensor.pseudo.objects", 6 | "id": "objects" 7 | }, 8 | { 9 | "type": "sensor.pseudo.actor_list", 10 | "id": "actor_list" 11 | }, 12 | { 13 | "type": "sensor.pseudo.markers", 14 | "id": "markers" 15 | }, 16 | { 17 | "type": "vehicle.tesla.model3", 18 | "id": "ego_vehicle", 19 | "sensors": 20 | [ 21 | { 22 | "type": "sensor.camera.rgb", 23 | "id": "rgb_front", 24 | "spawn_point": {"x": 2.0, "y": 0.0, "z": 2.0, "roll": 0.0, "pitch": 0.0, "yaw": 0.0}, 25 | "image_size_x": 800, 26 | "image_size_y": 600, 27 | "fov": 90.0 28 | }, 29 | { 30 | "type": "sensor.camera.rgb", 31 | "id": "rgb_view", 32 | "spawn_point": {"x": -4.5, "y": 0.0, "z": 2.8, "roll": 0.0, "pitch": 20.0, "yaw": 0.0}, 33 | "image_size_x": 800, 34 | "image_size_y": 600, 35 | "fov": 90.0, 36 | "attached_objects": 37 | [ 38 | { 39 | "type": "actor.pseudo.control", 40 | "id": "control" 41 | } 42 | ] 43 | }, 44 | { 45 | "type": "sensor.lidar.ray_cast", 46 | "id": "lidar", 47 | "spawn_point": {"x": 0.0, "y": 0.0, "z": 2.4, "roll": 0.0, "pitch": 0.0, "yaw": 0.0}, 48 | "range": 50, 49 | "channels": 64, 50 | "points_per_second": 320000, 51 | "upper_fov": 25.0, 52 | "lower_fov": -26.8, 53 | "rotation_frequency": 20, 54 | "noise_stddev": 0.0 55 | }, 56 | { 57 | "type": "sensor.pseudo.tf", 58 | "id": "tf" 59 | }, 60 | { 61 | "type": "sensor.pseudo.objects", 62 | "id": "objects" 63 | }, 64 | { 65 | "type": "sensor.pseudo.odom", 66 | "id": "odometry" 67 | }, 68 | { 69 | "type": "sensor.pseudo.speedometer", 70 | "id": "speedometer" 71 | } 72 | ] 73 | } 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /include/lidar_mapping.h: -------------------------------------------------------------------------------- 1 | // system 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // eigen 10 | #include 11 | 12 | // pcl 13 | #include 14 | #include 15 | #include 16 | 17 | // ros 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | typedef pcl::PointXYZ PointT; 29 | typedef pcl::PointCloud PointCloudT; 30 | 31 | const double pi = M_PI; 32 | 33 | 34 | struct Point { 35 | double x, y, z; 36 | 37 | Point() : x(0), y(0), z(0) {} 38 | 39 | Point(double setX, double setY, double setZ) : x(setX), y(setY), z(setZ) {} 40 | 41 | void Print() { 42 | std::cout << "x: " << x << " y: " << y << " z: " << z << std::endl; 43 | } 44 | }; 45 | 46 | struct Rotate { 47 | double roll, pitch, yaw; 48 | 49 | Rotate() : roll(0), yaw(0), pitch(0) {} 50 | 51 | Rotate(double setRoll, double setPitch, double setYaw) 52 | : roll(setRoll), pitch(setPitch), yaw(setYaw) {} 53 | 54 | void Print() { 55 | std::cout << "roll: " << roll << " pitch: " << pitch << " yaw: " << yaw << std::endl; 56 | } 57 | }; 58 | 59 | struct Pose { 60 | 61 | Point position; 62 | Rotate rotation; 63 | 64 | Pose() : position(Point(0, 0, 0)), rotation(Rotate(0, 0, 0)) {} 65 | 66 | Pose(Point setPos, Rotate setRotation) 67 | : position(setPos), rotation(setRotation) {} 68 | 69 | Pose operator-(const Pose &p) { 70 | Pose result(Point(position.x - p.position.x, position.y - p.position.y, 71 | position.z - p.position.z), 72 | Rotate(rotation.yaw - p.rotation.yaw, 73 | rotation.pitch - p.rotation.pitch, 74 | rotation.roll - p.rotation.roll)); 75 | return result; 76 | } 77 | }; 78 | 79 | class LidarMapping { 80 | public: 81 | LidarMapping(); 82 | ~LidarMapping(); 83 | 84 | private: 85 | // ros nodehandler 86 | ros::NodeHandle nh_; 87 | ros::NodeHandle private_nh_; 88 | 89 | // ros subscribers 90 | ros::Subscriber point_cloud_sub_; 91 | ros::Subscriber localization_sub_; 92 | 93 | // ros publishers 94 | ros::Publisher point_cloud_map_pub_; 95 | 96 | // ros timer 97 | ros::Timer timer_; 98 | 99 | // vars 100 | bool new_scan_; 101 | double vehicle_radius_; 102 | int cloud_resolution_; 103 | // ros::Time prev_scan_time_; 104 | std::chrono::time_point prev_scan_time_; 105 | Pose current_pose_; 106 | std::vector scan_poses_; 107 | PointCloudT::Ptr scaned_cloud_ptr_; 108 | PointCloudT pcl_cloud_; // point cloud map before filtering 109 | sensor_msgs::PointCloud2 saved_map_; 110 | 111 | 112 | // callback functions 113 | void pointCloudCallback(const sensor_msgs::PointCloud2::Ptr msg); 114 | void localizationCallback(const nav_msgs::Odometry::ConstPtr& msg); 115 | void timerCallback(const ros::TimerEvent &e); 116 | 117 | // core method 118 | void scanTransformation(const PointCloudT::Ptr scan_cloud); 119 | 120 | // methods 121 | Eigen::Matrix4d transform3D(double yaw, double pitch, double roll, double xt, double yt, double zt); 122 | Eigen::Quaternionf getQuaternion(float theta); 123 | Pose getPose(Eigen::Matrix4d matrix); 124 | double getDistance(Point p1, Point p2); 125 | double minDistance(Point p1, std::vector points); 126 | void print4x4Matrix(const Eigen::Matrix4d &matrix); 127 | void print4x4Matrixf(const Eigen::Matrix4f &matrix); 128 | PointCloudT::Ptr downsamplePointCloud(const PointCloudT& pcl_cloud); 129 | void savePointCloudMap(const PointCloudT::Ptr cloud_filtered); 130 | }; 131 | -------------------------------------------------------------------------------- /rviz/mapping.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /Status1 9 | Splitter Ratio: 0.5 10 | Tree Height: 719 11 | - Class: rviz/Selection 12 | Name: Selection 13 | - Class: rviz/Tool Properties 14 | Expanded: 15 | - /2D Pose Estimate1 16 | - /2D Nav Goal1 17 | - /Publish Point1 18 | Name: Tool Properties 19 | Splitter Ratio: 0.5886790156364441 20 | - Class: rviz/Views 21 | Expanded: 22 | - /Current View1 23 | Name: Views 24 | Splitter Ratio: 0.5 25 | - Class: rviz/Time 26 | Experimental: false 27 | Name: Time 28 | SyncMode: 0 29 | SyncSource: PointCloudMap 30 | Preferences: 31 | PromptSaveOnExit: true 32 | Toolbars: 33 | toolButtonStyle: 2 34 | Visualization Manager: 35 | Class: "" 36 | Displays: 37 | - Alpha: 0.5 38 | Cell Size: 1 39 | Class: rviz/Grid 40 | Color: 160; 160; 164 41 | Enabled: true 42 | Line Style: 43 | Line Width: 0.029999999329447746 44 | Value: Lines 45 | Name: Grid 46 | Normal Cell Count: 0 47 | Offset: 48 | X: 0 49 | Y: 0 50 | Z: 0 51 | Plane: XY 52 | Plane Cell Count: 10 53 | Reference Frame: 54 | Value: true 55 | - Class: rviz/MarkerArray 56 | Enabled: true 57 | Marker Topic: /carla/markers 58 | Name: MarkerArray 59 | Namespaces: 60 | "": true 61 | Queue Size: 100 62 | Value: true 63 | - Angle Tolerance: 0.10000000149011612 64 | Class: rviz/Odometry 65 | Covariance: 66 | Orientation: 67 | Alpha: 0.5 68 | Color: 255; 255; 127 69 | Color Style: Unique 70 | Frame: Local 71 | Offset: 1 72 | Scale: 1 73 | Value: true 74 | Position: 75 | Alpha: 0.30000001192092896 76 | Color: 204; 51; 204 77 | Scale: 1 78 | Value: true 79 | Value: true 80 | Enabled: true 81 | Keep: 100 82 | Name: Odometry 83 | Position Tolerance: 0.10000000149011612 84 | Queue Size: 10 85 | Shape: 86 | Alpha: 1 87 | Axes Length: 1 88 | Axes Radius: 0.10000000149011612 89 | Color: 255; 25; 0 90 | Head Length: 0.30000001192092896 91 | Head Radius: 0.10000000149011612 92 | Shaft Length: 1 93 | Shaft Radius: 0.05000000074505806 94 | Value: Arrow 95 | Topic: /carla/ego_vehicle/odometry 96 | Unreliable: false 97 | Value: true 98 | - Alpha: 1 99 | Autocompute Intensity Bounds: true 100 | Autocompute Value Bounds: 101 | Max Value: 10 102 | Min Value: -10 103 | Value: true 104 | Axis: Z 105 | Channel Name: intensity 106 | Class: rviz/PointCloud2 107 | Color: 255; 255; 255 108 | Color Transformer: Intensity 109 | Decay Time: 0 110 | Enabled: true 111 | Invert Rainbow: false 112 | Max Color: 255; 255; 255 113 | Min Color: 0; 0; 0 114 | Name: LidarPointCLoud 115 | Position Transformer: XYZ 116 | Queue Size: 10 117 | Selectable: true 118 | Size (Pixels): 3 119 | Size (m): 0.20000000298023224 120 | Style: Flat Squares 121 | Topic: /carla/ego_vehicle/lidar 122 | Unreliable: false 123 | Use Fixed Frame: true 124 | Use rainbow: true 125 | Value: true 126 | - Alpha: 1 127 | Autocompute Intensity Bounds: true 128 | Autocompute Value Bounds: 129 | Max Value: 10 130 | Min Value: -10 131 | Value: true 132 | Axis: Z 133 | Channel Name: intensity 134 | Class: rviz/PointCloud2 135 | Color: 255; 255; 255 136 | Color Transformer: Intensity 137 | Decay Time: 0 138 | Enabled: true 139 | Invert Rainbow: false 140 | Max Color: 255; 255; 255 141 | Min Color: 0; 0; 0 142 | Name: PointCloudMap 143 | Position Transformer: XYZ 144 | Queue Size: 10 145 | Selectable: true 146 | Size (Pixels): 3 147 | Size (m): 0.10000000149011612 148 | Style: Flat Squares 149 | Topic: /point_cloud_map 150 | Unreliable: false 151 | Use Fixed Frame: true 152 | Use rainbow: true 153 | Value: true 154 | Enabled: true 155 | Global Options: 156 | Background Color: 48; 48; 48 157 | Default Light: true 158 | Fixed Frame: ego_vehicle 159 | Frame Rate: 30 160 | Name: root 161 | Tools: 162 | - Class: rviz/Interact 163 | Hide Inactive Objects: true 164 | - Class: rviz/MoveCamera 165 | - Class: rviz/Select 166 | - Class: rviz/FocusCamera 167 | - Class: rviz/Measure 168 | - Class: rviz/SetInitialPose 169 | Theta std deviation: 0.2617993950843811 170 | Topic: /initialpose 171 | X std deviation: 0.5 172 | Y std deviation: 0.5 173 | - Class: rviz/SetGoal 174 | Topic: /move_base_simple/goal 175 | - Class: rviz/PublishPoint 176 | Single click: true 177 | Topic: /clicked_point 178 | Value: true 179 | Views: 180 | Current: 181 | Class: rviz/Orbit 182 | Distance: 77.182373046875 183 | Enable Stereo Rendering: 184 | Stereo Eye Separation: 0.05999999865889549 185 | Stereo Focal Distance: 1 186 | Swap Stereo Eyes: false 187 | Value: false 188 | Field of View: 0.7853981852531433 189 | Focal Point: 190 | X: 0 191 | Y: 0 192 | Z: 0 193 | Focal Shape Fixed Size: true 194 | Focal Shape Size: 0.05000000074505806 195 | Invert Z Axis: false 196 | Name: Current View 197 | Near Clip Distance: 0.009999999776482582 198 | Pitch: 0.8003979921340942 199 | Target Frame: 200 | Yaw: 3.1253983974456787 201 | Saved: ~ 202 | Window Geometry: 203 | Displays: 204 | collapsed: true 205 | Height: 1016 206 | Hide Left Dock: true 207 | Hide Right Dock: true 208 | QMainWindow State: 000000ff00000000fd0000000400000000000001560000035afc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073000000003d0000035a000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000035afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d0000035a000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007380000003efc0100000002fb0000000800540069006d0065010000000000000738000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000007380000035a00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 209 | Selection: 210 | collapsed: false 211 | Time: 212 | collapsed: false 213 | Tool Properties: 214 | collapsed: false 215 | Views: 216 | collapsed: true 217 | Width: 1848 218 | X: 72 219 | Y: 297 220 | -------------------------------------------------------------------------------- /src/lidar_mapping.cpp: -------------------------------------------------------------------------------- 1 | #include "lidar_mapping.h" 2 | 3 | LidarMapping::LidarMapping() : nh_(), private_nh_("~") { 4 | // param server 5 | private_nh_.param("new_scan", new_scan_, true); 6 | private_nh_.param("vehicle_radius", vehicle_radius_, 8.0); 7 | private_nh_.param("cloud_resolution", cloud_resolution_, 5000); 8 | 9 | // initialize subscribers 10 | point_cloud_sub_ = nh_.subscribe("/carla/ego_vehicle/lidar", 1, &LidarMapping::pointCloudCallback, this); 11 | localization_sub_ = nh_.subscribe("/carla/ego_vehicle/odometry", 1, &LidarMapping::localizationCallback, this); 12 | 13 | // initialize publishers 14 | point_cloud_map_pub_ = nh_.advertise("/point_cloud_map", 1); 15 | 16 | // createTimer 17 | timer_ = nh_.createTimer(ros::Duration(0.1), &LidarMapping::timerCallback, this); 18 | 19 | // smart pointers 20 | scaned_cloud_ptr_ = boost::make_shared(); 21 | } 22 | 23 | LidarMapping::~LidarMapping() {} 24 | 25 | void LidarMapping::pointCloudCallback(const sensor_msgs::PointCloud2::Ptr msg) { 26 | ROS_INFO("Point cloud received ..."); 27 | pcl::fromROSMsg(*msg, *scaned_cloud_ptr_); 28 | } 29 | 30 | void LidarMapping::localizationCallback(const nav_msgs::Odometry::ConstPtr& msg) { 31 | ROS_INFO("Odometry received ..."); 32 | 33 | // update current_pose_.position 34 | current_pose_.position.x = msg->pose.pose.position.x; 35 | current_pose_.position.y = msg->pose.pose.position.y; 36 | current_pose_.position.z = msg->pose.pose.position.z; 37 | 38 | // quaternion to rpy 39 | geometry_msgs::Quaternion geo_quat = msg->pose.pose.orientation; 40 | 41 | // the incoming geometry_msgs::Quaternion is transformed to a tf::Quaterion 42 | tf::Quaternion quat; 43 | tf::quaternionMsgToTF(geo_quat, quat); 44 | 45 | // the tf::Quaternion has a method to acess roll pitch and yaw 46 | double roll, pitch, yaw; 47 | tf::Matrix3x3(quat).getRPY(roll, pitch, yaw); 48 | 49 | // update current_pose_.rotation 50 | current_pose_.rotation.roll = roll; 51 | current_pose_.rotation.pitch = pitch; 52 | current_pose_.rotation.yaw = yaw; 53 | } 54 | 55 | void LidarMapping::timerCallback(const ros::TimerEvent &e) { 56 | 57 | scanTransformation(scaned_cloud_ptr_); 58 | 59 | ////////////////////////////////////////////////////////////////////////// 60 | // Mapping Visualization 61 | ////////////////////////////////////////////////////////////////////////// 62 | saved_map_.header.frame_id = "map"; 63 | point_cloud_map_pub_.publish(saved_map_); 64 | } 65 | 66 | /////////////////////////////////////////////////////////// 67 | // Magic happens here: Transformation 68 | /////////////////////////////////////////////////////////// 69 | 70 | void LidarMapping::scanTransformation(const PointCloudT::Ptr scan_cloud) { 71 | // Set transform to pose using transform3D() 72 | Eigen::Matrix4d transform = transform3D( 73 | current_pose_.rotation.yaw, current_pose_.rotation.pitch, current_pose_.rotation.roll, 74 | current_pose_.position.x, current_pose_.position.y, current_pose_.position.z); 75 | 76 | for (auto point : *scan_cloud) { 77 | // Don't include points touching ego 78 | if ((point.x * point.x + 79 | point.y * point.y + 80 | point.z * point.z) > 81 | vehicle_radius_) { 82 | Eigen::Vector4d local_point(point.x, point.y, point.z, 1); 83 | // Multiply local_point by transform 84 | Eigen::Vector4d transform_point = transform * local_point; 85 | pcl_cloud_.points.push_back(PointT(transform_point[0], transform_point[1], transform_point[2])); 86 | } 87 | } 88 | 89 | PointCloudT::Ptr cloud_filtered = downsamplePointCloud(pcl_cloud_); 90 | pcl::toROSMsg(*cloud_filtered.get(), saved_map_); 91 | 92 | // scan resolution 93 | if (pcl_cloud_.points.size() > cloud_resolution_) { 94 | new_scan_ = false; 95 | // savePointCloudMap(cloud_filtered); 96 | } 97 | 98 | double distanceR_res = 5.0; // CANDO: Can modify this value 99 | double time_res = 1.0; // CANDO: Can modify this value 100 | auto current_time = std::chrono::system_clock::now(); 101 | if (!new_scan_) { 102 | std::chrono::duration prev_scan_seconds = current_time - prev_scan_time_; 103 | if (prev_scan_seconds.count() > time_res && minDistance(Point(current_pose_.position.x, current_pose_.position.y, current_pose_.position.z), scan_poses_) > distanceR_res) { 104 | scan_poses_.push_back(Point(current_pose_.position.x, current_pose_.position.y, current_pose_.position.z)); 105 | new_scan_ = true; 106 | } 107 | } 108 | 109 | prev_scan_time_ = std::chrono::system_clock::now(); 110 | } 111 | 112 | ///////////////////////////////////////////////////// 113 | // Other methods: mostly are helper functions 114 | ///////////////////////////////////////////////////// 115 | 116 | Eigen::Matrix4d LidarMapping::transform3D(double yaw, double pitch, double roll, double xt, double yt, double zt) { 117 | 118 | Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity(); 119 | 120 | matrix(0, 3) = xt; 121 | matrix(1, 3) = yt; 122 | matrix(2, 3) = zt; 123 | 124 | matrix(0, 0) = cos(yaw) * cos(pitch); 125 | matrix(0, 1) = cos(yaw) * sin(pitch) * sin(roll) - sin(yaw) * cos(roll); 126 | matrix(0, 2) = cos(yaw) * sin(pitch) * cos(roll) + sin(yaw) * sin(roll); 127 | matrix(1, 0) = sin(yaw) * cos(pitch); 128 | matrix(1, 1) = sin(yaw) * sin(pitch) * sin(roll) + cos(yaw) * cos(roll); 129 | matrix(1, 2) = sin(yaw) * sin(pitch) * cos(roll) - cos(yaw) * sin(roll); 130 | matrix(2, 0) = -sin(pitch); 131 | matrix(2, 1) = cos(pitch) * sin(roll); 132 | matrix(2, 2) = cos(pitch) * cos(roll); 133 | 134 | return matrix; 135 | } 136 | 137 | // angle around z axis 138 | Eigen::Quaternionf LidarMapping::getQuaternion(float theta) { 139 | Eigen::Matrix3f rotation_mat; 140 | rotation_mat << cos(theta), -sin(theta), 0, sin(theta), cos(theta), 0, 0, 0, 1; 141 | Eigen::Quaternionf q(rotation_mat); 142 | return q; 143 | } 144 | 145 | Pose LidarMapping::getPose(Eigen::Matrix4d matrix) { 146 | Pose pose(Point(matrix(0, 3), matrix(1, 3), matrix(2, 3)), 147 | Rotate(atan2(matrix(2, 1), matrix(2, 2)), 148 | atan2(-matrix(2, 0), sqrt(matrix(2, 1) * matrix(2, 1) + 149 | matrix(2, 2) * matrix(2, 2))), 150 | atan2(matrix(1, 0), matrix(0, 0)))); 151 | return pose; 152 | } 153 | 154 | double LidarMapping::getDistance(Point p1, Point p2) { 155 | return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + (p1.z - p2.z) * (p1.z - p2.z)); 156 | } 157 | 158 | double LidarMapping::minDistance(Point p1, std::vector points) { 159 | if (points.size() > 0) { 160 | double dist = getDistance(p1, points[0]); 161 | for (int index = 1; index < points.size(); index++) { 162 | double new_dist = getDistance(p1, points[index]); 163 | if (new_dist < dist) { 164 | dist = new_dist; 165 | } 166 | } 167 | return dist; 168 | } 169 | return -1; 170 | } 171 | 172 | void LidarMapping::print4x4Matrix(const Eigen::Matrix4d &matrix) { 173 | printf("Rotation matrix :\n"); 174 | printf(" | %6.3f %6.3f %6.3f | \n", matrix(0, 0), matrix(0, 1), 175 | matrix(0, 2)); 176 | printf("R = | %6.3f %6.3f %6.3f | \n", matrix(1, 0), matrix(1, 1), 177 | matrix(1, 2)); 178 | printf(" | %6.3f %6.3f %6.3f | \n", matrix(2, 0), matrix(2, 1), 179 | matrix(2, 2)); 180 | printf("Translation vector :\n"); 181 | printf("t = < %6.3f, %6.3f, %6.3f >\n\n", matrix(0, 3), matrix(1, 3), 182 | matrix(2, 3)); 183 | } 184 | 185 | void LidarMapping::print4x4Matrixf(const Eigen::Matrix4f &matrix) { 186 | printf("Rotation matrix :\n"); 187 | printf(" | %6.3f %6.3f %6.3f | \n", matrix(0, 0), matrix(0, 1), 188 | matrix(0, 2)); 189 | printf("R = | %6.3f %6.3f %6.3f | \n", matrix(1, 0), matrix(1, 1), 190 | matrix(1, 2)); 191 | printf(" | %6.3f %6.3f %6.3f | \n", matrix(2, 0), matrix(2, 1), 192 | matrix(2, 2)); 193 | printf("Translation vector :\n"); 194 | printf("t = < %6.3f, %6.3f, %6.3f >\n\n", matrix(0, 3), matrix(1, 3), 195 | matrix(2, 3)); 196 | } 197 | 198 | PointCloudT::Ptr LidarMapping::downsamplePointCloud(const PointCloudT& pcl_cloud) { 199 | PointCloudT::Ptr cloud(new PointCloudT); 200 | *cloud = pcl_cloud; 201 | cloud->width = cloud->points.size(); 202 | cloud->height = 1; 203 | 204 | // Downsample the map point cloud using a voxel filter 205 | PointCloudT::Ptr cloud_filtered(new PointCloudT); 206 | pcl::VoxelGrid vg; 207 | vg.setInputCloud(cloud); 208 | double filterRes = 0.5; 209 | vg.setLeafSize(filterRes, filterRes, filterRes); 210 | vg.filter(*cloud_filtered); 211 | 212 | return cloud_filtered; 213 | } 214 | 215 | void LidarMapping::savePointCloudMap(const PointCloudT::Ptr cloud_filtered) { 216 | // save the point cloud map 217 | pcl::io::savePCDFileASCII("town_map.pcd", *cloud_filtered); 218 | ROS_INFO("Saved pcd map."); 219 | } 220 | --------------------------------------------------------------------------------