3 Commits bc3158a63a ... b3e6da3872

Auteur SHA1 Message Date
  yct b3e6da3872 20230112 rslidar support il y a 2 ans
  yct c88a0cd0e8 20221108 chutian huli il y a 2 ans
  yct f0d062d4c2 20221101 chutian il y a 2 ans
100 fichiers modifiés avec 7103 ajouts et 1330 suppressions
  1. 53 8
      CMakeLists.txt
  2. 77 77
      error_code/error_code.cpp
  3. 22 22
      error_code/error_code.h
  4. 24 2
      main.cpp
  5. 47 704
      message/measure_message.pb.cc
  6. 5 462
      message/measure_message.pb.h
  7. 15 12
      message/measure_message.proto
  8. 149 0
      message/message.proto
  9. 3 1
      proto.sh
  10. 6 4
      rabbitmq/rabbitmq.proto
  11. 65 37
      rabbitmq/rabbitmq_base.cpp
  12. 1 1
      rabbitmq/rabbitmq_base.h
  13. 79 0
      rabbitmq/胡力的rabbitmq-c说明文档.md
  14. 66 0
      rslidar/rs_driver/.clang-format
  15. 7 0
      rslidar/rs_driver/.gitignore
  16. 246 0
      rslidar/rs_driver/CHANGELOG.md
  17. 233 0
      rslidar/rs_driver/CMakeLists.txt
  18. 19 0
      rslidar/rs_driver/LICENSE
  19. 257 0
      rslidar/rs_driver/README.md
  20. 264 0
      rslidar/rs_driver/README_CN.md
  21. 86 0
      rslidar/rs_driver/cmake/FindPCAP.cmake
  22. 23 0
      rslidar/rs_driver/cmake/cmake_uninstall.cmake.in
  23. 29 0
      rslidar/rs_driver/cmake/rs_driverConfig.cmake.in
  24. 14 0
      rslidar/rs_driver/cmake/rs_driverConfigVersion.cmake.in
  25. 42 0
      rslidar/rs_driver/demo/CMakeLists.txt
  26. 162 0
      rslidar/rs_driver/demo/demo_online.cpp
  27. 190 0
      rslidar/rs_driver/demo/demo_online_multi_lidars.cpp
  28. 164 0
      rslidar/rs_driver/demo/demo_pcap.cpp
  29. 235 0
      rslidar/rs_driver/doc/howto/07_how_to_port_your_app_from_rs_driver_1.3.x_to_1.5.x.md
  30. 256 0
      rslidar/rs_driver/doc/howto/07_how_to_port_your_app_from_rs_driver_1.3.x_to_1.5.x_CN.md
  31. 268 0
      rslidar/rs_driver/doc/howto/08_how_to_decode_online_lidar.md
  32. 272 0
      rslidar/rs_driver/doc/howto/08_how_to_decode_online_lidar_CN.md
  33. 222 0
      rslidar/rs_driver/doc/howto/09_online_lidar_advanced_topics.md
  34. 230 0
      rslidar/rs_driver/doc/howto/09_online_lidar_advanced_topics_CN.md
  35. 284 0
      rslidar/rs_driver/doc/howto/10_how_to_decode_pcap_file.md
  36. 272 0
      rslidar/rs_driver/doc/howto/10_how_to_decode_pcap_file_CN.md
  37. 91 0
      rslidar/rs_driver/doc/howto/11_pcap_file_advanced_topics.md
  38. 91 0
      rslidar/rs_driver/doc/howto/11_pcap_file_advanced_topics_CN.md
  39. 154 0
      rslidar/rs_driver/doc/howto/12_how_to_configure_by_pcap_file.md
  40. 151 0
      rslidar/rs_driver/doc/howto/12_how_to_configure_by_pcap_file_CN.md
  41. 90 0
      rslidar/rs_driver/doc/howto/13_how_to_capture_pcap_file.md
  42. 90 0
      rslidar/rs_driver/doc/howto/13_how_to_capture_pcap_file_CN.md
  43. 107 0
      rslidar/rs_driver/doc/howto/14_how_to_use_rs_driver_viewer.md
  44. 106 0
      rslidar/rs_driver/doc/howto/14_how_to_use_rs_driver_viewer_CN.md
  45. 52 0
      rslidar/rs_driver/doc/howto/15_how_to_transform_pointcloud.md
  46. 47 0
      rslidar/rs_driver/doc/howto/15_how_to_transform_pointcloud_CN.md
  47. 333 0
      rslidar/rs_driver/doc/howto/16_how_to_compile_on_windows.md
  48. 331 0
      rslidar/rs_driver/doc/howto/16_how_to_compile_on_windows_CN.md
  49. 98 0
      rslidar/rs_driver/doc/howto/17_how_to_avoid_packet_loss.md
  50. 98 0
      rslidar/rs_driver/doc/howto/17_how_to_avoid_packet_loss_CN.md
  51. 187 0
      rslidar/rs_driver/doc/howto/18_about_point_layout.md
  52. 184 0
      rslidar/rs_driver/doc/howto/18_about_point_layout_CN.md
  53. 121 0
      rslidar/rs_driver/doc/howto/19_about_splitting_frame.md
  54. 121 0
      rslidar/rs_driver/doc/howto/19_about_splitting_frame_CN.md
  55. 131 0
      rslidar/rs_driver/doc/howto/20_about_usage_of_cpu_and_memory.md
  56. 133 0
      rslidar/rs_driver/doc/howto/20_about_usage_of_cpu_and_memory_CN.md
  57. BIN
      rslidar/rs_driver/doc/howto/img/07_01_branches.png
  58. BIN
      rslidar/rs_driver/doc/howto/img/07_02_cpu_usage_dev.png
  59. BIN
      rslidar/rs_driver/doc/howto/img/07_03_cpu_usage_dev_opt.png
  60. BIN
      rslidar/rs_driver/doc/howto/img/09_01_broadcast.png
  61. BIN
      rslidar/rs_driver/doc/howto/img/09_02_unicast.png
  62. BIN
      rslidar/rs_driver/doc/howto/img/09_03_multicast.png
  63. BIN
      rslidar/rs_driver/doc/howto/img/09_04_multi_lidars_port.png
  64. BIN
      rslidar/rs_driver/doc/howto/img/09_05_multi_lidars_ip.png
  65. BIN
      rslidar/rs_driver/doc/howto/img/09_06_vlan_layer.png
  66. BIN
      rslidar/rs_driver/doc/howto/img/09_07_vlan.png
  67. BIN
      rslidar/rs_driver/doc/howto/img/09_08_user_layer.png
  68. BIN
      rslidar/rs_driver/doc/howto/img/12_01_select_by_port.png
  69. BIN
      rslidar/rs_driver/doc/howto/img/12_02_select_by_non_port.png
  70. BIN
      rslidar/rs_driver/doc/howto/img/12_03_rs32_msop_packet.png
  71. BIN
      rslidar/rs_driver/doc/howto/img/12_04_rs32_difop_packet.png
  72. BIN
      rslidar/rs_driver/doc/howto/img/12_05_with_vlan.png
  73. BIN
      rslidar/rs_driver/doc/howto/img/12_06_with_user_layer.png
  74. BIN
      rslidar/rs_driver/doc/howto/img/12_07_with_tail_layer.png
  75. BIN
      rslidar/rs_driver/doc/howto/img/12_08_not_supported.png
  76. BIN
      rslidar/rs_driver/doc/howto/img/13_01_wireshark_select_nic.png
  77. BIN
      rslidar/rs_driver/doc/howto/img/13_02_wireshark_capture.png
  78. BIN
      rslidar/rs_driver/doc/howto/img/13_03_wireshark_filter_by_port.png
  79. BIN
      rslidar/rs_driver/doc/howto/img/13_04_wireshark_export_all.png
  80. BIN
      rslidar/rs_driver/doc/howto/img/13_05_wireshark_export_range.png
  81. BIN
      rslidar/rs_driver/doc/howto/img/13_06_wireshark_mark.png
  82. BIN
      rslidar/rs_driver/doc/howto/img/13_07_wireshark_export_marked.png
  83. BIN
      rslidar/rs_driver/doc/howto/img/13_08_wireshark_export_marked_range.png
  84. BIN
      rslidar/rs_driver/doc/howto/img/14_01_rs_driver_viewer_point_cloud.png
  85. BIN
      rslidar/rs_driver/doc/howto/img/16_01_demo_project.png
  86. BIN
      rslidar/rs_driver/doc/howto/img/16_02_demo_use_cpp14.png
  87. BIN
      rslidar/rs_driver/doc/howto/img/16_03_demo_extra_include.png
  88. BIN
      rslidar/rs_driver/doc/howto/img/16_04_demo_include_path.png
  89. BIN
      rslidar/rs_driver/doc/howto/img/16_05_demo_lib_path.png
  90. BIN
      rslidar/rs_driver/doc/howto/img/16_06_demo_lib.png
  91. BIN
      rslidar/rs_driver/doc/howto/img/16_07_demo_precompile_macro.png
  92. BIN
      rslidar/rs_driver/doc/howto/img/16_08_viewer_install_pcl.png
  93. BIN
      rslidar/rs_driver/doc/howto/img/16_09_viewer_install_pcl_dep.png
  94. BIN
      rslidar/rs_driver/doc/howto/img/16_10_viewer_add_env_var.png
  95. BIN
      rslidar/rs_driver/doc/howto/img/16_11_viewer_project.png
  96. BIN
      rslidar/rs_driver/doc/howto/img/16_12_viewer_sdl_check.png
  97. BIN
      rslidar/rs_driver/doc/howto/img/16_13_viewer_include_path.png
  98. BIN
      rslidar/rs_driver/doc/howto/img/16_14_viewer_lib_path.png
  99. BIN
      rslidar/rs_driver/doc/howto/img/16_15_viewer_lib.png
  100. 0 0
      rslidar/rs_driver/doc/howto/img/16_16_viewer_precompile_macro.png

+ 53 - 8
CMakeLists.txt

@@ -32,6 +32,8 @@ FIND_PACKAGE(OpenCV REQUIRED)
 FIND_PACKAGE(PCL REQUIRED)
 FIND_PACKAGE(Ceres REQUIRED)
 
+add_subdirectory(${PROJECT_SOURCE_DIR}/rslidar/rs_driver)
+find_package(rs_driver REQUIRED)
 
 
 MESSAGE(WARN "pcl:: ${PCL_INCLUDE_DIRS} --- ${PCL_LIBRARIES}")
@@ -41,6 +43,7 @@ include_directories(
         ${OpenCV_INCLUDE_DIRS}
         ${PROTOBUF_INCLUDE_DIRS}
 		${CERES_INCLUDE_DIRS}
+    ${rs_driver_INCLUDE_DIRS}
         communication
         message
         error_code
@@ -68,6 +71,7 @@ aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/velodyne_lidar/velodyne_driver VE
 aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/velodyne_lidar VELODYNE_LIDAR)
 
 aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/rabbitmq RABBIT_MQ)
+aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/rslidar RSLIDAR)
 
 add_executable(measure_wj
         main.cpp
@@ -86,13 +90,14 @@ add_executable(measure_wj
     ${VELODYNE_LIDAR_DRIVER}
     ${VELODYNE_LIDAR}
     ${RABBIT_MQ}
+    ${RSLIDAR}
 		)
 
 target_link_libraries(measure_wj
-        /usr/local/lib/libglog.a
-        /usr/local/lib/libgflags.a
-        # /usr/lib/x86_64-linux-gnu/libglog.so
-        # /usr/lib/x86_64-linux-gnu/libgflags.so
+        # /usr/local/lib/libglog.a
+        # /usr/local/lib/libgflags.a
+        /usr/lib/x86_64-linux-gnu/libglog.so
+        /usr/lib/x86_64-linux-gnu/libgflags.so
         /usr/local/lib/librabbitmq.a
         nnxx
         nanomsg
@@ -103,6 +108,7 @@ target_link_libraries(measure_wj
         ${PCL_LIBRARIES}
 		${CERES_LIBRARIES}
     ${YAML_CPP_LIBRARIES}
+    ${rs_driver_LIBRARIES}
 
         -lpthread
         )
@@ -127,12 +133,13 @@ ${VELODYNE_LIDAR_COMMON}
 ${VELODYNE_LIDAR_MATCH}
 ${VELODYNE_LIDAR_DRIVER}
 ${VELODYNE_LIDAR}
+${RSLIDAR}
 )
 target_link_libraries(vlp16 
-        /usr/local/lib/libglog.a
-        /usr/local/lib/libgflags.a
-        # /usr/lib/x86_64-linux-gnu/libglog.so
-        # /usr/lib/x86_64-linux-gnu/libgflags.so
+        # /usr/local/lib/libglog.a
+        # /usr/local/lib/libgflags.a
+        /usr/lib/x86_64-linux-gnu/libglog.so
+        /usr/lib/x86_64-linux-gnu/libgflags.so
 nnxx
 nanomsg
 
@@ -142,6 +149,44 @@ ${GLOG_LIBRARIES}
 ${PCL_LIBRARIES}
 ${CERES_LIBRARIES}
 ${YAML_CPP_LIBRARIES}
+${rs_driver_LIBRARIES}
+/usr/local/lib/librabbitmq.a
+)
+
+# helios 16P driver test
+add_executable(rshelios16 tests/rslidar_test.cpp 
+${ERROR_SRC}
+${message_src}
+
+${WANJI_LIDAR_SRC}
+${TASK_MANAGER_SRC}
+${TOOL_SRC}
+${COMMUNICATION_SRC}
+${SYSTEM_SRC}
+${RABBIT_MQ}
+${VERIFY_SRC}
+
+${VELODYNE_LIDAR_COMMON}
+${VELODYNE_LIDAR_MATCH}
+${VELODYNE_LIDAR_DRIVER}
+${VELODYNE_LIDAR}
+${RSLIDAR}
+)
+target_link_libraries(rshelios16 
+        # /usr/local/lib/libglog.a
+        # /usr/local/lib/libgflags.a
+        /usr/lib/x86_64-linux-gnu/libglog.so
+        /usr/lib/x86_64-linux-gnu/libgflags.so
+nnxx
+nanomsg
+
+${PROTOBUF_LIBRARIES}
+${OpenCV_LIBS}
+${GLOG_LIBRARIES}
+${PCL_LIBRARIES}
+${CERES_LIBRARIES}
+${YAML_CPP_LIBRARIES}
+${rs_driver_LIBRARIES}
 /usr/local/lib/librabbitmq.a
 )
 

+ 77 - 77
error_code/error_code.cpp

@@ -21,28 +21,28 @@ Error_manager::Error_manager(const Error_manager & error_manager)
 {
     this->m_error_code = error_manager.m_error_code;
     this->m_error_level = error_manager.m_error_level;
-	this->m_error_description=error_manager.m_error_description;
+    this->m_error_description=error_manager.m_error_description;
     return ;
 }
 //赋值构造
 Error_manager::Error_manager(Error_code error_code, Error_level error_level,
-    const char* p_error_description)
+                             const char* p_error_description)
 {
     m_error_code = error_code;
     m_error_level = error_level;
     if(p_error_description!= nullptr)
-	{
-		m_error_description=std::string(p_error_description);
-	}
-	return ;
+    {
+        m_error_description=std::string(p_error_description);
+    }
+    return ;
 }
 //赋值构造
-Error_manager::Error_manager(Error_code error_code, Error_level error_level , std::string & error_aggregate_string)
+Error_manager::Error_manager(Error_code error_code, Error_level error_level , std::string   error_aggregate_string)
 {
     m_error_code = error_code;
     m_error_level = error_level;
     m_error_description=error_aggregate_string;
-	return ;
+    return ;
 }
 //析构函数
 Error_manager::~Error_manager()
@@ -59,14 +59,14 @@ void Error_manager::error_manager_init(Error_code error_code, Error_level error_
 {
     m_error_code = error_code;
     m_error_level = error_level;
-	if(p_error_description!= nullptr)
-	{
-		m_error_description=std::string(p_error_description);
-	}
-	return ;
+    if(p_error_description!= nullptr)
+    {
+        m_error_description=std::string(p_error_description);
+    }
+    return ;
 }
 //初始化
-void Error_manager::error_manager_init(Error_code error_code, Error_level error_level , std::string & error_aggregate_string)
+void Error_manager::error_manager_init(Error_code error_code, Error_level error_level , std::string error_aggregate_string)
 {
     m_error_code = error_code;
     m_error_level = error_level;
@@ -78,26 +78,26 @@ void Error_manager::error_manager_reset(Error_code error_code, Error_level error
 {
     m_error_code = error_code;
     m_error_level = error_level;
-	if(p_error_description!= nullptr)
-	{
-		m_error_description=std::string(p_error_description);
-	}
-	return ;
+    if(p_error_description!= nullptr)
+    {
+        m_error_description=std::string(p_error_description);
+    }
+    return ;
 }
 //重置
-void Error_manager::error_manager_reset(Error_code error_code, Error_level error_level , std::string & error_aggregate_string)
+void Error_manager::error_manager_reset(Error_code error_code, Error_level error_level , std::string error_aggregate_string)
 {
     m_error_code = error_code;
     m_error_level = error_level;
     m_error_description=error_aggregate_string;
-	return;
+    return;
 }
 //重置
 void Error_manager::error_manager_reset(const Error_manager & error_manager)
 {
     this->m_error_code = error_manager.m_error_code;
     this->m_error_level = error_manager.m_error_level;
-	this->m_error_description=error_manager.m_error_description;
+    this->m_error_description=error_manager.m_error_description;
     return ;
 }
 //清除所有内容
@@ -105,27 +105,27 @@ void Error_manager::error_manager_clear_all()
 {
     m_error_code = SUCCESS;
     m_error_level = NORMAL;
-	m_error_description.clear();
-	return;
+    m_error_description.clear();
+    return;
 }
 
 //重载=
 Error_manager& Error_manager::operator=(const Error_manager & error_manager)
 {
     error_manager_reset(error_manager);
-	return *this;
+    return *this;
 }
 //重载=,支持Error_manager和Error_code的直接转化,会清空错误等级和描述
 Error_manager& Error_manager::operator=(Error_code error_code)
 {
     error_manager_clear_all();
     set_error_code(error_code);
-	return *this;
+    return *this;
 }
 //重载==
 bool Error_manager::operator==(const Error_manager & error_manager)
 {
-	return is_equal_error_manager(error_manager);
+    return is_equal_error_manager(error_manager);
 }
 //重载==,支持Error_manager和Error_code的直接比较
 bool Error_manager::operator==(Error_code error_code)
@@ -143,7 +143,7 @@ bool Error_manager::operator==(Error_code error_code)
 //重载!=
 bool Error_manager::operator!=(const Error_manager & error_manager)
 {
-	return (! is_equal_error_manager(error_manager));
+    return (! is_equal_error_manager(error_manager));
 }
 //重载!=,支持Error_manager和Error_code的直接比较
 bool Error_manager::operator!=(Error_code error_code)
@@ -160,8 +160,8 @@ bool Error_manager::operator!=(Error_code error_code)
 //重载<<,支持cout<<
 std::ostream & operator<<(std::ostream &out, Error_manager &error_manager)
 {
-	out << error_manager.to_string();
-	return out;
+    out << error_manager.to_string();
+    return out;
 }
 
 //获取错误码
@@ -189,14 +189,14 @@ void Error_manager::copy_error_description(const char* p_error_description)
     {
         m_error_description=std::string(p_error_description);
     }
-	return;
+    return;
 }
 //复制错误描述,(深拷贝)
 //output:error_description_string     错误描述的string
-void Error_manager::copy_error_description(std::string & error_description_string)
+void Error_manager::copy_error_description(std::string error_description_string)
 {
     m_error_description=error_description_string;
-	return;
+    return;
 }
 
 //设置错误码
@@ -232,17 +232,17 @@ void Error_manager::set_error_level_location(Error_level error_level)
 //设置错误描述
 void Error_manager::set_error_description(const char* p_error_description)
 {
-	if(p_error_description != NULL)
-	{
-		m_error_description=std::string(p_error_description);
-	}
-	return;
+    if(p_error_description != NULL)
+    {
+        m_error_description=std::string(p_error_description);
+    }
+    return;
 }
 //设置错误描述
-void Error_manager::set_error_description(std::string & error_description_string)
+void Error_manager::set_error_description(std::string error_description_string)
 {
-	m_error_description = error_description_string;
-	return ;
+    m_error_description = error_description_string;
+    return ;
 }
 
 //尾部追加错误描述
@@ -254,7 +254,7 @@ void Error_manager::add_error_description(const char* p_error_description)
     }
 }
 //尾部追加错误描述
-void Error_manager::add_error_description(std::string & error_description_string)
+void Error_manager::add_error_description(std::string error_description_string)
 {
     m_error_description+=error_description_string;
 }
@@ -283,7 +283,7 @@ void Error_manager::compare_and_cover_error(const Error_manager & error_manager)
     }
     else if (error_manager.m_error_code == SUCCESS)
     {
-		return;
+        return;
     }
     else
     {
@@ -297,64 +297,64 @@ void Error_manager::compare_and_cover_error(const Error_manager & error_manager)
         }
         else
         {
-			((Error_manager&)error_manager).translate_error_to_string(pt_string_inside);
+            ((Error_manager&)error_manager).translate_error_to_string(pt_string_inside);
             add_error_description(pt_string_inside);
         }
     }
-	return;
+    return;
 }
 //比较并覆盖错误,讲低级错误转为字符串存放于描述中,
 //如果错误相同,则保留this的,将输入参数转入描述。
 void Error_manager::compare_and_cover_error( Error_manager * p_error_manager)
 {
-	if(this->m_error_code == SUCCESS)
-	{
-		error_manager_reset(*p_error_manager);
-	}
-	else if (p_error_manager->m_error_code == SUCCESS)
-	{
-		return;
-	}
-	else
-	{
-		Error_manager t_error_manager_new;
-		std::string pt_string_inside;
-		if(this->m_error_level < p_error_manager->m_error_level)
-		{
-			translate_error_to_string(pt_string_inside);
-			error_manager_reset(*p_error_manager);
-			add_error_description(pt_string_inside);
-		}
-		else
-		{
-			p_error_manager->translate_error_to_string(pt_string_inside);
-			add_error_description(pt_string_inside);
-		}
-	}
+    if(this->m_error_code == SUCCESS)
+    {
+        error_manager_reset(*p_error_manager);
+    }
+    else if (p_error_manager->m_error_code == SUCCESS)
+    {
+        return;
+    }
+    else
+    {
+        Error_manager t_error_manager_new;
+        std::string pt_string_inside;
+        if(this->m_error_level < p_error_manager->m_error_level)
+        {
+            translate_error_to_string(pt_string_inside);
+            error_manager_reset(*p_error_manager);
+            add_error_description(pt_string_inside);
+        }
+        else
+        {
+            p_error_manager->translate_error_to_string(pt_string_inside);
+            add_error_description(pt_string_inside);
+        }
+    }
 }
 
 
 //output:error_description_string     错误汇总的string
-void Error_manager::translate_error_to_string(std::string & error_aggregate_string)
+void Error_manager::translate_error_to_string(std::string error_aggregate_string)
 {
     char t_string_array[255] = {0};
     sprintf(t_string_array, "error_code:0x%08x, error_level:%02d, error_description:",
             m_error_code , m_error_level);
 
     error_aggregate_string = t_string_array ;
-	error_aggregate_string+= m_error_description;
-	return;
+    error_aggregate_string+= m_error_description;
+    return;
 }
 //错误码转字符串的简易版,可支持cout<<
 //return     错误汇总的string
 std::string Error_manager::to_string()
 {
-	char t_string_array[255] = {0};
-	sprintf(t_string_array, "error_code:0x%08x, error_level:%02d, error_description:",
+    char t_string_array[255] = {0};
+    sprintf(t_string_array, "error_code:0x%08x, error_level:%02d, error_description:",
             m_error_code , m_error_level);
     std::string error_aggregate_string = t_string_array ;
-	error_aggregate_string+= m_error_description;
-	return error_aggregate_string;
+    error_aggregate_string+= m_error_description;
+    return error_aggregate_string;
 }
 
 
@@ -376,7 +376,7 @@ void Error_manager::reallocate_memory_and_copy_string(const char* p_error_descri
 
 //重新分配错误描述的内存,并从外部拷贝新的(深拷贝)
 //input:error_aggregate_string     错误描述的string
-void Error_manager::reallocate_memory_and_copy_string(std::string & error_aggregate_string)
+void Error_manager::reallocate_memory_and_copy_string(std::string error_aggregate_string)
 {
     return;
 }

+ 22 - 22
error_code/error_code.h

@@ -598,21 +598,21 @@ enum Error_code
 enum Error_level
 {
 //    正常,没有错误,默认值0
-    NORMAL                = 0,
+            NORMAL                = 0,
 
 
 //    轻微故障,可忽略的故障,NEGLIGIBLE_ERROR
 //    提示作用,不做任何处理,不影响代码的流程,
 //    用作一些不重要的事件,即使出错也不会影响到系统功能,
 //    例如:文件保存错误,等
-    NEGLIGIBLE_ERROR      = 1,
+            NEGLIGIBLE_ERROR      = 1,
 
 
 //    一般故障,MINOR_ERROR
 //    用作底层功能函数的错误返回,表示该功能函数执行失败,
 //    返回给应用层之后,需要做故障分析和处理,
 //    例如:雷达数据传输失败,应用层就需要进行重新扫描,或者重连,或者重置参数等。
-    MINOR_ERROR           = 2,
+            MINOR_ERROR           = 2,
 
 
 //    严重故障,MAJOR_ERROR
@@ -621,7 +621,7 @@ enum Error_level
 //    从一般故障升级为严重故障,然后进行回退流程,回退已经执行的操作,最终回到故障待机状态。
 //    需要外部清除故障,并复位至正常待机状态,才能恢复功能的使用。
 //    例如:雷达扫描任务失败,且无法自动恢复。
-    MAJOR_ERROR           = 3,
+            MAJOR_ERROR           = 3,
 
 
 //    致命故障,CRITICAL_ERROR
@@ -630,7 +630,7 @@ enum Error_level
 //    此时不允许再执行任何函数和任务指令,防止系统故障更加严重。
 //    也不需要做任何错误处理了,快速执行紧急流程。
 //    例如:内存错误,进程挂死,关键设备失控,监控设备报警,等
-    CRITICAL_ERROR        = 4,
+            CRITICAL_ERROR        = 4,
 };
 
 
@@ -645,7 +645,7 @@ public://外部接口函数
     Error_manager(Error_code error_code, Error_level error_level = NORMAL,
                   const char* p_error_description = NULL);
     //赋值构造
-    Error_manager(Error_code error_code, Error_level error_level , std::string & error_aggregate_string);
+    Error_manager(Error_code error_code, Error_level error_level , std::string error_aggregate_string);
     //析构函数
     ~Error_manager();
 
@@ -655,12 +655,12 @@ public://外部接口函数
     void error_manager_init(Error_code error_code, Error_level error_level = NORMAL,
                             const char* p_error_description = NULL);
     //初始化
-    void error_manager_init(Error_code error_code, Error_level error_level , std::string & error_aggregate_string);
+    void error_manager_init(Error_code error_code, Error_level error_level , std::string error_aggregate_string);
     //重置
     void error_manager_reset(Error_code error_code, Error_level error_level = NORMAL,
                              const char* p_error_description = NULL);
     //重置
-    void error_manager_reset(Error_code error_code, Error_level error_level , std::string & error_aggregate_string);
+    void error_manager_reset(Error_code error_code, Error_level error_level , std::string error_aggregate_string);
     //重置
     void error_manager_reset(const Error_manager & error_manager);
     //清除所有内容
@@ -678,8 +678,8 @@ public://外部接口函数
     bool operator!=(const Error_manager & error_manager);
     //重载!=,支持Error_manager和Error_code的直接比较
     bool operator!=(Error_code error_code);
-	//重载<<,支持cout<<
-	friend std::ostream & operator<<(std::ostream &out, Error_manager &error_manager);
+    //重载<<,支持cout<<
+    friend std::ostream & operator<<(std::ostream &out, Error_manager &error_manager);
 
 
     //获取错误码
@@ -695,7 +695,7 @@ public://外部接口函数
     void copy_error_description(const char* p_error_description);
     //复制错误描述,(深拷贝)
     //output:error_description_string     错误描述的string
-    void copy_error_description(std::string & error_description_string);
+    void copy_error_description(std::string error_description_string);
 
     //设置错误码
     void set_error_code(Error_code error_code);
@@ -708,25 +708,25 @@ public://外部接口函数
     //设置错误描述
     void set_error_description(const char* p_error_description);
     //设置错误描述
-    void set_error_description(std::string & error_description_string);
+    void set_error_description(std::string error_description_string);
 
     //尾部追加错误描述
     void add_error_description(const char* p_error_description);
     //尾部追加错误描述
-    void add_error_description(std::string & error_description_string);
+    void add_error_description(std::string error_description_string);
 
     //比较错误是否相同,
     // 注:只比较错误码和等级
-	bool is_equal_error_manager(const Error_manager & error_manager);
-	//比较并覆盖错误,讲低级错误转为字符串存放于描述中,
-	//如果错误相同,则保留this的,将输入参数转入描述。
-	void compare_and_cover_error(const Error_manager & error_manager);
-	//比较并覆盖错误,讲低级错误转为字符串存放于描述中,
-	//如果错误相同,则保留this的,将输入参数转入描述。
-	void compare_and_cover_error( Error_manager * p_error_manager);
+    bool is_equal_error_manager(const Error_manager & error_manager);
+    //比较并覆盖错误,讲低级错误转为字符串存放于描述中,
+    //如果错误相同,则保留this的,将输入参数转入描述。
+    void compare_and_cover_error(const Error_manager & error_manager);
+    //比较并覆盖错误,讲低级错误转为字符串存放于描述中,
+    //如果错误相同,则保留this的,将输入参数转入描述。
+    void compare_and_cover_error( Error_manager * p_error_manager);
 
     //output:error_description_string     错误汇总的string
-    void translate_error_to_string(std::string & error_aggregate_string);
+    void translate_error_to_string(std::string error_aggregate_string);
     //错误码转字符串的简易版,可支持cout<<
     //return     错误汇总的string
     std::string to_string();
@@ -750,7 +750,7 @@ public:
 
     //重新分配错误描述的内存,并从外部拷贝新的(深拷贝)
     //input:error_aggregate_string     错误描述的string
-    void reallocate_memory_and_copy_string(std::string & error_aggregate_string);
+    void reallocate_memory_and_copy_string(std::string error_aggregate_string);
 };
 
 

+ 24 - 2
main.cpp

@@ -54,8 +54,12 @@ void test_whole_process()
 
 int main(int argc,char* argv[])
 {
+	std::cout << " measure:: " << " xxxxxxxxxxxxxxxxx " << std::endl;
+
+
 	Error_manager t_error;
 	Error_manager t_result ;
+    Error_manager ec;
 
 #ifdef POINT_DEBUG
 	pcl::PointCloud<pcl::PointXYZ>::Ptr t_cloud = pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
@@ -79,6 +83,21 @@ int main(int argc,char* argv[])
 	FLAGS_max_log_size = 1024;            // Set max log file size(GB)
 	FLAGS_stop_logging_if_full_disk = true;
 
+
+//huli test
+//    ec = System_communication_mq::get_instance_references().rabbitmq_init();
+//    if(ec != SUCCESS)
+//    {
+//        LOG(ERROR) << "system communication mq init failed: " << ec.to_string();
+//        return -1;
+//    }
+//
+//    while (true)
+//    {}
+//
+//    return 0;
+
+
 	// std::cout << " huli test :::: " << " wanji_manager_init = " << 1 << std::endl;
 
 	// 定义服务的终端id
@@ -90,7 +109,7 @@ int main(int argc,char* argv[])
 	}
 	std::cout << " huli test :::: " << " t_terminal_id = " << t_terminal_id << std::endl;
 
-	Error_manager ec;
+//	Error_manager ec;
 
 	// 初始化
 	if(WJ_VELO == 0 || WJ_VELO == 2)
@@ -106,9 +125,11 @@ int main(int argc,char* argv[])
 	if(WJ_VELO == 1 || WJ_VELO == 2)
 	{
 		ec = Velodyne_manager::get_instance_references().velodyne_manager_init(t_terminal_id);
-		std::cout << "veodyne_manager = " << Velodyne_manager::get_instance_references().check_status().to_string() << std::endl;
+
 		if (ec != SUCCESS)
 		{
+            std::cout << "veodyne_manager = " << ec.to_string() << std::endl;
+            std::cout << "veodyne_manager = " << Velodyne_manager::get_instance_references().check_status().to_string() << std::endl;
 			LOG(ERROR) << "velodyne_manager init failed: " << ec.to_string();
 			return -1;
 		}
@@ -139,6 +160,7 @@ int main(int argc,char* argv[])
 		LOG(ERROR) << "system communication mq init failed: " << ec.to_string();
 		return -1;
 	}
+	System_communication_mq::get_instance_references().set_encapsulate_status_cycle_time(100);
 
 	// prev_test_pred_task();
 	// test_whole_process();

+ 47 - 704
message/measure_message.pb.cc

@@ -60,11 +60,6 @@ class Ground_status_msgDefaultTypeInternal {
   ::google::protobuf::internal::ExplicitlyConstructed<Ground_status_msg>
       _instance;
 } _Ground_status_msg_default_instance_;
-class measure_infoDefaultTypeInternal {
- public:
-  ::google::protobuf::internal::ExplicitlyConstructed<measure_info>
-      _instance;
-} _measure_info_default_instance_;
 class Cloud_coordinateDefaultTypeInternal {
  public:
   ::google::protobuf::internal::ExplicitlyConstructed<Cloud_coordinate>
@@ -191,20 +186,6 @@ static void InitDefaultsGround_status_msg() {
       &protobuf_message_5fbase_2eproto::scc_info_Error_manager.base,
       &protobuf_measure_5fmessage_2eproto::scc_info_Cloud_coordinate.base,}};
 
-static void InitDefaultsmeasure_info() {
-  GOOGLE_PROTOBUF_VERIFY_VERSION;
-
-  {
-    void* ptr = &::message::_measure_info_default_instance_;
-    new (ptr) ::message::measure_info();
-    ::google::protobuf::internal::OnShutdownDestroyMessage(ptr);
-  }
-  ::message::measure_info::InitAsDefaultInstance();
-}
-
-::google::protobuf::internal::SCCInfo<0> scc_info_measure_info =
-    {{ATOMIC_VAR_INIT(::google::protobuf::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsmeasure_info}, {}};
-
 static void InitDefaultsCloud_coordinate() {
   GOOGLE_PROTOBUF_VERIFY_VERSION;
 
@@ -275,14 +256,13 @@ void InitDefaults() {
   ::google::protobuf::internal::InitSCC(&scc_info_Ground_detect_request_msg.base);
   ::google::protobuf::internal::InitSCC(&scc_info_Ground_detect_response_msg.base);
   ::google::protobuf::internal::InitSCC(&scc_info_Ground_status_msg.base);
-  ::google::protobuf::internal::InitSCC(&scc_info_measure_info.base);
   ::google::protobuf::internal::InitSCC(&scc_info_Cloud_coordinate.base);
   ::google::protobuf::internal::InitSCC(&scc_info_Cloud_type.base);
   ::google::protobuf::internal::InitSCC(&scc_info_Locate_sift_request_msg.base);
   ::google::protobuf::internal::InitSCC(&scc_info_Locate_sift_response_msg.base);
 }
 
-::google::protobuf::Metadata file_level_metadata[11];
+::google::protobuf::Metadata file_level_metadata[10];
 const ::google::protobuf::EnumDescriptor* file_level_enum_descriptors[7];
 
 const ::google::protobuf::uint32 TableStruct::offsets[] GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {
@@ -380,31 +360,6 @@ const ::google::protobuf::uint32 TableStruct::offsets[] GOOGLE_PROTOBUF_ATTRIBUT
   7,
   3,
   ~0u,
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, _has_bits_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, _internal_metadata_),
-  ~0u,  // no _extensions_
-  ~0u,  // no _oneof_case_
-  ~0u,  // no _weak_field_map_
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, cx_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, cy_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, theta_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, length_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, width_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, height_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, wheelbase_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, front_theta_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, border_stuta_),
-  GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::measure_info, ground_status_),
-  0,
-  1,
-  2,
-  3,
-  4,
-  5,
-  6,
-  7,
-  8,
-  9,
   GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::Cloud_coordinate, _has_bits_),
   GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(::message::Cloud_coordinate, _internal_metadata_),
   ~0u,  // no _extensions_
@@ -463,11 +418,10 @@ static const ::google::protobuf::internal::MigrationSchema schemas[] GOOGLE_PROT
   { 43, 51, sizeof(::message::Ground_detect_request_msg)},
   { 54, 64, sizeof(::message::Ground_detect_response_msg)},
   { 69, 84, sizeof(::message::Ground_status_msg)},
-  { 94, 109, sizeof(::message::measure_info)},
-  { 119, 127, sizeof(::message::Cloud_coordinate)},
-  { 130, 136, sizeof(::message::Cloud_type)},
-  { 137, 147, sizeof(::message::Locate_sift_request_msg)},
-  { 152, 163, sizeof(::message::Locate_sift_response_msg)},
+  { 94, 102, sizeof(::message::Cloud_coordinate)},
+  { 105, 111, sizeof(::message::Cloud_type)},
+  { 112, 122, sizeof(::message::Locate_sift_request_msg)},
+  { 127, 138, sizeof(::message::Locate_sift_response_msg)},
 };
 
 static ::google::protobuf::Message const * const file_default_instances[] = {
@@ -477,7 +431,6 @@ static ::google::protobuf::Message const * const file_default_instances[] = {
   reinterpret_cast<const ::google::protobuf::Message*>(&::message::_Ground_detect_request_msg_default_instance_),
   reinterpret_cast<const ::google::protobuf::Message*>(&::message::_Ground_detect_response_msg_default_instance_),
   reinterpret_cast<const ::google::protobuf::Message*>(&::message::_Ground_status_msg_default_instance_),
-  reinterpret_cast<const ::google::protobuf::Message*>(&::message::_measure_info_default_instance_),
   reinterpret_cast<const ::google::protobuf::Message*>(&::message::_Cloud_coordinate_default_instance_),
   reinterpret_cast<const ::google::protobuf::Message*>(&::message::_Cloud_type_default_instance_),
   reinterpret_cast<const ::google::protobuf::Message*>(&::message::_Locate_sift_request_msg_default_instance_),
@@ -499,7 +452,7 @@ void protobuf_AssignDescriptorsOnce() {
 void protobuf_RegisterTypes(const ::std::string&) GOOGLE_PROTOBUF_ATTRIBUTE_COLD;
 void protobuf_RegisterTypes(const ::std::string&) {
   protobuf_AssignDescriptorsOnce();
-  ::google::protobuf::internal::RegisterAllTypes(file_level_metadata, 11);
+  ::google::protobuf::internal::RegisterAllTypes(file_level_metadata, 10);
 }
 
 void AddDescriptorsImpl() {
@@ -545,54 +498,49 @@ void AddDescriptorsImpl() {
       "round_statu\022\025\n\rborder_status\030\010 \002(\005\022-\n\rer"
       "ror_manager\030\t \002(\0132\026.message.Error_manage"
       "r\022(\n\005cloud\030\n \003(\0132\031.message.Cloud_coordin"
-      "ate\"\271\001\n\014measure_info\022\n\n\002cx\030\001 \002(\002\022\n\n\002cy\030\002"
-      " \002(\002\022\r\n\005theta\030\003 \002(\002\022\016\n\006length\030\004 \002(\002\022\r\n\005w"
-      "idth\030\005 \002(\002\022\016\n\006height\030\006 \002(\002\022\021\n\twheelbase\030"
-      "\007 \002(\002\022\023\n\013front_theta\030\010 \002(\002\022\024\n\014border_stu"
-      "ta\030\t \002(\005\022\025\n\rground_status\030\n \002(\005\"3\n\020Cloud"
-      "_coordinate\022\t\n\001x\030\001 \002(\002\022\t\n\001y\030\002 \002(\002\022\t\n\001z\030\003"
-      " \002(\002\"\032\n\nCloud_type\022\014\n\004type\030\001 \002(\005\"\304\001\n\027Loc"
-      "ate_sift_request_msg\022%\n\tbase_info\030\001 \002(\0132"
-      "\022.message.Base_info\022\023\n\013command_key\030\002 \002(\t"
-      "\022%\n\tid_struct\030\003 \002(\0132\022.message.Id_struct\022"
-      "\020\n\010lidar_id\030\004 \002(\005\0224\n\021cloud_coordinates\030\005"
-      " \003(\0132\031.message.Cloud_coordinate\"\347\001\n\030Loca"
-      "te_sift_response_msg\022%\n\tbase_info\030\001 \002(\0132"
-      "\022.message.Base_info\022\023\n\013command_key\030\002 \002(\t"
-      "\022%\n\tid_struct\030\003 \002(\0132\022.message.Id_struct\022"
-      "\020\n\010lidar_id\030\004 \002(\005\022\'\n\ncloud_type\030\005 \003(\0132\023."
-      "message.Cloud_type\022-\n\rerror_manager\030\006 \002("
-      "\0132\026.message.Error_manager*\237\001\n\024Laser_mana"
-      "ger_status\022\030\n\024LASER_MANAGER_UNKNOW\020\000\022\027\n\023"
-      "LASER_MANAGER_READY\020\001\022\035\n\031LASER_MANAGER_I"
-      "SSUED_TASK\020\002\022\034\n\030LASER_MANAGER_WAIT_REPLY"
-      "\020\003\022\027\n\023LASER_MANAGER_FAULT\020\004*U\n\013Laser_sta"
-      "tu\022\024\n\020LASER_DISCONNECT\020\000\022\017\n\013LASER_READY\020"
-      "\001\022\016\n\nLASER_BUSY\020\002\022\017\n\013LASER_FAULT\020\003*\261\001\n\025L"
-      "ocate_manager_status\022\031\n\025LOCATE_MANAGER_U"
-      "NKNOW\020\000\022\030\n\024LOCATE_MANAGER_READY\020\001\022\027\n\023LOC"
-      "ATE_MANAGER_SIFT\020\002\022\026\n\022LOCATE_MANAGER_CAR"
-      "\020\003\022\030\n\024LOCATE_MANAGER_WHEEL\020\004\022\030\n\024LOCATE_M"
-      "ANAGER_FAULT\020\005*\367\001\n\024Wanji_manager_status\022"
-      "\031\n\025WANJI_MANAGER_UNKNOWN\020\000\022\027\n\023WANJI_MANA"
-      "GER_READY\020\001\022\026\n\022WANJI_MANAGER_BUSY\020\002\022\035\n\031W"
-      "ANJI_MANAGER_ISSUED_SCAN\020\003\022\033\n\027WANJI_MANA"
-      "GER_WAIT_SCAN\020\004\022\037\n\033WANJI_MANAGER_ISSUED_"
-      "DETECT\020\005\022\035\n\031WANJI_MANAGER_WAIT_DETECT\020\006\022"
-      "\027\n\023WANJI_MANAGER_FAULT\020\n*\267\001\n\031Wanji_lidar"
-      "_device_status\022\036\n\032WANJI_LIDAR_DEVICE_UNK"
-      "NOWN\020\000\022\034\n\030WANJI_LIDAR_DEVICE_READY\020\001\022!\n\035"
-      "WANJI_LIDAR_DEVICE_DISCONNECT\020\002\022\033\n\027WANJI"
-      "_LIDAR_DEVICE_BUSY\020\003\022\034\n\030WANJI_LIDAR_DEVI"
-      "CE_FAULT\020\n*{\n\024Region_worker_status\022\031\n\025RE"
-      "GION_WORKER_UNKNOWN\020\000\022\027\n\023REGION_WORKER_R"
-      "EADY\020\001\022\026\n\022REGION_WORKER_BUSY\020\002\022\027\n\023REGION"
-      "_WORKER_FAULT\020\n*O\n\014Ground_statu\022\013\n\007Nothi"
-      "ng\020\000\022\t\n\005Noise\020\001\022\017\n\013Car_correct\020\002\022\026\n\022Car_"
-      "border_reached\020\003"
+      "ate\"3\n\020Cloud_coordinate\022\t\n\001x\030\001 \002(\002\022\t\n\001y\030"
+      "\002 \002(\002\022\t\n\001z\030\003 \002(\002\"\032\n\nCloud_type\022\014\n\004type\030\001"
+      " \002(\005\"\304\001\n\027Locate_sift_request_msg\022%\n\tbase"
+      "_info\030\001 \002(\0132\022.message.Base_info\022\023\n\013comma"
+      "nd_key\030\002 \002(\t\022%\n\tid_struct\030\003 \002(\0132\022.messag"
+      "e.Id_struct\022\020\n\010lidar_id\030\004 \002(\005\0224\n\021cloud_c"
+      "oordinates\030\005 \003(\0132\031.message.Cloud_coordin"
+      "ate\"\347\001\n\030Locate_sift_response_msg\022%\n\tbase"
+      "_info\030\001 \002(\0132\022.message.Base_info\022\023\n\013comma"
+      "nd_key\030\002 \002(\t\022%\n\tid_struct\030\003 \002(\0132\022.messag"
+      "e.Id_struct\022\020\n\010lidar_id\030\004 \002(\005\022\'\n\ncloud_t"
+      "ype\030\005 \003(\0132\023.message.Cloud_type\022-\n\rerror_"
+      "manager\030\006 \002(\0132\026.message.Error_manager*\237\001"
+      "\n\024Laser_manager_status\022\030\n\024LASER_MANAGER_"
+      "UNKNOW\020\000\022\027\n\023LASER_MANAGER_READY\020\001\022\035\n\031LAS"
+      "ER_MANAGER_ISSUED_TASK\020\002\022\034\n\030LASER_MANAGE"
+      "R_WAIT_REPLY\020\003\022\027\n\023LASER_MANAGER_FAULT\020\004*"
+      "U\n\013Laser_statu\022\024\n\020LASER_DISCONNECT\020\000\022\017\n\013"
+      "LASER_READY\020\001\022\016\n\nLASER_BUSY\020\002\022\017\n\013LASER_F"
+      "AULT\020\003*\261\001\n\025Locate_manager_status\022\031\n\025LOCA"
+      "TE_MANAGER_UNKNOW\020\000\022\030\n\024LOCATE_MANAGER_RE"
+      "ADY\020\001\022\027\n\023LOCATE_MANAGER_SIFT\020\002\022\026\n\022LOCATE"
+      "_MANAGER_CAR\020\003\022\030\n\024LOCATE_MANAGER_WHEEL\020\004"
+      "\022\030\n\024LOCATE_MANAGER_FAULT\020\005*\367\001\n\024Wanji_man"
+      "ager_status\022\031\n\025WANJI_MANAGER_UNKNOWN\020\000\022\027"
+      "\n\023WANJI_MANAGER_READY\020\001\022\026\n\022WANJI_MANAGER"
+      "_BUSY\020\002\022\035\n\031WANJI_MANAGER_ISSUED_SCAN\020\003\022\033"
+      "\n\027WANJI_MANAGER_WAIT_SCAN\020\004\022\037\n\033WANJI_MAN"
+      "AGER_ISSUED_DETECT\020\005\022\035\n\031WANJI_MANAGER_WA"
+      "IT_DETECT\020\006\022\027\n\023WANJI_MANAGER_FAULT\020\n*\267\001\n"
+      "\031Wanji_lidar_device_status\022\036\n\032WANJI_LIDA"
+      "R_DEVICE_UNKNOWN\020\000\022\034\n\030WANJI_LIDAR_DEVICE"
+      "_READY\020\001\022!\n\035WANJI_LIDAR_DEVICE_DISCONNEC"
+      "T\020\002\022\033\n\027WANJI_LIDAR_DEVICE_BUSY\020\003\022\034\n\030WANJ"
+      "I_LIDAR_DEVICE_FAULT\020\n*{\n\024Region_worker_"
+      "status\022\031\n\025REGION_WORKER_UNKNOWN\020\000\022\027\n\023REG"
+      "ION_WORKER_READY\020\001\022\026\n\022REGION_WORKER_BUSY"
+      "\020\002\022\027\n\023REGION_WORKER_FAULT\020\n*O\n\014Ground_st"
+      "atu\022\013\n\007Nothing\020\000\022\t\n\005Noise\020\001\022\017\n\013Car_corre"
+      "ct\020\002\022\026\n\022Car_border_reached\020\003"
   };
   ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
-      descriptor, 3376);
+      descriptor, 3188);
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
     "measure_message.proto", &protobuf_RegisterTypes);
   ::protobuf_message_5fbase_2eproto::AddDescriptors();
@@ -3815,608 +3763,6 @@ void Ground_status_msg::InternalSwap(Ground_status_msg* other) {
 }
 
 
-// ===================================================================
-
-void measure_info::InitAsDefaultInstance() {
-}
-#if !defined(_MSC_VER) || _MSC_VER >= 1900
-const int measure_info::kCxFieldNumber;
-const int measure_info::kCyFieldNumber;
-const int measure_info::kThetaFieldNumber;
-const int measure_info::kLengthFieldNumber;
-const int measure_info::kWidthFieldNumber;
-const int measure_info::kHeightFieldNumber;
-const int measure_info::kWheelbaseFieldNumber;
-const int measure_info::kFrontThetaFieldNumber;
-const int measure_info::kBorderStutaFieldNumber;
-const int measure_info::kGroundStatusFieldNumber;
-#endif  // !defined(_MSC_VER) || _MSC_VER >= 1900
-
-measure_info::measure_info()
-  : ::google::protobuf::Message(), _internal_metadata_(NULL) {
-  ::google::protobuf::internal::InitSCC(
-      &protobuf_measure_5fmessage_2eproto::scc_info_measure_info.base);
-  SharedCtor();
-  // @@protoc_insertion_point(constructor:message.measure_info)
-}
-measure_info::measure_info(const measure_info& from)
-  : ::google::protobuf::Message(),
-      _internal_metadata_(NULL),
-      _has_bits_(from._has_bits_) {
-  _internal_metadata_.MergeFrom(from._internal_metadata_);
-  ::memcpy(&cx_, &from.cx_,
-    static_cast<size_t>(reinterpret_cast<char*>(&ground_status_) -
-    reinterpret_cast<char*>(&cx_)) + sizeof(ground_status_));
-  // @@protoc_insertion_point(copy_constructor:message.measure_info)
-}
-
-void measure_info::SharedCtor() {
-  ::memset(&cx_, 0, static_cast<size_t>(
-      reinterpret_cast<char*>(&ground_status_) -
-      reinterpret_cast<char*>(&cx_)) + sizeof(ground_status_));
-}
-
-measure_info::~measure_info() {
-  // @@protoc_insertion_point(destructor:message.measure_info)
-  SharedDtor();
-}
-
-void measure_info::SharedDtor() {
-}
-
-void measure_info::SetCachedSize(int size) const {
-  _cached_size_.Set(size);
-}
-const ::google::protobuf::Descriptor* measure_info::descriptor() {
-  ::protobuf_measure_5fmessage_2eproto::protobuf_AssignDescriptorsOnce();
-  return ::protobuf_measure_5fmessage_2eproto::file_level_metadata[kIndexInFileMessages].descriptor;
-}
-
-const measure_info& measure_info::default_instance() {
-  ::google::protobuf::internal::InitSCC(&protobuf_measure_5fmessage_2eproto::scc_info_measure_info.base);
-  return *internal_default_instance();
-}
-
-
-void measure_info::Clear() {
-// @@protoc_insertion_point(message_clear_start:message.measure_info)
-  ::google::protobuf::uint32 cached_has_bits = 0;
-  // Prevent compiler warnings about cached_has_bits being unused
-  (void) cached_has_bits;
-
-  cached_has_bits = _has_bits_[0];
-  if (cached_has_bits & 255u) {
-    ::memset(&cx_, 0, static_cast<size_t>(
-        reinterpret_cast<char*>(&front_theta_) -
-        reinterpret_cast<char*>(&cx_)) + sizeof(front_theta_));
-  }
-  if (cached_has_bits & 768u) {
-    ::memset(&border_stuta_, 0, static_cast<size_t>(
-        reinterpret_cast<char*>(&ground_status_) -
-        reinterpret_cast<char*>(&border_stuta_)) + sizeof(ground_status_));
-  }
-  _has_bits_.Clear();
-  _internal_metadata_.Clear();
-}
-
-bool measure_info::MergePartialFromCodedStream(
-    ::google::protobuf::io::CodedInputStream* input) {
-#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure
-  ::google::protobuf::uint32 tag;
-  // @@protoc_insertion_point(parse_start:message.measure_info)
-  for (;;) {
-    ::std::pair<::google::protobuf::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u);
-    tag = p.first;
-    if (!p.second) goto handle_unusual;
-    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
-      // required float cx = 1;
-      case 1: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(13u /* 13 & 0xFF */)) {
-          set_has_cx();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &cx_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required float cy = 2;
-      case 2: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(21u /* 21 & 0xFF */)) {
-          set_has_cy();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &cy_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required float theta = 3;
-      case 3: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(29u /* 29 & 0xFF */)) {
-          set_has_theta();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &theta_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required float length = 4;
-      case 4: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(37u /* 37 & 0xFF */)) {
-          set_has_length();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &length_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required float width = 5;
-      case 5: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(45u /* 45 & 0xFF */)) {
-          set_has_width();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &width_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required float height = 6;
-      case 6: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(53u /* 53 & 0xFF */)) {
-          set_has_height();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &height_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required float wheelbase = 7;
-      case 7: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(61u /* 61 & 0xFF */)) {
-          set_has_wheelbase();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &wheelbase_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required float front_theta = 8;
-      case 8: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(69u /* 69 & 0xFF */)) {
-          set_has_front_theta();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
-                 input, &front_theta_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required int32 border_stuta = 9;
-      case 9: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(72u /* 72 & 0xFF */)) {
-          set_has_border_stuta();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
-                 input, &border_stuta_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      // required int32 ground_status = 10;
-      case 10: {
-        if (static_cast< ::google::protobuf::uint8>(tag) ==
-            static_cast< ::google::protobuf::uint8>(80u /* 80 & 0xFF */)) {
-          set_has_ground_status();
-          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
-                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
-                 input, &ground_status_)));
-        } else {
-          goto handle_unusual;
-        }
-        break;
-      }
-
-      default: {
-      handle_unusual:
-        if (tag == 0) {
-          goto success;
-        }
-        DO_(::google::protobuf::internal::WireFormat::SkipField(
-              input, tag, _internal_metadata_.mutable_unknown_fields()));
-        break;
-      }
-    }
-  }
-success:
-  // @@protoc_insertion_point(parse_success:message.measure_info)
-  return true;
-failure:
-  // @@protoc_insertion_point(parse_failure:message.measure_info)
-  return false;
-#undef DO_
-}
-
-void measure_info::SerializeWithCachedSizes(
-    ::google::protobuf::io::CodedOutputStream* output) const {
-  // @@protoc_insertion_point(serialize_start:message.measure_info)
-  ::google::protobuf::uint32 cached_has_bits = 0;
-  (void) cached_has_bits;
-
-  cached_has_bits = _has_bits_[0];
-  // required float cx = 1;
-  if (cached_has_bits & 0x00000001u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(1, this->cx(), output);
-  }
-
-  // required float cy = 2;
-  if (cached_has_bits & 0x00000002u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->cy(), output);
-  }
-
-  // required float theta = 3;
-  if (cached_has_bits & 0x00000004u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(3, this->theta(), output);
-  }
-
-  // required float length = 4;
-  if (cached_has_bits & 0x00000008u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(4, this->length(), output);
-  }
-
-  // required float width = 5;
-  if (cached_has_bits & 0x00000010u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(5, this->width(), output);
-  }
-
-  // required float height = 6;
-  if (cached_has_bits & 0x00000020u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(6, this->height(), output);
-  }
-
-  // required float wheelbase = 7;
-  if (cached_has_bits & 0x00000040u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(7, this->wheelbase(), output);
-  }
-
-  // required float front_theta = 8;
-  if (cached_has_bits & 0x00000080u) {
-    ::google::protobuf::internal::WireFormatLite::WriteFloat(8, this->front_theta(), output);
-  }
-
-  // required int32 border_stuta = 9;
-  if (cached_has_bits & 0x00000100u) {
-    ::google::protobuf::internal::WireFormatLite::WriteInt32(9, this->border_stuta(), output);
-  }
-
-  // required int32 ground_status = 10;
-  if (cached_has_bits & 0x00000200u) {
-    ::google::protobuf::internal::WireFormatLite::WriteInt32(10, this->ground_status(), output);
-  }
-
-  if (_internal_metadata_.have_unknown_fields()) {
-    ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
-        _internal_metadata_.unknown_fields(), output);
-  }
-  // @@protoc_insertion_point(serialize_end:message.measure_info)
-}
-
-::google::protobuf::uint8* measure_info::InternalSerializeWithCachedSizesToArray(
-    bool deterministic, ::google::protobuf::uint8* target) const {
-  (void)deterministic; // Unused
-  // @@protoc_insertion_point(serialize_to_array_start:message.measure_info)
-  ::google::protobuf::uint32 cached_has_bits = 0;
-  (void) cached_has_bits;
-
-  cached_has_bits = _has_bits_[0];
-  // required float cx = 1;
-  if (cached_has_bits & 0x00000001u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(1, this->cx(), target);
-  }
-
-  // required float cy = 2;
-  if (cached_has_bits & 0x00000002u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(2, this->cy(), target);
-  }
-
-  // required float theta = 3;
-  if (cached_has_bits & 0x00000004u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(3, this->theta(), target);
-  }
-
-  // required float length = 4;
-  if (cached_has_bits & 0x00000008u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(4, this->length(), target);
-  }
-
-  // required float width = 5;
-  if (cached_has_bits & 0x00000010u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(5, this->width(), target);
-  }
-
-  // required float height = 6;
-  if (cached_has_bits & 0x00000020u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(6, this->height(), target);
-  }
-
-  // required float wheelbase = 7;
-  if (cached_has_bits & 0x00000040u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(7, this->wheelbase(), target);
-  }
-
-  // required float front_theta = 8;
-  if (cached_has_bits & 0x00000080u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(8, this->front_theta(), target);
-  }
-
-  // required int32 border_stuta = 9;
-  if (cached_has_bits & 0x00000100u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(9, this->border_stuta(), target);
-  }
-
-  // required int32 ground_status = 10;
-  if (cached_has_bits & 0x00000200u) {
-    target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(10, this->ground_status(), target);
-  }
-
-  if (_internal_metadata_.have_unknown_fields()) {
-    target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
-        _internal_metadata_.unknown_fields(), target);
-  }
-  // @@protoc_insertion_point(serialize_to_array_end:message.measure_info)
-  return target;
-}
-
-size_t measure_info::RequiredFieldsByteSizeFallback() const {
-// @@protoc_insertion_point(required_fields_byte_size_fallback_start:message.measure_info)
-  size_t total_size = 0;
-
-  if (has_cx()) {
-    // required float cx = 1;
-    total_size += 1 + 4;
-  }
-
-  if (has_cy()) {
-    // required float cy = 2;
-    total_size += 1 + 4;
-  }
-
-  if (has_theta()) {
-    // required float theta = 3;
-    total_size += 1 + 4;
-  }
-
-  if (has_length()) {
-    // required float length = 4;
-    total_size += 1 + 4;
-  }
-
-  if (has_width()) {
-    // required float width = 5;
-    total_size += 1 + 4;
-  }
-
-  if (has_height()) {
-    // required float height = 6;
-    total_size += 1 + 4;
-  }
-
-  if (has_wheelbase()) {
-    // required float wheelbase = 7;
-    total_size += 1 + 4;
-  }
-
-  if (has_front_theta()) {
-    // required float front_theta = 8;
-    total_size += 1 + 4;
-  }
-
-  if (has_border_stuta()) {
-    // required int32 border_stuta = 9;
-    total_size += 1 +
-      ::google::protobuf::internal::WireFormatLite::Int32Size(
-        this->border_stuta());
-  }
-
-  if (has_ground_status()) {
-    // required int32 ground_status = 10;
-    total_size += 1 +
-      ::google::protobuf::internal::WireFormatLite::Int32Size(
-        this->ground_status());
-  }
-
-  return total_size;
-}
-size_t measure_info::ByteSizeLong() const {
-// @@protoc_insertion_point(message_byte_size_start:message.measure_info)
-  size_t total_size = 0;
-
-  if (_internal_metadata_.have_unknown_fields()) {
-    total_size +=
-      ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
-        _internal_metadata_.unknown_fields());
-  }
-  if (((_has_bits_[0] & 0x000003ff) ^ 0x000003ff) == 0) {  // All required fields are present.
-    // required float cx = 1;
-    total_size += 1 + 4;
-
-    // required float cy = 2;
-    total_size += 1 + 4;
-
-    // required float theta = 3;
-    total_size += 1 + 4;
-
-    // required float length = 4;
-    total_size += 1 + 4;
-
-    // required float width = 5;
-    total_size += 1 + 4;
-
-    // required float height = 6;
-    total_size += 1 + 4;
-
-    // required float wheelbase = 7;
-    total_size += 1 + 4;
-
-    // required float front_theta = 8;
-    total_size += 1 + 4;
-
-    // required int32 border_stuta = 9;
-    total_size += 1 +
-      ::google::protobuf::internal::WireFormatLite::Int32Size(
-        this->border_stuta());
-
-    // required int32 ground_status = 10;
-    total_size += 1 +
-      ::google::protobuf::internal::WireFormatLite::Int32Size(
-        this->ground_status());
-
-  } else {
-    total_size += RequiredFieldsByteSizeFallback();
-  }
-  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
-  SetCachedSize(cached_size);
-  return total_size;
-}
-
-void measure_info::MergeFrom(const ::google::protobuf::Message& from) {
-// @@protoc_insertion_point(generalized_merge_from_start:message.measure_info)
-  GOOGLE_DCHECK_NE(&from, this);
-  const measure_info* source =
-      ::google::protobuf::internal::DynamicCastToGenerated<const measure_info>(
-          &from);
-  if (source == NULL) {
-  // @@protoc_insertion_point(generalized_merge_from_cast_fail:message.measure_info)
-    ::google::protobuf::internal::ReflectionOps::Merge(from, this);
-  } else {
-  // @@protoc_insertion_point(generalized_merge_from_cast_success:message.measure_info)
-    MergeFrom(*source);
-  }
-}
-
-void measure_info::MergeFrom(const measure_info& from) {
-// @@protoc_insertion_point(class_specific_merge_from_start:message.measure_info)
-  GOOGLE_DCHECK_NE(&from, this);
-  _internal_metadata_.MergeFrom(from._internal_metadata_);
-  ::google::protobuf::uint32 cached_has_bits = 0;
-  (void) cached_has_bits;
-
-  cached_has_bits = from._has_bits_[0];
-  if (cached_has_bits & 255u) {
-    if (cached_has_bits & 0x00000001u) {
-      cx_ = from.cx_;
-    }
-    if (cached_has_bits & 0x00000002u) {
-      cy_ = from.cy_;
-    }
-    if (cached_has_bits & 0x00000004u) {
-      theta_ = from.theta_;
-    }
-    if (cached_has_bits & 0x00000008u) {
-      length_ = from.length_;
-    }
-    if (cached_has_bits & 0x00000010u) {
-      width_ = from.width_;
-    }
-    if (cached_has_bits & 0x00000020u) {
-      height_ = from.height_;
-    }
-    if (cached_has_bits & 0x00000040u) {
-      wheelbase_ = from.wheelbase_;
-    }
-    if (cached_has_bits & 0x00000080u) {
-      front_theta_ = from.front_theta_;
-    }
-    _has_bits_[0] |= cached_has_bits;
-  }
-  if (cached_has_bits & 768u) {
-    if (cached_has_bits & 0x00000100u) {
-      border_stuta_ = from.border_stuta_;
-    }
-    if (cached_has_bits & 0x00000200u) {
-      ground_status_ = from.ground_status_;
-    }
-    _has_bits_[0] |= cached_has_bits;
-  }
-}
-
-void measure_info::CopyFrom(const ::google::protobuf::Message& from) {
-// @@protoc_insertion_point(generalized_copy_from_start:message.measure_info)
-  if (&from == this) return;
-  Clear();
-  MergeFrom(from);
-}
-
-void measure_info::CopyFrom(const measure_info& from) {
-// @@protoc_insertion_point(class_specific_copy_from_start:message.measure_info)
-  if (&from == this) return;
-  Clear();
-  MergeFrom(from);
-}
-
-bool measure_info::IsInitialized() const {
-  if ((_has_bits_[0] & 0x000003ff) != 0x000003ff) return false;
-  return true;
-}
-
-void measure_info::Swap(measure_info* other) {
-  if (other == this) return;
-  InternalSwap(other);
-}
-void measure_info::InternalSwap(measure_info* other) {
-  using std::swap;
-  swap(cx_, other->cx_);
-  swap(cy_, other->cy_);
-  swap(theta_, other->theta_);
-  swap(length_, other->length_);
-  swap(width_, other->width_);
-  swap(height_, other->height_);
-  swap(wheelbase_, other->wheelbase_);
-  swap(front_theta_, other->front_theta_);
-  swap(border_stuta_, other->border_stuta_);
-  swap(ground_status_, other->ground_status_);
-  swap(_has_bits_[0], other->_has_bits_[0]);
-  _internal_metadata_.Swap(&other->_internal_metadata_);
-}
-
-::google::protobuf::Metadata measure_info::GetMetadata() const {
-  protobuf_measure_5fmessage_2eproto::protobuf_AssignDescriptorsOnce();
-  return ::protobuf_measure_5fmessage_2eproto::file_level_metadata[kIndexInFileMessages];
-}
-
-
 // ===================================================================
 
 void Cloud_coordinate::InitAsDefaultInstance() {
@@ -6021,9 +5367,6 @@ template<> GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE ::message::Ground_detect_response_
 template<> GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE ::message::Ground_status_msg* Arena::CreateMaybeMessage< ::message::Ground_status_msg >(Arena* arena) {
   return Arena::CreateInternal< ::message::Ground_status_msg >(arena);
 }
-template<> GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE ::message::measure_info* Arena::CreateMaybeMessage< ::message::measure_info >(Arena* arena) {
-  return Arena::CreateInternal< ::message::measure_info >(arena);
-}
 template<> GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE ::message::Cloud_coordinate* Arena::CreateMaybeMessage< ::message::Cloud_coordinate >(Arena* arena) {
   return Arena::CreateInternal< ::message::Cloud_coordinate >(arena);
 }

+ 5 - 462
message/measure_message.pb.h

@@ -40,7 +40,7 @@ namespace protobuf_measure_5fmessage_2eproto {
 struct TableStruct {
   static const ::google::protobuf::internal::ParseTableField entries[];
   static const ::google::protobuf::internal::AuxillaryParseTableField aux[];
-  static const ::google::protobuf::internal::ParseTable schema[11];
+  static const ::google::protobuf::internal::ParseTable schema[10];
   static const ::google::protobuf::internal::FieldMetadata field_metadata[];
   static const ::google::protobuf::internal::SerializationTable serialization_table[];
   static const ::google::protobuf::uint32 offsets[];
@@ -78,9 +78,6 @@ extern Measure_response_msgDefaultTypeInternal _Measure_response_msg_default_ins
 class Measure_status_msg;
 class Measure_status_msgDefaultTypeInternal;
 extern Measure_status_msgDefaultTypeInternal _Measure_status_msg_default_instance_;
-class measure_info;
-class measure_infoDefaultTypeInternal;
-extern measure_infoDefaultTypeInternal _measure_info_default_instance_;
 }  // namespace message
 namespace google {
 namespace protobuf {
@@ -94,7 +91,6 @@ template<> ::message::Locate_sift_response_msg* Arena::CreateMaybeMessage<::mess
 template<> ::message::Measure_request_msg* Arena::CreateMaybeMessage<::message::Measure_request_msg>(Arena*);
 template<> ::message::Measure_response_msg* Arena::CreateMaybeMessage<::message::Measure_response_msg>(Arena*);
 template<> ::message::Measure_status_msg* Arena::CreateMaybeMessage<::message::Measure_status_msg>(Arena*);
-template<> ::message::measure_info* Arena::CreateMaybeMessage<::message::measure_info>(Arena*);
 }  // namespace protobuf
 }  // namespace google
 namespace message {
@@ -1350,213 +1346,6 @@ class Ground_status_msg : public ::google::protobuf::Message /* @@protoc_inserti
 };
 // -------------------------------------------------------------------
 
-class measure_info : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:message.measure_info) */ {
- public:
-  measure_info();
-  virtual ~measure_info();
-
-  measure_info(const measure_info& from);
-
-  inline measure_info& operator=(const measure_info& from) {
-    CopyFrom(from);
-    return *this;
-  }
-  #if LANG_CXX11
-  measure_info(measure_info&& from) noexcept
-    : measure_info() {
-    *this = ::std::move(from);
-  }
-
-  inline measure_info& operator=(measure_info&& from) noexcept {
-    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
-      if (this != &from) InternalSwap(&from);
-    } else {
-      CopyFrom(from);
-    }
-    return *this;
-  }
-  #endif
-  inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
-    return _internal_metadata_.unknown_fields();
-  }
-  inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
-    return _internal_metadata_.mutable_unknown_fields();
-  }
-
-  static const ::google::protobuf::Descriptor* descriptor();
-  static const measure_info& default_instance();
-
-  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
-  static inline const measure_info* internal_default_instance() {
-    return reinterpret_cast<const measure_info*>(
-               &_measure_info_default_instance_);
-  }
-  static constexpr int kIndexInFileMessages =
-    6;
-
-  void Swap(measure_info* other);
-  friend void swap(measure_info& a, measure_info& b) {
-    a.Swap(&b);
-  }
-
-  // implements Message ----------------------------------------------
-
-  inline measure_info* New() const final {
-    return CreateMaybeMessage<measure_info>(NULL);
-  }
-
-  measure_info* New(::google::protobuf::Arena* arena) const final {
-    return CreateMaybeMessage<measure_info>(arena);
-  }
-  void CopyFrom(const ::google::protobuf::Message& from) final;
-  void MergeFrom(const ::google::protobuf::Message& from) final;
-  void CopyFrom(const measure_info& from);
-  void MergeFrom(const measure_info& from);
-  void Clear() final;
-  bool IsInitialized() const final;
-
-  size_t ByteSizeLong() const final;
-  bool MergePartialFromCodedStream(
-      ::google::protobuf::io::CodedInputStream* input) final;
-  void SerializeWithCachedSizes(
-      ::google::protobuf::io::CodedOutputStream* output) const final;
-  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
-      bool deterministic, ::google::protobuf::uint8* target) const final;
-  int GetCachedSize() const final { return _cached_size_.Get(); }
-
-  private:
-  void SharedCtor();
-  void SharedDtor();
-  void SetCachedSize(int size) const final;
-  void InternalSwap(measure_info* other);
-  private:
-  inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
-    return NULL;
-  }
-  inline void* MaybeArenaPtr() const {
-    return NULL;
-  }
-  public:
-
-  ::google::protobuf::Metadata GetMetadata() const final;
-
-  // nested types ----------------------------------------------------
-
-  // accessors -------------------------------------------------------
-
-  // required float cx = 1;
-  bool has_cx() const;
-  void clear_cx();
-  static const int kCxFieldNumber = 1;
-  float cx() const;
-  void set_cx(float value);
-
-  // required float cy = 2;
-  bool has_cy() const;
-  void clear_cy();
-  static const int kCyFieldNumber = 2;
-  float cy() const;
-  void set_cy(float value);
-
-  // required float theta = 3;
-  bool has_theta() const;
-  void clear_theta();
-  static const int kThetaFieldNumber = 3;
-  float theta() const;
-  void set_theta(float value);
-
-  // required float length = 4;
-  bool has_length() const;
-  void clear_length();
-  static const int kLengthFieldNumber = 4;
-  float length() const;
-  void set_length(float value);
-
-  // required float width = 5;
-  bool has_width() const;
-  void clear_width();
-  static const int kWidthFieldNumber = 5;
-  float width() const;
-  void set_width(float value);
-
-  // required float height = 6;
-  bool has_height() const;
-  void clear_height();
-  static const int kHeightFieldNumber = 6;
-  float height() const;
-  void set_height(float value);
-
-  // required float wheelbase = 7;
-  bool has_wheelbase() const;
-  void clear_wheelbase();
-  static const int kWheelbaseFieldNumber = 7;
-  float wheelbase() const;
-  void set_wheelbase(float value);
-
-  // required float front_theta = 8;
-  bool has_front_theta() const;
-  void clear_front_theta();
-  static const int kFrontThetaFieldNumber = 8;
-  float front_theta() const;
-  void set_front_theta(float value);
-
-  // required int32 border_stuta = 9;
-  bool has_border_stuta() const;
-  void clear_border_stuta();
-  static const int kBorderStutaFieldNumber = 9;
-  ::google::protobuf::int32 border_stuta() const;
-  void set_border_stuta(::google::protobuf::int32 value);
-
-  // required int32 ground_status = 10;
-  bool has_ground_status() const;
-  void clear_ground_status();
-  static const int kGroundStatusFieldNumber = 10;
-  ::google::protobuf::int32 ground_status() const;
-  void set_ground_status(::google::protobuf::int32 value);
-
-  // @@protoc_insertion_point(class_scope:message.measure_info)
- private:
-  void set_has_cx();
-  void clear_has_cx();
-  void set_has_cy();
-  void clear_has_cy();
-  void set_has_theta();
-  void clear_has_theta();
-  void set_has_length();
-  void clear_has_length();
-  void set_has_width();
-  void clear_has_width();
-  void set_has_height();
-  void clear_has_height();
-  void set_has_wheelbase();
-  void clear_has_wheelbase();
-  void set_has_front_theta();
-  void clear_has_front_theta();
-  void set_has_border_stuta();
-  void clear_has_border_stuta();
-  void set_has_ground_status();
-  void clear_has_ground_status();
-
-  // helper for ByteSizeLong()
-  size_t RequiredFieldsByteSizeFallback() const;
-
-  ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  ::google::protobuf::internal::HasBits<1> _has_bits_;
-  mutable ::google::protobuf::internal::CachedSize _cached_size_;
-  float cx_;
-  float cy_;
-  float theta_;
-  float length_;
-  float width_;
-  float height_;
-  float wheelbase_;
-  float front_theta_;
-  ::google::protobuf::int32 border_stuta_;
-  ::google::protobuf::int32 ground_status_;
-  friend struct ::protobuf_measure_5fmessage_2eproto::TableStruct;
-};
-// -------------------------------------------------------------------
-
 class Cloud_coordinate : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:message.Cloud_coordinate) */ {
  public:
   Cloud_coordinate();
@@ -1599,7 +1388,7 @@ class Cloud_coordinate : public ::google::protobuf::Message /* @@protoc_insertio
                &_Cloud_coordinate_default_instance_);
   }
   static constexpr int kIndexInFileMessages =
-    7;
+    6;
 
   void Swap(Cloud_coordinate* other);
   friend void swap(Cloud_coordinate& a, Cloud_coordinate& b) {
@@ -1736,7 +1525,7 @@ class Cloud_type : public ::google::protobuf::Message /* @@protoc_insertion_poin
                &_Cloud_type_default_instance_);
   }
   static constexpr int kIndexInFileMessages =
-    8;
+    7;
 
   void Swap(Cloud_type* other);
   friend void swap(Cloud_type& a, Cloud_type& b) {
@@ -1850,7 +1639,7 @@ class Locate_sift_request_msg : public ::google::protobuf::Message /* @@protoc_i
                &_Locate_sift_request_msg_default_instance_);
   }
   static constexpr int kIndexInFileMessages =
-    9;
+    8;
 
   void Swap(Locate_sift_request_msg* other);
   friend void swap(Locate_sift_request_msg& a, Locate_sift_request_msg& b) {
@@ -2028,7 +1817,7 @@ class Locate_sift_response_msg : public ::google::protobuf::Message /* @@protoc_
                &_Locate_sift_response_msg_default_instance_);
   }
   static constexpr int kIndexInFileMessages =
-    10;
+    9;
 
   void Swap(Locate_sift_response_msg* other);
   friend void swap(Locate_sift_response_msg& a, Locate_sift_response_msg& b) {
@@ -3743,250 +3532,6 @@ Ground_status_msg::cloud() const {
 
 // -------------------------------------------------------------------
 
-// measure_info
-
-// required float cx = 1;
-inline bool measure_info::has_cx() const {
-  return (_has_bits_[0] & 0x00000001u) != 0;
-}
-inline void measure_info::set_has_cx() {
-  _has_bits_[0] |= 0x00000001u;
-}
-inline void measure_info::clear_has_cx() {
-  _has_bits_[0] &= ~0x00000001u;
-}
-inline void measure_info::clear_cx() {
-  cx_ = 0;
-  clear_has_cx();
-}
-inline float measure_info::cx() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.cx)
-  return cx_;
-}
-inline void measure_info::set_cx(float value) {
-  set_has_cx();
-  cx_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.cx)
-}
-
-// required float cy = 2;
-inline bool measure_info::has_cy() const {
-  return (_has_bits_[0] & 0x00000002u) != 0;
-}
-inline void measure_info::set_has_cy() {
-  _has_bits_[0] |= 0x00000002u;
-}
-inline void measure_info::clear_has_cy() {
-  _has_bits_[0] &= ~0x00000002u;
-}
-inline void measure_info::clear_cy() {
-  cy_ = 0;
-  clear_has_cy();
-}
-inline float measure_info::cy() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.cy)
-  return cy_;
-}
-inline void measure_info::set_cy(float value) {
-  set_has_cy();
-  cy_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.cy)
-}
-
-// required float theta = 3;
-inline bool measure_info::has_theta() const {
-  return (_has_bits_[0] & 0x00000004u) != 0;
-}
-inline void measure_info::set_has_theta() {
-  _has_bits_[0] |= 0x00000004u;
-}
-inline void measure_info::clear_has_theta() {
-  _has_bits_[0] &= ~0x00000004u;
-}
-inline void measure_info::clear_theta() {
-  theta_ = 0;
-  clear_has_theta();
-}
-inline float measure_info::theta() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.theta)
-  return theta_;
-}
-inline void measure_info::set_theta(float value) {
-  set_has_theta();
-  theta_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.theta)
-}
-
-// required float length = 4;
-inline bool measure_info::has_length() const {
-  return (_has_bits_[0] & 0x00000008u) != 0;
-}
-inline void measure_info::set_has_length() {
-  _has_bits_[0] |= 0x00000008u;
-}
-inline void measure_info::clear_has_length() {
-  _has_bits_[0] &= ~0x00000008u;
-}
-inline void measure_info::clear_length() {
-  length_ = 0;
-  clear_has_length();
-}
-inline float measure_info::length() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.length)
-  return length_;
-}
-inline void measure_info::set_length(float value) {
-  set_has_length();
-  length_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.length)
-}
-
-// required float width = 5;
-inline bool measure_info::has_width() const {
-  return (_has_bits_[0] & 0x00000010u) != 0;
-}
-inline void measure_info::set_has_width() {
-  _has_bits_[0] |= 0x00000010u;
-}
-inline void measure_info::clear_has_width() {
-  _has_bits_[0] &= ~0x00000010u;
-}
-inline void measure_info::clear_width() {
-  width_ = 0;
-  clear_has_width();
-}
-inline float measure_info::width() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.width)
-  return width_;
-}
-inline void measure_info::set_width(float value) {
-  set_has_width();
-  width_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.width)
-}
-
-// required float height = 6;
-inline bool measure_info::has_height() const {
-  return (_has_bits_[0] & 0x00000020u) != 0;
-}
-inline void measure_info::set_has_height() {
-  _has_bits_[0] |= 0x00000020u;
-}
-inline void measure_info::clear_has_height() {
-  _has_bits_[0] &= ~0x00000020u;
-}
-inline void measure_info::clear_height() {
-  height_ = 0;
-  clear_has_height();
-}
-inline float measure_info::height() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.height)
-  return height_;
-}
-inline void measure_info::set_height(float value) {
-  set_has_height();
-  height_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.height)
-}
-
-// required float wheelbase = 7;
-inline bool measure_info::has_wheelbase() const {
-  return (_has_bits_[0] & 0x00000040u) != 0;
-}
-inline void measure_info::set_has_wheelbase() {
-  _has_bits_[0] |= 0x00000040u;
-}
-inline void measure_info::clear_has_wheelbase() {
-  _has_bits_[0] &= ~0x00000040u;
-}
-inline void measure_info::clear_wheelbase() {
-  wheelbase_ = 0;
-  clear_has_wheelbase();
-}
-inline float measure_info::wheelbase() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.wheelbase)
-  return wheelbase_;
-}
-inline void measure_info::set_wheelbase(float value) {
-  set_has_wheelbase();
-  wheelbase_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.wheelbase)
-}
-
-// required float front_theta = 8;
-inline bool measure_info::has_front_theta() const {
-  return (_has_bits_[0] & 0x00000080u) != 0;
-}
-inline void measure_info::set_has_front_theta() {
-  _has_bits_[0] |= 0x00000080u;
-}
-inline void measure_info::clear_has_front_theta() {
-  _has_bits_[0] &= ~0x00000080u;
-}
-inline void measure_info::clear_front_theta() {
-  front_theta_ = 0;
-  clear_has_front_theta();
-}
-inline float measure_info::front_theta() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.front_theta)
-  return front_theta_;
-}
-inline void measure_info::set_front_theta(float value) {
-  set_has_front_theta();
-  front_theta_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.front_theta)
-}
-
-// required int32 border_stuta = 9;
-inline bool measure_info::has_border_stuta() const {
-  return (_has_bits_[0] & 0x00000100u) != 0;
-}
-inline void measure_info::set_has_border_stuta() {
-  _has_bits_[0] |= 0x00000100u;
-}
-inline void measure_info::clear_has_border_stuta() {
-  _has_bits_[0] &= ~0x00000100u;
-}
-inline void measure_info::clear_border_stuta() {
-  border_stuta_ = 0;
-  clear_has_border_stuta();
-}
-inline ::google::protobuf::int32 measure_info::border_stuta() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.border_stuta)
-  return border_stuta_;
-}
-inline void measure_info::set_border_stuta(::google::protobuf::int32 value) {
-  set_has_border_stuta();
-  border_stuta_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.border_stuta)
-}
-
-// required int32 ground_status = 10;
-inline bool measure_info::has_ground_status() const {
-  return (_has_bits_[0] & 0x00000200u) != 0;
-}
-inline void measure_info::set_has_ground_status() {
-  _has_bits_[0] |= 0x00000200u;
-}
-inline void measure_info::clear_has_ground_status() {
-  _has_bits_[0] &= ~0x00000200u;
-}
-inline void measure_info::clear_ground_status() {
-  ground_status_ = 0;
-  clear_has_ground_status();
-}
-inline ::google::protobuf::int32 measure_info::ground_status() const {
-  // @@protoc_insertion_point(field_get:message.measure_info.ground_status)
-  return ground_status_;
-}
-inline void measure_info::set_ground_status(::google::protobuf::int32 value) {
-  set_has_ground_status();
-  ground_status_ = value;
-  // @@protoc_insertion_point(field_set:message.measure_info.ground_status)
-}
-
-// -------------------------------------------------------------------
-
 // Cloud_coordinate
 
 // required float x = 1;
@@ -4628,8 +4173,6 @@ inline void Locate_sift_response_msg::set_allocated_error_manager(::message::Err
 
 // -------------------------------------------------------------------
 
-// -------------------------------------------------------------------
-
 
 // @@protoc_insertion_point(namespace_scope)
 

+ 15 - 12
message/measure_message.proto

@@ -152,18 +152,21 @@ message Ground_status_msg
 }
 
 /*测量信息*/
-message measure_info {
-  required float cx=1;
-  required float cy=2;
-  required float theta=3;
-  required float length=4;
-  required float width=5;
-  required float height=6;
-  required float wheelbase=7;
-  required float front_theta=8;
-  required int32 border_stuta=9;
-  required int32 ground_status=10; // 电子围栏状态, 0 ok, 1 nothing, 2 noise, 3 border
-}
+//20221102, huli move to message.proto
+/*
+//message measure_info {
+//  required float cx=1;
+//  required float cy=2;
+//  required float theta=3;
+//  required float length=4;
+//  required float width=5;
+//  required float height=6;
+//  required float wheelbase=7;
+//  required float front_theta=8;
+//  required int32 border_statu=9;
+//  required int32 ground_status=10; // 电子围栏状态, 0 ok, 1 nothing, 2 noise, 3 border
+//}
+*/
 
 //点云坐标
 message Cloud_coordinate

+ 149 - 0
message/message.proto

@@ -0,0 +1,149 @@
+syntax = "proto3";
+
+/*测量信息*/
+message measure_info {
+  float cx=1;
+  float cy=2;
+  float theta=3;
+  float length=4;
+  float width=5;
+  float height=6;
+  float wheelbase=7;
+  float front_theta=8;
+  int32 border_statu=9;
+  int32 ground_status=10;                   //0  ok 1,nothing 2,noise  3,border
+}
+
+/*分配的车位信息*/
+message parkspace_info{
+  int32 id=1;
+  int32 unit_id=2;  //单元号
+  int32 floor=3;    //楼层号
+  int32 room_id=4;    //同层编号
+}
+
+enum STATU{
+  eNormal=0;
+  eError=1;
+}
+
+/*
+表单执行状态
+ */
+message table_statu{
+  STATU execute_statu=1;    //执行状态
+  string statu_description=2; //状态描述
+}
+
+/*
+停车表单
+ */
+message park_table{
+  table_statu statu=1;  //表单状态
+  int32 queue_id=2;        //指令排队编号
+
+  string car_number=3;
+  int32 unit_id=4;
+  int32 terminal_id=5;
+  string primary_key=6;
+
+  measure_info entrance_measure_info=7;   //入口测量信息
+
+
+  parkspace_info allocated_space_info=8;       //分配的车位信息
+
+  measure_info actually_measure_info=9;   //实际测量信息或者叫二次测量信息
+
+  parkspace_info actually_space_info=10;      //实际停放的车位
+
+
+}
+
+/*
+取车表单
+ */
+message pick_table{
+  table_statu statu=1;  //表单状态
+  int32 queue_id=2;        //指令排队编号
+
+  string car_number=3;
+  int32 unit_id=4;
+  int32 terminal_id=5;
+  string primary_key=6;
+
+  parkspace_info actually_space_info=7; //实际停放的车位信息
+
+  measure_info actually_measure_info=8; //存车时的实际测量信息(轴距)
+
+  int32 export_id=9;            //分配出口
+  bool  is_leaved=10;         //是否离开
+
+
+}
+
+
+
+/*
+以下是状态消息
+ */
+
+/*
+单片机节点状态
+ */
+message out_mcpu_statu{     //数值+1后
+  int32 door_statu=1;       //外门状态       0无效, 1无效, 2开到位, 3 关到位, 4开关中, 5 故障
+  int32 outside_safety=2;    //是否有车      0无效, 1无车, 2有车
+}
+
+message in_mcpu_statu{      //数值+1后
+  int32 door_statu=1;       //外门状态       0无效, 1无效, 2开到位, 3 关到位, 4开关中, 5 故障
+  int32 back_io=2;          //后超界       0无效, 1后超界, 2正常
+  int32 is_occupy=3;        //是否有车      0无效, 1无车, 2有车
+  int32 heighth=4;          //车高状态      0无效, 1无效, 2小车, 3中车, 4大车, 5故障, 6故障
+}
+/*
+测量节点状态
+ */
+message measure_statu{
+  measure_info info=1;
+}
+
+//搬运器状态枚举
+enum CarrierStatu{
+  eIdle=0;
+  eBusy=1;
+  eFault=2;
+}
+
+//调度入口汽车范围的修正信息
+message dispatch_region_info
+{
+    int32 terminal_id=1;                //入口终端编号, 1~6
+    float turnplate_angle_min=2;        //转盘角度最小值, 负值, 例如 -5度
+    float turnplate_angle_max=3;        //转盘角度最大值, 正值, 例如 +5度
+}
+
+/*
+搬运器状态消息
+ */
+message dispatch_node_statu{
+  CarrierStatu statu=1;
+  int32 idle_stop_floor=2;  //空闲时停留位置
+  park_table  running_pack_info=3;  //正在执行的停车表单
+  pick_table  running_pick_info=4;  //正在执行的取车表单
+
+    int32                                   unit_id = 5;                            //单元号, 1~3
+    int32                                   plc_heartbeat = 6;                      //plc心跳信息
+    int32                                   plc_status_info = 7;                    //plc状态的集合
+                                                                                    		//0 bit, 手动模式
+                                                                                    		//1 bit, 自动模式
+                                                                                    		//2 bit, 自动运行中
+                                                                                    		//3 bit, 复位
+                                                                                    		//4 bit, 1号口可以进车
+                                                                                    		//5 bit, 2号口可以进车
+                                                                                    		//6 bit, 预留
+                                                                                    		//7 bit, 预留
+    repeated dispatch_region_info           dispatch_region_info_vector = 8;        //调度入口汽车范围的修正信息
+
+}
+

+ 3 - 1
proto.sh

@@ -1,9 +1,11 @@
 protoc -I=./message message_base.proto --cpp_out=./message
+
+protoc -I=./message message.proto --cpp_out=./message
 ###
  # @Author: yct 18202736439@163.com
  # @Date: 2022-07-26 23:01:22
  # @LastEditors: yct 18202736439@163.com
- # @LastEditTime: 2022-09-30 18:51:59
+ # @LastEditTime: 2023-01-12 10:46:53
  # @FilePath: /puai_wj_2021/proto.sh
  # @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 ### 

+ 6 - 4
rabbitmq/rabbitmq.proto

@@ -29,14 +29,16 @@ message Rabbitmq_channel_queue_consume
                                                             //1表示消息被消费者接受后,就自动删除消息, 当接收端断连后,队列也会删除,
                                                             //0表示消息被消费者接受后,不会自动删除消息, 需要手动ack才会删除消息, 队列不会删除
                                                             //一般情况下设为0,然后让接受者手动删除.
+     optional int32 queue_meassage_ttl = 11[default = 0];   //队列配置的扩展参数, x-message-ttl 队列接受消息 的超时时间 (单位毫秒)
+                                                            //默认写0, 不配置超时, 一般在状态消息的临时队列写入1000ms
 
 
-    optional string consume_name = 11;                       //消费者名称,必写, 不能相同
-    optional int32 consume_no_local = 12[default = 0];      //是否非本地, 默认0,表示本地
-    optional int32 consume_no_ack = 13[default = 0];        //是否确认应答,默认0,表示接收后需要应答
+    optional string consume_name = 12;                       //消费者名称,必写, 不能相同
+    optional int32 consume_no_local = 13[default = 0];      //是否非本地, 默认0,表示本地
+    optional int32 consume_no_ack = 14[default = 0];        //是否确认应答,默认0,表示接收后需要应答
                                                             //请求消息必须写0, 必须应答之后才能接受下一条
                                                             //状态消息必须写1, 可以无限循环接受,收到的瞬间,服务器就会删除这一条消息
-    optional int32 consume_exclusive = 14;                  //是否独立,默认0
+    optional int32 consume_exclusive = 15;                  //是否独立,默认0
 
 }
 

+ 65 - 37
rabbitmq/rabbitmq_base.cpp

@@ -1,5 +1,6 @@
 
 #include "rabbitmq_base.h"
+#include "../tool/time_tool.h"
 
 Rabbitmq_base::Rabbitmq_base()
 {
@@ -94,7 +95,7 @@ Error_manager Rabbitmq_base::rabbitmq_init_from_protobuf(Rabbitmq_proto::Rabbitm
 	if ( t_status != AMQP_STATUS_OK )
 	{
 		return Error_manager(Error_code::RABBITMQ_AMQP_SOCKET_OPEN_ERROR, Error_level::MINOR_ERROR,
-							 amqp_error_to_string(t_status, "amqp_socket_open").c_str() );
+							 amqp_error_to_string(t_status, "amqp_socket_open") );
 	}
 
 	//amqp_login() 登录代理服务器,
@@ -114,7 +115,7 @@ Error_manager Rabbitmq_base::rabbitmq_init_from_protobuf(Rabbitmq_proto::Rabbitm
 	if ( t_reply.reply_type != AMQP_RESPONSE_NORMAL )
 	{
 		return Error_manager(Error_code::RABBITMQ_AMQP_LOGIN_ERROR, Error_level::MINOR_ERROR,
-							 amqp_error_to_string(t_reply, "amqp_login").c_str() );
+							 amqp_error_to_string(t_reply, "amqp_login") );
 	}
 
 	//清除channel_map, 通道的缓存,防止重复开启, (channel允许重复使用, 但是不能重复初始化)
@@ -122,7 +123,6 @@ Error_manager Rabbitmq_base::rabbitmq_init_from_protobuf(Rabbitmq_proto::Rabbitm
 
 	//创建通道队列消费者, (交换机和永久队列不在代码里创建,请在服务器上手动创建)
 	t_error = rabbitmq_new_channel_queue_consume(rabbitmq_parameter_all);
-	std::cout<<"rabbitmq config: "<<rabbitmq_parameter_all.DebugString()<<std::endl;
 	if ( t_error != Error_code::SUCCESS )
 	{
 		return t_error;
@@ -161,7 +161,7 @@ Error_manager Rabbitmq_base::rabbitmq_new_channel_queue_consume(Rabbitmq_proto::
 			if ( t_reply.reply_type != AMQP_RESPONSE_NORMAL )
 			{
 				return Error_manager(Error_code::RABBITMQ_AMQP_CHANNEL_OPEN_ERROR, Error_level::MINOR_ERROR,
-									 amqp_error_to_string(t_reply, "amqp_channel_open").c_str() );
+									 amqp_error_to_string(t_reply, "amqp_channel_open") );
 			}
 			if ( t_inf.consume_no_ack() == 0 )
 			{
@@ -178,22 +178,55 @@ Error_manager Rabbitmq_base::rabbitmq_new_channel_queue_consume(Rabbitmq_proto::
 		//临时队列需要代码创建, 永久队列需要在服务器上提前手动创建
 		if ( t_inf.queue_durable() == 0 )
 		{
-			//amqp_queue_declare() 队列声明, 就是创建新的队列.
-			//输入 amqp_connection_state_t state 连接状态参数的结构体
-			//输入 amqp_channel_t channel 连接通道的编号
-			//输入 amqp_bytes_t queue 队列名称,可以手动命名,如果写空,系统就会自动分配, 手动写amqp_cstring_bytes("abcdefg"), 默认空 amqp_empty_bytes
-			//输入 amqp_boolean_t passive 是否被动,默认0
-			//输入 amqp_boolean_t durable 是否持久,默认0, 节点代码可以创建临时队列(所有权归节点), 服务器手动创建永久队列(所有权归服务器)
-			//		1表示永久队列,当节点死掉,队列在服务器保留,仍然可以接受数据,节点上线后,可以接受掉线期间的所有数据
-			//		0表示临时队列,当节点死掉,队列消失,不再接受数据,直到下次恢复正常
-			//输入 amqp_boolean_t exclusive 是否独立,默认0
-			//输入 amqp_boolean_t auto_delete 是否自动删除,默认0, 1表示消息被消费者接受后,就自动删除消息, 当接收端断连后,队列也会才删除,
-			// 													一般情况下设为0,然后让接受者手动删除.
-			//输入 amqp_table_t arguments 预留参数,默认空 amqp_empty_table
-			//返回 amqp_queue_declare_ok_t *	返回结果
-			amqp_queue_declare(mp_connect, t_inf.channel(), amqp_cstring_bytes(t_inf.queue_name().c_str()),
-							   t_inf.queue_passive(), t_inf.queue_durable(), t_inf.queue_exclusive(),
-							   t_inf.queue_auto_delete(), amqp_empty_table);
+			//目前只填充超时时间,  x-message-ttl 队列接受消息 的超时时间 (单位毫秒)
+			if ( t_inf.queue_meassage_ttl() != 0 )
+			{
+				amqp_table_t t_arguments;		//队列的扩展属性 num_entries 是map长度, amqp_table_entry_t_ 是map指针
+				//目前只填充超时时间,  x-message-ttl 队列接受消息 的超时时间 (单位毫秒)
+				t_arguments.num_entries = 1;
+				amqp_table_entry_t_ t_map_arg;
+				t_map_arg.key = amqp_cstring_bytes("x-message-ttl");		//需要配置的参数
+				t_map_arg.value.kind = AMQP_FIELD_KIND_U16;								//需要配置的数据类型, 如果是字符串, 写 AMQP_FIELD_KIND_UTF8
+				t_map_arg.value.value.u16 = t_inf.queue_meassage_ttl();					//需要配置的数值
+				t_arguments.entries = &t_map_arg;
+
+				//amqp_queue_declare() 队列声明, 就是创建新的队列.
+				//输入 amqp_connection_state_t state 连接状态参数的结构体
+				//输入 amqp_channel_t channel 连接通道的编号
+				//输入 amqp_bytes_t queue 队列名称,可以手动命名,如果写空,系统就会自动分配, 手动写amqp_cstring_bytes("abcdefg"), 默认空 amqp_empty_bytes
+				//输入 amqp_boolean_t passive 是否被动,默认0
+				//输入 amqp_boolean_t durable 是否持久,默认0, 节点代码可以创建临时队列(所有权归节点), 服务器手动创建永久队列(所有权归服务器)
+				//		1表示永久队列,当节点死掉,队列在服务器保留,仍然可以接受数据,节点上线后,可以接受掉线期间的所有数据
+				//		0表示临时队列,当节点死掉,队列消失,不再接受数据,直到下次恢复正常
+				//输入 amqp_boolean_t exclusive 是否独立,默认0
+				//输入 amqp_boolean_t auto_delete 是否自动删除,默认0, 1表示消息被消费者接受后,就自动删除消息, 当接收端断连后,队列也会才删除,
+				// 													一般情况下设为0,然后让接受者手动删除.
+				//输入 amqp_table_t arguments 预留参数,默认空 amqp_empty_table
+				//返回 amqp_queue_declare_ok_t *	返回结果
+				amqp_queue_declare(mp_connect, t_inf.channel(), amqp_cstring_bytes(t_inf.queue_name().c_str()),
+								   t_inf.queue_passive(), t_inf.queue_durable(), t_inf.queue_exclusive(),
+								   t_inf.queue_auto_delete(), t_arguments);
+			}
+			else
+			{
+				//amqp_queue_declare() 队列声明, 就是创建新的队列.
+				//输入 amqp_connection_state_t state 连接状态参数的结构体
+				//输入 amqp_channel_t channel 连接通道的编号
+				//输入 amqp_bytes_t queue 队列名称,可以手动命名,如果写空,系统就会自动分配, 手动写amqp_cstring_bytes("abcdefg"), 默认空 amqp_empty_bytes
+				//输入 amqp_boolean_t passive 是否被动,默认0
+				//输入 amqp_boolean_t durable 是否持久,默认0, 节点代码可以创建临时队列(所有权归节点), 服务器手动创建永久队列(所有权归服务器)
+				//		1表示永久队列,当节点死掉,队列在服务器保留,仍然可以接受数据,节点上线后,可以接受掉线期间的所有数据
+				//		0表示临时队列,当节点死掉,队列消失,不再接受数据,直到下次恢复正常
+				//输入 amqp_boolean_t exclusive 是否独立,默认0
+				//输入 amqp_boolean_t auto_delete 是否自动删除,默认0, 1表示消息被消费者接受后,就自动删除消息, 当接收端断连后,队列也会才删除,
+				// 													一般情况下设为0,然后让接受者手动删除.
+				//输入 amqp_table_t arguments 预留参数,默认空 amqp_empty_table
+				//返回 amqp_queue_declare_ok_t *	返回结果
+				amqp_queue_declare(mp_connect, t_inf.channel(), amqp_cstring_bytes(t_inf.queue_name().c_str()),
+								   t_inf.queue_passive(), t_inf.queue_durable(), t_inf.queue_exclusive(),
+								   t_inf.queue_auto_delete(), amqp_empty_table);
+			}
+
 
 			//amqp_queue_bind 队列绑定, 将队列加载到服务器的交换机下面, 交换机收到消息后,就会检查key,然后放到指定的队列.
 			//输入 amqp_connection_state_t state 连接状态参数的结构体
@@ -212,7 +245,7 @@ Error_manager Rabbitmq_base::rabbitmq_new_channel_queue_consume(Rabbitmq_proto::
 			if ( t_reply.reply_type != AMQP_RESPONSE_NORMAL )
 			{
 				return Error_manager(Error_code::RABBITMQ_AMQP_QUEUE_BIND_ERROR, Error_level::MINOR_ERROR,
-									 amqp_error_to_string(t_reply, "amqp_queue_bind").c_str() );
+									 amqp_error_to_string(t_reply, "amqp_queue_bind") );
 			}
 		}
 
@@ -234,7 +267,7 @@ Error_manager Rabbitmq_base::rabbitmq_new_channel_queue_consume(Rabbitmq_proto::
 		if ( t_reply.reply_type != AMQP_RESPONSE_NORMAL )
 		{
 			return Error_manager(Error_code::RABBITMQ_AMQP_NEW_CONSUME_ERROR, Error_level::MINOR_ERROR,
-								 amqp_error_to_string(t_reply, "amqp_basic_consume").c_str() );
+								 amqp_error_to_string(t_reply, "amqp_basic_consume") );
 		}
 	}
 
@@ -255,7 +288,7 @@ Error_manager Rabbitmq_base::rabbitmq_new_channel_queue_consume(Rabbitmq_proto::
 			if ( t_reply.reply_type != AMQP_RESPONSE_NORMAL )
 			{
 				return Error_manager(Error_code::RABBITMQ_AMQP_CHANNEL_OPEN_ERROR, Error_level::MINOR_ERROR,
-									 amqp_error_to_string(t_reply, "amqp_channel_open").c_str() );
+									 amqp_error_to_string(t_reply, "amqp_channel_open") );
 			}
 			m_channel_map[t_inf1.channel()] = true;
 		}
@@ -278,7 +311,7 @@ Error_manager Rabbitmq_base::rabbitmq_new_channel_queue_consume(Rabbitmq_proto::
 			if ( t_reply.reply_type != AMQP_RESPONSE_NORMAL )
 			{
 				return Error_manager(Error_code::RABBITMQ_AMQP_CHANNEL_OPEN_ERROR, Error_level::MINOR_ERROR,
-									 amqp_error_to_string(t_reply, "amqp_channel_open").c_str() );
+									 amqp_error_to_string(t_reply, "amqp_channel_open") );
 			}
 			m_channel_map[t_inf2.channel()] = true;
 		}
@@ -429,7 +462,7 @@ Error_manager Rabbitmq_base::rabbitmq_reconnnect()
 	if ( t_status != AMQP_STATUS_OK )
 	{
 		return Error_manager(Error_code::RABBITMQ_AMQP_SOCKET_OPEN_ERROR, Error_level::MINOR_ERROR,
-							 amqp_error_to_string(t_status, "amqp_socket_open").c_str() );
+							 amqp_error_to_string(t_status, "amqp_socket_open") );
 	}
 
 	//amqp_login() 登录代理服务器,
@@ -449,7 +482,7 @@ Error_manager Rabbitmq_base::rabbitmq_reconnnect()
 	if ( t_reply.reply_type != AMQP_RESPONSE_NORMAL )
 	{
 		return Error_manager(Error_code::RABBITMQ_AMQP_LOGIN_ERROR, Error_level::MINOR_ERROR,
-							 amqp_error_to_string(t_reply, "amqp_login").c_str() );
+							 amqp_error_to_string(t_reply, "amqp_login") );
 	}
 
 	//清除channel_map, 通道的缓存,防止重复开启, (channel允许重复使用, 但是不能重复初始化)
@@ -507,7 +540,8 @@ void Rabbitmq_base::receive_analysis_thread()
 		m_receive_analysis_condition.wait();
 		if ( m_receive_analysis_condition.is_alive() )
 		{
-			std::this_thread::yield();
+            std::this_thread::sleep_for(std::chrono::microseconds(100));
+            std::this_thread::yield();
 
 			amqp_rpc_reply_t t_reply;		//运行结果
 			amqp_envelope_t t_envelope;		//数据包, 含有一些包裹属性和数据内容
@@ -537,6 +571,8 @@ void Rabbitmq_base::receive_analysis_thread()
 
 			if ( AMQP_RESPONSE_NORMAL == t_reply.reply_type )//正常接受到消息
 			{
+
+
 				m_rabbitmq_status = RABBITMQ_STATUS_READY;
 				//从t_envelope数据包里面提取信息
 				std::string t_receive_string = std::string((char*)t_envelope.message.body.bytes, t_envelope.message.body.len);
@@ -564,7 +600,7 @@ void Rabbitmq_base::receive_analysis_thread()
 							//处理消息
 							if ( execute_msg(&t_rabbitmq_message) == SUCCESS )
 							{
-								//...............
+
 							}
 							//else不做处理
 						}
@@ -675,7 +711,7 @@ Error_manager Rabbitmq_base::ack_msg(Rabbitmq_message* p_msg)
 	if ( ack_result != 0 )
 	{
 		return Error_manager(Error_code::RABBITMQ_AMQP_BASIC_ACK_ERROR, Error_level::MINOR_ERROR,
-							 amqp_error_to_string(ack_result, "amqp_basic_ack").c_str() );
+							 amqp_error_to_string(ack_result, "amqp_basic_ack") );
 	}
 	return Error_code::SUCCESS;
 }
@@ -849,14 +885,6 @@ Error_manager Rabbitmq_base::encapsulate_task_msg(std::string message, int vecto
 //手动封装状态消息, 系统会使用rabbitmq.proto的配置参数,
 Error_manager Rabbitmq_base::encapsulate_status_msg(std::string message, int vector_index)
 {
-	if(m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector_size() <= vector_index)
-	{
-		std::cout<<"msg vector size error: "<<m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector_size()<<std::endl;
-		return Error_manager(Error_code::PARAMETER_ERROR, Error_level::MINOR_ERROR,
-							 (std::string(" Communication_socket_base::encapsulate_msg vector index limit ")
-							 +std::to_string(m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector_size())).c_str());
-	}
-
 	int channel = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(vector_index).channel();
 	std::string exchange_name = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(vector_index).exchange_name();
 	std::string routing_key = m_rabbitmq_parameter_all.rabbitmq_parameters().rabbitmq_sender_status_vector(vector_index).routing_key();

+ 1 - 1
rabbitmq/rabbitmq_base.h

@@ -33,7 +33,7 @@
 //#else
 //#include <sys/time.h>
 //#endif
-#include <string>
+
 #include <rabbitmq-c/amqp.h>
 #include <rabbitmq-c/tcp_socket.h>
 #include <assert.h>

+ 79 - 0
rabbitmq/胡力的rabbitmq-c说明文档.md

@@ -0,0 +1,79 @@
+
+
+安装 rabbitMQ-c 的注意事项:(这里只介绍C语言版本的客户端)
+
+1.代码下载
+	rabbitMQ官网 		https://www.rabbitmq.com/
+	各种语言支持			https://www.rabbitmq.com/devtools.html
+		里面有C/C++的支持
+	github上源码			https://github.com/alanxz/rabbitmq-c
+
+2.安装方式
+	终端指令:注意了,要用sudo使用管理员权限
+	git clone https://github.com/alanxz/rabbitmq-c  
+	cd rabbitmq-c
+	mkdir build
+	cd build
+	sudo cmake ..
+	sudo cmake --build .
+	sudo make 
+	sudo make install
+
+	详情参考		https://blog.csdn.net/caicaiatnbu/article/details/98099779?ops_request_misc=&request_id=&biz_id=102&utm_term=RabbitMQ-C&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-98099779.nonecase&spm=1018.2226.3001.4187
+
+	如果提示openssl有报错,卸载并重装openssl
+	详情参考		https://blog.csdn.net/Cai181191/article/details/120648055?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%8D%B8%E8%BD%BDopenssl&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-120648055.142^v41^pc_rank_34,185^v2^control&spm=1018.2226.3001.4187
+
+3.examples代码编译
+	方案一:不建议
+	打开		./rabbitmq-c/CMakeLists.txt
+		找到		option(BUILD_EXAMPLES "Build Examples" OFF)
+		修改为	option(BUILD_EXAMPLES "Build Examples" ON)
+	然后重新编译 ./rabbitmq-c/CMakeLists.txt  就是把上面第二步再做一遍
+		cd ./rabbitmq-c/build
+		sudo cmake ..
+		sudo cmake --build .
+		sudo make 
+	然后就可以看到	./rabbitmq-c/build/examples 路径下面出现了样例的执行文件
+
+	方案二:强烈推荐
+	进入		./rabbitmq-c/examples 文件夹	,后面的操作都在这个文件夹里面
+	打开		./rabbitmq-c/examples/CMakeLists.txt
+	全局替换		rabbitmq::rabbitmq   改为	rabbitmq
+	就是删除 rabbitmq::
+	然后 编译 ./rabbitmq-c/examples/CMakeLists.txt
+		cd ./rabbitmq-c/examples/
+		mkdir build
+		cd build
+		cmake ..
+		make 
+	然后就可以看到	./rabbitmq-c/examples/build 路径下面出现了样例的执行文件
+
+4.自己开发rabbitMQ-c代码
+	在自己的  CMakeLists.txt  里面 增加系统头文件和库文件的目录.
+	例如:
+		include_directories(
+        /usr/local/include
+		)
+		link_directories("/usr/local/lib")
+
+	在自己的  CMakeLists.txt  里面 target_link_libraries 追加 rabbitmq
+	例如:	
+		target_link_libraries(xxx工程名		rabbitmq )
+
+	具体的函数使用,参考examples里面的amqp_listen.c和amqp_sendstring.c
+
+5.服务器网站配置
+	登录服务器   http://127.0.0.1:15672/     或者     http://192.168.2.39:15672/
+		默认账号密码   guest   guest    (注, guest只能本机访问,其他电脑需要新建账户)
+	
+	终端指令: 使用sudo
+		创建用户 rabbitmqctl add_user admin admin
+		设置管理员 rabbitmqctl set_user_tags admin administrator
+		设置权限  rabbitmqctl set_permissions -p/admin ".*"".*"".*"
+		查看用户  rabbitmqctl list_users
+		详见  https://blog.csdn.net/z446981439/article/details/103634524?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166141324016782388032414%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166141324016782388032414&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-103634524-null-null.142^v42^pc_rank_34,185^v2^control&utm_term=rabbitmq%20%E7%94%A8%E6%88%B7&spm=1018.2226.3001.4187
+
+		个人建议:只在终端上创建账户,然后使用guest在网站上面配置权限.
+	
+	

+ 66 - 0
rslidar/rs_driver/.clang-format

@@ -0,0 +1,66 @@
+---
+BasedOnStyle:  Google
+AccessModifierOffset: -2
+ConstructorInitializerIndentWidth: 2
+AlignEscapedNewlinesLeft: false
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakTemplateDeclarations: true
+AlwaysBreakBeforeMultilineStrings: false
+BreakBeforeBinaryOperators: false
+BreakBeforeTernaryOperators: false
+BreakConstructorInitializersBeforeComma: true
+BinPackParameters: true
+ColumnLimit:    120
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+DerivePointerBinding: false
+PointerBindsToType: true
+ExperimentalAutoDetectBinPacking: false
+IndentCaseLabels: true
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 60
+PenaltyBreakString: 1
+PenaltyBreakFirstLessLess: 1000
+PenaltyExcessCharacter: 1000
+PenaltyReturnTypeOnItsOwnLine: 90
+SpacesBeforeTrailingComments: 2
+Cpp11BracedListStyle: false
+Standard:        Auto
+IndentWidth:     2
+TabWidth:        2
+UseTab:          Never
+IndentFunctionDeclarationAfterType: false
+SpacesInParentheses: false
+SpacesInAngles:  false
+SpaceInEmptyParentheses: false
+SpacesInCStyleCastParentheses: false
+SpaceAfterControlStatementKeyword: true
+SpaceBeforeAssignmentOperators: true
+ContinuationIndentWidth: 4
+SortIncludes: false
+SpaceAfterCStyleCast: false
+
+# Configure each individual brace in BraceWrapping
+BreakBeforeBraces: Custom
+
+# Control of individual brace wrapping cases
+BraceWrapping: {
+    AfterClass: 'true'
+    AfterControlStatement: 'true'
+    AfterEnum : 'true'
+    AfterFunction : 'true'
+    AfterNamespace : 'true'
+    AfterStruct : 'true'
+    AfterUnion : 'true'
+    BeforeCatch : 'true'
+    BeforeElse : 'true'
+    IndentBraces : 'false'
+}
+...

+ 7 - 0
rslidar/rs_driver/.gitignore

@@ -0,0 +1,7 @@
+build*
+.vscode
+.vs
+CMakeSettings.json
+rs_driverConfigVersion.cmake
+rs_driverConfig.cmake
+version.h

+ 246 - 0
rslidar/rs_driver/CHANGELOG.md

@@ -0,0 +1,246 @@
+# CHANGLOG
+
+## Unreleased
+
+### Changed 
+
+## v1.5.8 2022-12-09
+
+### Added
+- Add User's guide document
+
+### Changed
+- Rename RSEOS as RSE1
+- Let user's distance values cover LiDAR's
+
+### Fixed
+- Revert "Report error ERRCODE_MSOP_TIMEOUT if only DIFOP packet is received", to avoid incorrect error report.
+- Fix error distance of RSM2. Change it to 250m. 
+
+## v1.5.7 2022-10-09
+
+### Added
+- Add tool to save as PCD file
+- Seperate RSBPV4 from RSBP
+- Add demo app demo_online_multi_lidars 
+
+### Changed
+- Disable error report in case of wrong block id for RS128/RS80 temporarily
+
+### Fixed
+- Fix distance range of helios series. Also update distance ranges of other lidars
+- Report error ERRCODE_MSOP_TIMEOUT if only DIFOP packet is received
+
+## v1.5.6 2022-09-01
+
+### Added
+- Add option ENABLE_DOUBLE_RCVBUF to solve the packet-loss problem
+- Add option ENABLE_WAIT_IF_QUEUE_EMPTY to reduce CPU usage.
+- Add option ENABLE_STAMP_WITH_LOCAL to convert point cloud's timestamp to local time
+
+### Changed 
+- Make ERRCODEs different for MSOP/DIFOP Packet
+- Rename error code CLOUDOVERFLOW
+- For RSM2, recover its coordinate to ROS-compatible
+- For RSM2, adapt to increased MSOP packet len 
+- Update `demo_pcap` and `rs_driver_viewer` with cloud queue
+- Accept angle.csv with vert angle only
+- Update help documents
+
+### Fixed
+- For Ruby and Ruby Plus, fix the problem of parsing point cloud' timestamp.
+- Fix ERRCODE_MSOPTIMEOUT problem of input_sock_epoll 
+
+### Removed
+- Remove option ENABLE_RCVMMSG
+
+## v1.5.5 2022-08-01
+
+### Added
+- Compiled rs_driver_viewer on Windows, and add help doc
+- Add option to double RECVBUF of UDP sockets
+
+### Changed 
+- Update demo_online to exit orderly.
+
+### Fixed
+- Fix runtime error of Eigen in case of ENABLE_TRANSFORM 
+- Fix Compiling error on QNX
+- Fix pcap_rate
+- Fix the problem with repeated stop and start of driver
+
+### Removed
+- Remove option of high priority thread
+
+## v1.5.4 2022-07-01
+
+### Added
+- Support Ruby_3.0_48
+- Add option to stamp point cloud with first point
+
+### Updated
+- Distinguish 80/80v with lidar model
+- Use ROS coordinate for EOS 
+- Enable PCAP file parsing in default mode
+- Parse DIFOP packet in case of jumbo pcap file
+- Update demo_online example to use ponit cloud queue
+- Update help documents
+
+### Fixed
+- Fix lidar temperature 
+
+## v1.5.3 2022-06-01
+
+### Added
+- Add option to receive packet with epoll()
+- Support Jumbo Mode
+
+### Fixed
+- Check overflow of point cloud
+- Fix compiling error of multiple definition
+
+## v1.5.2 2022-05-20
+
+### Added
+- Support RSP128/RSP80/RSP48 lidars
+- Support EOS lidar
+- Add option to usleep() when no packets to be handled
+
+### Changed
+- Limit error information when error happens
+- Use raw buffer for packet callback
+- Split frame by seq 1 (for MEMS lidars)
+- Remove difop handle thread
+
+## v1.5.1 - 2022-04-28
+
+### Changed
+- When replay MSOP/DIFOP file, use the timestamp when it is recording.
+For Mechanical LiDARs,
+- Split frame by block instead of by packet
+- Let every point has its own timestamp, instead of using the block's one.
+- 
+
+## v1.5.0 - 2022-04-21
+-- Refactory the coder part
+
+## v1.4.6 - 2022-04-21
+
+### Added
+- Check msop timeout
+- Support M2
+- add cmake option ENABLE_RECVMMSG
+
+### Changed
+- Optimize point cloud transform
+
+## v1.4.5 - 2022-03-09
+
+### Added
+- Support dense attribute
+- Support to bind to a specifed ip
+- Limit max size of packet queue
+- Apply SO_REUSEADDR option to the receiving socket
+- Support user layer and tail layer
+- add macro option to disable the PCAP function.
+
+### Changed
+- Join multicast group with code instead of shell script
+
+### Fixed
+- Fix memory leaks problem
+- Fix temperature calculation (for M1 only)
+
+## v1.4.0 - 2021-11-01
+
+### Changed
+Optimazation to decrease CPU uage, includes: 
+- Replace point with point cloud as template parameter
+- Instead of alloc/free packet, use packet pool
+- Instead of alloc/free point cloud, always keep point cloud memory
+- By default, use conditional macro to disable scan_msg/camera_trigger related code
+
+## V1.3.1
+### Added
+- Add vlan support
+- Add somip support
+- Add split frame when pkt_cnt < last_pkt_cnt in mems
+- Add temperature in mems
+- Add ROCK support
+
+### Fixed
+- Fix don't get time when PointType doesn't have timestamp member
+- Fix ROCK light center compensation algorithm
+
+### Removed
+- Remove redundance condition code in vec.emplace_back(std::move(point)) in mech lidars
+
+## v1.3.0 - 2020-11-10
+
+### Added
+
+- Add RSHELIOS support
+- Add RSM1 (B3) support
+- Add Windows support
+- Add rs_driver_viewer, a small tool to show point cloud
+- Add save_by_rows argument
+- Add multi-cast support
+- Add points transformation function
+
+### Changed
+
+- Update some decoding part for LiDARs
+- Change the definition of packet message
+- Update documents
+
+
+
+## v1.2.1 - 2020-09-04
+
+### Fixed
+
+- Fix the timestamp calculation for RS16,RS32 & RSBP. Now the output lidar timestamp will be UTC time and will not be affected by system time zone.
+
+## v1.2.0 - 2020-09-01
+
+### Added
+
+- Add interface in driver core to get lidar temperature
+- Add support for point type XYZIRT (R - ring id)(T - timestamp)
+- Add RS80 support
+- Add interface in driver core to get camera trigger info
+
+### Changed
+
+- Update the decoding part for ruby in echo-dual mode
+- Update the compiler version from C++11 to C++14
+
+## v1.1.0 - 2020-07-01
+
+### Added
+
+- Add the limit of the length of the msop queue 
+- Add the exception capture when loading .csv file
+
+### Fixed
+- Fix the bug in calculating the timestamp of 128
+- Fix the bug in calculating RPM
+
+### Changed
+- Update some functions' names
+- Update the program structure
+
+### Removed
+- Remove unused variables in point cloud message
+
+## v1.0.0 - 2020-06-01
+
+### Added 
+
+- New program structure
+
+- Easy to do advanced development
+
+- Remove the redundant code in old driver.
+
+  

+ 233 - 0
rslidar/rs_driver/CMakeLists.txt

@@ -0,0 +1,233 @@
+cmake_minimum_required(VERSION 3.5)
+
+cmake_policy(SET CMP0048 NEW) # CMake 3.6 required
+
+if(WIN32)
+  cmake_policy(SET CMP0074 NEW) # CMake 3.12 required
+endif(WIN32)
+
+project(rs_driver VERSION 1.5.8)
+
+#========================
+#  Project setup
+#========================
+if (CMAKE_BUILD_TYPE STREQUAL "")
+  set(CMAKE_BUILD_TYPE Release)
+endif()
+
+#=============================
+#  Compile Features
+#=============================
+option(DISABLE_PCAP_PARSE         "Disable PCAP file parse" OFF) 
+option(ENABLE_TRANSFORM           "Enable transform functions" OFF)
+
+option(ENABLE_DOUBLE_RCVBUF       "Enable double size of RCVBUF" OFF)
+option(ENABLE_WAIT_IF_QUEUE_EMPTY "Enable waiting for a while in handle thread if the queue is empty" OFF)
+option(ENABLE_EPOLL_RECEIVE       "Receive packets with epoll() instead of select()" OFF)
+
+option(ENABLE_STAMP_WITH_LOCAL    "Enable stamp point cloud with local time" OFF)
+option(ENABLE_PCL_POINTCLOUD      "Enable PCL Point Cloud" OFF)
+
+#=============================
+#  Compile Demos, Tools, Tests
+#=============================
+option(COMPILE_DEMOS "Build rs_driver demos" ON)
+option(COMPILE_TOOLS "Build rs_driver tools" OFF)
+option(COMPILE_TOOL_VIEWER "Build point cloud visualization tool" OFF)
+option(COMPILE_TOOL_PCDSAVER "Build point cloud pcd saver tool" OFF)
+option(COMPILE_TESTS "Build rs_driver unit tests" OFF)
+
+#========================
+#  Platform cross setup
+#========================
+if(MSVC)
+
+  set(COMPILER "MSVC Compiler")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Wall")
+  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi")
+  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
+  set(CompilerFlags
+    CMAKE_CXX_FLAGS                       CMAKE_C_FLAGS
+    CMAKE_CXX_FLAGS_DEBUG                 CMAKE_C_FLAGS_DEBUG
+    CMAKE_CXX_FLAGS_RELEASE               CMAKE_C_FLAGS_RELEASE
+	  CMAKE_CXX_FLAGS_MINSIZEREL            CMAKE_C_FLAGS_MINSIZEREL
+	  CMAKE_CXX_FLAGS_RELWITHDEBINFO        CMAKE_C_FLAGS_RELWITHDEBINFO)
+
+  foreach(CompilerFlag ${CompilerFlags})
+    string(REPLACE "/MT" "/MD" ${CompilerFlag} "${${CompilerFlag}}")
+  endforeach()
+
+  add_compile_definitions(_DISABLE_EXTENDED_ALIGNED_STORAGE) # to disable a msvc error
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:100000000")
+
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+
+  if(UNIX)
+    set(COMPILER "UNIX GNU Compiler")
+  elseif(MINGW)
+    set(COMPILER "MINGW Compiler")
+  else()
+    message(FATAL_ERROR "Unsupported compiler.")
+  endif()
+
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wno-unused-parameter")
+  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
+  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
+endif()
+
+message(=============================================================)
+message("-- CMake run for ${COMPILER}")
+message(=============================================================)
+
+#========================
+#  Path Setup
+#========================
+set(CMAKE_INSTALL_PREFIX /usr/local)
+set(INSTALL_DRIVER_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_NAME}/include)
+set(INSTALL_CMAKE_DIR ${CMAKE_INSTALL_PREFIX}/lib/cmake)
+set(DRIVER_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/src)
+set(DRIVER_CMAKE_ROOT ${CMAKE_CURRENT_LIST_DIR}/cmake)
+
+if(WIN32)
+else()
+  if (CMAKE_SYSTEM_NAME STREQUAL "QNX")
+    list(APPEND EXTERNAL_LIBS socket)
+  else()
+    list(APPEND EXTERNAL_LIBS pthread)
+  endif()
+endif(WIN32)
+
+#========================
+#  Build Features
+#========================
+
+if(${ENABLE_EPOLL_RECEIVE})
+
+  message(=============================================================)
+  message("-- Enable Epoll Receive")
+  message(=============================================================)
+
+  add_definitions("-DENABLE_EPOLL_RECEIVE")
+endif(${ENABLE_EPOLL_RECEIVE})
+
+if(${DISABLE_PCAP_PARSE})
+
+  message(=============================================================)
+  message("-- Disable PCAP parse")
+  message(=============================================================)
+
+  add_definitions("-DDISABLE_PCAP_PARSE")
+
+else()
+
+  if(WIN32)
+    set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
+    find_package(PCAP REQUIRED)
+    include_directories(${PCAP_INCLUDE_DIR})
+    list(APPEND EXTERNAL_LIBS ${PCAP_LIBRARY})
+  else()
+    list(APPEND EXTERNAL_LIBS pcap)
+  endif(WIN32)
+
+endif(${DISABLE_PCAP_PARSE})
+
+if(${ENABLE_TRANSFORM})
+
+  message(=============================================================)
+  message("-- Enable Transform Fucntions")
+  message(=============================================================)
+
+  add_definitions("-DENABLE_TRANSFORM")
+
+  # Eigen
+  find_package(Eigen3 REQUIRED)
+  include_directories(${EIGEN3_INCLUDE_DIR})
+
+endif(${ENABLE_TRANSFORM})
+
+#============================
+#  Build Demos, Tools, Tests
+#============================
+
+if(${ENABLE_DOUBLE_RCVBUF})
+  add_definitions("-DENABLE_DOUBLE_RCVBUF")
+endif(${ENABLE_DOUBLE_RCVBUF})
+
+if(${ENABLE_WAIT_IF_QUEUE_EMPTY})
+  add_definitions("-DENABLE_WAIT_IF_QUEUE_EMPTY")
+endif(${ENABLE_WAIT_IF_QUEUE_EMPTY})
+
+if(${ENABLE_STAMP_WITH_LOCAL})
+  add_definitions("-DENABLE_STAMP_WITH_LOCAL")
+endif(${ENABLE_STAMP_WITH_LOCAL})
+
+if(${ENABLE_PCL_POINTCLOUD})
+  add_definitions("-DENABLE_PCL_POINTCLOUD")
+endif(${ENABLE_PCL_POINTCLOUD})
+
+if(${COMPILE_DEMOS})
+  add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/demo)
+endif(${COMPILE_DEMOS})
+
+if (${COMPILE_TOOLS})
+  set(COMPILE_TOOL_VIEWER ON)
+  set(COMPILE_TOOL_PCDSAVER ON)
+endif (${COMPILE_TOOLS})
+
+if(${COMPILE_TOOL_VIEWER} OR ${COMPILE_TOOL_PCDSAVER})
+  add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tool)
+endif(${COMPILE_TOOL_VIEWER} OR ${COMPILE_TOOL_PCDSAVER})
+
+if(${COMPILE_TESTS})
+  add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/test)
+endif(${COMPILE_TESTS})
+
+#========================
+#  Cmake
+#========================  
+configure_file(
+  ${DRIVER_CMAKE_ROOT}/rs_driverConfig.cmake.in
+  ${DRIVER_CMAKE_ROOT}/rs_driverConfig.cmake 
+  @ONLY)
+
+configure_file(
+  ${DRIVER_CMAKE_ROOT}/rs_driverConfigVersion.cmake.in
+  ${DRIVER_CMAKE_ROOT}/rs_driverConfigVersion.cmake 
+  @ONLY)
+
+configure_file (
+  ${CMAKE_CURRENT_LIST_DIR}/src/rs_driver/macro/version.hpp.in
+  ${CMAKE_CURRENT_LIST_DIR}/src/rs_driver/macro/version.hpp
+  @ONLY)
+
+if(NOT ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
+  set(rs_driver_DIR ${DRIVER_CMAKE_ROOT} PARENT_SCOPE)
+endif()
+
+#========================
+#  Install & Uninstall
+#========================
+if(UNIX AND ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
+
+  install(FILES ${DRIVER_CMAKE_ROOT}/rs_driverConfig.cmake
+                ${DRIVER_CMAKE_ROOT}/rs_driverConfigVersion.cmake
+          DESTINATION ${INSTALL_CMAKE_DIR}/${CMAKE_PROJECT_NAME})
+
+  install(DIRECTORY src/
+          DESTINATION ${INSTALL_DRIVER_DIR}
+          FILES_MATCHING PATTERN "*.h")
+
+  install(DIRECTORY src/
+          DESTINATION ${INSTALL_DRIVER_DIR}
+          FILES_MATCHING PATTERN "*.hpp")
+
+  if(NOT TARGET uninstall)
+    configure_file(
+      ${CMAKE_CURRENT_LIST_DIR}/cmake/cmake_uninstall.cmake.in
+      ${PROJECT_BINARY_DIR}/cmake_uninstall.cmake @ONLY)
+    add_custom_target(uninstall
+      COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
+  endif(NOT TARGET uninstall)
+
+endif(UNIX AND ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
+

Fichier diff supprimé car celui-ci est trop grand
+ 19 - 0
rslidar/rs_driver/LICENSE


+ 257 - 0
rslidar/rs_driver/README.md

@@ -0,0 +1,257 @@
+# 1 **rs_driver**  
+
+[中文介绍](README_CN.md) 
+
+
+
+## 1.1 Introduction
+
+**rs_driver** is the driver kernel for the RoboSense LiDARs.
+
+Please download the official release from [github](https://github.com/RoboSense-LiDAR/rs_driver/releases), or get the latest version with the git client tool.
+
+````shell
+git clone https://github.com/RoboSense-LiDAR/rs_driver.git
+````
+
+
+
+## 1.2 Supported LiDARs
+
+Below are the supported LiDARS.
+
+- RS-LiDAR-16
+- RS-LiDAR-32
+- RS-Bpearl
+- RS-Bpearl-V4
+- RS-Helios
+- RS-Helios-16P
+- RS-Ruby-128
+- RS-Ruby-80
+- RS-Ruby-48
+- RS-Ruby-Plus-128
+- RS-Ruby-Plus-80
+- RS-Ruby-Plus-48
+- RS-LiDAR-M1
+- RS-LiDAR-M2
+- RS-LiDAR-E1
+
+
+
+## 1.3 Supported Platforms
+
+**rs_driver** is supported on the following platforms and compilers. Note the compiler should support `C++14`.
+
+- Ubuntu (16.04, 18.04)
+  - gcc (4.8+)
+
+- Windows
+  - MSVC ( tested with Win10 / VS2019)
+
+
+
+## 1.4 Dependency Libraries
+
+**rs_driver** depends on the following third-party libraries. 
+
+- libpcap (optional, needed to parse PCAP file)
+- Eigen3 (optional, needed to use the internal transformation function)
+- PCL (optional, needed to build the visualization tool)
+- Boost (optional, needed to build the visualization tool)
+
+
+
+## 1.5 Compile On Ubuntu
+
+### 1.5.1 Dependency Libraries
+
+```sh
+sudo apt-get install libpcap-dev libeigen3-dev libboost-dev libpcl-dev
+```
+
+### 1.5.2 Compilation
+
+```bash
+cd rs_driver
+mkdir build && cd build
+cmake .. && make -j4
+```
+
+### 1.5.3 Installation
+
+```bash
+sudo make install
+```
+
+### 1.5.4 Use `rs_driver` as a third party library
+
+In your ```CMakeLists.txt```, find the **rs_driver** package and link to it .
+
+```cmake
+find_package(rs_driver REQUIRED)
+include_directories(${rs_driver_INCLUDE_DIRS})
+target_link_libraries(your_project ${rs_driver_LIBRARIES})
+```
+
+### 1.5.5 Use `rs_driver` as a submodule
+
+Add **rs_driver** into your project as a submodule. 
+
+In your ```CMakeLists.txt```, find the **rs_driver** package and link to it .
+
+```cmake
+add_subdirectory(${PROJECT_SOURCE_DIR}/rs_driver)
+find_package(rs_driver REQUIRED)
+include_directories(${rs_driver_INCLUDE_DIRS})
+target_link_libraries(your_project ${rs_driver_LIBRARIES})
+```
+
+
+
+## 1.6 Compile On Windows
+
+### 1.6.1 Dependency Libraries
+
+#### libpcap
+
+Install [libpcap runtime library](https://www.winpcap.org/install/bin/WinPcap_4_1_3.exe).
+
+Unzip [libpcap's developer's pack](https://www.winpcap.org/install/bin/WpdPack_4_1_2.zip) to your favorite location, and add the path to the folder ```WpdPack_4_1_2/WpdPack``` to the environment variable ```PATH``` . 
+
+#### PCL
+
+To compile with VS2019, please use the official installation package [PCL All-in-one installer](https://github.com/PointCloudLibrary/pcl/releases).
+
+Select the "Add PCL to the system PATH for xxx" option during the installation.
+![](./img/01_install_pcl.png)
+
+### 1.6.2 Installation
+
+Installation is not supported on Windows.
+
+
+
+## 1.7 Quick Start
+
+**rs_driver** offers two demo programs in ```rs_driver/demo```.
+
+- demo_online.cpp
+
+`demo_online` connects to online lidar, and output point cloud.
+
+- demo_pcap.cpp
+
+`demo_pcap` parses pcap file, and output point cloud. It is based on libpcap.
+
+To build `demo_online` and `demo_pcap`, enable the CMake option `COMPILE_DEMOS`.
+
+```bash
+cmake -DCOMPILE_DEMOS=ON ..
+```
+
+For more info about `demo_online`, Please refer to [Decode online LiDAR](doc/howto/07_how_to_decode_online_lidar.md)
+
+For more info about `demo_pcap`, Please refer to [Decode pcap file](doc/howto/09_how_to_decode_pcap_file.md)
+
+
+
+## 1.8 Visualization of Point Cloud
+
+**rs_driver** offers a visualization tool `rs_driver_viwer` in ```rs_driver/tool``` , which is based on PCL.
+
+To build it, enable the CMake option CMOPILE_TOOLS.
+
+```bash
+cmake -DCOMPILE_TOOLS=ON ..
+```
+
+For more info about `rs_driver_viewer`, please refer to [Visualization tool guide](doc/howto/13_how_to_use_rs_driver_viewer.md) 
+
+
+
+## 1.9 API files
+
+For more info about the `rs_driver` API, Please refer to:
+- **Point Cloud message definition**: ```rs_driver/src/rs_driver/msg/point_cloud_msg.hpp```, ```rs_driver/src/rs_driver/msg/pcl_point_cloud_msg.hpp```
+- **API definition**: ```rs_driver/src/rs_driver/api/lidar_driver.hpp```
+- **Parameters definition**: ```rs_driver/src/rs_driver/driver/driver_param.hpp```, 
+- **Error code definition**: ```rs_driver/src/rs_driver/common/error_code.hpp```
+
+
+
+## 1.10 More Topics
+
+For more topics, Please refer to:
+
++ What is available in the **rs_driver** project? Include examples, tools, documents. 
+
+Please see [Directories and Files](doc/intro/02_directories_and_files.md) 
+
++ There are two versions of **rs_driver**: `v1.3.x` and `v1.5.x`. Which is suggested? Is it suggested to upgrade to `v1.5.x` ? How to upgrade?
+
+Please see [How to port from v1.3.x to v1.5.x](doc/howto/07_how_to_port_your_app_from_rs_driver_1.3.x_to_1.5.x.md) 
+
++ How to compile **rs_driver** on Windows ?
+
+Please see [How to compile `rs_driver` on Windows](./doc/howto/16_how_to_compile_on_windows.md)
+
++ What is the interface of **rs_driver** like ? How to integrate it into my system?
+
+Please see [Thread Model and Interface](./doc/intro/03_thread_model.md)
+
++ Is there any examples about how to integrate **rs_driver** ? And if mulitple LiDARs?
+
+Please see [How to connect to online LiDAR](doc/howto/08_how_to_decode_online_lidar.md),[How to decode PCAP file](doc/howto/10_how_to_decode_pcap_file.md)。
+
++ How to configure **rs_driver**'s network options in unicast/multicast/broadcast mode? And if mulitple Lidars? With VLAN layer? May I add extra layers before/after MSOP/DIFOP packet? All configurations are OK, and Wireshark/tcpdump can capture the packets, but **rs_driver** still fails to get them. Why ?
+
+Please see [Online LiDAR - Advanced Topics](doc/howto/09_online_lidar_advanced_topics.md) 
+
++ To decode a PCAP file with only one LiDAR data, how to configure `rs_driver`? And if multiple LiDARs? With VLAN layer? With extra layers before/after MSOP/DIFOP packet?
+
+Please see [PCAP File - Advanced Topics](doc/howto/11_pcap_file_advanced_topics.md) 
+
++ Can I determine how to configure `rs_driver` by a PCAP file? For both online LiDAR and PCAP file?
+
+Please see [How to configure rs_driver by PCAP file](doc/howto/12_how_to_configure_by_pcap_file.md)
+
++ What format of PCAP file is supported by `rs_driver`? How to capture such a file?
+
+Please see [How to capture a PCAP file for rs_driver](doc/howto/13_how_to_capture_pcap_file.md)
+
++ How to compile demo apps/tools? How to avoid packet loss? How much is CPU usage? Point cloud is UTC time. How to convert it to loca time? How to disable PCAP file decoding on embedded Linux? How to enable point transformation?
+
+Please see [rs_driver CMake Macros](./doc/intro/05_cmake_macros_intro.md)
+
++ How to specify Lidar type? How to specify data source (online LiDAR, PCAP file, user's data) ? How to timestamp point cloud with LiDAR clock or Host clock? How to discard NAN points in point cloud? Where does point cloud get its timestamp? from its first point, or last point? How to configure point transform? How to change the splitting angle of mechanical LiDAR?
+
+Please see [rs_driver configuration parameters](./doc/intro/04_parameter_intro.md)
+
++ **rs_driver** reports an error `ERRCODE_MSOPTIMEOUT`/`ERRCODE_WRONGMSOPID`/`ERRCODE_PKTBUFOVERFLOW` ...... What does it mean?
+
+Please see [rs_driver Error Code](./doc/intro/06_error_code_intro.md)
+
++ How to transform point cloud to another position?
+
+Please see [How to transform point cloud](doc/howto/15_how_to_transform_pointcloud.md) 
+
++ How much is data flow of RoboSense LiDARs ? When may it loss packets? How to avoid this?
+
+Please see [How to avoid packet Loss](doc/howto/17_how_to_avoid_packet_loss.md)
+
++ How much CPU and memory does **rs_driver** need?
+
+Please see [CPU Usage and Memory Usage of `rs_driver`](doc/howto/20_about_usage_of_cpu_and_memory.md)
+
++ What is point layout like? What's the coordinate system of **rs_driver**? What is `ring` of point? Where is point timestamp from?
+
+Please see [Point Layout in point cloud](doc/howto/18_about_point_layout.md)
+
++ How does RoboSense split frames? It uses the UDP protocol. What if packet loss or out of order?
+
+Please see [Splitting frames](doc/howto/19_about_splitting_frame.md)
+
++ Want to know more implementation details about `rs_driver`?
+
+Please see [Analysis of rs_driver's source code](doc/src_intro/rs_driver_intro_CN.md)
+

+ 264 - 0
rslidar/rs_driver/README_CN.md

@@ -0,0 +1,264 @@
+# 1 **rs_driver预览**
+
+[English Version](README.md) 
+
+
+
+## 1.1 概述
+
+**rs_driver**是RoboSense雷达的驱动库。
+
+可以从[github](https://github.com/RoboSense-LiDAR/rs_driver/releases)下载正式版本。也可以使用git工具得到最新版本。
+
+```
+git clone https://github.com/RoboSense-LiDAR/rs_driver.git
+```
+
+
+
+## 1.2 支持的雷达型号
+
+**rs_driver**支持的雷达型号如下。
+
++ RS-LiDAR-16
++ RS-LiDAR-32
++ RS-Bpearl
++ RS-Bpearl-V4
++ RS-Helios
++ RS-Helios-16P
++ RS-Ruby-128
++ RS-Ruby-80
++ RS-Ruby-48
++ RS-Ruby-Plus-128
++ RS-Ruby-Plus-80
++ RS-Ruby-Plus-48
++ RS-LiDAR-M1
++ RS-LiDAR-M2
++ RS-LiDAR-E1
+
+
+
+## 1.3 支持的操作系统
+
+**rs_driver**支持的操作系统及编译器如下。注意编译器需支持`C++14`标准。
+
+- Ubuntu (16.04, 18.04, 20.04)
+  - gcc (4.8+)
+
+- Windows
+  - MSVC  (Win10 / VS2019 已测试)
+
+
+
+## 1.4 依赖的第三方库
+
+**rs_driver**依赖的第三方库如下。
+
+- `libpcap` (可选。如不需要解析PCAP文件,可忽略)
+- `eigen3` (可选。如不需要内置坐标变换,可忽略)
+- `PCL` (可选。如不需要可视化工具,可忽略)
+- `Boost` (可选。如不需要可视化工具,可忽略)
+
+
+
+## 1.5 Ubuntu下的编译及安装
+
+### 1.5.1 安装第三方库
+
+```bash
+sudo apt-get install libpcap-dev libeigen3-dev libboost-dev libpcl-dev
+```
+### 1.5.2 编译
+
+```bash
+cd rs_driver
+mkdir build && cd build
+cmake .. && make -j4
+```
+
+### 1.5.3 安装
+
+```bash
+sudo make install
+```
+
+### 1.5.4 作为第三方库使用
+
+配置您的```CMakeLists.txt```文件,使用find_package()指令找到**rs_driver**库,并链接。
+
+```cmake
+find_package(rs_driver REQUIRED)
+include_directories(${rs_driver_INCLUDE_DIRS})
+target_link_libraries(your_project ${rs_driver_LIBRARIES})
+```
+
+### 1.5.5 作为子模块使用
+
+将**rs_driver**作为子模块添加到您的工程,相应配置您的```CMakeLists.txt```文件。
+
+使用find_package()指令找到**rs_driver**库,并链接。
+
+```cmake
+add_subdirectory(${PROJECT_SOURCE_DIR}/rs_driver)
+find_package(rs_driver REQUIRED)
+include_directories(${rs_driver_INCLUDE_DIRS})
+target_link_libraries(project ${rs_driver_LIBRARIES})
+```
+
+
+
+## 1.6 Windows下的编译及安装
+
+### 1.6.1 安装第三方库
+
+#### libpcap
+
+安装[libpcap运行库](https://www.winpcap.org/install/bin/WinPcap_4_1_3.exe)。
+
+解压[libpcap开发者包](https://www.winpcap.org/install/bin/WpdPack_4_1_2.zip)到任意位置,并将```WpdPack_4_1_2/WpdPack``` 的路径添加到环境变量```PATH```。
+
+#### PCL
+
+如果使用MSVC编译器,可使用PCL官方提供的[PCL安装包](https://github.com/PointCloudLibrary/pcl/releases)安装。
+
+安装过程中选择 `Add PCL to the system PATH for xxx`。
+![](./img/01_install_pcl.png)
+
+### 1.6.2 安装
+
+Windows下,**rs_driver** 暂不支持安装。
+
+### 1.6.3 VS工程文件
+
+工程目录`rs_driver/win`下,有编译实例程序和工具的MSVS工程文件。
+
+关于Windows下编译和使用**rs_driver**的更多说明,可以参考[如何在Windows下编译rs_driver](doc/howto/16_how_to_compile_on_windows_CN.md)
+
+
+
+## 1.7 示例程序
+
+**rs_driver**在目录```rs_driver/demo``` 下,提供了两个示例程序。
+
+- `demo_online.cpp`
+
+`demo_online`解析在线雷达的数据,输出点云。
+
+
+- `demo_pcap.cpp`
+
+`demo_pcap`解析PCAP文件,输出点云。编译`demo_pcap`需要安装`libpcap`库。
+
+要编译这两个程序,需使能`COMPILE_DEMOS`选项。
+
+```bash
+cmake -DCOMPILE_DEMOS=ON ..
+```
+
+关于`demo_online`的更多说明,可以参考[如何连接在线雷达](doc/howto/08_how_to_decode_online_lidar_CN.md)
+
+关于`demo_pcap`的更多说明,可以参考[如何解码PCAP文件](doc/howto/10_how_to_decode_pcap_file_CN.md)
+
+
+
+## 1.8 点云可视化工具
+
+**rs_driver**在目录```rs_driver/tool``` 下,提供了一个点云可视化工具`rs_driver_viewer`。
+
+要编译这个工具,需使能`COMPILE_TOOS`选项。编译它需要安装PCL库和Boost库。
+
+```bash
+cmake -DCOMPILE_TOOLS=ON ..
+```
+
+关于`rs_driver_viewer`的使用方法,请参考[如何使用可视化工具rs_driver_viewer](doc/howto/14_how_to_use_rs_driver_viewer_CN.md) 
+
+
+
+## 1.9 接口文件
+
+**rs_driver**的主要接口文件如下。
+
+- 点云消息定义: ```rs_driver/src/rs_driver/msg/point_cloud_msg.hpp```, ```rs_driver/src/rs_driver/msg/pcl_point_cloud_msg.hpp```
+- 接口定义: ```rs_driver/src/rs_driver/api/lidar_driver.hpp```
+- 参数定义: ```rs_driver/src/rs_driver/driver/driver_param.hpp```
+- 错误码定义: ```rs_driver/src/rs_driver/common/error_code.hpp```
+
+
+
+## 1.10 更多主题
+
+关于**rs_driver**的其他主题,请参考如下链接。
+
++ **rs_driver**工程中包括哪些例子、工具、和文档?
+
+请参考[rs_driver的目录结构](doc/intro/02_directories_and_files_CN.md) 
+
++ **rs_driver**有`v1.3.x`和`v1.5.x`两个版本?该选哪一个?现在正在使用`v1.3.x`,需要升级到`v1.5.x`吗?如何升级?
+
+请参考[如何从v1.3.x升级到v1.5.x](doc/howto/07_how_to_port_your_app_from_rs_driver_1.3.x_to_1.5.x_CN.md) 
+
++ Windows上如何编译**rs_driver**?
+
+请参考[如何在Windows上编译rs_driver](./doc/howto/16_how_to_compile_on_windows_CN.md)
+
++ **rs_driver**的接口如何设计的?如何将**rs_driver**集成到我的系统中,有什么注意事项吗?
+
+请参考[rs_driver的线程模型与接口设计](./doc/intro/03_thread_model_CN.md)
+
++ **rs_driver**如何集成到我自己的系统中?有参考的例子吗?有多雷达的例子吗?
+
+请参考[如何连接在线雷达](doc/howto/08_how_to_decode_online_lidar_CN.md),[如何解析PCAP文件](doc/howto/10_how_to_decode_pcap_file_CN.md)。
+
++ 在单播、组播、广播的情况下,应该如何配置**rs_driver**的网络选项?在多雷达的情况下如何配置?在VLAN的情况下如何配置?可以在MSOP/DIFOP数据的前后加入自己的层吗?这些网络配置都确认无误,用抓包软件也能抓到MSOP/DIFOP包,为什么还是接收不到点云?
+
+请参考[在线雷达 - 高级主题](doc/howto/09_online_lidar_advanced_topics_CN.md) 
+
++ 一般情况下,PCAP文件中只有一个雷达的数据,如何配置网络选项? 如果文件中有多个雷达的数据,如何配置? 在VLAN的情况下如何配置?可以在MSOP/DIFOP数据的前后加入自己的层吗?
+
+请参考[PCAP文件 - 高级主题](doc/howto/11_pcap_file_advanced_topics_CN.md) 
+
++ 手上只有一个PCAP文件,可以根据它确定**rs_driver**的网络配置选项吗?包括连接在线雷达和解析PCAP文件。
+
+请参考[根据PCAP文件确定网络配置选项](doc/howto/12_how_to_configure_by_pcap_file_CN.md)
+
++ **rs_driver**支持什么格式的PCAP文件?如何录制这样的文件?
+
+请参考[如何为rs_driver录制PCAP文件](doc/howto/13_how_to_capture_pcap_file_CN.md)
+
++ 想编译示例程序/小工具,怎么指定编译选项? **rs_driver**丢包了,如何解决?**rs_driver**的CPU占用率?点云的时间戳是UTC时间,能改成本地时间吗?嵌入式平台不需要解析PCAP文件,可以不编译这个特性吗?如何启用点云坐标转换功能?
+
+请参考[rs_driver CMake编译宏](./doc/intro/05_cmake_macros_intro_CN.md)
+
++ **rs_driver**如何指定雷达类型?如何指定数据源为在线雷达、PCAP文件或用户数据? 想先试用一下,但是对雷达作PTP时间同步很麻烦,可以先用电脑的系统时间给点云打时间戳吗?想节省一点内存空间,可以丢弃点云中的无效点吗?点云的时间戳来自它的第一个点,还是最后一个点?可以配置吗?点云坐标转换的位移、旋转参数如何设置?机械式雷达的分帧角度可以设置吗?
+
+请参考[rs_driver配置选项](./doc/intro/04_parameter_intro_CN.md)
+
++ **rs_driver**报告了一个错误`ERRCODE_MSOPTIMEOUT`/`ERRCODE_WRONGMSOPID`/`ERRCODE_PKTBUFOVERFLOW` ...... 这个错误码是什么意思?
+
+请参考[rs_driver错误码](./doc/intro/06_error_code_intro_CN.md)
+
++ 如何将点云变换到另一个位置和角度上去?
+
+请参考[点云坐标变换](doc/howto/15_how_to_transform_pointcloud_CN.md) 
+
++ RoboSense雷达的数据量有多大?在什么情况下可能丢包?怎么避免?
+
+请参考[丢包问题以及如何解决](doc/howto/17_how_to_avoid_packet_loss_CN.md)
+
++ **rs_driver**要占用多少CPU和内存?
+
+请参考[rs_driver的CPU和内存占用](doc/howto/20_about_usage_of_cpu_and_memory_CN.md)
+
++ 点在点云中是怎样布局的?点坐标的参考系是什么?点的通道号指什么?点的时间戳来自哪里?
+
+请参考[点云中点的布局](doc/howto/18_about_point_layout_CN.md)
+
++ RoboSense雷达怎么分帧?RoboSense使用UDP协议,万一丢包或乱序,会影响**rs_driver**的分帧处理吗?
+
+请参考[RoboSense雷达如何分帧](doc/howto/19_about_splitting_frame_CN.md)
+
++ 希望进一步深入了解**rs_driver**的设计细节?
+
+请参考[rs_driver源代码解析](doc/src_intro/rs_driver_intro_CN.md)
+

+ 86 - 0
rslidar/rs_driver/cmake/FindPCAP.cmake

@@ -0,0 +1,86 @@
+# - Try to find libpcap include dirs and libraries
+#
+# Usage of this module as follows:
+#
+#     find_package(PCAP)
+#
+# Variables used by this module, they can change the default behaviour and need
+# to be set before calling find_package:
+#
+#  PCAP_ROOT_DIR             Set this variable to the root installation of
+#                            libpcap if the module has problems finding the
+#                            proper installation path.
+#
+# Variables defined by this module:
+#
+#  PCAP_FOUND                System has libpcap, include and library dirs found
+#  PCAP_INCLUDE_DIR          The libpcap include directories.
+#  PCAP_LIBRARY              The libpcap library (possibly includes a thread
+#                            library e.g. required by pf_ring's libpcap)
+#  HAVE_PF_RING              If a found version of libpcap supports PF_RING
+#  HAVE_PCAP_IMMEDIATE_MODE  If the version of libpcap found supports immediate mode
+
+find_path(PCAP_ROOT_DIR
+    NAMES include/pcap.h
+)
+
+find_path(PCAP_INCLUDE_DIR
+    NAMES pcap.h
+    HINTS ${PCAP_ROOT_DIR}/include
+)
+
+set (HINT_DIR ${PCAP_ROOT_DIR}/Lib)
+
+# On x64 windows, we should look also for the .lib at /lib/x64/
+# as this is the default path for the WinPcap developer's pack
+if (${CMAKE_SIZEOF_VOID_P} EQUAL 8 AND WIN32)
+    set (HINT_DIR ${PCAP_ROOT_DIR}/Lib/x64 ${HINT_DIR})
+endif ()
+
+find_library(PCAP_LIBRARY
+    NAMES pcap wpcap
+    HINTS ${HINT_DIR}
+    NO_DEFAULT_PATH
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(PCAP DEFAULT_MSG
+    PCAP_LIBRARY
+    PCAP_INCLUDE_DIR
+)
+
+include(CheckCXXSourceCompiles)
+set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
+check_cxx_source_compiles("int main() { return 0; }" PCAP_LINKS_SOLO)
+set(CMAKE_REQUIRED_LIBRARIES)
+
+# check if linking against libpcap also needs to link against a thread library
+if (NOT PCAP_LINKS_SOLO)
+    find_package(Threads)
+    if (THREADS_FOUND)
+        set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
+        check_cxx_source_compiles("int main() { return 0; }" PCAP_NEEDS_THREADS)
+        set(CMAKE_REQUIRED_LIBRARIES)
+    endif (THREADS_FOUND)
+    if (THREADS_FOUND AND PCAP_NEEDS_THREADS)
+        set(_tmp ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
+        list(REMOVE_DUPLICATES _tmp)
+        set(PCAP_LIBRARY ${_tmp}
+            CACHE STRING "Libraries needed to link against libpcap" FORCE)
+    else (THREADS_FOUND AND PCAP_NEEDS_THREADS)
+        message(FATAL_ERROR "Couldn't determine how to link against libpcap")
+    endif (THREADS_FOUND AND PCAP_NEEDS_THREADS)
+endif (NOT PCAP_LINKS_SOLO)
+
+include(CheckFunctionExists)
+set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
+check_function_exists(pcap_get_pfring_id HAVE_PF_RING)
+check_function_exists(pcap_set_immediate_mode HAVE_PCAP_IMMEDIATE_MODE)
+check_function_exists(pcap_set_tstamp_precision HAVE_PCAP_TIMESTAMP_PRECISION)
+set(CMAKE_REQUIRED_LIBRARIES)
+
+mark_as_advanced(
+    PCAP_ROOT_DIR
+    PCAP_INCLUDE_DIR
+    PCAP_LIBRARY
+)

+ 23 - 0
rslidar/rs_driver/cmake/cmake_uninstall.cmake.in

@@ -0,0 +1,23 @@
+if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+
+file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+    "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+    OUTPUT_VARIABLE rm_out
+    RETURN_VALUE rm_retval
+    )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif(NOT "${rm_retval}" STREQUAL 0)
+    else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+      message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+endforeach(file)
+
+

+ 29 - 0
rslidar/rs_driver/cmake/rs_driverConfig.cmake.in

@@ -0,0 +1,29 @@
+# - Config file for the @PROJECT_NAME_LOWER@ package
+# It defines the following variables
+#  rs_driver_INCLUDE_DIRS - include directories for @PROJECT_NAME_LOWER@
+#  rs_driver_LIBRARIES    - libraries to link against
+#  rs_driver_FOUND        - found flag
+
+if(WIN32)
+  if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64-bit
+    set(Boost_ARCHITECTURE "-x64")
+  elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32-bit
+    set(Boost_ARCHITECTURE "-x32")
+  endif()
+  set(Boost_USE_STATIC_LIBS ON)
+  set(Boost_USE_MULTITHREADED ON)
+  set(Boost_USE_STATIC_RUNTIME OFF)
+endif(WIN32)
+
+if(${ENABLE_TRANSFORM})
+  add_definitions("-DENABLE_TRANSFORM")
+endif(${ENABLE_TRANSFORM})
+
+set(rs_driver_INCLUDE_DIRS "@DRIVER_INCLUDE_DIRS@;@INSTALL_DRIVER_DIR@")
+set(RS_DRIVER_INCLUDE_DIRS "@DRIVER_INCLUDE_DIRS@;@INSTALL_DRIVER_DIR@")
+
+set(rs_driver_LIBRARIES "@EXTERNAL_LIBS@")
+set(RS_DRIVER_LIBRARIES "@EXTERNAL_LIBS@")
+
+set(rs_driver_FOUND true)
+set(RS_DRIVER_FOUND true)

+ 14 - 0
rslidar/rs_driver/cmake/rs_driverConfigVersion.cmake.in

@@ -0,0 +1,14 @@
+set (PACKAGE_VERSION "@rs_driver_VERSION@")
+message(=============================================================)
+message("-- rs_driver Version : v${PACKAGE_VERSION}")
+message(=============================================================)
+
+# Check whether the requested PACKAGE_FIND_VERSION is compatible
+if ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
+  set (PACKAGE_VERSION_COMPATIBLE FALSE)
+else ()
+  set (PACKAGE_VERSION_COMPATIBLE TRUE)
+  if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
+    set (PACKAGE_VERSION_EXACT TRUE)
+  endif ()
+endif ()

+ 42 - 0
rslidar/rs_driver/demo/CMakeLists.txt

@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(rs_driver_demos)
+
+message(=============================================================)
+message("-- Ready to compile demos")
+message(=============================================================)
+
+if (${ENABLE_PCL_POINTCLOUD})
+
+find_package(PCL REQUIRED)
+include_directories(${PCL_INCLUDE_DIRS})
+link_directories(${PCL_LIBRARY_DIRS})
+add_definitions(${PCL_DEFINITIONS})
+
+endif (${ENABLE_PCL_POINTCLOUD})
+
+include_directories(${DRIVER_INCLUDE_DIRS} )
+
+add_executable(demo_online
+              demo_online.cpp)
+
+target_link_libraries(demo_online
+                    ${EXTERNAL_LIBS})
+
+add_executable(demo_online_multi_lidars
+              demo_online_multi_lidars.cpp)
+
+target_link_libraries(demo_online_multi_lidars
+                    ${EXTERNAL_LIBS})
+
+if(NOT ${DISABLE_PCAP_PARSE})
+
+add_executable(demo_pcap
+               demo_pcap.cpp)
+
+target_link_libraries(demo_pcap
+                     ${EXTERNAL_LIBS})
+
+endif(NOT ${DISABLE_PCAP_PARSE})
+
+        

+ 162 - 0
rslidar/rs_driver/demo/demo_online.cpp

@@ -0,0 +1,162 @@
+/*********************************************************************************************************************
+Copyright (c) 2020 RoboSense
+All rights reserved
+
+By downloading, copying, installing or using the software you agree to this license. If you do not agree to this
+license, do not download, install, copy or use the software.
+
+License Agreement
+For RoboSense LiDAR SDK Library
+(3-clause BSD License)
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the names of the RoboSense, nor Suteng Innovation Technology, nor the names of other contributors may be used
+to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*********************************************************************************************************************/
+
+#include <rs_driver/api/lidar_driver.hpp>
+
+#ifdef ENABLE_PCL_POINTCLOUD
+#include <rs_driver/msg/pcl_point_cloud_msg.hpp>
+#else
+#include <rs_driver/msg/point_cloud_msg.hpp>
+#endif
+
+//#define ORDERLY_EXIT
+
+typedef PointXYZI PointT;
+typedef PointCloudT<PointT> PointCloudMsg;
+
+using namespace robosense::lidar;
+
+SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue;
+SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue;
+
+//
+// @brief point cloud callback function. The caller should register it to the lidar driver.
+//        Via this fucntion, the driver gets an free/unused point cloud message from the caller.
+// @param msg  The free/unused point cloud message.
+//
+std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
+{
+  // Note: This callback function runs in the packet-parsing/point-cloud-constructing thread of the driver, 
+  //       so please DO NOT do time-consuming task here.
+  std::shared_ptr<PointCloudMsg> msg = free_cloud_queue.pop();
+  if (msg.get() != NULL)
+  {
+    return msg;
+  }
+
+  return std::make_shared<PointCloudMsg>();
+}
+
+//
+// @brief point cloud callback function. The caller should register it to the lidar driver.
+//        Via this function, the driver gets/returns a stuffed point cloud message to the caller. 
+// @param msg  The stuffed point cloud message.
+//
+void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> msg)
+{
+  // Note: This callback function runs in the packet-parsing/point-cloud-constructing thread of the driver, 
+  //       so please DO NOT do time-consuming task here. Instead, process it in caller's own thread. (see processCloud() below)
+  stuffed_cloud_queue.push(msg);
+}
+
+//
+// @brief exception callback function. The caller should register it to the lidar driver.
+//        Via this function, the driver inform the caller that something happens.
+// @param code The error code to represent the error/warning/information
+//
+void exceptionCallback(const Error& code)
+{
+  // Note: This callback function runs in the packet-receving and packet-parsing/point-cloud_constructing thread of the driver, 
+  //       so please DO NOT do time-consuming task here.
+  RS_WARNING << code.toString() << RS_REND;
+}
+
+bool to_exit_process = false;
+void processCloud(void)
+{
+  while (!to_exit_process)
+  {
+    std::shared_ptr<PointCloudMsg> msg = stuffed_cloud_queue.popWait();
+    if (msg.get() == NULL)
+    {
+      continue;
+    }
+
+    // Well, it is time to process the point cloud msg, even it is time-consuming.
+    RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+
+#if 0
+    for (auto it = msg->points.begin(); it != msg->points.end(); it++)
+    {
+      std::cout << std::fixed << std::setprecision(3) 
+                << "(" << it->x << ", " << it->y << ", " << it->z << ", " << (int)it->intensity << ")" 
+                << std::endl;
+    }
+#endif
+
+    free_cloud_queue.push(msg);
+  }
+}
+
+int main(int argc, char* argv[])
+{
+  RS_TITLE << "------------------------------------------------------" << RS_REND;
+  RS_TITLE << "            RS_Driver Core Version: v" << getDriverVersion() << RS_REND;
+  RS_TITLE << "------------------------------------------------------" << RS_REND;
+
+  RSDriverParam param;                  ///< Create a parameter object
+  param.input_type = InputType::ONLINE_LIDAR;
+  param.input_param.msop_port = 6699;   ///< Set the lidar msop port number, the default is 6699
+  param.input_param.difop_port = 7788;  ///< Set the lidar difop port number, the default is 7788
+  param.lidar_type = LidarType::RSM1;   ///< Set the lidar type. Make sure this type is correct
+  param.print();
+
+  LidarDriver<PointCloudMsg> driver;               ///< Declare the driver object
+  driver.regPointCloudCallback(driverGetPointCloudFromCallerCallback, driverReturnPointCloudToCallerCallback); ///< Register the point cloud callback functions
+  driver.regExceptionCallback(exceptionCallback);  ///< Register the exception callback function
+  if (!driver.init(param))                         ///< Call the init function
+  {
+    RS_ERROR << "Driver Initialize Error..." << RS_REND;
+    return -1;
+  }
+
+  std::thread cloud_handle_thread = std::thread(processCloud);
+
+  driver.start();  ///< The driver thread will start
+  RS_DEBUG << "RoboSense Lidar-Driver Linux online demo start......" << RS_REND;
+
+#ifdef ORDERLY_EXIT
+  std::this_thread::sleep_for(std::chrono::seconds(10));
+
+  driver.stop();
+
+  to_exit_process = true;
+  cloud_handle_thread.join();
+#else
+  while (true)
+  {
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+  }
+#endif
+
+  return 0;
+}

+ 190 - 0
rslidar/rs_driver/demo/demo_online_multi_lidars.cpp

@@ -0,0 +1,190 @@
+/*********************************************************************************************************************
+Copyright (c) 2020 RoboSense
+All rights reserved
+
+By downloading, copying, installing or using the software you agree to this license. If you do not agree to this
+license, do not download, install, copy or use the software.
+
+License Agreement
+For RoboSense LiDAR SDK Library
+(3-clause BSD License)
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the names of the RoboSense, nor Suteng Innovation Technology, nor the names of other contributors may be used
+to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*********************************************************************************************************************/
+
+#include <rs_driver/api/lidar_driver.hpp>
+
+#ifdef ENABLE_PCL_POINTCLOUD
+#include <rs_driver/msg/pcl_point_cloud_msg.hpp>
+#else
+#include <rs_driver/msg/point_cloud_msg.hpp>
+#endif
+
+//#define ORDERLY_EXIT
+
+typedef PointXYZI PointT;
+typedef PointCloudT<PointT> PointCloudMsg;
+
+using namespace robosense::lidar;
+
+class DriverClient
+{
+public:
+
+  DriverClient(const std::string name)
+    : name_(name)
+  {
+  }
+
+  bool init(const RSDriverParam& param)
+  {
+    RS_INFO << "------------------------------------------------------" << RS_REND;
+    RS_INFO << "                      " << name_ << RS_REND;
+    RS_INFO << "------------------------------------------------------" << RS_REND;
+    param.print();
+
+    driver_.regPointCloudCallback (std::bind(&DriverClient::driverGetPointCloudFromCallerCallback, this),
+                                  std::bind(&DriverClient::driverReturnPointCloudToCallerCallback, this, std::placeholders::_1));
+    driver_.regExceptionCallback (std::bind(&DriverClient::exceptionCallback, this, std::placeholders::_1));
+
+    if (!driver_.init(param))
+    {
+      RS_ERROR << name_ << ": Failed to initialize driver." << RS_REND;
+      return false;
+    }
+
+    return true;
+  }
+
+  bool start()
+  {
+    to_exit_process_ = false;
+    cloud_handle_thread_ = std::thread(std::bind(&DriverClient::processCloud, this));
+
+    driver_.start();
+    RS_DEBUG << name_ << ": Started driver." << RS_REND;
+
+    return true;
+  }
+
+  void stop()
+  {
+    driver_.stop();
+
+    to_exit_process_ = true;
+    cloud_handle_thread_.join();
+  }
+
+protected:
+
+  std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
+  {
+    std::shared_ptr<PointCloudMsg> msg = free_cloud_queue_.pop();
+    if (msg.get() != NULL)
+    {
+      return msg;
+    }
+
+    return std::make_shared<PointCloudMsg>();
+  }
+
+  void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> msg)
+  {
+    stuffed_cloud_queue_.push(msg);
+  }
+
+  void processCloud(void)
+  {
+    while (!to_exit_process_)
+    {
+      std::shared_ptr<PointCloudMsg> msg = stuffed_cloud_queue_.popWait();
+      if (msg.get() == NULL)
+      {
+        continue;
+      }
+
+      RS_MSG << name_ << ": msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+
+      free_cloud_queue_.push(msg);
+    }
+  }
+
+  void exceptionCallback(const Error& code)
+  {
+    RS_WARNING << name_ << ": " << code.toString() << RS_REND;
+  }
+
+  std::string name_;
+  LidarDriver<PointCloudMsg> driver_;
+  bool to_exit_process_;
+  std::thread cloud_handle_thread_;
+  SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue_;
+  SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue_;
+};
+
+int main(int argc, char* argv[])
+{
+  RS_TITLE << "------------------------------------------------------" << RS_REND;
+  RS_TITLE << "            RS_Driver Core Version: v" << getDriverVersion() << RS_REND;
+  RS_TITLE << "------------------------------------------------------" << RS_REND;
+
+  RSDriverParam param_left;                  ///< Create a parameter object
+  param_left.input_type = InputType::ONLINE_LIDAR;
+  param_left.input_param.msop_port = 6004;   ///< Set the lidar msop port number, the default is 6699
+  param_left.input_param.difop_port = 7004;  ///< Set the lidar difop port number, the default is 7788
+  param_left.lidar_type = LidarType::RSM1;   ///< Set the lidar type. Make sure this type is correct
+
+  DriverClient client_left("LEFT ");
+  if (!client_left.init(param_left))                         ///< Call the init function
+  {
+    return -1;
+  }
+
+  RSDriverParam param_right;                  ///< Create a parameter object
+  param_right.input_type = InputType::ONLINE_LIDAR;
+  param_right.input_param.msop_port = 6005;   ///< Set the lidar msop port number, the default is 6699
+  param_right.input_param.difop_port = 7005;  ///< Set the lidar difop port number, the default is 7788
+  param_right.lidar_type = LidarType::RSM1;   ///< Set the lidar type. Make sure this type is correct
+
+  DriverClient client_right("RIGHT");
+  if (!client_right.init(param_right))                         ///< Call the init function
+  {
+    return -1;
+  }
+
+  client_left.start();
+  client_right.start();
+
+#ifdef ORDERLY_EXIT
+  std::this_thread::sleep_for(std::chrono::seconds(10));
+
+  client_left.stop();
+  client_right.stop();
+#else
+  while (true)
+  {
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+  }
+#endif
+
+  return 0;
+}
+

+ 164 - 0
rslidar/rs_driver/demo/demo_pcap.cpp

@@ -0,0 +1,164 @@
+/*********************************************************************************************************************
+Copyright (c) 2020 RoboSense
+All rights reserved
+
+By downloading, copying, installing or using the software you agree to this license. If you do not agree to this
+license, do not download, install, copy or use the software.
+
+License Agreement
+For RoboSense LiDAR SDK Library
+(3-clause BSD License)
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the names of the RoboSense, nor Suteng Innovation Technology, nor the names of other contributors may be used
+to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*********************************************************************************************************************/
+
+#include <rs_driver/api/lidar_driver.hpp>
+
+#ifdef ENABLE_PCL_POINTCLOUD
+#include <rs_driver/msg/pcl_point_cloud_msg.hpp>
+#else
+#include <rs_driver/msg/point_cloud_msg.hpp>
+#endif
+
+//#define ORDERLY_EXIT
+
+typedef PointXYZI PointT;
+typedef PointCloudT<PointT> PointCloudMsg;
+
+using namespace robosense::lidar;
+
+SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue;
+SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue;
+
+//
+// @brief point cloud callback function. The caller should register it to the lidar driver.
+//        Via this fucntion, the driver gets an free/unused point cloud message from the caller.
+// @param msg  The free/unused point cloud message.
+//
+std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
+{
+  // Note: This callback function runs in the packet-parsing/point-cloud-constructing thread of the driver, 
+  //       so please DO NOT do time-consuming task here.
+  std::shared_ptr<PointCloudMsg> msg = free_cloud_queue.pop();
+  if (msg.get() != NULL)
+  {
+    return msg;
+  }
+
+  return std::make_shared<PointCloudMsg>();
+}
+
+//
+// @brief point cloud callback function. The caller should register it to the lidar driver.
+//        Via this function, the driver gets/returns a stuffed point cloud message to the caller. 
+// @param msg  The stuffed point cloud message.
+//
+void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> msg)
+{
+  // Note: This callback function runs in the packet-parsing/point-cloud-constructing thread of the driver, 
+  //       so please DO NOT do time-consuming task here. Instead, process it in caller's own thread. (see processCloud() below)
+  stuffed_cloud_queue.push(msg);
+}
+
+//
+// @brief exception callback function. The caller should register it to the lidar driver.
+//        Via this function, the driver inform the caller that something happens.
+// @param code The error code to represent the error/warning/information
+//
+void exceptionCallback(const Error& code)
+{
+  // Note: This callback function runs in the packet-receving and packet-parsing/point-cloud_constructing thread of the driver, 
+  //       so please DO NOT do time-consuming task here.
+  RS_WARNING << code.toString() << RS_REND;
+}
+
+bool to_exit_process = false;
+void processCloud(void)
+{
+  while (!to_exit_process)
+  {
+    std::shared_ptr<PointCloudMsg> msg = stuffed_cloud_queue.popWait();
+    if (msg.get() == NULL)
+    {
+      continue;
+    }
+
+    // Well, it is time to process the point cloud msg, even it is time-consuming.
+    RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+
+#if 0
+    for (auto it = msg->points.begin(); it != msg->points.end(); it++)
+    {
+      std::cout << std::fixed << std::setprecision(3) 
+                << "(" << it->x << ", " << it->y << ", " << it->z << ", " << (int)it->intensity << ")" 
+                << std::endl;
+    }
+#endif
+
+    free_cloud_queue.push(msg);
+  }
+}
+
+int main(int argc, char* argv[])
+{
+  RS_TITLE << "------------------------------------------------------" << RS_REND;
+  RS_TITLE << "            RS_Driver Core Version: v" << getDriverVersion() << RS_REND;
+  RS_TITLE << "------------------------------------------------------" << RS_REND;
+
+  RSDriverParam param;                                         ///< Create a parameter object
+  param.input_type = InputType::PCAP_FILE;
+  param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file directory
+  param.input_param.msop_port = 6699;                          ///< Set the lidar msop port number, the default is 6699
+  param.input_param.difop_port = 7788;                         ///< Set the lidar difop port number, the default is 7788
+  param.lidar_type = LidarType::RSM1;                          ///< Set the lidar type. Make sure this type is correct
+  param.print();
+
+  LidarDriver<PointCloudMsg> driver;               ///< Declare the driver object
+  driver.regPointCloudCallback(driverGetPointCloudFromCallerCallback, driverReturnPointCloudToCallerCallback); ///< Register the point cloud callback functions
+  driver.regExceptionCallback(exceptionCallback);  ///< Register the exception callback function
+  if (!driver.init(param))                         ///< Call the init function
+  {
+    RS_ERROR << "Driver Initialize Error..." << RS_REND;
+    return -1;
+  }
+
+  std::thread cloud_handle_thread = std::thread(processCloud);
+
+  driver.start();  ///< The driver thread will start
+
+  RS_DEBUG << "RoboSense Lidar-Driver Linux pcap demo start......" << RS_REND;
+
+#ifdef ORDERLY_EXIT
+  std::this_thread::sleep_for(std::chrono::seconds(10));
+
+  driver.stop();
+
+  to_exit_process = true;
+  cloud_handle_thread.join();
+#else
+  while (true)
+  {
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+  }
+#endif
+
+  return 0;
+}

+ 235 - 0
rslidar/rs_driver/doc/howto/07_how_to_port_your_app_from_rs_driver_1.3.x_to_1.5.x.md

@@ -0,0 +1,235 @@
+# 7 How to port your app from rs_driver v1.3.x to v1.5.x
+
+
+
+## 7.1 Branches and Versions
+
+Now `rs_driver` have the following braches.
++ `dev`. The development branch of `v1.3.x`. The latest version is `v1.3.2`. It is no longer developed.
++ `dev_opt`. The development branch of `v1.5.x`. It is refactoried from `v1.3.x`.
++ `main`. stable branch. It tracked `dev` before, and tracked `dev_opt` now.  
+
+Generally, the modifications in `dev_opt` will be merged into `main` monthly. At the same time, a new version is created.
+
+Occasionally, to solve a emegency problem, hotfix patches will be commited and merged into `dev_opt` and `main`.
+
+
+
+## 7.2 What enhancement ?
+
+The main advantage of `v1.5.x` is to reduce CPU usage obviously. 
+
+On a platfrom, CPU usage of `dev` is as below.
+![](./img/07_02_cpu_usage_dev.png)
+
+CPU usage of `dev_opt` is as below.
+![](./img/07_03_cpu_usage_dev_opt.png)
+
+
+
+Other advantages are as below.
+
++ Remove the dependency on the Boost library, help to port `rs_driver` to embedded Linux.
++ Add compile options to solve packet-loss problems on some platforms.
++ Refactory `Input` and `Decoder`, to support different use cases.
++ For mechanical LiDARs, use Block as splitting unit instead of Packet, to avoid flash of point cloud .
++ Improve the help documents, including [understand source code of rs_driver](../src_intro/rs_driver_intro_CN.md), to help user understand it.
+
+
+
+## 7.3 How to port ?
+
+The interface of rs_driver contains two parts: RSDriverParam and LidarDriver.
+
+### 7.3.1 RSDriverParam
+
+#### 7.3.1.1 RSDriverParam
+
+RSDriverParam contains the options to change the rs_driver's behavior.
+
+RSDriverParam is as below in rs_driver `1.3.x`.
+
+```c++
+typedef struct RSDriverParam
+{
+  RSInputParam input_param;                ///< Input parameter
+  RSDecoderParam decoder_param;            ///< Decoder parameter
+  std::string angle_path = "null";         ///< Path of angle calibration files(angle.csv).Only used for internal debugging.
+  std::string frame_id = "rslidar";        ///< The frame id of LiDAR message
+  LidarType lidar_type = LidarType::RS16;  ///< Lidar type
+  bool wait_for_difop = true;              ///< true: start sending point cloud until receive difop packet
+  bool saved_by_rows = false;              ///< true: the output point cloud will be saved by rows (default is saved by columns)
+};
+```
+
+And it is as below in rs_driver `1.5.x`.
+
+```c++
+typedef struct RSDriverParam
+{
+  LidarType lidar_type = LidarType::RS16;  ///< Lidar type
+  InputType input_type = InputType::ONLINE_LIDAR; ///< Input type
+  RSInputParam input_param;                ///< Input parameter
+  RSDecoderParam decoder_param;            ///< Decoder parameter
+};
+```
+
+Below are the changes.
++ A new member `input_type` is added. It means the type of data sources: 
+  + `ONLINE_LIDAR`
+  +  `PCAP_FILE` 
+  + `RAW_PACKET`. User's data captured via `rs_driver`. In `v1.3.x`, a group of functions are for it, and  in `v1.5.x`, it is uniformed to `input_type`.
+
++ The member `wait_for_difop` specifies whether to handle MSOP packets before handling DIFOP packet. For mechenical LiDARs, the point cloud will be flat without the angle data in DIFOP packet。This option is about decoding, so it is shifted to RSDecoderParam.
++ The member `saved_by_rows` specifies to give point cloud row by row. There are two reasons to remove it: 
+  + Even point cloud is given column by column, it is easier to access row by row (by skipping column). 
+  + The option consumes two much CPU resources, so not suggested.
+  + As a exception on ROS,  `rslidar_sdk` can give point cloud row by row with the option `ros_send_by_rows=true`. This is done while copying point cloud, so as not to cost more efforts.
++ The member `angle_path` specifies a file which contains the angle data. It is about data source, so it is shifted to RSInputParam.
++ The member `frame_id` is a concept from ROS. It is removed, and the driver `rslidar_sdk`  (for ROS) will handle it.
+
+#### 7.3.1.2 RSInputParam
+
+RSInputParam is as below in rs_driver `1.3.x`.
+
+```c++
+typedef struct RSInputParam  ///< The LiDAR input parameter
+{
+  std::string device_ip = "192.168.1.200";     ///< Ip of LiDAR
+  std::string multi_cast_address = "0.0.0.0";  ///< Address of multicast
+  std::string host_address = "0.0.0.0";        ///< Address of host
+  uint16_t msop_port = 6699;                   ///< Msop packet port number
+  uint16_t difop_port = 7788;                  ///< Difop packet port number
+  bool read_pcap = false;          ///< true: The driver will process the pcap through pcap_path. false: The driver will
+                                   ///< Get data from online LiDAR
+  double pcap_rate = 1;            ///< Rate to read the pcap file
+  bool pcap_repeat = true;         ///< true: The pcap bag will repeat play
+  std::string pcap_path = "null";  ///< Absolute path of pcap file
+  bool use_vlan = false;           ///< Vlan on-off
+  bool use_someip = false;         ///< Someip on-off
+  bool use_custom_proto = false;   ///< Customer Protocol on-off
+};
+```
+
+And it is as below in rs_driver `1.5.x`.
+
+```c++
+typedef struct RSInputParam  ///< The LiDAR input parameter
+{
+  uint16_t msop_port = 6699;                   ///< Msop packet port number
+  uint16_t difop_port = 7788;                  ///< Difop packet port number
+  std::string host_address = "0.0.0.0";        ///< Address of host
+  std::string group_address = "0.0.0.0";       ///< Address of multicast group
+  std::string pcap_path = "";                  ///< Absolute path of pcap file
+  bool pcap_repeat = true;                     ///< true: The pcap bag will repeat play
+  float pcap_rate = 1.0f;                      ///< Rate to read the pcap file
+  bool use_vlan = false;                       ///< Vlan on-off
+  uint16_t user_layer_bytes = 0;    ///< Bytes of user layer. thers is no user layer if it is 0
+  uint16_t tail_layer_bytes = 0;    ///< Bytes of tail layer. thers is no tail layer if it is 0
+};
+```
+
+Below are the changes.
++ The member `device_ip` is not needed for receiving packets, so it is removed.
++ The member `multi_cast_address` is renamed to `group_address`。This is to say, in the multicast mode, the host will join the group to receive packets.
++ The member `read_pcap` is not needed now. `RSDriverParam.input_type` replaces it. 
++ The members `use_someip` and `use_custom_proto` is not needed now. `user_layer_bytes` replaces them.
+
+#### 7.3.1.3 RSDecoderParam
+
+RSDecoderParam is as below in rs_driver 1.3.x.
+
+```c++
+typedef struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  float max_distance = 200.0f;                                       ///< Max distance of point cloud range
+  float min_distance = 0.2f;                                         ///< Minimum distance of point cloud range
+  float start_angle = 0.0f;                                          ///< Start angle of point cloud
+  float end_angle = 360.0f;                                          ///< End angle of point cloud
+  SplitFrameMode split_frame_mode = SplitFrameMode::SPLIT_BY_ANGLE;  ///< 1: Split frames by cut_angle;
+                                                                     ///< 2: Split frames by fixed number of packets;
+                                                             ///< 3: Split frames by custom number of packets (num_pkts_split)
+  uint32_t num_pkts_split = 1;         ///< Number of packets in one frame, only be used when split_frame_mode=3
+  float cut_angle = 0.0f;              ///< Cut angle(degree) used to split frame, only be used when split_frame_mode=1
+  bool use_lidar_clock = false;        ///< true: use LiDAR clock as timestamp; false: use system clock as timestamp
+  RSTransformParam transform_param;    ///< Used to transform points
+  RSCameraTriggerParam trigger_param;  ///< Used to trigger camera
+};
+```
+
+And it is as below in rs_driver 1.5.x.
+
+```c++
+typedef struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  bool config_from_file = false; ///< Internal use only for debugging
+  std::string angle_path = "";   ///< Internal use only for debugging
+  bool wait_for_difop = true;    ///< true: start sending point cloud until receive difop packet
+  float min_distance = 0.2f;     ///< Minimum distance of point cloud range
+  float max_distance = 200.0f;   ///< Max distance of point cloud range
+  float start_angle = 0.0f;      ///< Start angle of point cloud
+  float end_angle = 360.0f;      ///< End angle of point cloud
+  SplitFrameMode split_frame_mode = SplitFrameMode::SPLIT_BY_ANGLE;  
+                                 ///< 1: Split frames by split_angle;
+                                 ///< 2: Split frames by fixed number of blocks;
+                                 ///< 3: Split frames by custom number of blocks (num_blks_split)
+  float split_angle = 0.0f;      ///< Split angle(degree) used to split frame, only be used when split_frame_mode=1
+  uint16_t num_blks_split = 1;   ///< Number of packets in one frame, only be used when split_frame_mode=3
+  bool use_lidar_clock = false;  ///< true: use LiDAR clock as timestamp; false: use system clock as timestamp
+  bool dense_points = false;     ///< true: discard NAN points; false: reserve NAN points
+  RSTransformParam transform_param; ///< Used to transform points
+};
+```
+
+Below are the changes.
++ The member `cut_angle` is renamed to `split_angle`, to align to `split_frame_mode`.
++ The member `config_from_file` is added. Only if it is true, the member `angle_path` is valid.
++ The member `num_pkts_split` is renamed to `number_blks_split`. That means to split frames `by blocks (in MSOP packets)` instead of `by MSOP packets`.
+
+### 7.3.2 LidarDriver
+
+LidarDriver contains the functions to run rs_driver.
+
+LidarDriver is as below in rs_driver 1.3.x.
+
+```c++
+template <typename PointT>
+class LidarDriver
+{
+public:
+
+  inline void regRecvCallback(const std::function<void(const PointCloudMsg<PointT>&)>& callback)
+  {
+    driver_ptr_->regRecvCallback(callback);
+  }
+  ...
+};
+```
+
+And it is as below in rs_driver 1.5.x.
+
+```c++
+template <typename T_PointCloud>
+class LidarDriver
+{
+public:
+
+ inline void regPointCloudCallback(const std::function<std::shared_ptr<T_PointCloud>(void)>& cb_get_cloud,
+      const std::function<void(std::shared_ptr<T_PointCloud>)>& cb_put_cloud)
+  {
+    driver_ptr_->regPointCloudCallback(cb_get_cloud, cb_put_cloud);
+  }
+  ...
+};
+```
+
+Below are the changes.
++ The template parameter is changed from typename `PointT` to typename `T_PointCloud`.
++ The function to get point cloud is now requested to have two callbacks instead of one.
+  + `cb_get_cloud` is to get free point cloud instance from caller. `cb_put_cloud` is to return stuffed point cloud to caller.
+
+
+For details of these two changes, please refer to [Decode online LiDAR](./08_how_to_decode_online_lidar.md) and [Thread Mode and Interface](../intro/03_thread_model.md). 
+
+
+

+ 256 - 0
rslidar/rs_driver/doc/howto/07_how_to_port_your_app_from_rs_driver_1.3.x_to_1.5.x_CN.md

@@ -0,0 +1,256 @@
+# 7 **如何从rs_driver v1.3.x升级到v1.5.x**
+
+
+
+## 7.1 目前的分支与版本
+
+目前github上的[rs_driver](https://github.com/RoboSense-LiDAR/rs_driver)工程主要有如下分支。
+
++ `dev`分支: `v1.3.x`的开发分支,最后版本是`v1.3.2`。这个分支已经停止开发。
++ `dev_opt`分支: 在`v1.3.x`基础上重构的`v1.5.x`的开发分支。
++ `main`分支: 稳定分支。以前跟踪`dev`分支,现在跟踪`dev_opt`分支。
+![](./img/07_01_branches.png)
+
+一般情况下,`dev_opt`分支中的修改,在基本稳定后,会合并到`main`分支中,并建立新的版本号。
+
+特别地,对一些影响较大的问题,会推出紧急修复补丁,这个补丁会同时合并到`dev_opt`分支和`main`分支中。
+
+
+
+## 7.2 v1.5.x相对于v1.3.x的改进
+
+相对于`v1.3.x`,rs_driver `v1.5.x`的主要改进是明显减少CPU占用率。
+
+如下是在某平台上针对`dev`分支和`dev_opt`分支进行对比测试的结果。
+
+`dev`分支的cpu占用率如下图。
+![](./img/07_02_cpu_usage_dev.png)
+
+`dev_opt`分支的cpu占用率如下图。
+![](./img/07_03_cpu_usage_dev_opt.png)
+
+`dev_opt`分支的CPU占用率比`dev`分支降低了约`5%`,降低的比例为`16.7%`。
+
+
+
+其他的改进还包括:
+
++ 去除对Boost库的依赖,更容易移植到一些嵌入式Linux平台上
+
++ 增加可选的编译选项,解决在某些平台上丢包的问题
+
++ 重构网络部分和解码部分的代码,完整支持不同场景下的配置
+
++ 对于机械式雷达,将分帧的粒度从Packet细化为Block,避免分帧角度附近点云闪烁的问题
+
++ 完善帮助文档,尤其是提供了[rs_driver源代码解析](../src_intro/rs_driver_intro_CN.md)文档,帮助用户深入理解驱动的实现,以方便功能裁剪。
+
+对于新客户,推荐使用`v1.5.x`。对于老客户,如果有以上问题的困扰,也推荐升级到`v1.5.x`。
+
+
+
+## 7.3 如何升级?
+
+相对于`v1.3.x`, `v1.5.x`调整了接口的定义,也调整了部分配置选项的名字和位置。对这些调整的说明如下。
+
+`rs_driver`的接口包括了两个部分:`RSDriverParam`和`LidarDriver`。
+
+### 7.3.1 RSDriverParam
+
+#### 7.3.1.1 RSDriverParam
+
+RSDriverParam包括一些配置选项,这些选项可以改变`rs_driver`的行为。
+
+在`v1.3.x`中,RSDriverParam定义如下。
+
+```c++
+typedef struct RSDriverParam
+{
+  RSInputParam input_param;                ///< Input parameter
+  RSDecoderParam decoder_param;            ///< Decoder parameter
+  std::string angle_path = "null";         ///< Path of angle calibration files(angle.csv).Only used for internal debugging.
+  std::string frame_id = "rslidar";        ///< The frame id of LiDAR message
+  LidarType lidar_type = LidarType::RS16;  ///< Lidar type
+  bool wait_for_difop = true;              ///< true: start sending point cloud until receive difop packet
+  bool saved_by_rows = false;              ///< true: the output point cloud will be saved by rows (default is saved by columns)
+};
+```
+
+在`v1.5.x`中,RSDriverParam定义如下。
+
+```c++
+typedef struct RSDriverParam
+{
+  LidarType lidar_type = LidarType::RS16;  ///< Lidar type
+  InputType input_type = InputType::ONLINE_LIDAR; ///< Input type
+  RSInputParam input_param;                ///< Input parameter
+  RSDecoderParam decoder_param;            ///< Decoder parameter
+};
+```
+
+变动如下:
++ 加入新成员`input_type`。这个成员指定数据源的类型:
+  + `ONLINE_LIDAR` 在线雷达, 
+  + `PCAP_FILE` PCAP文件
+  + `RAW_PACKET` 用户自己保存的MSOP/DIFOP Packet数据
+
+指定了哪种类型,就从哪类数据源获得数据。用户自己的数据在`v1.3.x`中有一组专门的函数处理,在`v1.5.x`中则统一到数据源类型`RAW_PACKET`。
+
++ 成员`wait_for_difop` 指定在处理MSOP包之前,是否先等待DIFOP包。对于机械式雷达,如果没有DIFOP包中的垂直角信息,点云就是扁平的。这个选项与解码相关,所以把它移到RSDecoderParam中。
+
++ 删除成员`saved_by_rows`。雷达每轮扫描得到一组点(每个通道一个点),依次保存在点云的vector成员中,这是`按列保存`。在`v1.3.x`中,成员`saved_by_rows` 指定`按行保存`,也就是每次保存一个通道上的所有点。删除这个成员有两个原因:
+  + 在`按列保存`的vector中,按照行的顺序检索点,也是容易的。跳过通道数就可以了。
+  + `v1.3.x`实现`saved_by_rows`的方式是将点云重排一遍,这样复制点云的代码太大。
+  
+  如果您的代码依赖`按行保存`这个特性,建议按`跳过通道数`访问达到同样的效果。
+  
+  作为一个特例,如果您的代码基于ROS,那么适配ROS的驱动`rslidar_sdk`有个选项`ros_send_by_rows`,也可以做到`按行保存`。`rslidar_sdk`本来就需要将`rs_driver`点云转换到ROS点云,行列变换同时进行,不会带来额外的复制成本。
+  
++ 成员`angle_path`指定`垂直角/水平角的修正值`从指定的文件读取,而不是解析DIFOP包得到。这个选项是关于数据源的,所以移到RSInputParam。
+
++ 成员`frame_id` 来自ROS的概念,`rs_driver`本身不需要它,所以删除。适配ROS的驱动 `rslidar_sdk`会创建和处理它。
+
+#### 7.3.1.2 RSInputParam
+
+在`v1.3.x`中,RSInputParam定义如下。
+
+```c++
+typedef struct RSInputParam  ///< The LiDAR input parameter
+{
+  std::string device_ip = "192.168.1.200";     ///< Ip of LiDAR
+  std::string multi_cast_address = "0.0.0.0";  ///< Address of multicast
+  std::string host_address = "0.0.0.0";        ///< Address of host
+  uint16_t msop_port = 6699;                   ///< Msop packet port number
+  uint16_t difop_port = 7788;                  ///< Difop packet port number
+  bool read_pcap = false;          ///< true: The driver will process the pcap through pcap_path. false: The driver will
+                                   ///< Get data from online LiDAR
+  double pcap_rate = 1;            ///< Rate to read the pcap file
+  bool pcap_repeat = true;         ///< true: The pcap bag will repeat play
+  std::string pcap_path = "null";  ///< Absolute path of pcap file
+  bool use_vlan = false;           ///< Vlan on-off
+  bool use_someip = false;         ///< Someip on-off
+  bool use_custom_proto = false;   ///< Customer Protocol on-off
+};
+```
+
+在`v1.5.x`中,RSInputParam定义如下。
+
+```c++
+typedef struct RSInputParam  ///< The LiDAR input parameter
+{
+  uint16_t msop_port = 6699;                   ///< Msop packet port number
+  uint16_t difop_port = 7788;                  ///< Difop packet port number
+  std::string host_address = "0.0.0.0";        ///< Address of host
+  std::string group_address = "0.0.0.0";       ///< Address of multicast group
+  std::string pcap_path = "";                  ///< Absolute path of pcap file
+  bool pcap_repeat = true;                     ///< true: The pcap bag will repeat play
+  float pcap_rate = 1.0f;                      ///< Rate to read the pcap file
+  bool use_vlan = false;                       ///< Vlan on-off
+  uint16_t user_layer_bytes = 0;    ///< Bytes of user layer. thers is no user layer if it is 0
+  uint16_t tail_layer_bytes = 0;    ///< Bytes of tail layer. thers is no tail layer if it is 0
+};
+```
+
+变动如下:
++ 删除成员`device_ip`。rs_driver接收MSOP/DIFOP包时,其实不需要这个值。
++ 成员`multi_cast_address`改名为`group_address`。新名字想表达的含义是:在组播模式下,`rs_driver`会将IP地址为`host_address`的网卡加入`group_address`指定的组播组。
++ 删除成员`read_pcap`。 既然`RSDriverParam.input_type` 已经明确了数据源类型,`read_pcap`就不需要了。
++ 删除成员`use_someip` and `use_custom_proto`。它们的功能已经被新的选项 `RSInputParam.user_layer_bytes`所取代。
+
+#### 7.3.1.3 RSDecoderParam
+
+在`v1.3.x`中,RSDecoderParam定义如下。
+
+```c++
+typedef struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  float max_distance = 200.0f;                                       ///< Max distance of point cloud range
+  float min_distance = 0.2f;                                         ///< Minimum distance of point cloud range
+  float start_angle = 0.0f;                                          ///< Start angle of point cloud
+  float end_angle = 360.0f;                                          ///< End angle of point cloud
+  SplitFrameMode split_frame_mode = SplitFrameMode::SPLIT_BY_ANGLE;  ///< 1: Split frames by cut_angle;
+                                                                     ///< 2: Split frames by fixed number of packets;
+                                                             ///< 3: Split frames by custom number of packets (num_pkts_split)
+  uint32_t num_pkts_split = 1;         ///< Number of packets in one frame, only be used when split_frame_mode=3
+  float cut_angle = 0.0f;              ///< Cut angle(degree) used to split frame, only be used when split_frame_mode=1
+  bool use_lidar_clock = false;        ///< true: use LiDAR clock as timestamp; false: use system clock as timestamp
+  RSTransformParam transform_param;    ///< Used to transform points
+  RSCameraTriggerParam trigger_param;  ///< Used to trigger camera
+};
+```
+
+在`v1.5.x`中,RSDecoderParam定义如下。
+
+```c++
+typedef struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  bool config_from_file = false; ///< Internal use only for debugging
+  std::string angle_path = "";   ///< Internal use only for debugging
+  bool wait_for_difop = true;    ///< true: start sending point cloud until receive difop packet
+  float min_distance = 0.0f;     ///< Minimum distance of point cloud range
+  float max_distance = 200.0f;   ///< Max distance of point cloud range
+  float start_angle = 0.0f;      ///< Start angle of point cloud
+  float end_angle = 360.0f;      ///< End angle of point cloud
+  SplitFrameMode split_frame_mode = SplitFrameMode::SPLIT_BY_ANGLE;  
+                                 ///< 1: Split frames by split_angle;
+                                 ///< 2: Split frames by fixed number of blocks;
+                                 ///< 3: Split frames by custom number of blocks (num_blks_split)
+  float split_angle = 0.0f;      ///< Split angle(degree) used to split frame, only be used when split_frame_mode=1
+  uint16_t num_blks_split = 1;   ///< Number of packets in one frame, only be used when split_frame_mode=3
+  bool use_lidar_clock = false;  ///< true: use LiDAR clock as timestamp; false: use system clock as timestamp
+  bool dense_points = false;     ///< true: discard NAN points; false: reserve NAN points
+  RSTransformParam transform_param; ///< Used to transform points
+};
+```
+
+变动如下:
++ 成员`cut_angle`改名为`split_angle`, 以便与成员 `split_frame_mode`的名字保持一致。
++ 增加成员`config_from_file`。只有这个成员为`true`,成员`angle_path`才有效。
++ 成员`num_pkts_split`改名为 `number_blks_split`. 因为在`v1.3.x`中,分帧的最小单位是Packet,而在`v1.5.x`中,分帧单位改成了更小的Block。
+
+
+
+### 7.3.2 LidarDriver
+
+创建LidarDriver实例,并调用它的成员函数,可以运行`rs_driver`。
+
+在`v1.3.x`中,LidarDriver定义如下。
+
+```c++
+template <typename PointT>
+class LidarDriver
+{
+public:
+
+  inline void regRecvCallback(const std::function<void(const PointCloudMsg<PointT>&)>& callback)
+  {
+    driver_ptr_->regRecvCallback(callback);
+  }
+  ...
+};
+```
+
+
+在`v1.5.x`中,LidarDriver定义如下。
+
+```c++
+template <typename T_PointCloud>
+class LidarDriver
+{
+public:
+
+ inline void regPointCloudCallback(const std::function<std::shared_ptr<T_PointCloud>(void)>& cb_get_cloud,
+      const std::function<void(std::shared_ptr<T_PointCloud>)>& cb_put_cloud)
+  {
+    driver_ptr_->regPointCloudCallback(cb_get_cloud, cb_put_cloud);
+  }
+  ...
+};
+```
+
+变动如下:
++ LidarDriver类的模板参数从点 `PointT`改成了点云 `T_PointCloud`。
++ LidarDriver的一个点云回调函数,变成了两个。
+  + 在`v1.3.x`中,LidarDriver只需要一个点云回调函数`callback`。rs_driver通过这个函数将构建好的点云返回给调用者。
+  + 在`v1.5.x`中,LidarDriver需要两个回调函数。一个函数`cb_put_cloud`与`v1.3.x`的类似,rs_driver将构建好的点云返回给调用者。另一个函数`cb_get_cloud`则是`rs_driver`需要通过这个函数,从调用者获取空闲的点云实例。关于这一点的更详细说明,请参考[连接在线雷达](./08_how_to_decode_online_lidar_CN.md) 和 [rs_driver的线程模型与接口设计](../intro/03_thread_model_CN.md)。
+

+ 268 - 0
rslidar/rs_driver/doc/howto/08_how_to_decode_online_lidar.md

@@ -0,0 +1,268 @@
+# 8 How to decode online LiDAR
+
+
+
+## 8.1 Introduction
+
+This document illustrates how to decode the MSOP/DIFOP packets from an online LiDAR with `rs_driver`'s API.
+
+For more info, please refer to the demo app `rs_driver/demo/demo_online.cpp`.
+
+Also refer to the demo app `rs_driver/demo/demo_online_multi_lidars.cpp`. It is an example of multiple LiDARs.
+
+Here the definitions of point and point cloud, is from the project file.
+
+`rs_driver/src/rs_driver/msg/point_cloud_msg.hpp`, `rs_driver/src/rs_driver/msg/pcl_point_cloud_msg.hpp`
+
+
+
+## 8.2 Steps
+
+### 8.2.1 Define Point
+
+Point is the base unit of Point Cloud. `rs_driver` supports these member variables.
+- x -- The x coordinate of point. float type.
+- y -- The y coordinate of point. float type.
+- z -- The z coordinate of point. float type.
+- intensity -- The intensity of point. uint8_t type. 
+- timestamp -- The timestamp of point. double type.  It may be generated by the LiDAR or `rs_driver` on the host machine.
+- ring -- The ring ID of the point, which represents the row/channel number. Take RS80 as an example, the range of ring ID is `0`~`79` (from bottom to top).
+
+Below are some examples: 
+
+- The point type contains **x, y, z, intensity**
+
+  ```c++
+  struct PointXYZI
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+  };
+  ```
+  
+- If using PCL, a simple way is to use **pcl::PointXYZI**.
+
+- The point type contains **x, y, z, intensity, timestamp, ring**
+
+  ```c++
+  struct PointXYZIRT
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+    double timestamp;
+    uint16_t ring;
+  };
+  ```
+
+Here user may add new member variables, remove member variables, or change the order of them, but should not change names or types of them.
+
+### 8.2.2 Define Point Cloud
+
+  Below is the definition of point cloud.
+
+  ```c++
+  template <typename T_Point>
+  class PointCloudT
+  {
+  public:
+    typedef T_Point PointT;
+    typedef std::vector<PointT> VectorT;
+
+    uint32_t height = 0;    ///< Height of point cloud
+    uint32_t width = 0;     ///< Width of point cloud
+    bool is_dense = false;  ///< If is_dense is true, the point cloud does not contain NAN points
+    double timestamp = 0.0; ///< Timestamp of point cloud
+    uint32_t seq = 0;       ///< Sequence number of message
+
+    VectorT points;
+  };
+  ```
+
+  Here user may add new members, and change the order of these members, but should not change or remove them.
+
+  This definition is a template class. It needs a Point type as template parameter.
+
+  ```
+  typedef PointXYZI PointT;
+  typedef PointCloudT<PointT> PointCloudMsg;
+  ```
+
+
+### 8.2.3 Define the driver object
+
+Define a driver object.
+
+```c++
+int main()
+{
+  LidarDriver<PointCloudMsg> driver;          ///< Declare the driver object
+  ...
+}
+```
+
+### 8.2.4 Configure the driver parameter
+
+Define a RSDriverParam variable and configure it.
++ `InputType::ONLINE_LIDAR` means that the driver get packets from an online LiDAR.
++ `LidarType::RS16` means a RS16 LiDAR.
++ Also set the local MSOP/DIFOP ports.
+
+```c++
+int main()
+{
+  ...
+  RSDriverParam param;                             ///< Create a parameter object
+  param.input_type = InputType::ONLINE_LIDAR;      /// get packet from online lidar
+  param.input_param.msop_port = 6699;             ///< Set the lidar msop port number, the default is 6699
+  param.input_param.difop_port = 7788;            ///< Set the lidar difop port number, the default is 7788
+  param.lidar_type = LidarType::RS16;             ///< Set the lidar type. Make sure this type is correct
+  ...
+}
+```
+
+### 8.2.5 Define and register Point Cloud callbacks
+
++ User is supposed to provide free point cloud to `rs_driver`. Here is the first callback. 
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue;
+
+std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
+{
+  std::shared_ptr<PointCloudMsg> msg = free_cloud_queue.pop();
+  if (msg.get() != NULL)
+  {
+    return msg;
+  }
+
+  return std::make_shared<PointCloudMsg>();
+}
+```
+
++ `rs_driver` returns stuffed point cloud to user.  Here is the second callback.
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue;
+
+void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> msg)
+{
+  stuffed_cloud_queue.push(msg);
+
+  RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+}
+```
+
+Note: The driver calls these two callback functions in the MSOP/DIFOP handling thread, so **don't do any time-consuming task** in them. 
+
++ User creates a new thread to processes the point cloud.
+
+```c++
+void processCloud(void)
+{
+  while (1)
+  {
+    std::shared_ptr<PointCloudMsg> msg = stuffed_cloud_queue.popWait();
+    if (msg.get() == NULL)
+    {
+      continue;
+    }
+
+    // Well, it is time to process the point cloud msg, even it is time-consuming.
+    RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+
+    free_cloud_queue.push(msg);
+  }
+}
+
+```
+
++ Register them to `rs_driver`.
+
+```c++
+int main()
+{
+  ...
+  driver.regPointCloudCallback(driverReturnPointCloudToCallerCallback, 
+                               driverReturnPointCloudToCallerCallback);
+  ...
+}
+```
+
+### 8.2.6 Define and register exception callbacks
+
++ When an error happens, `rs_driver` informs user. Here is the exception callback.
+
+```c++
+void exceptionCallback(const Error &code)
+{
+  RS_WARNING << "Error code : " << code.toString() << RS_REND;
+}
+```
+
+Once again, **don't do any time-consuming task** in it.
+
++ Register the callback.
+
+```c++
+int main()
+{
+  ...
+  driver.regExceptionCallback(exceptionCallback);  ///<Register the exception callback function
+  ...
+}
+```
+
+### 8.2.7 Initialize the driver
+
+Initialize `rs_driver` with the the RSDriverParam object.
+
+```c++
+int main()
+{
+  ...
+  if (!driver.init(param))  ///< Call the init function with the parameter
+  {
+    RS_ERROR << "Driver Initialize Error..." << RS_REND;
+    return -1;
+  }
+  ...
+}
+```
+
+### 8.2.8 Start the driver
+
+Start `rs_driver`.
+
+```c++
+int main()
+{
+  ...
+  driver.start();  ///< Call the start function. The driver thread will start
+  ...
+}
+```
+
+### 8.2.9 Congratulation
+
+Compile rs_driver and run it. It should print message as below.
+
+```c++
+RoboSense Lidar-Driver Linux online demo start......
+msg: 0 point cloud size: 96
+msg: 1 point cloud size: 28800
+msg: 2 point cloud size: 28800
+msg: 3 point cloud size: 28800
+msg: 4 point cloud size: 28800
+msg: 5 point cloud size: 28800
+msg: 6 point cloud size: 28800
+msg: 7 point cloud size: 28832
+msg: 8 point cloud size: 28800
+msg: 9 point cloud size: 28800
+```
+
+Please refer to [Online LiDAR - Advanced Topics](09_online_lidar_advanced_topics.md) for more info about how to configure `rs_driver`.
+

+ 272 - 0
rslidar/rs_driver/doc/howto/08_how_to_decode_online_lidar_CN.md

@@ -0,0 +1,272 @@
+#  8 **如何连接在线雷达**
+
+
+
+## 8.1 概述
+
+本文描述如何连接在线雷达,得到点云数据。
+
+这个例子连接单个雷达,完整的代码可以参考示例程序`rs_driver/demo/demo_online.cpp`。
+
+如果您需要连接多个雷达,请参考示例程序`demo_online_multi_lidars.cpp`。
+
+这里的点和点云定义,引用了`rs_driver`工程的消息文件。
+
+ ```rs_driver/src/rs_driver/msg/point_cloud_msg.hpp```, ```rs_driver/src/rs_driver/msg/pcl_point_cloud_msg.hpp```
+
+
+
+## 8.2 步骤
+
+### 8.2.1 定义点
+
+点是点云的基本组成单元。`rs_driver`支持点的如下成员。
+- x -- 坐标X,类型float
+- y -- 坐标Y,类型float
+- z -- 坐标Y,类型float
+- intensity -- 反射率,类型uint8_t
+- timestamp -- 时间戳,类型double
+- ring -- 通道编号。以RS80为例,通道编号的范围是 0~79 (从下往上)。
+
+如下是几个例子。
+
+- 点的成员包括 **x, y, z, intensity**
+
+  ```c++
+  struct PointXYZI
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+  };
+  ```
+  
+- 如果使用PCL库,也可以简单使用PCL的点定义 **pcl::PointXYZI**
+
+  ```c++
+  typedef pcl::PointXYZI PointXYZI; 
+  ```
+
+- 点的成员包括 **x, y, z, intensity, timestamp, ring**
+
+  ```c++
+  struct PointXYZIRT
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+    double timestamp;
+    uint16_t ring;
+  };
+  ```
+
+这些点的定义,使用者可以加入新成员,删除成员,改变成员顺序,但是不可以改变成员的类型。
+
+### 8.2.2 定义点云
+
+如下是点云的定义。
+
+  ```c++
+  template <typename T_Point>
+  class PointCloudT
+  {
+  public:
+    typedef T_Point PointT;
+    typedef std::vector<PointT> VectorT;
+
+    uint32_t height = 0;    ///< Height of point cloud
+    uint32_t width = 0;     ///< Width of point cloud
+    bool is_dense = false;  ///< If is_dense is true, the point cloud does not contain NAN points
+    double timestamp = 0.0; ///< Timestamp of point cloud
+    uint32_t seq = 0;       ///< Sequence number of message
+
+    VectorT points;
+  };
+  
+  ```
+这个点云的定义,使用者可以加入新成员,改变成员顺序,但是不可以删除成员,或者改变成员的类型。
+
+这个点云定义是一个模板类,还需要指定一个点类型作为模板参数。
+
+  ```c++
+  typedef PointXYZI PointT;
+  typedef PointCloudT<PointT> PointCloudMsg;
+  ```
+
+### 8.2.3 定义LidarDriver对象
+
+LidarDriver类是`rs_driver`的接口类。这里定义一个LidarDriver的实例。
+
+```c++
+int main()
+{
+  LidarDriver<PointCloudMsg> driver;          ///< Declare the driver object
+  ...
+}
+```
+
+### 8.2.4 配置LidarDriver的参数
+
+RSDriverParam定义LidarDriver的参数。这里定义RSDriverParam变量,并配置它。
+
++ `InputType::ONLINE_LIDAR`意味着从在线雷达得到MSOP/DIFOP包。
++ `LidarType::RS16`是雷达类型
++ 分别设置接收MSOP/DIFOP Packet的端口号。
+
+```c++
+int main()
+{
+  ...
+  RSDriverParam param;                             ///< Create a parameter object
+  param.input_type = InputType::ONLINE_LIDAR;      /// get packet from online lidar
+  param.input_param.msop_port = 6699;             ///< Set the lidar msop port number, the default is 6699
+  param.input_param.difop_port = 7788;            ///< Set the lidar difop port number, the default is 7788
+  param.lidar_type = LidarType::RS16;             ///< Set the lidar type. Make sure this type is correct
+  ...
+}
+```
+
+### 8.2.5 定义和注册点云回调函数
+
++ `rs_driver`需要调用者通过回调函数,提供空闲的点云实例。这里定义这第一个点云回调函数。
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue;
+
+std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
+{
+  std::shared_ptr<PointCloudMsg> msg = free_cloud_queue.pop();
+  if (msg.get() != NULL)
+  {
+    return msg;
+  }
+
+  return std::make_shared<PointCloudMsg>();
+}
+```
+
++ `rs_driver`通过回调函数,将填充好的点云返回给调用者。这里定义这第二个点云回调函数。
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue;
+
+void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> msg)
+{
+  stuffed_cloud_queue.push(msg);
+
+  RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+}
+```
+
+注意这两个回调函数都运行在`rs_driver`的MSOP/DIFOP Packet的处理线程中,所以它们不可以做太耗时的任务,否则会导致MSOP/DIFOP Packet不能及时处理。
+
++ 使用者在自己的线程中,处理点云。
+
+```c++
+void processCloud(void)
+{
+  while (1)
+  {
+    std::shared_ptr<PointCloudMsg> msg = stuffed_cloud_queue.popWait();
+    if (msg.get() == NULL)
+    {
+      continue;
+    }
+
+    // Well, it is time to process the point cloud msg, even it is time-consuming.
+    RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+
+    free_cloud_queue.push(msg);
+  }
+}
+
+```
+
++ 注册两个点云回调函数。
+
+```c++
+int main()
+{
+  ...
+  driver.regPointCloudCallback(driverReturnPointCloudToCallerCallback, 
+                               driverReturnPointCloudToCallerCallback);
+  ...
+}
+```
+
+### 8.2.6 定义和注册异常回调函数
+
++ `rs_driver`检测到异常发生时,通过回调函数通知调用者。这里定义异常回调函数。
+
+```c++
+void exceptionCallback(const Error &code)
+{
+  RS_WARNING << "Error code : " << code.toString() << RS_REND;
+}
+```
+
+再一次提醒,这个回调函数运行在`rs_driver`的线程中,所以不可以做太耗时的任务,否则可能导致MSOP/DIFOP包不能及时接收和处理。
+
++ 在主函数中,注册异常回调函数。
+
+```c++
+int main()
+{
+  ...
+  driver.regExceptionCallback(exceptionCallback);  ///<Register the exception callback function
+  ...
+}
+```
+
+### 8.2.7 初始化LidarDriver对象
+
+按照RSDriverParam指定的配置,初始化LidarDriver对象。
+
+```c++
+int main()
+{
+  ...
+  if (!driver.init(param))  ///< Call the init function with the parameter
+  {
+    RS_ERROR << "Driver Initialize Error..." << RS_REND;
+    return -1;
+  }
+  ...
+}
+```
+
+### 8.2.8 启动LidarDriver
+
+启动LidarDriver对象。
+
+```c++
+int main()
+{
+  ...
+  driver.start();  ///< Call the start function. The driver thread will start
+  ...
+}
+```
+
+### 8.2.9 祝贺
+
+编译`demo_online`并运行。您应该可以看到类似如下点云打印了。
+
+```c++
+RoboSense Lidar-Driver Linux online demo start......
+msg: 0 point cloud size: 96
+msg: 1 point cloud size: 28800
+msg: 2 point cloud size: 28800
+msg: 3 point cloud size: 28800
+msg: 4 point cloud size: 28800
+msg: 5 point cloud size: 28800
+msg: 6 point cloud size: 28800
+msg: 7 point cloud size: 28832
+msg: 8 point cloud size: 28800
+msg: 9 point cloud size: 28800
+```
+
+如果您没有看到,可能是因为网络选项的配置不正确,请参考[在线雷达-高级主题](./09_online_lidar_advanced_topics_CN.md),获得正确的配置。
+

+ 222 - 0
rslidar/rs_driver/doc/howto/09_online_lidar_advanced_topics.md

@@ -0,0 +1,222 @@
+# 9 **Online Lidar - Advanced Topics**
+
+
+
+## 9.1 Introduction
+
+ The RoboSense LiDAR may work in unicast/multicast/broadcast mode, with VLAN layer and with user layers.
+
++ This document illustrates how to configure the driver in each case.
+
+   Before configure `rs_driver`, first find out what case the LiDAR is. Please refer to [How to configure rs_driver by PCAP file](./12_how_to_configure_by_pcap_file.md).
+
++ Even if all configure are correct, some system settings may block `rs_driver` to receive MOSP/DIFOP packets. This document also list them.
+
+
+
+## 9.2 Unicast, Multicast and Broadcast
+
+### 9.2.1 Broadcast mode
+
+The simplest way is broadcast mode. 
+
+The LiDAR sends MSOP/DIFOP packets to the host machine (`rs_driver` runs on it). For simplicity, the DIFOP port is ommited here.
++ The LiDAR sends to `255.255.255.255` : `6699`, and the host binds to port `6699`.
+
+![](./img/09_01_broadcast.png)
+
+Below is how to configure RSDriverParam variable.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+### 9.2.2 Unicast mode
+
+To reduce the network load, the LiDAR is suggested to work in unicast mode.
++ The LiDAR sends to `192.168.1.102` : `6699`, and the host binds to port `6699`.
+
+![](./img/09_02_unicast.png)
+
+Below is how to configure the RSDriverParam variable. In fact, it is same with the broadcast case.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+### 9.2.3 Multicast mode
+
+The Lidar may also works in multicast mode.
++ The lidar sends to `224.1.1.1`:`6699` 
++ The host binds to port `6699`. And it makes local NIC (Network Interface Card) join the multicast group `224.1.1.1`. The local NIC's IP is `192.168.1.102`.
+
+![](./img/09_03_multicast.png)
+
+Below is how to configure the RSDriverParam variable.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.group_address = "224.1.1.1";    ///< Set the multicast group address.
+param.input_param.host_address = "192.168.1.102"; ///< Set the host address.
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type. Make sure this type is correct 
+```
+
+
+
+## 9.3 Multiple Lidars
+
+### 9.3.1 Different remote ports
+
+If you have two LiDARs, it is suggested to set different remote ports.
++ First LiDAR sends to `192.168.1.102`:`6699`, and the first driver instance binds to `6699`.
++ Second LiDAR sends to `192.168.1.102`:`5599`, and the second driver instance binds to `5599`.
+
+![](./img/09_04_multi_lidars_port.png)
+
+Below is how to configure the RSDriverParam variables.
+
+```c++
+RSDriverParam param1;                              ///< Create a parameter object for Lidar 192.168.1.200
+param1.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param1.input_param.msop_port = 6699;               ///< Set the lidar msop port number
+param1.input_param.difop_port = 7788;              ///< Set the lidar difop port number
+param1.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+
+RSDriverParam param2;                              ///< Create a parameter object for Lidar 192.168.1.201
+param2.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param2.input_param.msop_port = 5599;               ///< Set the lidar msop port number
+param2.input_param.difop_port = 6688;              ///< Set the lidar difop port number
+param2.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+### 9.3.2 Different remote IPs
+
+An alternate way is to set different remote IPs. 
++ The host has two NICs: `192.168.1.102` and `192.168.1.103`.
++ First LiDAR sends to `192.168.1.102`:`6699`, and the first driver instance binds to `192.168.1.102:6699`.
++ Second LiDAR sends to `192.168.1.103`:`6699`, and the second driver instance binds to `192.168.1.103:6699`.
+
+![](./img/09_05_multi_lidars_ip.png)
+
+Below is how to configure the RSDriverParam variables.
+
+```c++
+RSDriverParam param1;                              ///< Create a parameter object for Lidar 192.168.1.200
+param1.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param1.input_param.host_address = "192.168.1.102"; ///< Set the host address.
+param1.input_param.msop_port = 6699;               ///< Set the lidar msop port number
+param1.input_param.difop_port = 7788;              ///< Set the lidar difop port number
+param1.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+
+RSDriverParam param2;                              ///< Create a parameter object for Lidar 192.168.1.201
+param2.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param2.input_param.host_address = "192.168.1.103"; ///< Set the host address.
+param2.input_param.msop_port = 6699;               ///< Set the lidar msop port number
+param2.input_param.difop_port = 7788;              ///< Set the lidar difop port number
+param2.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 9.4 VLAN
+
+In some user cases, The LiDAR may work on VLAN.  Its packets have a VLAN layer.
+
+![](./img/09_06_vlan_layer.png)
+
+`rs_driver` cannot parse this packet. Instead, it depends on a virtual NIC to strip the VLAN layer.
+
+Below is an example.
++ The LiDAR works on VLAN `80`. It sends packets to `192.168.1.102` : `6699`. The packet has a VLAN layer.
++ Suppose there is a physical NIC `eno1` on the host.  It receives packets with VLAN layer.
+
+![](./img/09_07_vlan.png)
+
+To strip the VLAN layer, create a virtual NIC `eno1.80` on `eno1`, and assign IP `192.168.1.102` to it.
+
+```shell
+sudo apt-get install vlan -y
+sudo modprobe 8021q
+
+sudo vconfig add eno1 80
+sudo ifconfig eno1.80 192.168.1.102 up
+```
+
+Keep `eno1` with IP `0.0.0.0`. At least do NOT set it as same as `eno1.80`. This may block eno1.80.
+
+Now the driver may take `eno1.80` as a general NIC, and receives packets without VLAN layer.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 9.5 User Layer, Tail Layer 
+
+In some user cases, User may add extra layers before or after the MSOP/DIFOP packet.
++ USER_LAYER is before the packet and TAIL_LAYER is after it.
+
+![](./img/09_08_user_layer.png)
+
+These extra layers are parts of UDP data. The driver can strip them. 
+
+To strip them, just give their lengths in bytes. 
+
+In the following example, USER_LAYER is 8 bytes, and TAIL_LAYER is 4 bytes.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.input_param.user_layer_bytes = 8;           ///< user layer bytes. there is no user layer if it is 0
+param.input_param.tail_layer_bytes = 4;           ///< tail layer bytes. there is no user layer if it is 0
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 9.6 System Settings
+
+In below cases, Wireshark can capture MSOP/DIFOP packets, and `rs_driver` is configured correctly, but it can not get MOSP/DIFOP packets. 
+
++ The LiDAR works on VLAN, but the phisical NIC occupies the destination IP address of the LiDAR.
++ The LiDAR works in broadcast mode, but the host machine have a incorrect netmask, so it takes MSOP/DIFOP packets as unicast and discard them.
++ Firewall blocks MSOP/DIFOP packets.
+  
+  On ubuntu, use iptables to list the rules
+  
+  ```shell
+  sudo iptables -L # list all rules
+  suod iptalbes --flush # clear all rules
+  ```
+  
++ Other processes of `rs_driver`, or other programs(such as RSView), have bound the port.
+
+
+
+
+
+
+
+
+
+
+

+ 230 - 0
rslidar/rs_driver/doc/howto/09_online_lidar_advanced_topics_CN.md

@@ -0,0 +1,230 @@
+# 9 **在线雷达 - 高级主题**
+
+## 9.1 概述
+
+RoboSense雷达可以工作在单播/组播/广播模式下,也可以工作在VLAN环境下,也可以加入用户自己的层。
+
++ 本文说明了在每种场景下如何设置`rs_driver`的网络配置选项。
+
+设置网络配置选项的前提,是先确定雷达工作在哪种场景。这一点,请参考[根据PCAP文件确定网络配置选项](./12_how_to_configure_by_pcap_file_CN.md)
+
+为了清晰,本文所有的图都只列出了MSOP端口,DIFOP端口的配置与MSOP类似。
+
++ 在所有配置选项都正确的前提下,一些系统设置不正确,也可能导致MSOP/DIFOP Packet无法接收。本文也对这些环境因素作了说明。
+
+
+
+## 9.2 单播、组播、广播
+
+### 9.2.1 广播模式
+
+广播模式的配置最简单。
+
+下面的图中,雷达发送MSOP/DIFOP Packet到主机,`rs_driver`运行在主机上。
++ 雷达发送到 `255.255.255.255` : `6699`, rs_driver绑定到端口`6699`.
+![](./img/09_01_broadcast.png)
+
+如下代码配置RSDriverParam。
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+### 9.2.2 单播模式
+
+为减少网络流量,推荐使用单播模式。
+
+如下的例子中,
+
++ 雷达发送到`192.168.1.102` : `6699`, `rs_driver`绑定到端口`6699`。
+![](./img/09_02_unicast.png)
+
+如下代码配置RSDriverParam。它与广播模式的配置完全相同。
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+### 9.2.3 组播模式
+
+雷达也可以工作在组播模式。
+
+如下的例子中,
+
++ 雷达发送到`224.1.1.1`:`6699` 
++ `rs_driver`绑定到端口`6699`。 `rs_driver`让本地网卡加入组播组`224.1.1.1`. 这个网卡的地址是`192.168.1.102`。
+![](./img/09_03_multicast.png)
+
+如下代码配置RSDriverParam。
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.group_address = "224.1.1.1";    ///< Set the multicast group address.
+param.input_param.host_address = "192.168.1.102"; ///< Set the host address.
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type. Make sure this type is correct 
+```
+
+
+
+## 9.3 多雷达的情况
+
+### 9.3.1 雷达目的端口不同
+
+如果要接入两个雷达,推荐给它们配置不同的目的端口。
+
+如下的例子中,
+
++ 第一个雷达发送到`192.168.1.102`:`6699`,rs_driver的第一个实例绑定到端口`6699`。
++ 第二个雷达发送到`192.168.1.102`:`5599`,rs_driver的第二个实例绑定到端口`5599`。
+![](./img/09_04_multi_lidars_port.png)
+
+```c++
+RSDriverParam param1;                              ///< Create a parameter object for Lidar 192.168.1.200
+param1.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param1.input_param.msop_port = 6699;               ///< Set the lidar msop port number
+param1.input_param.difop_port = 7788;              ///< Set the lidar difop port number
+param1.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+
+RSDriverParam param2;                              ///< Create a parameter object for Lidar 192.168.1.201
+param2.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param2.input_param.msop_port = 5599;               ///< Set the lidar msop port number
+param2.input_param.difop_port = 6688;              ///< Set the lidar difop port number
+param2.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+### 9.3.2 雷达的目的IP不同
+
+虽然不推荐,也可以给接入的两个雷达配置不同的目的IP。
++ 主机有两个网卡,地址分别是`192.168.1.102` 和`192.168.1.103`。
++ 第一个雷达发送到`192.168.1.102`:`6699`,第一个rs_driver实例绑定到`192.168.1.102:6699`。
++ 第二个雷达发送到`192.168.1.103`:`6699`,第二个rs_driver实例绑定到`192.168.1.103:6699`。
+![](./img/09_05_multi_lidars_ip.png)
+
+如下代码分别配置两个`rs_driver`实例的RSDriverParam。
+
+```c++
+RSDriverParam param1;                              ///< Create a parameter object for Lidar 192.168.1.200
+param1.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param1.input_param.host_address = "192.168.1.102"; ///< Set the host address.
+param1.input_param.msop_port = 6699;               ///< Set the lidar msop port number
+param1.input_param.difop_port = 7788;              ///< Set the lidar difop port number
+param1.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+
+RSDriverParam param2;                              ///< Create a parameter object for Lidar 192.168.1.201
+param2.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param2.input_param.host_address = "192.168.1.103"; ///< Set the host address.
+param2.input_param.msop_port = 6699;               ///< Set the lidar msop port number
+param2.input_param.difop_port = 7788;              ///< Set the lidar difop port number
+param2.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 9.4 VLAN
+
+有些场景下,雷达工作在VLAN环境下。这时MSOP/DIFOP包带VLAN层,如下图。
+![](./img/09_06_vlan_layer.png)
+
+`rs_driver`工作在应用层,接触不到VLAN层。这时需要用户手工创建一个虚拟网卡来剥除VLAN层。
+
+如下是一个例子。
++ 给雷达分配的VLAN ID是`80`。雷达发送到`192.168.1.102` : `6699`。 发送的包带VLAN层。
++ 主机上装的物理网卡`eno1`也在VLAN ID `80`上,它接收雷达发出的带VLAN层的包。
+![](./img/09_07_vlan.png)
+
+要剥除VLAN层,需要用户手工创建一个虚拟网卡。如下的命令在物理网卡`eno1`上创建虚拟网卡`eno1.80`,并给它指定IP地址`192.168.1.102` 。
+
+```shell
+sudo apt-get install vlan -y
+sudo modprobe 8021q
+
+sudo vconfig add eno1 80
+sudo ifconfig eno1.80 192.168.1.102 up
+```
+
+注意这里不需要设置`eno1`的IP地址,保持`0.0.0.0`就好了。尤其不要设置成与`eno1.80`一样,这样反而可能导致`eno1.80`无法接收。
+
+现在`rs_driver`就可以从`eno1.80`网卡上接收MSOP/DIFOP Packet了,这些包不带VLAN层。
+
+
+如下代码配置RSDriverParam。
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 9.5 User Layer, Tail Layer 
+
+某些场景下,用户可能在MSOP/DIFOP数据前后加入自己的层。
++ `USER_LAYER` 在MSOP/DIFOP数据之前,`TAIL_LAYER`在MSOP/DIFOP数据之后。
+![](./img/09_08_user_layer.png)
+
+这些层是UDP数据的一部分,所以`rs_driver`可以自己剥除他们。只需要告诉`rs_driver`每个层的字节数就可以。
+
+如下的例子中,指定`USER_LAYER`为8字节,`TAIL_LAYER`为4字节。
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;       ///< get packet from online lidar
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.input_param.user_layer_bytes = 8;           ///< user layer bytes. there is no user layer if it is 0
+param.input_param.tail_layer_bytes = 4;           ///< tail layer bytes. there is no user layer if it is 0
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 9.6 其他可能影响接收的系统设置
+
+Wireshark等抓包工具能抓到包,`rs_driver`的所有的网络配置选项也是正确的,但它仍有可能收不到MSOP/DIFOP Packet。
+
+如下是一些可能的原因。
+
++ 雷达工作在VLAN模式,物理网卡(而不是虚拟网卡)配置了雷达发送MSOP/DIFOP Packet的目标地址。
++ 雷达工作在广播模式,但是主机的掩码不正确,导致主机不认为雷达发出的包是广播包。
+
+  举个例子。假设主机的IP地址是`192.168.1.102`/`255.255.0.0`,则广播地址就是`192.168.255.255`。
+  如果雷达的目的地址是`192.168.1.255`,那它发出的包,就不是广播包,主机会丢掉这些包。
+
++ 防火墙可能挡住了MSOP/DIFOP Packet。
+
+  在Ubuntu上,可以用`iptables`工具查看防火墙设置。
+
+  ```shell
+  sudo iptables -L # list all rules
+  suod iptalbes --flush # clear all rules
+  ```
+
++ `rs_driver`的其他实例,或者其他程序(如RSView),已经先绑定了相应的的端口。
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 284 - 0
rslidar/rs_driver/doc/howto/10_how_to_decode_pcap_file.md

@@ -0,0 +1,284 @@
+# 10 **How to decode PCAP file**
+
+
+
+## 10.1 Introduction
+
+This document illustrates how to decode the MSOP/DIFOP packets from PCAP file with `rs_driver`'s API.
+
+For more info, please refer to the demo app `rs_driver/demo/demo_pcap.cpp`.
+
+Here the definitions of point and point cloud, is from the project file.
+
+`rs_driver/src/rs_driver/msg/point_cloud_msg.hpp`, `rs_driver/src/rs_driver/msg/pcl_point_cloud_msg.hpp`
+
+
+
+## 10.2 Steps
+
+
+
+### 10.2.1 Define Point
+
+Point is the base unit of Point Cloud. `rs_driver` supports these member variables.
+- x -- The x coordinate of point. float type.
+- y -- The y coordinate of point. float type.
+- z -- The z coordinate of point. float type.
+- intensity -- The intensity of point. uint8_t type. 
+- timestamp -- The timestamp of point. double type.  It may be generated by the LiDAR or `rs_driver` on the host machine.
+- ring -- The ring ID of the point, which represents the row/channel number. Take RS80 as an example, the range of ring ID is `0`~`79` (from bottom to top).
+
+Below are some examples: 
+
+- The point type contains **x, y, z, intensity**
+
+  ```c++
+  struct PointXYZI
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+  };
+  ```
+  
+- If using PCL, a simple way is to use **pcl::PointXYZI**.
+
+- The point type contains **x, y, z, intensity, timestamp, ring**
+
+  ```c++
+  struct PointXYZIRT
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+    double timestamp;
+    uint16_t ring;
+  };
+  ```
+
+Here user may add new member variables, remove member variables, or change the order of them, but should not change names or types of them.
+
+
+
+### 10.2.2 Define Point Cloud
+
+  Below is the definition of point cloud.
+
+  ```c++
+  template <typename T_Point>
+  class PointCloudT
+  {
+  public:
+    typedef T_Point PointT;
+    typedef std::vector<PointT> VectorT;
+
+    uint32_t height = 0;    ///< Height of point cloud
+    uint32_t width = 0;     ///< Width of point cloud
+    bool is_dense = false;  ///< If is_dense is true, the point cloud does not contain NAN points
+    double timestamp = 0.0; ///< Timestamp of point cloud
+    uint32_t seq = 0;       ///< Sequence number of message
+
+    VectorT points;
+  };
+  ```
+
+  Here user may add new members, and change the order of these members, but should not change or remove them.
+
+  This definition is a template class. It needs a Point type as template parameter.
+
+  ```
+  typedef PointXYZI PointT;
+  typedef PointCloudT<PointT> PointCloudMsg;
+  ```
+
+
+
+### 10.2.3 Define the driver object
+
+Define a driver object.
+
+```c++
+int main()
+{
+  LidarDriver<PointCloudMsg> driver;          ///< Declare the driver object
+  ...
+}
+```
+
+
+
+### 10.2.4 Configure the driver parameter
+
+Define a RSDriverParam variable and configure it.
++ `InputType::PCAP_FILE` means that `rs_driver` get packets from a PCAP file, which is `/home/robosense/lidar.pcap`.
++ `LidarType::RS16` means a RS16 LiDAR.
++ Also set the local MSOP/DIFOP ports.
+
+```c++
+int main()
+{
+  ...
+  RSDriverParam param;                             ///< Create a parameter object
+  param.input_type = InputType::PCAP_FILE;         /// get packet from the pcap file 
+  param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+  param.input_param.msop_port = 6699;             ///< Set the lidar msop port number, the default is 6699
+  param.input_param.difop_port = 7788;            ///< Set the lidar difop port number, the default is 7788
+  param.lidar_type = LidarType::RS16;             ///< Set the lidar type. Make sure this type is correct
+  ...
+}
+```
+
+
+
+### 10.2.5 Define and register Point Cloud callbacks
+
++ User is supposed to provide free point cloud to `rs_driver`. Here is the first callback. 
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue;
+
+std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
+{
+  std::shared_ptr<PointCloudMsg> msg = free_cloud_queue.pop();
+  if (msg.get() != NULL)
+  {
+    return msg;
+  }
+
+  return std::make_shared<PointCloudMsg>();
+}
+```
+
++ `rs_driver` returns stuffed point cloud to user.  Here is the second callback.
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue;
+
+void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> msg)
+{
+  stuffed_cloud_queue.push(msg);
+
+  RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+}
+```
+
+Note: The driver calls these two callback functions in the MSOP/DIFOP handling thread, so **don't do any time-consuming task** in them. 
+
++ User creates a new thread to processes the point cloud.
+
+```c++
+void processCloud(void)
+{
+  while (1)
+  {
+    std::shared_ptr<PointCloudMsg> msg = stuffed_cloud_queue.popWait();
+    if (msg.get() == NULL)
+    {
+      continue;
+    }
+
+    // Well, it is time to process the point cloud msg, even it is time-consuming.
+    RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+
+    free_cloud_queue.push(msg);
+  }
+}
+
+```
+
++ Register them to `rs_driver`.
+
+```c++
+int main()
+{
+  ...
+  driver.regPointCloudCallback(driverReturnPointCloudToCallerCallback, 
+                               driverReturnPointCloudToCallerCallback);
+  ...
+}
+```
+
+
+
+### 10.2.6 Define and register exception callbacks
+
++ When an error happens, `rs_driver` informs user. Here is the exception callback.
+
+```c++
+void exceptionCallback(const Error &code)
+{
+  RS_WARNING << "Error code : " << code.toString() << RS_REND;
+}
+```
+
+Once again, **don't do any time-consuming task** in it.
+
++ Register the callback.
+
+```c++
+int main()
+{
+  ...
+  driver.regExceptionCallback(exceptionCallback);  ///<Register the exception callback function
+  ...
+}
+```
+
+
+
+### 10.2.7 Initialize the driver
+
+Initialize `rs_driver` with the the RSDriverParam object.
+
+```c++
+int main()
+{
+  ...
+  if (!driver.init(param))  ///< Call the init function with the parameter
+  {
+    RS_ERROR << "Driver Initialize Error..." << RS_REND;
+    return -1;
+  }
+  ...
+}
+```
+
+
+
+### 10.2.8 Start the driver
+
+Start `rs_driver`.
+
+```c++
+int main()
+{
+  ...
+  driver.start();  ///< Call the start function. The driver thread will start
+  ...
+}
+```
+
+
+
+### 10.2.9 Congratulations
+
+Compile `rs_driver` and run it. It should print message as below.
+
+```c++
+RoboSense Lidar-Driver Linux online demo start......
+msg: 0 point cloud size: 96
+msg: 1 point cloud size: 28800
+msg: 2 point cloud size: 28800
+msg: 3 point cloud size: 28800
+msg: 4 point cloud size: 28800
+msg: 5 point cloud size: 28800
+msg: 6 point cloud size: 28800
+msg: 7 point cloud size: 28832
+msg: 8 point cloud size: 28800
+msg: 9 point cloud size: 28800
+```
+
+Please refer to [PCAP File - Advanced Topics](11_pcap_file_advanced_topics.md) for more info about how to configure `rs_driver`.
+

+ 272 - 0
rslidar/rs_driver/doc/howto/10_how_to_decode_pcap_file_CN.md

@@ -0,0 +1,272 @@
+#  10 **如何解码PCAP文件**
+
+
+
+## 10.1 概述
+
+本文说明了如何使用`rs_driver`解码PCAP文件。
+
+关于如何获得PCAP文件,请参考[为rs_driver录制PCAP文件](./13_how_to_capture_pcap_file_CN.md)
+
+这里的点和点云定义,引用了`rs_driver`工程的消息文件。
+
+ ```rs_driver/src/rs_driver/msg/point_cloud_msg.hpp```, ```rs_driver/src/rs_driver/msg/pcl_point_cloud_msg.hpp```
+
+
+
+## 10.2 步骤
+
+### 10.2.1 定义点
+
+点是点云的基本组成单元。`rs_driver`支持点的如下成员。
+- x -- 坐标X,类型float
+- y -- 坐标Y,类型float
+- z -- 坐标Y,类型float
+- intensity -- 反射率,类型uint8_t
+- timestamp -- 时间戳,类型double
+- ring -- 通道编号。以RS80为例,通道编号的范围是 0~79 (从下往上)。
+
+如下是几个例子。
+
+- 点的成员包括 **x, y, z, intensity**
+
+  ```c++
+  struct PointXYZI
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+  };
+  ```
+  
+- 如果使用PCL库,也可以简单使用PCL的点定义 **pcl::PointXYZI**
+
+  ```c++
+  typedef pcl::PointXYZI PointXYZI; 
+  ```
+
+- 点的成员包括 **x, y, z, intensity, timestamp, ring**
+
+  ```c++
+  struct PointXYZIRT
+  {
+    float x;
+    float y;
+    float z;
+    uint8_t intensity;
+    double timestamp;
+    uint16_t ring;
+  };
+  ```
+
+这些点的定义,使用者可以加入新成员,删除成员,改变成员顺序,但是不可以改变成员的类型。
+
+### 10.2.2 定义点云
+
+如下是点云的定义。
+
+  ```c++
+  template <typename T_Point>
+  class PointCloudT
+  {
+  public:
+    typedef T_Point PointT;
+    typedef std::vector<PointT> VectorT;
+
+    uint32_t height = 0;    ///< Height of point cloud
+    uint32_t width = 0;     ///< Width of point cloud
+    bool is_dense = false;  ///< If is_dense is true, the point cloud does not contain NAN points
+    double timestamp = 0.0; ///< Timestamp of point cloud
+    uint32_t seq = 0;       ///< Sequence number of message
+
+    VectorT points;
+  };
+  
+  ```
+这个点云的定义,使用者可以加入新成员,改变成员顺序,但是不可以删除成员,或者改变成员的类型。
+
+这个点云定义是一个模板类,还需要指定一个点类型作为模板参数。
+
+  ```c++
+  typedef PointXYZI PointT;
+  typedef PointCloudT<PointT> PointCloudMsg;
+  ```
+
+### 10.2.3 定义LidarDriver对象
+
+LidarDriver类是`rs_driver`的接口类。这里定义一个LidarDriver的实例。
+
+```c++
+int main()
+{
+  LidarDriver<PointCloudMsg> driver;          ///< Declare the driver object
+  ...
+}
+```
+
+### 10.2.4 配置LidarDriver的参数
+
+RSDriverParam定义LidarDriver的参数。这里定义RSDriverParam变量,并配置它。
+
++ `InputType::PCAP_FILE` 意味着解析PCAP文件得到MSOP/DIFOP包。这里的PCAP文件是`/home/robosense/lidar.pcap`。
++ `LidarType::RS16`是雷达类型
++ 分别设置接收MSOP/DIFO包的端口号。
+
+```c++
+int main()
+{
+  ...
+  RSDriverParam param;                             ///< Create a parameter object
+  param.input_type = InputType::PCAP_FILE;         /// get packet from the pcap file 
+  param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+  param.input_param.msop_port = 6699;             ///< Set the lidar msop port number, the default is 6699
+  param.input_param.difop_port = 7788;            ///< Set the lidar difop port number, the default is 7788
+  param.lidar_type = LidarType::RS16;             ///< Set the lidar type. Make sure this type is correct
+  ...
+}
+```
+
+### 10.2.5 定义和注册点云回调函数
+
++ `rs_driver`需要调用者通过回调函数,提供空闲的点云实例。这里定义这第一个点云回调函数。
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue;
+
+std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
+{
+  std::shared_ptr<PointCloudMsg> msg = free_cloud_queue.pop();
+  if (msg.get() != NULL)
+  {
+    return msg;
+  }
+
+  return std::make_shared<PointCloudMsg>();
+}
+```
+
++ `rs_driver`通过回调函数,将填充好的点云返回给调用者。这里定义这第二个点云回调函数。
+
+```c++
+SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue;
+
+void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> msg)
+{
+  stuffed_cloud_queue.push(msg);
+
+  RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+}
+```
+
+注意这两个回调函数都运行在`rs_driver`的MSOP/DIFOP Packet的处理线程中,所以它们不可以做太耗时的任务,否则会导致MSOP/DIFOP Packet不能及时处理。
+
++ 使用者在自己的线程中,处理点云。
+
+```c++
+void processCloud(void)
+{
+  while (1)
+  {
+    std::shared_ptr<PointCloudMsg> msg = stuffed_cloud_queue.popWait();
+    if (msg.get() == NULL)
+    {
+      continue;
+    }
+
+    // Well, it is time to process the point cloud msg, even it is time-consuming.
+    RS_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << RS_REND;
+
+    free_cloud_queue.push(msg);
+  }
+}
+
+```
+
++ 注册两个点云回调函数。
+
+```c++
+int main()
+{
+  ...
+  driver.regPointCloudCallback(driverReturnPointCloudToCallerCallback, 
+                               driverReturnPointCloudToCallerCallback);
+  ...
+}
+```
+
+### 10.2.6 定义和注册异常回调函数
+
++ `rs_driver`检测到异常发生时,通过回调函数通知调用者。这里定义异常回调函数。
+
+```c++
+void exceptionCallback(const Error &code)
+{
+  RS_WARNING << "Error code : " << code.toString() << RS_REND;
+}
+```
+
+再一次提醒,这个回调函数运行在`rs_driver`的线程中,所以不可以做太耗时的任务,否则可能导致MSOP/DIFOP包不能及时接收和处理。
+
++ 在主函数中,注册异常回调函数。
+
+```c++
+int main()
+{
+  ...
+  driver.regExceptionCallback(exceptionCallback);  ///<Register the exception callback function
+  ...
+}
+```
+
+### 10.2.7 初始化LidarDriver对象
+
+按照RSDriverParam指定的配置,初始化LidarDriver对象。
+
+```c++
+int main()
+{
+  ...
+  if (!driver.init(param))  ///< Call the init function with the parameter
+  {
+    RS_ERROR << "Driver Initialize Error..." << RS_REND;
+    return -1;
+  }
+  ...
+}
+```
+
+### 10.2.8 启动LidarDriver
+
+启动LidarDriver对象。
+
+```c++
+int main()
+{
+  ...
+  driver.start();  ///< Call the start function. The driver thread will start
+  ...
+}
+```
+
+### 10.2.9 祝贺
+
+编译`demo_pcap`并运行。您应该可以看到类似如下点云打印了。
+
+```c++
+RoboSense Lidar-Driver Linux online demo start......
+msg: 0 point cloud size: 96
+msg: 1 point cloud size: 28800
+msg: 2 point cloud size: 28800
+msg: 3 point cloud size: 28800
+msg: 4 point cloud size: 28800
+msg: 5 point cloud size: 28800
+msg: 6 point cloud size: 28800
+msg: 7 point cloud size: 28832
+msg: 8 point cloud size: 28800
+msg: 9 point cloud size: 28800
+
+```
+
+如果您没有看到,可能是因为网络选项的配置不正确,请参考[PCAP文件-高级主题](11_pcap_file_advanced_topics_CN.md),获得正确的配置。
+

+ 91 - 0
rslidar/rs_driver/doc/howto/11_pcap_file_advanced_topics.md

@@ -0,0 +1,91 @@
+# 11 **PCAP file - Advanced Topics**
+
+
+
+## 11.1 Introduction
+
+ The RoboSense LiDAR may work in unicast/multicast/broadcast mode, with VLAN layer and with user layers.
+
+This document illustrates how to configure the driver in each case.
+
+Before configure `rs_driver`, first find out what case the LiDAR is. Please refer to [How to configure rs_driver by PCAP file](./12_how_to_configure_by_pcap_file.md).
+
+
+
+## 11.2 General Case
+
+Generally, below code is for decoding a PCAP file in these cases.
++ Broadcast/multicast/unicast mode
++ There are multiple LiDARs in a file.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::PCAP_FILE;          ///< get packet from online lidar
+param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+The only exception is "Multiple Lidars with same ports but different IPs", which is not supported now.
+
+
+
+## 11.3 VLAN
+
+In some user cases, The LiDar may work on VLAN.  Its packets have a VLAN layer.
+![](./img/09_06_vlan_layer.png)
+
+rs_driver decodes PCAP file and gets all parts of MSOP packets, including the VLAN layer. 
+
+To strip the VLAN layer, just set `use_vlan=true`.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::PCAP_FILE;          ///< get packet from online lidar
+param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.input_param.use_vlan = true;                ///< Whether to use VLAN layer.
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 11.4 User Layer, Tail Layer 
+
+In some user cases, User may add extra layers before or/and after the MSOP/DIFOP packet.
++ USER_LAYER is before the packet and TAIL_LAYER is after it.
+![](./img/09_08_user_layer.png)
+
+These extra layers are parts of UDP data. The driver can strip them. 
+
+To strip them, just give their lengths in bytes. 
+
+In the following example, USER_LAYER is 8 bytes, and TAIL_LAYER is 4 bytes.
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::PCAP_FILE;          ///< get packet from online lidar
+param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.input_param.user_layer_bytes = 8;           ///< user layer bytes. there is no user layer if it is 0
+param.input_param.tail_layer_bytes = 4;           ///< tail layer bytes. there is no user layer if it is 0
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 91 - 0
rslidar/rs_driver/doc/howto/11_pcap_file_advanced_topics_CN.md

@@ -0,0 +1,91 @@
+# 11 **PCAP文件 - 高级主题**
+
+
+
+## 11.1 概述
+
+RoboSense雷达可以工作在单播/组播/广播模式下,也可以工作在VLAN环境下,也可以加入用户自己的层。
+
+本文说明了在每种场景下如何设置`rs_driver`的网络配置选项。
+
+设置网络配置选项的前提,是先确定雷达工作在哪种场景。这一点,请参考[根据PCAP文件确定网络配置选项](./12_how_to_configure_by_pcap_file_CN.md)
+
+为了清晰,本文所有的图都只列出了MSOP端口,DIFOP端口的配置与MSOP类似。
+
+
+
+## 11.2 一般场景
+
+这里的配置代码适用于如下的场景。
++ 广播/组播/单播模式
++ PCAP文件中有多个雷达的数据
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::PCAP_FILE;          ///< get packet from online lidar
+param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+一个例外是:PCAP文件中有多个雷达的数据,但这些雷达目的端口相同,使用不同的目的IP地址来区分。这种情况不支持。
+
+
+
+## 11.3 VLAN
+
+有些场景下,雷达工作在VLAN环境下。这时MSOP/DIFOP Packet带VLAN层,如下图。
+![](./img/09_06_vlan_layer.png)
+
+`rs_driver`使用`libpcap`库解析PCAP文件,可以得到完整的、包括VLAN层的MSOP/DIFOP Packet。
+
+要剥除VLAN层,只需要设置`use_vlan=true`。
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::PCAP_FILE;          ///< get packet from online lidar
+param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.input_param.use_vlan = true;                ///< Whether to use VLAN layer.
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+## 11.4 User Layer, Tail Layer 
+
+某些场景下,用户可能在MSOP/DIFOP数据前后加入自己的层。
++ `USER_LAYER` 在MSOP/DIFOP数据之前,`TAIL_LAYER`在MSOP/DIFOP数据之后。
+![](./img/09_08_user_layer.png)
+
+这些层是UDP数据的一部分,所以`rs_driver`可以自己剥除他们。只需要告诉它每个层的字节数就可以。
+
+如下的例子中,指定`USER_LAYER`为8字节,`TAIL_LAYER`为4字节。
+
+```c++
+RSDriverParam param;                              ///< Create a parameter object
+param.input_type = InputType::PCAP_FILE;          ///< get packet from online lidar
+param.input_param.pcap_path = "/home/robosense/lidar.pcap";  ///< Set the pcap file path
+param.input_param.msop_port = 6699;               ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;              ///< Set the lidar difop port number, the default is 7788
+param.input_param.user_layer_bytes = 8;           ///< user layer bytes. there is no user layer if it is 0
+param.input_param.tail_layer_bytes = 4;           ///< tail layer bytes. there is no user layer if it is 0
+param.lidar_type = LidarType::RS32;               ///< Set the lidar type.
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 154 - 0
rslidar/rs_driver/doc/howto/12_how_to_configure_by_pcap_file.md

@@ -0,0 +1,154 @@
+
+#  12 **Configure rs_driver by PCAP file**
+
+
+
+## 12.1 Overview
+
+By PCAP files, we can learn what the MSOP/DIFOP packet is like, so as to learn how to configure `rs_driver` and get point cloud.
+
+Here we use the 3rd party tool `WireShark`.
+
+About how to capture a PCAP file, please refer to [How to pcap PCAP file](./13_how_to_capture_pcap_file.md).
+
+
+
+## 12.2 Format of MSOP/DIFOP
+
+### 12.2.1 Protocol Layer
+
+MSOP/DIFOP Packet is based on UDP protocol. It includes:
+
++ Ethernet
++ VLAN(Optional) 
++ IP 
++ UDP
++ User Layer(Prefix part, Optional) 
++ MSOP/DIFOP Packet(Real data)
++ Tail Layer(Suffix part, Optional)
+
+`VLAN` Layer is optional. It is up to:
+
++ Whether the LiDAR is on VLAN
++ Capture MOSP/DIFOP packets on phisical NIC or virtual NIC
+
+`User Layer`  is added by the user. User determines its length.
+
+`Tail Layer` is from the user too. One Example is that Domain Controller of Intelligent Car appends its 4 bytes.
+
+### 12.2.2 Packet ID
+
+WireShark can parse `Ethernet` layer,`IP` layer,`UDP` layer, etc.
+
+To parse MSOP/DIFOP Packet and `User Layer`,first locate the `ID` of MSOP/DIFOP Packet.
+
+To parse MSOP/DIFOP Packet与`Tail Layer`,first find out the length of MSOP/DIFOP Packet (excludes `User Layer` and `Tail Layer`).
+
+
+
+The `ID` and the length of LiDARs is fixed as below.
+
+| LiDAR Type              |Packet Type |  ID                 |Packet Length |
+| ----                               | ----     | ----                        | ----    |
+| RS16/RS32/RSBP                     |  MSOP    |  55 aa 05 0a 5a a5 50 a0    |  1248   |
+|                                    |  DIFOP   |  a5 ff 00 5a 11 11 55 55    |  1248   |
+| RS128/RS80/RS48/RSP128/RSP80/RSP48/RSHELIOS/RSHELIOS_16P | MSOP |55 aa 05 5a|  1248   |
+|                                    |  DIFOP   |  a5 ff 00 5a 11 11 55 55    |  1248   |
+| RSM1/RSM2/RSE1                     |  MSOP    |  55 aa 5a a5                |  1210   |
+|                                    |  DIFOP   |  a5 ff 00 5a 11 11 55 55    |  256    |
+
+Note:  MSOP and DIFOP packets have different `ID`s, so we can determine which packet is MSOP or DIFOP.
+
+
+
+## 12.3 Steps
+
+Below are the steps to analysis PCAP file.
++ Find out the destination IP address of MSOP/DIFOP Packet。In design of RoboSense LiDAR, MSOP and DIFOP packets always have the same destination IPs.
+
+Also find out what address is it? broadcast/multicast/unicast address? If it is multicast, `groud_address` and `host_address` are required.
+
++ Find out the destination port of MSOP/DIFOP packets.
+
+Here show a specified type of packet with filter criteria.
+![](./img/12_01_select_by_port.png)
+
+Here exclude a specified type with filter criteria.
+![](./img/12_02_select_by_non_port.png)
+
+Just set filter criteria on `msop_port` and `difop_port`.
+
++ Is there VLAN layer? If Yes,
+
+    + create virtual NIC in case of online LiDAR.
+
+    + set `use_vlan=true`, in case of PCAP file.
+
+
++ Find out the ID bytes first. Is there User Layer?  How long is it?
+
+If Yes, set `user_layer_bytes`.
+
++ Is there Tail Layer ? How long is it?
+
+If Yes, set `tail_layer_bytes`.
+
+
+
+## 12.4 Examples
+
+### 12.4.1 RS32 - MSOP Packet
+
+Below is a example of RS32 MSOP packet.
++ Destination IP is `192.168.1.102` (labeled by red 1)
++ Destination port is `6699` (labeled by red 2)
++ Length of MSOP Packet is 1248`(labeled by red 3)
++ ID bytes of RS32 MSOP Packet is `55 aa 05 0a 5a a5 50 a0`. It is at the beginning (labeled by red 4), so no User Layer here.
+![](./img/12_03_rs32_msop_packet.png)
+
+
+
+### 12.4.2 RS32 - DIFOP Packet
+
+Below is an example of RS32 DIFOP Packet.
++ Destination IP is `192.168.1.102` (labeled by red 1)
++ Destination port is `7788` (labeled by red 2)
++ Length of MSOP Packet is 1248`(labeled by red 3)
++ ID bytes of DIFOP Packet is `a5 ff 00 5a 11 11 55 55`. It is at the begining(labeled by red 4), so no User Layer here.
+![](./img/12_04_rs32_difop_packet.png)
+
+
+
+### 12.4.3 VLAN Layer
+
+Below is an example of VLAN layer. This is a MSOP packet of RSM1.
+
+`VLAN Layer` is between `Ethernet Layer` and `IP Layer`.  `vlan id` is `69`.
+![](./img/12_05_with_vlan.png)
+
+
+
+### 12.4.4 User Layer
+
+Below is an example of User Layer. This is a MSOP Packet of RSM1.
+
+ID bytes of MSOP Packet is `55 aa 5a a5`. There are `16` bytes before it。This is just User Layer.
+![](./img/12_06_with_user_layer.png)
+
+
+
+### 12.4.5 Tail Layer
+
+Below is an example of User Layer. This is a MSOP Packet of RSM1.
+The length of MSOP Packet is `1210` bytes, plus bytes of lower laytes `14 + 20 + 8 = 42`, and then it should be `1252` bytes. Actually WireShark captures `1256` bytes. These extra `4` bytes are at the tail of MSOP packet(`c9 77 8e c3` in the picture). This  is Tail Layer.
+![](./img/12_07_with_tail_layer.png)
+
+
+
+### 12.4.6 Unsupported Case
+
+Below is unsupported case with `Linux cooked capture`(labeled line). It is captured on any NICs, such as `tcpdump -i any`.
+
+It is not `Ethernet`protocol, `rs_driver`doesn't accept it.
+![](./img/12_08_not_supported.png)
+

+ 151 - 0
rslidar/rs_driver/doc/howto/12_how_to_configure_by_pcap_file_CN.md

@@ -0,0 +1,151 @@
+
+#  12 **根据PCAP文件确定`rs_driver`的网络配置选项**
+
+
+
+## 12.1 概述
+
+通过分析PCAP文件中的MSOP/DIFOP Packet格式,可以确定`rs_driver`的网络选项,并显示点云。
+
+这里使用第三方工具WireShark分析PCAP文件。
+
+关于如何使用第三方工具录制PCAP文件,请阅读[如何录制PCAP文件](./13_how_to_capture_pcap_file_CN.md)
+
+
+
+## 12.2 MSOP/DIFOP格式
+
+### 12.2.1 协议分层
+
+MSOP/DIFOP Packet基于UDP协议,包括如下层。
+
++ Ethernet
++ VLAN(可选) 
++ IP 
++ UDP
++ 用户自定义层(前缀部分,可选) 
++ MSOP/DIFOP Packet(真正的数据部分)
++ 尾部层(后缀部分,可选)
+
+`VLAN`层是可选的。有没有`VLAN`层取决于两点:
+
++ 雷达是不是运行在VLAN环境下
++ 是在物理网卡还是在虚拟网卡上抓的包
+
+`用户自定义层`是客户为了适配自己的运行环境加的层,长度客户自己确定。
+
+`尾部层`也是客户环境带来的。其中一个常见的例子是:智能汽车的`域控制器`可能会在MSOP/DIFOP Packet后面加上自己的4个字节。
+
+### 12.2.2 Packet ID
+
+WireShark软件可以区分`Ethernet`、`IP`、`UDP`这些层。
+
+要区分MSOP/DIFOP Packet与`用户自定义层`,需要先定位MSOP/DIFOP Packet的`ID`,也就是它们的`标志字节`。
+
+要区分MSOP/DIFOP Packet与`尾部层`,还需要知道MSOP/DIFOP Packet的长度(这个长度不包括`用户自定义层`和`尾部层`)。
+
+各雷达的MSOP/DIFOP Packet的`ID`和长度都是确定的,如下面的表。
+
+| 雷达类型                           |Packet类型 |  标志字节                   |Packet长度 |
+| ----                               | ----     | ----                        | ----    |
+| RS16/RS32/RSBP                     |  MSOP    |  55 aa 05 0a 5a a5 50 a0    |  1248   |
+|                                    |  DIFOP   |  a5 ff 00 5a 11 11 55 55    |  1248   |
+| RS128/RS80/RS48/RSP128/RSP80/RSP48/RSHELIOS/RSHELIOS_16P | MSOP |55 aa 05 5a|  1248   |
+|                                    |  DIFOP   |  a5 ff 00 5a 11 11 55 55    |  1248   |
+| RSM1/RSM2/RSE1                     |  MSOP    |  55 aa 5a a5                |  1210   |
+|                                    |  DIFOP   |  a5 ff 00 5a 11 11 55 55    |  256    |
+
+这里还可以看到,MSOP Packet的标志字节与DIFOP Packet的不同,所以在PCAP文件中,可以通过它确定哪个Packet是`MSOP`,哪个是`DIFOP`。
+
+
+
+## 12.3 一般步骤
+
+如下是查看PCAP文件的一般步骤。
++ 确定MSOP/DIFOP Packet的`目的地址`。在RoboSense雷达的设计中,MSOP和DIFOP的`目的地址`总是相同的。
+
+需要明确的是,这是个什么地址?`广播地址`/`组播地址`/`单播地址`?如果是`组播地址`,则需要同时指定`group_address`和`host_address`。
+
++ MSOP/DIFOP Packet的`目的端口`。
+
+可以通过设置过滤条件来显示某种类型的Packet。
+![](./img/12_01_select_by_port.png)
+
+也可以排除某种类型的Packet。
+![](./img/12_02_select_by_non_port.png)
+
+需要分别设置`msop_port`和`difop_port`。
+
++ 是否有`VLAN层`?
+
+如果有`VLAN层`,则:
+  + 连接在线雷达时,需手工创建虚拟网卡
+  + 解析PCAP文件时,需指定`use_vlan`选项
+
++ 通过MSOP/DIFOP Packet的`标志字节`,确定是否有`用户自定义层`。如果有,是多长?
+
+如果有`用户自定义层`,需指定`user_layer_bytes`。
+
++ 通过MSOP/DIFOP Packet的长度, 确定是否有`尾部层`。如果有,是多长?
+
+如果有`尾部层`,需指定`tail_layer_bytes`。
+
+
+
+## 12.4 举例
+
+### 12.4.1 RS32雷达 - MSOP Packet
+
+如下图,是RS32雷达MSOP Packet的例子。
++ `目的地址`为`192.168.1.102`(红色1标注)。
++ 目的端口为`6699` (红色2标注) 
++ MSOP Packet的包长为`1248`(红色3标注)
++ RS32雷达的MSOP Packet的标志字节是`55 aa 05 0a 5a a5 50 a0`,它就在UDP数据的一开始(红色4标注),所以这里没有`用户自定义层`。
+![](./img/12_03_rs32_msop_packet.png)
+
+
+
+### 12.4.2 RS32雷达 - DIFOP Packet
+
+如下图,是RS32雷达DIFOP Packet的例子。
++ 目的地址为`192.168.1.102`(红色1标注)
++ 目的端口为`7788`(红色2标注)
++ MSOP Packet的包长为`1248`(红色3标注)
++ DIFOP Packet的ID是`a5 ff 00 5a 11 11 55 55`,它在UDP数据部分的一开始,所以这里没有`用户自定义层`(红色4标注)。
+![](./img/12_04_rs32_difop_packet.png)
+
+
+
+### 12.4.3 有VLAN的情况
+
+下图是有`VLAN层`的情况。这是一个RSM1雷达的MSOP包。
+
+`VLAN层`在`Ethernet层`与`IP层`之间。这个`VLAN`层的`vlan id`为`69`。
+![](./img/12_05_with_vlan.png)
+
+
+
+### 12.4.4 有用户自定义层的情况
+
+下图是有`用户自定义层`的情况。这是一个RSM1雷达的MSOP Packet。
+
+MSOP Packet的标志字节是`55 aa 5a a5`。可以看到在标志字节之前还有`16`字节数据,这就是`用户自定义层`。
+![](./img/12_06_with_user_layer.png)
+
+
+
+### 12.4.5 有尾部层的情况
+
+下图是有用户尾部层的情况。这是一个RSM1雷达的MSOP Packet。
+MSOP Packet的长度是`1210`字节,加上前面各个层的头`14 + 20 + 8 = 42`个字节,应该是`1252`个字节,但实际上WireShark抓到了`1256`个字节。多的`4`个字节附加在MSOP Packet尾部(图中的`c9 77 8e c3`),这个就是`尾部层`。
+![](./img/12_07_with_tail_layer.png)
+
+
+
+### 12.4.6 不支持的情况
+
+下图是不支持的格式。`Linux cooked capture`一般是在抓包时指定了任意网卡,如`tcpdump -i any`。
+
+这样抓的包不是`Ethernet`格式,不符合`rs_driver`的要求。
+![](./img/12_08_not_supported.png)
+

+ 90 - 0
rslidar/rs_driver/doc/howto/13_how_to_capture_pcap_file.md

@@ -0,0 +1,90 @@
+# 13 **Capture PCAP file for `rs_driver`**
+
+
+
+## 13.1 Capture with WireShark
+
+### 13.1.1 Choose NIC
+First choose a NIC。Here choose `enp0s3`.
++ Don't choose `any`.  If so, the protocol of PCAP file is `linux cooked capture`. rs_driver doesn't accept it.
++ If the LiDAR works on `VLAN`, select to capture on physical NIC or virtual NIC.  There is VLAN layer in former case, and there is no VLAN in latter case.
+![](./img/13_01_wireshark_select_nic.png)
+
+
+
+Use the following network tools to find out the NIC name.
+
++ `ipconfig` -  on Windows
++ `ifconfig` - on Linux
+
+
+
+### 13.1.2 Capture
+
+Now capture packets.
+![](./img/13_02_wireshark_capture.png)
+
+
+
+### 13.1.3 Export PCAP files
+
+#### 13.1.3.1 filter out non-MSOP/DIFOP packets
+
+You may exclude non-MSOP/DIFOP packets with filter criteria.
+
++ Filter criteria with ports `6699` and `7788`.
+![](./img/13_03_wireshark_filter_by_port.png)
+
+
+#### 13.1.3.2 Export All
+
+Select Menu `File` -> `Export Specified Packets ...` to get this dialog.
+![](./img/13_04_wireshark_export_all.png)
+
+Export all packets. In above picture, select `All Packets` under `Packet Range`, to export all packets.
+
++ Select `pcap` format instead of `pcapng`. `rs_driver` doesn't support the latter.
++ Under `Packet Range`, there are two columns: `Captured` and `Displayed`. Keep `Displayed` here.
+  + `Captured` are all captured packets. `Displayed` are the filtered. For example,  in the line of `All packets`, captures `24333`, and leaves `23879` after filtering.
+  + If `Captured` export packets from all captured packets, else export only the filtered.
+
+#### 13.1.3.3 Export a Range of packets
+
+Here export a range of packets.
+
++ This example give a range `1`-`10000`. These two numbers are from all packets. Actually `9805` packets are exported.
+![](./img/13_05_wireshark_export_range.png)
+
+#### 13.1.3.4 Mark and Export
+
+Here mark a few packets and export them. First mark the packets with Menu  `Mark/Unmark Packet`.
+![](./img/13_06_wireshark_mark.png)
+
+Select `Marked packets only`.
+![](./img/13_07_wireshark_export_marked.png)
+
+#### 13.1.3.5 Mark and Export A Range
+
+Here mark two packets, and export all packet between them. Select `First to last marked`.
+![](./img/13_08_wireshark_export_marked_range.png)
+
+
+
+## 13.2 Capture with tcpdump
+
+`tcpdump`  is the a tool on Unix/Linux.
+
+As below, capture packets on NIC `eno1` and save them into `a.pcap`, with filter criteria `UDP` and destination port `6699` or `7788`. And capture `30000` packets. 
+
+```shell
+sudo tcpdump -i eno1 -w a.pcap -c 30000 'udp dst port 7788 or 6699'
+```
+
+Use `ifconfig` to find out the NIC name.
+
+Don't use the option`-i any`. It captures on  NIC `any` , and the protocol is `linux cooked capture`. This is not the Ethernet protocol. rs_driver doesn't accept it.
+
+```shell
+sudo tcpdump -i any
+```
+

+ 90 - 0
rslidar/rs_driver/doc/howto/13_how_to_capture_pcap_file_CN.md

@@ -0,0 +1,90 @@
+# 13 **如何为rs_driver录制一个PCAP文件**
+
+
+
+## 13.1 使用WireShark抓包
+
+### 13.1.1 选择网卡
+打开WireShark的第一步,是选择在哪个网卡抓包。这里选择的网卡是`enp0s3`。
++ 不要选择`any`,这样录制的PCAP文件,协议为`linux cooked capture`。这不是`Ethernet`格式,`rs_driver`不能识别。
++ 如果雷达支持`VLAN`,可以选择在物理网卡上还是在虚拟网卡上抓包。在前者抓包带`VLAN`层,在后者抓包则不带。
+![](./img/13_01_wireshark_select_nic.png)
+
+
+
+使用如下的网络工具,可以确定网卡的名字。
+
++ `ipconfig` -  Windows平台
++ `ifconfig` - Linux平台
+
+
+
+### 13.1.2 抓包
+
+指定了网卡,就可以抓包了。
+![](./img/13_02_wireshark_capture.png)
+
+
+
+### 13.1.3 导出PCAP文件
+
+#### 13.1.3.1 过滤掉不要的包
+
+导出PCAP文件时,你可能希望只保留MSOP/DIFOP Packet。指定过滤条件就可以了。
+
++ 这里指定了两个端口`6699`和`7788`进行过滤。
+![](./img/13_03_wireshark_filter_by_port.png)
+
+
+#### 13.1.3.2 导出所有包
+
+选择菜单`File` -> `Export Specified Packets ... `,得到如下的对话框。
+![](./img/13_04_wireshark_export_all.png)
+
+可以选择导出所有包。在上面的图中,`Packet Range`选项,选择`All Packets`,导出所有包。
+
++ 保存文件时,选择`pcap`格式,而不是`pcapng`格式,后者`rs_driver`还不支持。
++ `Packet Range`下有两列数字:`Captured`和`Displayed`。这里保持`Displayed`就好了。
+  + `Captured`是抓到的所有包,`Displayed`是根据前面的过滤条件过滤后的包。比如`All packets`这一栏,总共抓到`24333`个包,过滤后还有`23879`个包。
+  + 如果选择`Captured`,则导出的PCAP文件是从所有包中提取;如果选择`Displayed`,则只从过滤后的包中提取。
+
+#### 13.1.3.3 导出指定范围内的包
+
+如果包数太多,只想导出部分包,则可以指定导出的范围。
+
++ 下面的例子指定的范围是`1`到`10000`,这两个序号是在抓到的所有包中的序号,而实际导出的包数是`9805`个。
+![](./img/13_05_wireshark_export_range.png)
+
+#### 13.1.3.4 标记并导出特定包
+
+有时候需要精确指定导出哪几个包。这时可以先通过右键菜单 `Mark/Unmark Packet`标记这几个包。
+![](./img/13_06_wireshark_mark.png)
+
+导出时,选择`Marked packets only`。
+![](./img/13_07_wireshark_export_marked.png)
+
+#### 13.1.3.5 标记并导出特定范围内的包
+
+也可以先标记两个包,再导出它们两个之间的所有包。在导出时,选择`First to last marked`就可以。
+![](./img/13_08_wireshark_export_marked_range.png)
+
+
+
+## 13.2 使用tcpdump抓包
+
+`tcpdump`是Unix/Linux下的抓包工具。
+
+如下的命令在网卡`eno1`上抓包,保存到文件`a.pcap`,过滤条件是`UDP`协议,且目标端口为`6699`或`7788`。这里抓包`30000`个,对于一般问题的分析,这个包数够用了。
+
+```shell
+sudo tcpdump -i eno1 -w a.pcap -c 30000 'udp dst port 7788 or 6699'
+```
+
+使用网络工具`ifconfig`,可以确定网卡的名字。
+
+不要使用选项`-i any`抓包。这个选项针对任意网卡抓包,协议为`linux cooked capture`。这不是Ethernet格式,rs_driver不能识别。
+
+```shell
+sudo tcpdump -i any
+```
+

+ 107 - 0
rslidar/rs_driver/doc/howto/14_how_to_use_rs_driver_viewer.md

@@ -0,0 +1,107 @@
+# 14 **How to visualize point cloud with `rs_driver_viewer`**
+
+
+
+## 14.1 Introduction
+
+The `rs_driver_viewer` is a visualization tool for the point cloud. This document illustrates how to use it.
+![](./img/14_01_rs_driver_viewer_point_cloud.png)
+
+
+
+## 14.2 Compile and Run
+
+Compile the driver with the option COMPILE_TOOLS=ON. 
+
+```bash
+cmake -DCOMPILE_TOOS=ON ..
+```
+
+Run the tool.
+
+```bash
+./tool/rs_driver_viewer 
+```
+
+### 14.2.1 Help Menu
+
+- -h
+
+   print the argument menu 
+
+- -type
+
+   LiDAR type, the default value is *RS16*
+
+- -pcap
+
+   Full path of the PCAP file. If this argument is set, the driver read packets from the pcap file, else from online Lidar. 
+
+- -msop
+
+   MSOP port number of LiDAR, the default value is *6699*
+
+- -difop
+
+   DIFOP port number of LiDAR, the default value is *7788*
+   
+- -group
+
+   the multicast group address
+
+- -host
+
+   the host address
+
+- -x
+
+   Transformation parameter, default is 0, unit: m
+
+- -y
+
+   Transformation parameter, default is 0, unit: m
+
+- -z
+
+   Transformation parameter, default is 0, unit: m
+
+- -roll
+
+   Transformation parameter, default is 0, unit: radian
+
+- -pitch
+
+   Transformation parameter, default is 0, unit: radian
+
+- -yaw
+
+   Transformation parameter, default is 0, unit: radian
+
+Note:
+
+**The point cloud transformation function is available only if the CMake option ENABLE_TRANSFORM is ON.**
+
+
+
+## 14.3 Examples
+
+- Decode from an online RS128 LiDAR. Its MSOP port is ```9966```, and DIFOP port is ```8877```
+
+  ```bash
+  rs_driver_viewer -type RS128 -msop 9966 -difop 8877 
+  ```
+
+- Decode from a PCAP file with RSHELIOS LiDAR data.
+
+  ```bash
+  rs_driver_viewer -type RSHELIOS -pcap /home/robosense/helios.pcap
+  ```
+
+- Decode with the coordinate transformation parameters: x=1.5, y=2, z=0, roll=1.57, pitch=0, yaw=0
+
+  ```bash
+  rs_driver_viewer -type RS16 -x 1.5 -y 2 -roll 1.57 
+  ```
+
+  
+

+ 106 - 0
rslidar/rs_driver/doc/howto/14_how_to_use_rs_driver_viewer_CN.md

@@ -0,0 +1,106 @@
+#  14 **如何可视化点云**
+
+
+
+## 14.1 概述
+
+`rs_driver_viewer`是`rs_driver`自带的小工具,可以用于显示点云。
+
+本文说明如何使用这个工具。
+![](./img/14_01_rs_driver_viewer_point_cloud.png)
+
+
+
+## 14.2 编译和运行
+
+要编译`rs_driver_viewer`,需要使能编译选项COMPILE_TOOLS=ON。
+
+```bash
+cmake -DCOMPILE_TOOS=ON ..
+```
+
+运行`rs_driver_viewer`。
+
+```bash
+./tool/rs_driver_viewer 
+```
+
+### 14.2.1 帮助菜单
+
+- -h
+
+   打印帮助菜单 
+
+- -type
+
+   雷达类型,默认值是*RS16*
+
+- -pcap
+
+   PCAP文件的全路径。如果设置这个参数,则rs_driver_viewer从PCAP文件读取MSOP/DIFOP包,而不是从UDP socket。
+
+- -msop
+
+   接收MSOP包的端口号。默认值是*6699*
+
+- -difop
+
+   接收DIFOP包的端口号,默认值是 *7788*
+   
+- -group
+
+   主机要加入的组播组的IP地址
+
+- -host
+
+   主机地址
+
+- -x
+
+   坐标转换参数,默认值为0,单位米
+
+- -y
+
+   坐标转换参数,默认值为0,单位米
+
+- -z
+
+   坐标转换参数,默认值为0,单位米
+
+- -roll
+
+   坐标转换参数,默认值为0,单位度
+
+- -pitch
+
+   坐标转换参数,默认值为0,单位米
+
+- -yaw
+
+   坐标转换参数,默认值为0,单位米
+
+注意:**要使用坐标转换功能,需要使能编译选项ENABLE_TRANSFORM=ON.**
+
+
+
+## 14.3 使用示例
+
+- 从在线雷达```RS128```接收MSOP/DIFOP包。MSOP端口是```9966```, DIFOP端口是```8877```。
+
+  ```bash
+  rs_driver_viewer -type RS128 -msop 9966 -difop 8877 
+  ```
+
+- 从PCAP文件解码,雷达类型是```RSHELIOS```。
+
+  ```bash
+  rs_driver_viewer -type RSHELIOS -pcap /home/robosense/helios.pcap
+  ```
+
+- 从在线雷达```RS16```接收MSOP/DIFOP包。坐标转换参数为x=1.5, y=2, z=0, roll=1.57, pitch=0, yaw=0。
+
+  ```bash
+  rs_driver_viewer -type RS16 -x 1.5 -y 2 -roll 1.57 
+  ```
+
+  

+ 52 - 0
rslidar/rs_driver/doc/howto/15_how_to_transform_pointcloud.md

@@ -0,0 +1,52 @@
+# 15 **How to transform point cloud**
+
+
+
+## 15.1 Introduction
+
+This document illustrate how to transform the point cloud to a different position with the built-in transform function.
+
+Warning: 
+
++ It costs much CPU resources. This function is only for test purpose.  
++ **Never enable this function in your released products**.
+
+
+
+## 15.2 Steps
+
+### 15.2.1 Compile
+
+To enable the transformation function, compile the driver with the option ENABLE_TRANSFORM=ON.
+
+```bash
+cmake -DENABLE_TRANSFORM=ON ..
+```
+
+### 15.2.2 Config parameters
+
+Configure the transformation parameters. These parameters' default value is ```0```.  
+
++ The unit of x, y, z, is ```m``` 
++ the unit of roll, pitch, yaw, is ```radian```
+
++ The rotation order of the transformation is **yaw - pitch - row**. 
+
+Below is an example with x=1, y=0, z=2.5, roll=0.1, pitch=0.2, yaw=1.57. 
+
+```c++
+RSDriverParam param;                            ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;     /// get packet from online lidar
+param.input_param.msop_port = 6699;             ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;            ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS16;             ///< Set the lidar type. Make sure this type is correct
+
+param.decoder_param.transform_param.x = 1;	  ///< unit: m
+param.decoder_param.transform_param.y = 0;	  ///< unit: m
+param.decoder_param.transform_param.z = 2.5;	  ///< unit: m
+param.decoder_param.transform_param.roll = 0.1; ///< unit: radian
+param.decoder_param.transform_param.pitch = 0.2;///< unit: radian
+param.decoder_param.transform_param.yaw = 1.57; ///< unit: radian
+
+```
+

+ 47 - 0
rslidar/rs_driver/doc/howto/15_how_to_transform_pointcloud_CN.md

@@ -0,0 +1,47 @@
+# 15 **如何对点云作坐标转换**
+
+## 15.1 概述
+
+本文说明如何使用坐标转换功能,将点云变换到另一个位置、另一个角度上去。
+
+警告:
++ 坐标转换显著消耗CPU资源。提供这个功能,仅用于测试目的。
++ **不要在您发布的产品中使能坐标转换**,除非您已经仔细评估,确定您的系统能容忍这些消耗。
+
+
+
+## 15.2 步骤
+
+### 15.2.1 CMake编译宏
+
+要使用坐标转换功能,需要使能CMake编译选项```ENABLE_TRANSFORM=ON```。
+
+```bash
+cmake -DENABLE_TRANSFORM=ON ..
+```
+
+### 15.2.2 配置参数
+
+配置坐标转换的参数,这些参数的默认值是`0`。
++ x, y, z的单位是`米`
++ yaw, pitch, roll的单位是`度`
++ 旋转的顺序是 `yaw -> pitch -> roll`
+
+例子如下。
+
+```c++
+RSDriverParam param;                             ///< Create a parameter object
+param.input_type = InputType::ONLINE_LIDAR;      /// get packet from online lidar
+param.input_param.msop_port = 6699;              ///< Set the lidar msop port number, the default is 6699
+param.input_param.difop_port = 7788;             ///< Set the lidar difop port number, the default is 7788
+param.lidar_type = LidarType::RS16;              ///< Set the lidar type. Make sure this type is correct
+
+param.decoder_param.transform_param.x = 1;	     ///< unit: m
+param.decoder_param.transform_param.y = 0;	     ///< unit: m
+param.decoder_param.transform_param.z = 2.5;	 ///< unit: m
+param.decoder_param.transform_param.yaw = 1.57;  ///< unit: radian
+param.decoder_param.transform_param.pitch = 0.2; ///< unit: radian
+param.decoder_param.transform_param.roll = 0.1;  ///< unit: radian
+
+```
+

+ 333 - 0
rslidar/rs_driver/doc/howto/16_how_to_compile_on_windows.md

@@ -0,0 +1,333 @@
+# 16 **How to compile rs_driver on Windows**
+
+
+
+## 16.1 Overview
+
+This document illustrates how to comile two parts of `rs_driver`.
+- Demo apps, includes `demo_online` and`demo_pcap`
+- Point point visualization tool `rs_driver_viewer`
+
+They depends on different 3rd-party libraries. 
+
+Below steps are done with `VS2019` on `Windows 10`. 
+
+
+
+## 16.2 Compile demo_online
+
+Here take `demo_online` as an example. `demo_pcap` is similar to  `demo_online`.
+
+### 16.2.1 Setup 3rd Libraries
+
+- To parse PCAP files, first setup `libpcap`. 
+
+```
+WpdPack_4_1_2.zip  // header files and library files
+WinPcap_4_1_3.exe // runtime libraries
+```
+
+- Unzip `WpdPack_4_1_2.zip` to the directory `C:/Program Files`.
+
+- Run `WinPcap_4_1_3.exe`, and setup into`C:/Program Files`.
+
+### 16.2.2 Setup Project demo_online 
+
+- Setup the `demo_online` project, and add the source file`demo_online.cpp`.
+![](./img/16_01_demo_project.png)
+
+### 16.2.3 Configure Project demo_online
+
+- Comply with `C++14`.
+![](./img/16_02_demo_use_cpp14.png)
+
+- `demo_online` depends on `rs_driver`。set its header file path.
+![](./img/16_03_demo_extra_include.png)
+
+- set the header file path of `libpcap`.
+![](./img/16_04_demo_include_path.png)
+
+- set the library file path of `libpcap`.
+![](./img/16_05_demo_lib_path.png)
+
+- Add the dependency library of `libpcap`. It is `wpcap.lib`。Also add`ws2_32.lib`. It is Windows socket library. `rs_driver` depends on it.
+![](./img/16_06_demo_lib.png)
+
+- set the compile option `_CRT_SECURE_NO_WARNINGS` to avoid unnecessary compiling errors.
+![](./img/16_07_demo_precompile_macro.png)
+
+### 16.2.4 Compile and Run
+
+- Compile the `demo_online` project, and run it.
+  
+  
+
+
+
+## 16.3 Compile rs_driver_viewer
+
+### 16.3.1 Setup 3rd Party Library
+
+- Setup the `libpcap` library.
+
+- `rs_driver_viewer` Also depends on `PCL`, and then `Boost`、`Eigen` etc. It is lucky that `PCL` offers a setup package which contains all these libraries. This package fits `VS2019`.
+
+```
+PCL-1.11.1-AllInOne-msvc2019-win64.exe
+```
+
+  Setup it to  the directory C:/Program Files`.
+![](./img/16_08_viewer_install_pcl.png)
+
+it also setup its dependency libraries.
+![](./img/16_09_viewer_install_pcl_dep.png)
+
+The components are in the these directories.
+```
+C:\Program Files\PCL 1.11.1          # PCL libraries
+C:\Program Files\OpenNI2             # OpenNI2 libraries depended by PCL
+C:\Program Files\PCL 1.11.1\3rdParty # Other libraries depended by PCL
+```
+
+### 16.3.2 configure 3rd-party Library
+
+Add paths of runtime libraries as below.
+![](./img/16_10_viewer_add_env_var.png)
+
+```
+C:\Program Files\PCL 1.11.1\bin
+C:\Program Files\PCL 1.11.1\3rdParty\VTK\bin
+C:\Program Files\PCL 1.11.1\3rdParty\OpenNI2\Redist
+```
+
+### 16.3.3 Setup the rs_driver_viewer Project
+
+- Setup the `rs_driver_viewer` project,  and add the source file`rs_driver_viewer.cpp`.
+![](./img/16_11_viewer_project.png)
+
+### 16.3.4 Configure Project rs_driver_viewer
+
+- Comply with `C++14`
+![](./img/16_02_demo_use_cpp14.png)
+
+- Disable SDL check
+![](./img/16_12_viewer_sdl_check.png)
+
+- Set the header file path of `rs_driver`.
+![](./img/16_03_demo_extra_include.png)
+
+- Set the header file path of `PCL`. Set its dependency libraries also.
+![](./img/16_13_viewer_include_path.png)
+
+```
+C:\Program Files\PCL 1.11.1\include\pcl-1.11
+C:\Program Files\PCL 1.11.1\3rdParty\Boost\include\boost-1_74
+C:\Program Files\PCL 1.11.1\3rdParty\Eigen\eigen3
+C:\Program Files\PCL 1.11.1\3rdParty\FLANN\include
+C:\Program Files\PCL 1.11.1\3rdParty\Qhull\include
+C:\Program Files\PCL 1.11.1\3rdParty\VTK\include\vtk-8.2
+C:\Program Files\OpenNI2\Include
+```
+
+- Set the library file path of `PCL`. Set its dependency libraries also.
+![](./img/16_14_viewer_lib_path.png)
+
+```
+C:\Program Files\PCL 1.11.1\lib
+C:\Program Files\PCL 1.11.1\3rdParty\Boost\lib
+C:\Program Files\PCL 1.11.1\3rdParty\FLANN\lib
+C:\Program Files\PCL 1.11.1\3rdParty\Qhull\lib
+C:\Program Files\PCL 1.11.1\3rdParty\VTK\lib
+C:\Program Files\OpenNI2\Lib
+```
+
+- Set `PCL` libraries, including `PCL` and `vtk`. Also set `wpcap.lib` and `ws2_32.lib`.
+
+`PCL` libraries are as below.
+
+```
+pcl_common.lib
+pcl_commond.lib
+pcl_features.lib
+pcl_featuresd.lib
+pcl_filters.lib
+pcl_filtersd.lib
+pcl_io.lib
+pcl_iod.lib
+pcl_io_ply.lib
+pcl_io_plyd.lib
+pcl_kdtree.lib
+pcl_kdtreed.lib
+pcl_keypoints.lib
+pcl_keypointsd.lib
+pcl_ml.lib
+pcl_mld.lib
+pcl_octree.lib
+pcl_octreed.lib
+pcl_outofcore.lib
+pcl_outofcored.lib
+pcl_people.lib
+pcl_peopled.lib
+pcl_recognition.lib
+pcl_recognitiond.lib
+pcl_registration.lib
+pcl_registrationd.lib
+pcl_sample_consensus.lib
+pcl_sample_consensusd.lib
+pcl_search.lib
+pcl_searchd.lib
+pcl_segmentation.lib
+pcl_segmentationd.lib
+pcl_stereo.lib
+pcl_stereod.lib
+pcl_surface.lib
+pcl_surfaced.lib
+pcl_tracking.lib
+pcl_trackingd.lib
+pcl_visualization.lib
+pcl_visualizationd.lib
+```
+
+`vtk` has `debug` version and `release`version. Here take `release` as an example. 
+![](./img/16_15_viewer_lib.png)
+
+```
+vtkChartsCore-8.2.lib
+vtkCommonColor-8.2.lib
+vtkCommonComputationalGeometry-8.2.lib
+vtkCommonCore-8.2.lib
+vtkCommonDataModel-8.2.lib
+vtkCommonExecutionModel-8.2.lib
+vtkCommonMath-8.2.lib
+vtkCommonMisc-8.2.lib
+vtkCommonSystem-8.2.lib
+vtkCommonTransforms-8.2.lib
+vtkDICOMParser-8.2.lib
+vtkDomainsChemistry-8.2.lib
+vtkDomainsChemistryOpenGL2-8.2.lib
+vtkdoubleconversion-8.2.lib
+vtkexodusII-8.2.lib
+vtkexpat-8.2.lib
+vtkFiltersAMR-8.2.lib
+vtkFiltersCore-8.2.lib
+vtkFiltersExtraction-8.2.lib
+vtkFiltersFlowPaths-8.2.lib
+vtkFiltersGeneral-8.2.lib
+vtkFiltersGeneric-8.2.lib
+vtkFiltersGeometry-8.2.lib
+vtkFiltersHybrid-8.2.lib
+vtkFiltersHyperTree-8.2.lib
+vtkFiltersImaging-8.2.lib
+vtkFiltersModeling-8.2.lib
+vtkFiltersParallel-8.2.lib
+vtkFiltersParallelImaging-8.2.lib
+vtkFiltersPoints-8.2.lib
+vtkFiltersProgrammable-8.2.lib
+vtkFiltersSelection-8.2.lib
+vtkFiltersSMP-8.2.lib
+vtkFiltersSources-8.2.lib
+vtkFiltersStatistics-8.2.lib
+vtkFiltersTexture-8.2.lib
+vtkFiltersTopology-8.2.lib
+vtkFiltersVerdict-8.2.lib
+vtkfreetype-8.2.lib
+vtkGeovisCore-8.2.lib
+vtkgl2ps-8.2.lib
+vtkglew-8.2.lib
+vtkGUISupportMFC-8.2.lib
+vtkhdf5-8.2.lib
+vtkhdf5_hl-8.2.lib
+vtkImagingColor-8.2.lib
+vtkImagingCore-8.2.lib
+vtkImagingFourier-8.2.lib
+vtkImagingGeneral-8.2.lib
+vtkImagingHybrid-8.2.lib
+vtkImagingMath-8.2.lib
+vtkImagingMorphological-8.2.lib
+vtkImagingSources-8.2.lib
+vtkImagingStatistics-8.2.lib
+vtkImagingStencil-8.2.lib
+vtkInfovisCore-8.2.lib
+vtkInfovisLayout-8.2.lib
+vtkInteractionImage-8.2.lib
+vtkInteractionStyle-8.2.lib
+vtkInteractionWidgets-8.2.lib
+vtkIOAMR-8.2.lib
+vtkIOAsynchronous-8.2.lib
+vtkIOCityGML-8.2.lib
+vtkIOCore-8.2.lib
+vtkIOEnSight-8.2.lib
+vtkIOExodus-8.2.lib
+vtkIOExport-8.2.lib
+vtkIOExportOpenGL2-8.2.lib
+vtkIOExportPDF-8.2.lib
+vtkIOGeometry-8.2.lib
+vtkIOImage-8.2.lib
+vtkIOImport-8.2.lib
+vtkIOInfovis-8.2.lib
+vtkIOLegacy-8.2.lib
+vtkIOLSDyna-8.2.lib
+vtkIOMINC-8.2.lib
+vtkIOMovie-8.2.lib
+vtkIONetCDF-8.2.lib
+vtkIOParallel-8.2.lib
+vtkIOParallelXML-8.2.lib
+vtkIOPLY-8.2.lib
+vtkIOSegY-8.2.lib
+vtkIOSQL-8.2.lib
+vtkIOTecplotTable-8.2.lib
+vtkIOVeraOut-8.2.lib
+vtkIOVideo-8.2.lib
+vtkIOXML-8.2.lib
+vtkIOXMLParser-8.2.lib
+vtkjpeg-8.2.lib
+vtkjsoncpp-8.2.lib
+vtklibharu-8.2.lib
+vtklibxml2-8.2.lib
+vtklz4-8.2.lib
+vtklzma-8.2.lib
+vtkmetaio-8.2.lib
+vtkNetCDF-8.2.lib
+vtkogg-8.2.lib
+vtkParallelCore-8.2.lib
+vtkpng-8.2.lib
+vtkproj-8.2.lib
+vtkpugixml-8.2.lib
+vtkRenderingAnnotation-8.2.lib
+vtkRenderingContext2D-8.2.lib
+vtkRenderingContextOpenGL2-8.2.lib
+vtkRenderingCore-8.2.lib
+vtkRenderingExternal-8.2.lib
+vtkRenderingFreeType-8.2.lib
+vtkRenderingGL2PSOpenGL2-8.2.lib
+vtkRenderingImage-8.2.lib
+vtkRenderingLabel-8.2.lib
+vtkRenderingLOD-8.2.lib
+vtkRenderingOpenGL2-8.2.lib
+vtkRenderingVolume-8.2.lib
+vtkRenderingVolumeOpenGL2-8.2.lib
+vtksqlite-8.2.lib
+vtksys-8.2.lib
+vtktheora-8.2.lib
+vtktiff-8.2.lib
+vtkverdict-8.2.lib
+vtkViewsContext2D-8.2.lib
+vtkViewsCore-8.2.lib
+vtkViewsInfovis-8.2.lib
+vtkzlib-8.2.lib
+```
+
+- Set below compile options to avoid unnecessary compile errors. 
+![](./img/16_16_viewer_precompile_macro.png)
+
+```
+BOOST_USE_WINDOWS_H
+NOMINMAX
+_CRT_SECURE_NO_DEPRECATE
+```
+
+### 16.3.5 Compile and Run
+
+Compile `demo_online` to get `X64 release`version, and run it.
+![](./img/16_17_viewer_run.png)
+

+ 331 - 0
rslidar/rs_driver/doc/howto/16_how_to_compile_on_windows_CN.md

@@ -0,0 +1,331 @@
+# 16 **如何在Windows上编译rs_driver**
+
+## 16.1 概述
+
+这里的编译说明,针对`rs_driver`的两个部分。
+- 示例程序,包括`demo_online`和`demo_pcap`
+- 点云显示工具 `rs_driver_viewer`
+
+两者对第三方库的依赖不同,这里将分开说明。
+
+如下步骤在`Windows 10`系统下完成,使用的编译工具为`VS2019`。
+
+
+
+## 16.2 编译demo_online
+
+演示程序的编译,以`demo_online`为例说明。`demo_pcap`的编译步骤与`demo_online`相同。
+
+### 16.2.1 安装第三方库
+
+- 如果需要解析PCAP文件,则需要安装`libpcap`库,包括:
+
+```
+WpdPack_4_1_2.zip, 编译时需要的头文件和库文件
+WinPcap_4_1_3.exe, 包括运行时库
+```
+
+- 将`WpdPack_4_1_2.zip`解压到目录`C:/Program Files`下。
+
+- 运行`WinPcap_4_1_3.exe`,安装到目录`C:/Program Files`下。
+
+### 16.2.2 创建demo_online工程
+
+- 创建`demo_online`工程,加入源文件`demo_online.cpp`。
+![](./img/16_01_demo_project.png)
+
+### 16.2.3 配置demo_online工程
+
+- 遵循`C++14`标准
+![](./img/16_02_demo_use_cpp14.png)
+
+- `demo_online`当然依赖`rs_driver`库。设置`rs_driver`的头文件路径。
+![](./img/16_03_demo_extra_include.png)
+
+- 设置`libpcap`库的头文件路径。
+![](./img/16_04_demo_include_path.png)
+
+- 设置`libpcap`库的库文件路径。
+![](./img/16_05_demo_lib_path.png)
+
+- 设置依赖的`libpcap`库`wpcap.lib`。也同时设置`ws2_32.lib`,这是Windows的socket库,`rs_driver`依赖它。
+![](./img/16_06_demo_lib.png)
+
+- 设置编译选项 `_CRT_SECURE_NO_WARNINGS`,避免不必要的编译错误。
+![](./img/16_07_demo_precompile_macro.png)
+
+### 16.2.4 编译及运行
+
+- 编译`demo_online`工程,并运行。
+  
+  这个步骤没有什么特别的。
+
+
+
+## 16.3 编译rs_driver_viewer
+
+### 16.3.1 安装第三方库
+
+- 与`demo_online`一样,安装`libpcap`库。
+
+- `rs_driver_viewer`还依赖`PCL`库,后者又依赖`Boost`、`Eigen`等一系列库。幸运的是,`PCL`库提供了适配`MSVC2019`的安装包,而这个包中又自带了它所依赖的库。这里使用的安装包是:
+
+```
+PCL-1.11.1-AllInOne-msvc2019-win64.exe
+```
+
+  运行它,安装到目录`C:/Program Files`下。
+![](./img/16_08_viewer_install_pcl.png)
+
+  注意,安装`PCL`库的同时,也会安装它依赖的库。
+![](./img/16_09_viewer_install_pcl_dep.png)
+
+安装的组件位置如下:
+```
+C:\Program Files\PCL 1.11.1          # PCL自身的库
+C:\Program Files\OpenNI2             # PCL依赖的OpenNI2库
+C:\Program Files\PCL 1.11.1\3rdParty # PCL依赖的其他库
+```
+
+### 16.3.2 配置第三方库
+
+将如下运行时库的路径加入`PATH`。
+![](./img/16_10_viewer_add_env_var.png)
+
+```
+C:\Program Files\PCL 1.11.1\bin
+C:\Program Files\PCL 1.11.1\3rdParty\VTK\bin
+C:\Program Files\PCL 1.11.1\3rdParty\OpenNI2\Redist
+```
+
+### 16.3.3 创建rs_driver_viewer工程
+
+- 创建`rs_driver_viewer`工程,加入源文件`rs_driver_viewer.cpp`。
+![](./img/16_11_viewer_project.png)
+
+### 16.3.4 配置rs_driver_viewer工程
+
+- 与`demo_online`一样,遵循`C++14`标准
+![](./img/16_02_demo_use_cpp14.png)
+
+- 禁止SDL检查
+![](./img/16_12_viewer_sdl_check.png)
+
+- 与`demo_online`一样,设置`rs_driver`的头文件路径。
+![](./img/16_03_demo_extra_include.png)
+
+- 设置`PCL`库的头文件路径如下。(与`demo_online`一样,同时设置`libpcap`库)
+![](./img/16_13_viewer_include_path.png)
+
+```
+C:\Program Files\PCL 1.11.1\include\pcl-1.11
+C:\Program Files\PCL 1.11.1\3rdParty\Boost\include\boost-1_74
+C:\Program Files\PCL 1.11.1\3rdParty\Eigen\eigen3
+C:\Program Files\PCL 1.11.1\3rdParty\FLANN\include
+C:\Program Files\PCL 1.11.1\3rdParty\Qhull\include
+C:\Program Files\PCL 1.11.1\3rdParty\VTK\include\vtk-8.2
+C:\Program Files\OpenNI2\Include
+```
+
+- 设置`PCL`库的库文件路径。(与`demo_online`一样,同时设置`libpcap`)
+![](./img/16_14_viewer_lib_path.png)
+
+```
+C:\Program Files\PCL 1.11.1\lib
+C:\Program Files\PCL 1.11.1\3rdParty\Boost\lib
+C:\Program Files\PCL 1.11.1\3rdParty\FLANN\lib
+C:\Program Files\PCL 1.11.1\3rdParty\Qhull\lib
+C:\Program Files\PCL 1.11.1\3rdParty\VTK\lib
+C:\Program Files\OpenNI2\Lib
+```
+
+- 设置`PCL`及它依赖的库,包括`PCL`和`vtk`两部分。(与`demo_online`一样,同时设置`wpcap.lib`和`ws2_32.lib`)
+
+`PCL`的库文件如下:
+
+```
+pcl_common.lib
+pcl_commond.lib
+pcl_features.lib
+pcl_featuresd.lib
+pcl_filters.lib
+pcl_filtersd.lib
+pcl_io.lib
+pcl_iod.lib
+pcl_io_ply.lib
+pcl_io_plyd.lib
+pcl_kdtree.lib
+pcl_kdtreed.lib
+pcl_keypoints.lib
+pcl_keypointsd.lib
+pcl_ml.lib
+pcl_mld.lib
+pcl_octree.lib
+pcl_octreed.lib
+pcl_outofcore.lib
+pcl_outofcored.lib
+pcl_people.lib
+pcl_peopled.lib
+pcl_recognition.lib
+pcl_recognitiond.lib
+pcl_registration.lib
+pcl_registrationd.lib
+pcl_sample_consensus.lib
+pcl_sample_consensusd.lib
+pcl_search.lib
+pcl_searchd.lib
+pcl_segmentation.lib
+pcl_segmentationd.lib
+pcl_stereo.lib
+pcl_stereod.lib
+pcl_surface.lib
+pcl_surfaced.lib
+pcl_tracking.lib
+pcl_trackingd.lib
+pcl_visualization.lib
+pcl_visualizationd.lib
+```
+
+`vtk`库分为`debug`/`release`版本。如下是`release`版本。这里的步骤以`release`版本举例。
+![](./img/16_15_viewer_lib.png)
+
+```
+vtkChartsCore-8.2.lib
+vtkCommonColor-8.2.lib
+vtkCommonComputationalGeometry-8.2.lib
+vtkCommonCore-8.2.lib
+vtkCommonDataModel-8.2.lib
+vtkCommonExecutionModel-8.2.lib
+vtkCommonMath-8.2.lib
+vtkCommonMisc-8.2.lib
+vtkCommonSystem-8.2.lib
+vtkCommonTransforms-8.2.lib
+vtkDICOMParser-8.2.lib
+vtkDomainsChemistry-8.2.lib
+vtkDomainsChemistryOpenGL2-8.2.lib
+vtkdoubleconversion-8.2.lib
+vtkexodusII-8.2.lib
+vtkexpat-8.2.lib
+vtkFiltersAMR-8.2.lib
+vtkFiltersCore-8.2.lib
+vtkFiltersExtraction-8.2.lib
+vtkFiltersFlowPaths-8.2.lib
+vtkFiltersGeneral-8.2.lib
+vtkFiltersGeneric-8.2.lib
+vtkFiltersGeometry-8.2.lib
+vtkFiltersHybrid-8.2.lib
+vtkFiltersHyperTree-8.2.lib
+vtkFiltersImaging-8.2.lib
+vtkFiltersModeling-8.2.lib
+vtkFiltersParallel-8.2.lib
+vtkFiltersParallelImaging-8.2.lib
+vtkFiltersPoints-8.2.lib
+vtkFiltersProgrammable-8.2.lib
+vtkFiltersSelection-8.2.lib
+vtkFiltersSMP-8.2.lib
+vtkFiltersSources-8.2.lib
+vtkFiltersStatistics-8.2.lib
+vtkFiltersTexture-8.2.lib
+vtkFiltersTopology-8.2.lib
+vtkFiltersVerdict-8.2.lib
+vtkfreetype-8.2.lib
+vtkGeovisCore-8.2.lib
+vtkgl2ps-8.2.lib
+vtkglew-8.2.lib
+vtkGUISupportMFC-8.2.lib
+vtkhdf5-8.2.lib
+vtkhdf5_hl-8.2.lib
+vtkImagingColor-8.2.lib
+vtkImagingCore-8.2.lib
+vtkImagingFourier-8.2.lib
+vtkImagingGeneral-8.2.lib
+vtkImagingHybrid-8.2.lib
+vtkImagingMath-8.2.lib
+vtkImagingMorphological-8.2.lib
+vtkImagingSources-8.2.lib
+vtkImagingStatistics-8.2.lib
+vtkImagingStencil-8.2.lib
+vtkInfovisCore-8.2.lib
+vtkInfovisLayout-8.2.lib
+vtkInteractionImage-8.2.lib
+vtkInteractionStyle-8.2.lib
+vtkInteractionWidgets-8.2.lib
+vtkIOAMR-8.2.lib
+vtkIOAsynchronous-8.2.lib
+vtkIOCityGML-8.2.lib
+vtkIOCore-8.2.lib
+vtkIOEnSight-8.2.lib
+vtkIOExodus-8.2.lib
+vtkIOExport-8.2.lib
+vtkIOExportOpenGL2-8.2.lib
+vtkIOExportPDF-8.2.lib
+vtkIOGeometry-8.2.lib
+vtkIOImage-8.2.lib
+vtkIOImport-8.2.lib
+vtkIOInfovis-8.2.lib
+vtkIOLegacy-8.2.lib
+vtkIOLSDyna-8.2.lib
+vtkIOMINC-8.2.lib
+vtkIOMovie-8.2.lib
+vtkIONetCDF-8.2.lib
+vtkIOParallel-8.2.lib
+vtkIOParallelXML-8.2.lib
+vtkIOPLY-8.2.lib
+vtkIOSegY-8.2.lib
+vtkIOSQL-8.2.lib
+vtkIOTecplotTable-8.2.lib
+vtkIOVeraOut-8.2.lib
+vtkIOVideo-8.2.lib
+vtkIOXML-8.2.lib
+vtkIOXMLParser-8.2.lib
+vtkjpeg-8.2.lib
+vtkjsoncpp-8.2.lib
+vtklibharu-8.2.lib
+vtklibxml2-8.2.lib
+vtklz4-8.2.lib
+vtklzma-8.2.lib
+vtkmetaio-8.2.lib
+vtkNetCDF-8.2.lib
+vtkogg-8.2.lib
+vtkParallelCore-8.2.lib
+vtkpng-8.2.lib
+vtkproj-8.2.lib
+vtkpugixml-8.2.lib
+vtkRenderingAnnotation-8.2.lib
+vtkRenderingContext2D-8.2.lib
+vtkRenderingContextOpenGL2-8.2.lib
+vtkRenderingCore-8.2.lib
+vtkRenderingExternal-8.2.lib
+vtkRenderingFreeType-8.2.lib
+vtkRenderingGL2PSOpenGL2-8.2.lib
+vtkRenderingImage-8.2.lib
+vtkRenderingLabel-8.2.lib
+vtkRenderingLOD-8.2.lib
+vtkRenderingOpenGL2-8.2.lib
+vtkRenderingVolume-8.2.lib
+vtkRenderingVolumeOpenGL2-8.2.lib
+vtksqlite-8.2.lib
+vtksys-8.2.lib
+vtktheora-8.2.lib
+vtktiff-8.2.lib
+vtkverdict-8.2.lib
+vtkViewsContext2D-8.2.lib
+vtkViewsCore-8.2.lib
+vtkViewsInfovis-8.2.lib
+vtkzlib-8.2.lib
+```
+
+- 设置如下编译选项,避免不必要的编译错误。(与`demo_online`一样,设置`_CRT_SECURE_NO_WARNINGS`选项)
+![](./img/16_16_viewer_precompile_macro.png)
+
+```
+BOOST_USE_WINDOWS_H
+NOMINMAX
+_CRT_SECURE_NO_DEPRECATE
+```
+
+### 16.3.5 编译及运行
+
+编译`demo_online`工程的`X64 release`版本,并运行。
+![](./img/16_17_viewer_run.png)
+

+ 98 - 0
rslidar/rs_driver/doc/howto/17_how_to_avoid_packet_loss.md

@@ -0,0 +1,98 @@
+# 17 **How to avoid Packet Loss**
+
+
+
+## 17.1 Overview
+
+This document illustrate why and what case packet loss happens, and bring out a way to avoid that.
+
+
+
+## 17.2 Data flow of LiDARs
+
+### 17.2.1 Mechanical LiDARs
+
+If the rotate speed is `600` rpm, it cost 01. second per round.
+
+Take RS128 as an example. Its scan interval is `55.55`us, so it scans in a round.
+
+```c++
+ 0.1 * 1000,000/55.55 = 1800.18
+```
+
+Approximately `1800`. A MSOP Packet is `1248` bytes, which consists of `3` blocks ( 1 block for 1 scan).
+
+So data flow per frame is:
+
+```c++
+1800 / 3 * 1248 = 748,800 (bytes)
+```
+
+This is big. However Mechanical LiDARs send MSOP packet smoothly, so packet loss is rare.
+
+
+
+### 17.2.2 MEMS LiDAR
+
+M1 LiDAR finish a frame in `0.1` second too. A frame is from `630` MSOP packets. The packet length is `1210` bytes. So the data flow per frame is:
+
+```c++
+630 * 1210 = 762,300 (bytes)
+```
+
+M1 data flow is a little more than RS128.  However M1 may send many packet in a time. For example, it may send 630 packets in 10 times, 63 packet each send. Thus the possibility of loss and out of order is high.
+
+
+
+## 17.3 Is there packet loss ? 
+
+Run demo app`demo_online`, and check if it prints normal count of points.
++ Mechenical LiDARs should have a count close with its theoretical count. If it jump up and down, packet loss may happen.
++ M1 LiDAR should always have a count equal with its theoretical count, else packet loss happens.
+
+To observe multiple LiDARs case, open multiple terminals, and run multiple `demo_online`. Check if every LiDARs prints normally.
+
+
+
+## 17.4 In what cases Packet Loss happens
+
+Packet loss may happens in below cases.
++ on some platforms, such as Windows and embedded Linux
++ Multiple LiDARs
++ CPU resources is busy.
+
+
+
+## 17.5 Solution
+
+The solution is to increase the receiving buffer of MSOP Packet Socket.
+
+in `CMakeLists.txt`,CMake macro `ENABLE_DOUBLE_RCVBUF` enable this feature.
+
+```cmake
+option(ENABLE_DOUBLE_RCVBUF       "Enable double size of RCVBUF" OFF)
+```
+
+The code is as below.  Here it increases the buffer to 4 times. Please test it in your cases, and change it to a good value.
+
+```c++
+#ifdef ENABLE_DOUBLE_RCVBUF
+  {
+    uint32_t opt_val;
+    socklen_t opt_len = sizeof(uint32_t);
+    getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&opt_val, &opt_len);
+    opt_val *= 4;
+    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&opt_val, opt_len);
+  }
+#endif
+```
+
+On some platforms, such as Ubuntu, the maximum value of the receiving buffer is restricted, and setsockopt() may fails. 
+
+To enlarge the maximum value, run this command. It change the value to 851,968 bytes.
+
+
+```shell
+sudo bash -c "echo 851968 > /proc/sys/net/core/rmem_max"
+```
+

+ 98 - 0
rslidar/rs_driver/doc/howto/17_how_to_avoid_packet_loss_CN.md

@@ -0,0 +1,98 @@
+# **17 如何解决丢包问题**
+
+
+
+## 17.1 概述
+
+本文从分析雷达数据量入手,分析丢包原因,并给出解决丢包问题的方法。
+
+
+
+## 17.2 雷达数据量分析
+
+### 17.2.1 机械式雷达
+
+按照转速`600`圈/分钟算,雷达转一圈为`0.1`秒。
+
+以RS128雷达为例。它每轮扫描的间隔是`55.55`微秒,每一圈扫描的次数为:
+
+```c++
+ 0.1 * 1000,000/55.55 = 1800.18(次)
+```
+
+取整为`1800`轮扫描。每个MSOP Packet为`1248`个字节,其中包括`3`个Block(每个Block对应一次扫描)。
+
+所以每帧对应的数据量大小为:
+
+```c++
+1800 / 3 * 1248 = 748,800(字节)
+```
+
+这个数据量比较大,但由于解析式雷达发送MOSP Pakcet是持续均匀的,所以丢包少见。
+
+
+
+### 17.2.2 MEMS雷达
+
+M1雷达完成一帧也是`0.1`秒。它的一帧中包括`630`个MSOP Packet,每个Packet为`1210`字节。所以每帧的数据量大小为
+
+```c++
+630 * 1210 = 762,300(字节)
+```
+
+M1比RS128的每帧数据量略大一点,但是因为M1发送MSOP Packet不是理想的均匀发送,比如`630`个Packet可能分`10`次发送,每次发送`63`个包,这样的丢包和乱序的风险就明显增加了。
+
+
+
+## 17.3 如何确定是否丢包
+
+运行示例程序`demo_online`,观察每帧的点数是否正常。
++ 机械式雷达的每帧点数应该接近理论点数。如果有大幅波动,则可能是丢包了。
++ M1雷达的每帧点数应该就是理论点数,是固定的。如果少了,则可能是丢包了。
+
+要观察多雷达的情况,可以打开多个终端,分别运行多个`demo_online`的实例,看看每个雷达有没有丢包。
+
+
+
+## 17.4 丢包原因
+
+以下情况下可能丢包。
++ 在某些平台上,如Windows和嵌入式Linux平台
++ 多雷达的情况下
++ 系统CPU资源紧张时
+
+
+
+## 17.5 解决办法
+
+解决丢包的办法是将接收MSOP Packet的Socket的接收缓存增大。
+
+在`rs_driver`工程的`CMakeLists.txt`中,宏`ENABLE_DOUBLE_RCVBUF`可以使能这个特性。
+
+```cmake
+option(ENABLE_DOUBLE_RCVBUF       "Enable double size of RCVBUF" OFF)
+```
+
+代码如下,这里将接收缓存增大为原来的4倍。建议在实际场景下测试,再根据测试结果调整为合适的值。
+
+```c++
+#ifdef ENABLE_DOUBLE_RCVBUF
+  {
+    uint32_t opt_val;
+    socklen_t opt_len = sizeof(uint32_t);
+    getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&opt_val, &opt_len);
+    opt_val *= 4;
+    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&opt_val, opt_len);
+  }
+#endif
+```
+
+在某些平台下,如Ubuntu,系统设置了接收缓存的最大值。如果`rs_driver`设置的值超过了最大值,调用setsockopt()就不会成功。
+
+这时需要手工放宽这个限制,如下面的指令,将这个最大值改为`851,968`个字节。
+
+
+```shell
+sudo bash -c "echo 851968 > /proc/sys/net/core/rmem_max"
+```
+

+ 187 - 0
rslidar/rs_driver/doc/howto/18_about_point_layout.md

@@ -0,0 +1,187 @@
+
+# 18 **Point Type and Point Layout**
+
+
+
+## 18.1 Overview
+
+This document describes:
+
++ the member variables of point and point cloud, and
++ what they really means
+
+
+
+## 18.2 Point And Point Cloud
+
+### 18.2.1 Point
+
+The member variables of point are as below. 
+
+```c++
+struct PointXYZIRT
+{
+  float x;
+  float y;
+  float z;
+  uint8_t intensity;
+  uint16_t ring;
+  double timestamp;
+};
+```
+
+### 18.2.2 Point Cloud
+
+The member variables of point cloud are as below. Its member `points` is a `vector` of points.
+
+```c++
+template <typename T_Point>
+class PointCloudT
+{
+public:
+  typedef T_Point PointT;
+  typedef std::vector<PointT> VectorT;
+
+  uint32_t height = 0;    ///< Height of point cloud
+  uint32_t width = 0;     ///< Width of point cloud
+  bool is_dense = false;  ///< If is_dense is true, the point cloud does not contain NAN points
+  double timestamp = 0.0; ///< Timestamp of point cloud
+  uint32_t seq = 0;       ///< Sequence number of point cloud
+
+  VectorT points;
+};
+```
+
+
+
+## 18.3 Member `ring` of Point
+
+### 18.3.1 Mechanical LiDAR
+
+In design of mechanical LiDAR, its channels are numbered by scan order. However, this order is not a ascending/descending order, so vertical angles of channels are not either.   
+
+`rs_driver` sorts the channels by their vertical angels in ascending. The member `ring`  is such a  sorted number.
+![](./img/18_01_mech_lasers.png)
+
+
+### 18.3.2 MEMS LiDAR
+
+MEMS LiDAR scan 5 zone per scan, and get 5 points. Their timestamps are same. 
+
+The member `ring`  is the number of zone.
+
+
+![](./img/18_02_mems_lasers.png)
+
+
+
+## 18.4 Point Layout
+
+### 18.4.1 Mechanical LiDAR
+
+Take RS32 as an example.
+
+It gets 32 points per scan, 1 point per channel. These 32 points is packed into a `Block`, and written into MSOP packet.
+
+After a round of scan, LiDAR get a point set on a loop surface.
+![](./img/18_03_mech_lasers_and_points.png)
+
+
+
+MSOP packet consists of `Block`. `rs_driver` parses `Block`s one by one, and saves their points group by group. The points are in the member `points` of point cloud. LiDARs scans the channels, and `rs_driver` save their points in the same order.
+
+To traverse all points in a channel, just skip 32 points every time.
+![](./img/18_04_mech_points.png)
+
+### 18.4.2 MEMS LiDAR
+
+MEMS LiDAR scans 5 zones per scan, and get 5 points, and packs them into a `Block`, write into MSOP packet.
+
+`rs_driver`parses `Block`s one by one, 5 points in a `Block`is saved as a group in the member `points`  of point cloud.
+![](./img/18_05_mems_lasers_and_points.png)
+
+
+
+Here is the point cloud in each zone. 
+
+The zone is `126` lines x `125` columns. LiDAR scan from up to down in Z order. `rs_driver` same these points in the same order.
+![](./img/18_06_mems_points.png)
+
+
+
+## 18.5 Coordinate of points
+
+`rs_driver`comply with the right-handed coordinate system, because RoboSense LiDAR first apply on ROS, and ROS follows it.
+
+To change this, change mapping relationship of (x, y, z) as below. 
+
+```c++
+//.../decoder/decoder_RSM1.hpp
+
+    int pitch = ntohs(channel.pitch) - ANGLE_OFFSET;
+    int yaw = ntohs(channel.yaw) - ANGLE_OFFSET;
+
+    float x = distance * COS (pitch) * COS (yaw);
+    float y = distance * COS (pitch) * SIN (yaw);
+    float z = distance * SIN (pitch);
+```
+
+
+
+## 18.6  Member `timestamp` of Point
+
+Mechanical LiDAR (600 rpm) and MEMS LiDAR finish a frame in 0.1 second. Its points scatter into this time range.
+
+If you are not satisfied with timestamp of point cloud, please use  the point type of `XYZIRT`. `T` is the timestamp of point.
+
+
+
+## 18.7 member `timestamp` of Point Cloud
+
+`timestamp` of point cloud is from its points.
+
+
+
+### 18.7.1 From LiDAR, or From Host ?
+
+`rs_driver`may get `timestamp` of point cloud from LiDAR or from host.
+
++ From MSOP packet.  LiDAR set set the stampstamp based on its own system clock. Generally, user synchronizes LiDAR's clock with the PTP protocol. 
++ Invoke system API. With it, `rs_driver` gets the local system tme. This is the default.
+
+Use the option `use_lidar_clock` to change this.
+
+```c++
+struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  ...
+  bool use_lidar_clock = false;  ///< true: use LiDAR clock as timestamp; false: use system clock as timestamp
+  ...
+}
+```
+
+### 18.7.2 The first point, or The last point ?
+
+The default value is from the last point.
+
+Use the option `ts_first_point` to change this.
+
+```c++
+struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  ...
+  bool ts_first_point = false;   ///< true: time-stamp point cloud with the first point; false: with the last point;
+  ...
+}
+```
+
+### 18.7.3 UTC time or Local time ?
+
+LiDAR writes UTC time into MSOP packets.
+
+In `CMakeLists.txt`,  enable the macro `ENABLE_STAMP_WITH_LOCAL` to convert it to local time.
+
+```cmake
+option(ENABLE_STAMP_WITH_LOCAL    "Enable stamp point cloud with local time" OFF)
+```
+

+ 184 - 0
rslidar/rs_driver/doc/howto/18_about_point_layout_CN.md

@@ -0,0 +1,184 @@
+
+# 18 **点云格式与点布局**
+
+
+
+## 18.1 概述
+
+这里先回顾点及点云的定义,看看有哪些属性,再说明这些属性的确切含义。
+
+
+
+## 18.2 点云格式
+
+### 18.2.1 点
+
+点的属性如下。这里列出的是`XYZIRT`类型,它包括了`XYZI`类型的所有属性。
+
+```c++
+struct PointXYZIRT
+{
+  float x;
+  float y;
+  float z;
+  uint8_t intensity;
+  uint16_t ring;
+  double timestamp;
+};
+```
+
+### 18.2.2 定义点云类型
+
+点云的属性如下。它的成员`points`是一个点的`vector`。
+
+```c++
+template <typename T_Point>
+class PointCloudT
+{
+public:
+  typedef T_Point PointT;
+  typedef std::vector<PointT> VectorT;
+
+  uint32_t height = 0;    ///< Height of point cloud
+  uint32_t width = 0;     ///< Width of point cloud
+  bool is_dense = false;  ///< If is_dense is true, the point cloud does not contain NAN points
+  double timestamp = 0.0; ///< Timestamp of point cloud
+  uint32_t seq = 0;       ///< Sequence number of point cloud
+
+  VectorT points;
+};
+```
+
+
+
+## 18.3 点的ring
+
+### 18.3.1 机械式雷达
+
+机械式雷达的通道内部编号是按照激光扫描顺序排列的,但是这个扫描顺序并不是一直从下向上(或从上向下),所以按照这个编号的顺序,通道的垂直角不是递增(或递减)的。
+
+`rs_driver`根据通道的垂直角,对它们从小到大(升序)重新编号。点的`ring`属性是这样重新排列后的编号。
+![](./img/18_01_mech_lasers.png)
+
+
+### 18.3.2 MEMS雷达
+
+MEMS雷达每轮扫描同时扫`5`个区域,得到`5`个点,这`5`个点有一样的时间戳。
+
+点的`ring`是点所在区域的编号。
+![](./img/18_02_mems_lasers.png)
+
+
+
+## 18.4 点的布局
+
+### 18.4.1 机械式雷达
+
+这里以RS32雷达为例。
+
+雷达每轮扫描得到`32`个点,每个通道一个点。这`32`个点打包到一个`Block`,写入MSOP Packet。
+
+当雷达马达旋转一圈时,就得到环形面上一个点集合。
+![](./img/18_03_mech_lasers_and_points.png)
+
+
+
+`rs_driver`解析MSOP Packet时,按照`Block`的顺序解析。在点云的`points`数组中,每个`Block`的`32`个点连续保存为一组。
+
+如果要顺序访问同一通道上的点,只需要每次跳过32个点就好了。
+
+如前所述,在`Block`内,`32`个点的排列是按照扫描的顺序保存的,它们在`points`中排列也是这个顺序。
+![](./img/18_04_mech_points.png)
+
+### 18.4.2 MEMS雷达
+
+MEMS雷达每轮扫描同时扫`5`个区域,得到`5`个点,打包到一个`Block`,写入MSOP Packet。
+
+`rs_driver`解析MSOP Packet时,按照`Block`的顺序解析,所以在点云的`points`数组中,每个Block的`5`个点连续保存为一组。
+![](./img/18_05_mems_lasers_and_points.png)
+
+
+
+这里以M1为例,说明每个区域的点布局。
+
+M1的每个区域`126`行 x `125`列,从上往下呈Z字型扫描。在点云的`points`数组中,点的排列也是这个顺序。
+![](./img/18_06_mems_points.png)
+
+
+
+## 18.5 点的坐标系
+
+`rs_driver`输出的点遵循右手坐标系,这主要是历史原因。RoboSense雷达一开始应用于ROS上,而ROS采用右手坐标系。
+
+如果您想改变这一点,可以改变雷达解码器`Decoder`的实现代码,改变坐标(x,y,z)的映射关系。如下是M1 Decoder的例子。
+
+```c++
+//.../decoder/decoder_RSM1.hpp
+
+    int pitch = ntohs(channel.pitch) - ANGLE_OFFSET;
+    int yaw = ntohs(channel.yaw) - ANGLE_OFFSET;
+
+    float x = distance * COS (pitch) * COS (yaw);
+    float y = distance * COS (pitch) * SIN (yaw);
+    float z = distance * SIN (pitch);
+```
+
+
+
+## 18.6 点的timestamp
+
+机械式雷达(转速等于600圈/分钟时)和MEMS雷达扫描一帧的时间是100毫秒。点云中的点散布在这100毫秒的范围内。
+
+如果对点云的timestamp精度不满意,可以考虑使用`XYZIRT`点格式,它的`T`是点的时间戳。
+
+
+
+## 18.7 点云的timestamp
+
+点云的时间戳,是给整个点云的时间戳。这个时间戳来自它的点。
+
+
+
+### 18.7.1 雷达的时间,还是主机的时间?
+
+`rs_driver`有两种方式得到点云的时间戳。
+
++ 从MSOP Packet解析。这个时间是雷达根据自身的时间设置。一般需要对雷达进行PTP时间同步,以保证雷达的时间与系统内的其他传感器保持一致。
++ 调用系统函数,得到`rs_driver`运行的这台主机的时间。这是`rs_driver`的默认配置。
+
+`rs_driver`的选项`use_lidar_clock`可以改变这个配置。
+
+```c++
+struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  ...
+  bool use_lidar_clock = false;  ///< true: use LiDAR clock as timestamp; false: use system clock as timestamp
+  ...
+}
+```
+
+### 18.7.2 取第一个点的时间,还是最后一个点的?
+
+默认情况下,取最后一个点的时间。
+
+`rs_driver`的选项`ts_first_point`可以改变这个配置。
+
+```c++
+struct RSDecoderParam  ///< LiDAR decoder parameter
+{
+  ...
+  bool ts_first_point = false;   ///< true: time-stamp point cloud with the first point; false: with the last point;
+  ...
+}
+```
+
+### 18.7.3 UTC时间还是本地时间?
+
+雷达写入MSOP Packet的时间是UTC时间。
+
+`rs_driver`工程的`CMakeLists.txt`中的`ENABLE_STAMP_WITH_LOCAL`宏,可以根据本地的时区,将这个时间转换成本地时间。
+
+```cmake
+option(ENABLE_STAMP_WITH_LOCAL    "Enable stamp point cloud with local time" OFF)
+```
+

+ 121 - 0
rslidar/rs_driver/doc/howto/19_about_splitting_frame.md

@@ -0,0 +1,121 @@
+# 19 **Splitting Rule**
+
+
+
+## 19.1  Overview
+
+This document illustrates how RoboSense LiDAR splits frame. Mechanical LiDARs and MEMS LiDARs have different splitting rules. 
+
+This document imports some concepts from below documents.
+
+[How_to avoid packet loss](./17_how_to_avoid_packet_loss.md)
+
+[About point layout](./18_about_point_layout.md)
+
+
+
+## 19.2  Mechanical LiDAR
+
+Mechanical LiDAR rotates and sends out points. `rs_driver` splits them by an angle. Everytime`rs_driver` gets a frame. This angle's default value is `0`°. User may change it with the option `RSDecoderParam.split_angle`. 
+![split angle](./img/19_01_split_angle.png)
+
+For per scan, LiDAR gets a group of points, one point per channel. `rs_driver` packs them into a `Block`, and writes it into MSOP packet. 
+
+`Block` holds the angle. So called "splitting frame", is to check if the angle across the splitting angle. If so, this group belongs to the next frame, and the older groups belongs to the previous one. 
+
+### 19.2.1 Deviation of Splitting by angle
+
+`Block`'s angle is from its first point, and for latter points, need to plus a offset. These points may across the splitting angle. Around this angle, points may lose or repeat.
+
+Let's estimate the angle span of `Block`. 
+
+With  `600` rpm, seconds per round is:
+
+```c++
+1 / (600 / 60) = 0.1 (second)
+```
+
+Generally, a scan takes 50~60 us. Take RS16 as an example, it takes `55.5` us. scans per round is:
+
+```c++
+ 0.1 * 1000000 / 55.5 = 1801.8
+```
+
+Then the angle span of `Block` is:
+
+```c++
+360 / 1801.8 = 0.1998 (degree)
+```
+
+Then the deviation by angle is `0.2` degree.
+
+### 19.2.2 Deviation of Splitting by time
+
+Seconds per scans is `50`~`60` us. This is the deviation by time.
+
+### 19.2.3 points per frame vary
+
+Take RS16 as an example. 
+
+With `600` rpm, it takes `1801.8` scans per round. Round up to `1802` times. Then points per frame is:
+
+```
+16 * 1802 = 28,832 (points)
+```
+
+LiDAR doesn't  rotate smoothly. takes more scans if faster, and takes less less if slower.
+
+The difference is the laser number of LiDAR. For RS16, it is `16`. 
+
+### 19.2.4 Packet Loss and Out of Order
+
+Mechanical LiDAR sends MSOP packets smoothly. The time interval between two packets is enough, so packet loss and out of order is rare.
+
+
+
+## 19.3 MEMS LiDAR
+
+How MEMS LiDAR splits frame, is actually determined on the LiDAR end.
+
+It scans `5` zones, gets 5 points per scan, packs into a `Block`, and writes into MSOP packet. 
+
+In each zone, scan in the Z order. And then get a group of MSOP packets. 
+
+Number these packets from 1.  Take M1 as an example,  the numbers are `1`~`630`.
+
+What left for `rs_driver` to do, is split packets by the numbers. 
+
+### 19.3.1 Packet Loss and Out of Order
+
+MEMS LiDAR may send multiple MSOP packets at the same time, so packet loss and out of order may happen.
+
+Increase socket buffer is useful if packet loss happens on host, but make no help if it happens between LiDARs and host.
+
+`rs_driver` use `Safe Range` to handle this.
+
+
+
+First image how to handle this without `Safe Range`.
+
++ No packet loss or out of order.  Just check if the packet number is `1`. Split if so.
++ Packet loss, no out of order. For example, packet `1` is losed. Just add check if packet number is less than the previous. Split if so. 
++ Out of order. For example, packet `301` reaches before packet `300`, then this frame is splitted into two.  Dilemma.
+
+
+
+Consider `Safe Range` as below.
+
++ Give a range `RANGE` around `prev_seq`, which is the previous packet number.
+
+```c++
+safe_seq_min = prev_seq_ - RANGE
+safe_seq_max = prev_seq_ + RANGE
+```
+
++ If MSOP packet number is in (`safe_seq_min_`, `safe_seq_max_`), accept it and don't split.
+![safe range](./img/19_02_safe_range.png)
+
+Slight packet loss and out of order is tolerated. 
+
+
+

+ 121 - 0
rslidar/rs_driver/doc/howto/19_about_splitting_frame_CN.md

@@ -0,0 +1,121 @@
+# 19 **分帧策略**
+
+
+
+## 19.1  概述
+
+本文分别说明机械式雷达和MEMS的分帧策略。因为工作原理不同,所以它们的分帧策略也不同。
+
+本文引用了如下一些文档的概念,请阅读。
+
+[如何解决丢包问题](./17_how_to_avoid_packet_loss_CN.md)
+
+[点在点云中的布局](./18_about_point_layout_CN.md)
+
+
+
+## 19.2  机械式雷达
+
+机械式雷达绕轴持续`360`°旋转扫描,并输出点,`rs_driver`按指定角度分割这些点,每一次分割得到一圈点云,也就是一帧。
+
+这个分帧角度的默认值为`0`°。`rs_driver`的使用者,可以通过配置选项`RSDecoderParam.split_angle`指定这个角度。
+![split angle](./img/19_01_split_angle.png)
+
+每一轮扫描得到一组点,每个通道一个点,打包到一个`Block`,写入MSOP Packet中。`Block`保存这一组点的水平角。
+
+所谓分帧,就是检查这个水平角是不是跨过了分帧角度,如果跨过了,这一组点就属于下一帧,而之前的点就可以打包到上一帧了。
+
+### 19.2.1 分帧的水平角误差
+
+`Block`的水平角是它第一个点的时间戳。由于雷达一直在转,后面点的水平角还要在这个基础上加上一个偏移值。这样它们就可能跨过分帧角度,进而导致点云在分帧角度附近重复点或遗漏点。
+
+这里对`Block`跨过的水平角度估算一下,看看影响有多大。
+
+如果转速为`600`圈/分钟,那么每圈的时间为:
+
+```c++
+1 / (600 / 60) = 0.1 (秒) 
+```
+
+机械式雷达每轮扫描之间的时间差一般是`50`~`60`微秒。以RS16为例,它的扫描间隔为`55.5`微秒。那么一圈的扫描轮数是:
+
+```c++
+ 0.1 * 1000000 / 55.5 = 1801.8 (次)
+```
+
+`Block`的水平角跨度为:
+
+```c++
+360 / 1801.8 = 0.1998 (度)
+```
+
+这就是说,分帧角度附近重复或遗漏点的最大水平角跨度大约是`0.2`度。
+
+### 19.2.2 分帧的时间误差
+
+机械式雷达每轮扫描之间的时间差一般是`50`~`60`微秒。这也就是分帧的时间误差。
+
+### 19.2.3 每帧的点数是波动的
+
+以RS16为例。在`600`圈/分钟的情况下,一圈扫描`1801.8`次,取整`1802`次,那就有
+
+```c++
+16 * 1802 = 28832 (点)
+```
+
+但是雷达马达的旋转并不是这么匀速。转得快一点,每圈就少扫描几轮,慢一点,每圈就多扫描几轮。
+
+因为每轮扫描的点数是雷达的线数,所以少/多的点数是雷达线数的整数倍。对于RS16,就是`16`的整数倍。
+
+### 19.2.4 丢包与乱序处理
+
+机械式雷达均匀发送MSOP Packet,每次发送一个Packet,两个Packet的时间间隔较大,所以暂时没有发现丢包与乱序需要处理。
+
+
+
+## 19.3 MEMS雷达
+
+MEMS雷达的分帧,本质上在雷达端就确定了。
+
+雷达有`5`个扫描区域,每轮扫秒同时扫描这`5`个区域,得到`5`个点,打包到一个`Block`,写入MSOP Packet。
+
+在每个区域,按照Z字型进行扫描。这样一直继续,直到完成全部区域,得到一组MSOP Packet。
+
+这组Packet按扫描顺序从1开始编号。以M1雷达为例,编号为`1`~`630`。
+
+留给`rs_driver`做的,只是按照这些编号分割MSOP Packet。
+
+### 19.3.1 丢包与乱序处理
+
+MEMS雷达可能同时发送多个MSOP Packet,这样丢包和乱序的风险就变大了。
+
+主机端的丢包,可以通过增大Socket接收缓存解决,但是从雷达到主机之间的丢包、乱序还是可能存在。
+
+`rs_driver`引入`安全区间`的做法,来处理可能的丢包和乱序。
+
+
+
+先讨论没有`安全区间`时,如何处理丢包、乱序。
+
++ 理想情况下,如果不丢包不乱序,MSOP Packet编号从`1`到`630`, 只需要检查Packet编号是不是`1`。如果是就分帧。
++ 那假如只有丢包呢?举个例子,如果编号为`1`的Packet丢了,则可以加入检查条件,就是当前Packet编号小于前一个Packet的编号`prev_seq`,就分帧。
++ 在乱序的情况下,这个检查条件会导致另一个困境。举个例子,如果编号为`300`和`301`的两个Packet乱序,那么这个位置分帧,会导致原本的一帧拆分成两帧。
+
+
+
+`安全区间`的做法如下:
+
++ 以`prev_seq`为参考点,划定一个范围值`RANGE`, 
+
+```c++
+safe_seq_min = prev_seq_ - RANGE
+safe_seq_max = prev_seq_ + RANGE
+```
+
++ 如果MSOP Packet在范围(`safe_seq_min_`, `safe_seq_max_`)内,不算异常,不分帧。
+![safe range](./img/19_02_safe_range.png)
+
+这样轻微的乱序不会触发分帧,也就解决了前面说的困境。
+
+
+

+ 131 - 0
rslidar/rs_driver/doc/howto/20_about_usage_of_cpu_and_memory.md

@@ -0,0 +1,131 @@
+# 20 **CPU Usage and Memory Usage**
+
+
+
+## 20.1 Overview
+
+This document illustrates CPU usage and memory usage of `rs_driver`. 
+
+Please first read [thread model and interface](../intro/03_thread_model.md), since this document imports some concept from it.
+![](./img/20_01_components_and_threads.png)
+
+
+
+## 20.2 CPU Usage
+
+### 20.2.1 What cost CPU resources ?
+
+CPU usage is mainly from two factors.
+
++ The handing thread `process_thread` parses MSOP/DIFOP Packet, and creates point cloud.
++ While handling MSOP/DIFOP packets, `process_thread` loops in the routine of `waken` ->`sleep` ->`waken`. 
+  + This routine cost much CPU resource, because MSOP packets are frequent but not continuous. `process_thread` switches too many times.
+
+
+
+### 20.2.2 Solution
+
+CMake macro `ENABLE_WAIT_IF_QUEUE_EMPTY` may lower CPU usage, but increase the latency of point cloud. 
+
+Actually it make `process_thread` sleep a little longer, so the thread handle more packets every time, and switches less.
+
+
+```cmake
+option(ENABLE_WAIT_IF_QUEUE_EMPTY "Enable waiting for a while in handle thread if the queue is empty" OFF)
+```
+
+With this option enabled, `process_thread` calls usleep() instead of waiting for condition_variable.
+
+Based on a test on a specific platform, this macro lowers CPU usage from `25%` to `22%`.
+
+```c++
+#ifdef ENABLE_WAIT_IF_QUEUE_EMPTY
+  
+    ...
+
+    std::this_thread::sleep_for(std::chrono::microseconds(1000));
+    return value;
+#else
+
+    ...
+    
+    std::unique_lock<std::mutex> ul(mtx_);
+    cv_.wait_for(ul, std::chrono::microseconds(usec), [this] { return (!queue_.empty()); });
+
+    ...
+    
+    return value;
+#endif
+```
+
+
+
+Coins have two sides. The disadvantage is:
+
+usleep() is not definite.  If sleep() acrosses the splitting point, such as
+
++ Mechanical LiDAR, `Block`'s angle across the splitting angle
++ MEMS LiDAR, For M1, packet number across `630`
+
+
+splitting is delayed, and then user "see" this frame later. 
+
+
+
+## 20.3 Memory Usage
+
+### 20.3.1 MSOP/DIFOP Packet Queue
+
+MSOP/DIFOP Packet queue: `free_pkt_queue` and `stuffed_pkt_queue`.
+
+  + Packets in these queues is allocated on demand. The count depends on LiDAR type and user case.
+  + `rs_driver` never releases these packets. 
+
+### 20.3.2 Point Cloud Queue
+
+Point cloud is allocated by user. It is not discussed here.
+
+### 20.3.3 Calculate sin()/cos() by table
+
++ Mechanical LiDAR calculates sin()/cos() by table. Class `Trigon` wrap this work. It is a member of class `Decoder`.
+
+```c++
+template <typename T_PointCloud>
+class Decoder
+{
+  ...
+  Trigon trigon_;
+  ...
+};
+```
+
+`Trigon`Calculates sin/cos values in the range (-90, 450), with unit `0.01`degree. The size of two arrays is:
+
+```c++
+(450 - (-90)) / 0.01 * sizeof(float) * 2 = 432,000 (bytes)
+```
+
++ MEMS LiDAR
+  + M1 use tables and `Trigon` too. It takes same size of memory as mechanical LiDAR does.
+  + M2 doesn't need tables. No memory usage for this.
+
++ To connect to multiple LiDARs,  each  `rs_driver` instance has its own `Trigon` instance. Each instance occupies `432,000` bytes.
+
+### 20.3.4 Socket Receiving Buffer
+
+A hidden memory usage is Socket Receiving Buffer。
++ Each of MSOP/DIFOP Packets has their own sockets.
++ It may be increased in case of packet loss
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 133 - 0
rslidar/rs_driver/doc/howto/20_about_usage_of_cpu_and_memory_CN.md

@@ -0,0 +1,133 @@
+# 20 **CPU占用与内存占用**
+
+## 20.1 概述
+
+本文说明`rs_driver`对CPU和内存的使用。
+
+请先阅读[rs_driver的线程模型与接口设计](../intro/03_thread_model_CN.md),因为本文引用了其中的概念。
+![](./img/20_01_components_and_threads.png)
+
+
+
+## 20.2 CPU占用
+
+### 20.2.1 CPU占用的来源
+
+`rs_driver`的CPU占用主要来自两个方面:
+
++ MSOP/DIFOP Packet的处理线程`process_thread`解析MSOP/DIFOP Packet,构建点云
++ `process_thread`等待MSOP/DIFOP Packet时,反复被`唤醒` ->`睡眠` ->`唤醒`。
+  + 这个过程CPU占用率较高,原因是Packet是频率高但不连续的,`process_thread`切换状态的次数太多了。
+
+
+
+### 20.2.2 降低CPU占用率的办法
+
+CMake编译宏 ENABLE_WAIT_IF_QUEUE_EMPTY 可以让`rs_driver`的CPU占用率降低一点,但是弊端是点云延迟会加大。
+
+它的原理是让`process_thread`每次睡眠的时间长一点,这样每次唤醒时处理的包数就多一些,反复`唤醒` ->`睡眠` ->`唤醒`的次数就少一些。
+
+
+```cmake
+option(ENABLE_WAIT_IF_QUEUE_EMPTY "Enable waiting for a while in handle thread if the queue is empty" OFF)
+```
+
+
+
+打开这个宏后,Packet处理线程会调用usleep(),而不是等待条件变量通知。
+
+在某平台的测试结果表明,这个宏可以将CPU占用率从`25%`降低到`22%`。
+
+```c++
+#ifdef ENABLE_WAIT_IF_QUEUE_EMPTY
+  
+    ...
+
+    std::this_thread::sleep_for(std::chrono::microseconds(1000));
+    return value;
+#else
+
+    ...
+    
+    std::unique_lock<std::mutex> ul(mtx_);
+    cv_.wait_for(ul, std::chrono::microseconds(usec), [this] { return (!queue_.empty()); });
+
+    ...
+    
+    return value;
+#endif
+```
+
+
+
+这里也有必要说明这样做的弊端。
+
+对于usleep()的等待时间是不确定的。如果等待跨过了点云的分帧点,比如
+
++ 机械式雷达,MSOP Packet的Block水平角跨过了分帧角度
++ MEMS雷达,比如M1的MSOP Packet序列号跨过了`630`
+
+
+这样分帧就会拖后,`rs_driver`的使用者“看”到这一帧的时间点就会推迟。
+
+所以请您根据自己的场景,权衡CPU占用和点云延迟之间的利弊,再决定是否使能这个编译宏。
+
+
+
+## 20.3 内存占用
+
+### 20.3.1 MSOP/DIFOP Packet队列
+
+`rs_driver`的内存占用主要来自MSOP/DIFOP Packet队列 `free_pkt_queue`和`stuffed_pkt_queue`。
+
+  + 这两个队列中的Packet实例是按需分配的,分配的数量与雷达类型、使用场景相关。
+  + 在`rs_driver`的实现中,它不会释放这些实例。如果有偶然的意外发生,导致Packet实例的数量异常增多,那么后面它们所占用的内存也不会降低。
+
+### 20.3.2 点云队列
+
+`rs_driver`使用的点云实例是调用者分配、管理的,所以这里不讨论点云占用的内存。
+
+### 20.3.3 查表方式计算三角函数值
+
++ 机械式雷达使用查表的方式计算三角函数值。`Trigon`类负责做这件事,它是解码器`Decoder`的成员。
+
+```c++
+template <typename T_PointCloud>
+class Decoder
+{
+  ...
+  Trigon trigon_;
+  ...
+};
+```
+
+`Trigon`计算(-90, 450)角度范围内的`sin`和`cos`值,粒度是`0.01`度,所以保存它们的两个数组大小为:
+
+```c++
+(450 - (-90)) / 0.01 * sizeof(float) * 2 = 432,000 (字节)
+```
+
++ 关于MEMS雷达,
+  + M1也需要使用查表方式计算三角函数值,它也使用`Trigon`类,占用内存大小与机械式雷达相同。
+  + M2不需要计算三角函数值,也就不需要占用这部分内存。
+
++ 如果连接多个雷达,`rs_driver`的多个实例各有自己的`Trigon`实例。也就是每个实例都占用`432,000`字节。
+
+### 20.3.4 Socket接收缓存
+
+一个比较隐蔽的内存占用,是接收MSOP/DIFOP Packet的Socket的接收缓存。
++ MSOP Packet和DIFOP Packet一般各有自己接收的Socket
++ 在丢包的情况下,可能需要增加Socket接收缓存的大小
+
+
+
+
+
+
+
+
+
+
+
+
+

BIN
rslidar/rs_driver/doc/howto/img/07_01_branches.png


BIN
rslidar/rs_driver/doc/howto/img/07_02_cpu_usage_dev.png


BIN
rslidar/rs_driver/doc/howto/img/07_03_cpu_usage_dev_opt.png


BIN
rslidar/rs_driver/doc/howto/img/09_01_broadcast.png


BIN
rslidar/rs_driver/doc/howto/img/09_02_unicast.png


BIN
rslidar/rs_driver/doc/howto/img/09_03_multicast.png


BIN
rslidar/rs_driver/doc/howto/img/09_04_multi_lidars_port.png


BIN
rslidar/rs_driver/doc/howto/img/09_05_multi_lidars_ip.png


BIN
rslidar/rs_driver/doc/howto/img/09_06_vlan_layer.png


BIN
rslidar/rs_driver/doc/howto/img/09_07_vlan.png


BIN
rslidar/rs_driver/doc/howto/img/09_08_user_layer.png


BIN
rslidar/rs_driver/doc/howto/img/12_01_select_by_port.png


BIN
rslidar/rs_driver/doc/howto/img/12_02_select_by_non_port.png


BIN
rslidar/rs_driver/doc/howto/img/12_03_rs32_msop_packet.png


BIN
rslidar/rs_driver/doc/howto/img/12_04_rs32_difop_packet.png


BIN
rslidar/rs_driver/doc/howto/img/12_05_with_vlan.png


BIN
rslidar/rs_driver/doc/howto/img/12_06_with_user_layer.png


BIN
rslidar/rs_driver/doc/howto/img/12_07_with_tail_layer.png


BIN
rslidar/rs_driver/doc/howto/img/12_08_not_supported.png


BIN
rslidar/rs_driver/doc/howto/img/13_01_wireshark_select_nic.png


BIN
rslidar/rs_driver/doc/howto/img/13_02_wireshark_capture.png


BIN
rslidar/rs_driver/doc/howto/img/13_03_wireshark_filter_by_port.png


BIN
rslidar/rs_driver/doc/howto/img/13_04_wireshark_export_all.png


BIN
rslidar/rs_driver/doc/howto/img/13_05_wireshark_export_range.png


BIN
rslidar/rs_driver/doc/howto/img/13_06_wireshark_mark.png


BIN
rslidar/rs_driver/doc/howto/img/13_07_wireshark_export_marked.png


BIN
rslidar/rs_driver/doc/howto/img/13_08_wireshark_export_marked_range.png


BIN
rslidar/rs_driver/doc/howto/img/14_01_rs_driver_viewer_point_cloud.png


BIN
rslidar/rs_driver/doc/howto/img/16_01_demo_project.png


BIN
rslidar/rs_driver/doc/howto/img/16_02_demo_use_cpp14.png


BIN
rslidar/rs_driver/doc/howto/img/16_03_demo_extra_include.png


BIN
rslidar/rs_driver/doc/howto/img/16_04_demo_include_path.png


BIN
rslidar/rs_driver/doc/howto/img/16_05_demo_lib_path.png


BIN
rslidar/rs_driver/doc/howto/img/16_06_demo_lib.png


BIN
rslidar/rs_driver/doc/howto/img/16_07_demo_precompile_macro.png


BIN
rslidar/rs_driver/doc/howto/img/16_08_viewer_install_pcl.png


BIN
rslidar/rs_driver/doc/howto/img/16_09_viewer_install_pcl_dep.png


BIN
rslidar/rs_driver/doc/howto/img/16_10_viewer_add_env_var.png


BIN
rslidar/rs_driver/doc/howto/img/16_11_viewer_project.png


BIN
rslidar/rs_driver/doc/howto/img/16_12_viewer_sdl_check.png


BIN
rslidar/rs_driver/doc/howto/img/16_13_viewer_include_path.png


BIN
rslidar/rs_driver/doc/howto/img/16_14_viewer_lib_path.png


BIN
rslidar/rs_driver/doc/howto/img/16_15_viewer_lib.png


+ 0 - 0
rslidar/rs_driver/doc/howto/img/16_16_viewer_precompile_macro.png


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff