├── .gitattributes ├── CMakeLists.txt ├── README.md ├── data ├── log │ ├── ascii-robotdata2.log │ ├── ascii-robotdata3.log │ ├── ascii-robotdata4.log │ ├── robotdata1.log │ ├── robotdata1.log.gz │ ├── robotdata2.log.gz │ ├── robotdata3.log.gz │ ├── robotdata4.log.gz │ ├── robotdata5.log │ └── robotdata5.log.gz └── map │ ├── instruct.txt │ ├── robotmovie1.gif │ ├── wean.dat │ ├── wean.dat.gz │ └── wean.gif ├── img └── MCL.gif ├── include ├── MCL_localization.h ├── load_log.h ├── load_map.h └── localization_node.h ├── launch └── mcl_localization.launch ├── package.xml ├── rviz └── localization.rviz ├── src ├── MCL_localization.cpp ├── load_log.cpp ├── load_map.cpp └── localization_node.cpp └── urdf └── three_wheels_robot.urdf /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(pf_localization) 3 | 4 | ## Compile as C++11, supported in ROS Kinetic and newer 5 | # add_compile_options(-std=c++11) 6 | 7 | ## Find catkin macros and libraries 8 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 9 | ## is used, also find other catkin packages 10 | find_package(catkin REQUIRED COMPONENTS 11 | roscpp 12 | rospy 13 | std_msgs 14 | tf 15 | sensor_msgs 16 | nav_msgs 17 | ) 18 | 19 | ## System dependencies are found with CMake's conventions 20 | # find_package(Boost REQUIRED COMPONENTS system) 21 | 22 | 23 | ## Uncomment this if the package has a setup.py. This macro ensures 24 | ## modules and global scripts declared therein get installed 25 | ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html 26 | # catkin_python_setup() 27 | 28 | ################################################ 29 | ## Declare ROS messages, services and actions ## 30 | ################################################ 31 | 32 | ## To declare and build messages, services or actions from within this 33 | ## package, follow these steps: 34 | ## * Let MSG_DEP_SET be the set of packages whose message types you use in 35 | ## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). 36 | ## * In the file package.xml: 37 | ## * add a build_depend tag for "message_generation" 38 | ## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET 39 | ## * If MSG_DEP_SET isn't empty the following dependency has been pulled in 40 | ## but can be declared for certainty nonetheless: 41 | ## * add a exec_depend tag for "message_runtime" 42 | ## * In this file (CMakeLists.txt): 43 | ## * add "message_generation" and every package in MSG_DEP_SET to 44 | ## find_package(catkin REQUIRED COMPONENTS ...) 45 | ## * add "message_runtime" and every package in MSG_DEP_SET to 46 | ## catkin_package(CATKIN_DEPENDS ...) 47 | ## * uncomment the add_*_files sections below as needed 48 | ## and list every .msg/.srv/.action file to be processed 49 | ## * uncomment the generate_messages entry below 50 | ## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) 51 | 52 | ## Generate messages in the 'msg' folder 53 | # add_message_files( 54 | # FILES 55 | # Message1.msg 56 | # Message2.msg 57 | # ) 58 | 59 | ## Generate services in the 'srv' folder 60 | # add_service_files( 61 | # FILES 62 | # Service1.srv 63 | # Service2.srv 64 | # ) 65 | 66 | ## Generate actions in the 'action' folder 67 | # add_action_files( 68 | # FILES 69 | # Action1.action 70 | # Action2.action 71 | # ) 72 | 73 | ## Generate added messages and services with any dependencies listed here 74 | # generate_messages( 75 | # DEPENDENCIES 76 | # std_msgs # Or other packages containing msgs 77 | # ) 78 | 79 | ################################################ 80 | ## Declare ROS dynamic reconfigure parameters ## 81 | ################################################ 82 | 83 | ## To declare and build dynamic reconfigure parameters within this 84 | ## package, follow these steps: 85 | ## * In the file package.xml: 86 | ## * add a build_depend and a exec_depend tag for "dynamic_reconfigure" 87 | ## * In this file (CMakeLists.txt): 88 | ## * add "dynamic_reconfigure" to 89 | ## find_package(catkin REQUIRED COMPONENTS ...) 90 | ## * uncomment the "generate_dynamic_reconfigure_options" section below 91 | ## and list every .cfg file to be processed 92 | 93 | ## Generate dynamic reconfigure parameters in the 'cfg' folder 94 | # generate_dynamic_reconfigure_options( 95 | # cfg/DynReconf1.cfg 96 | # cfg/DynReconf2.cfg 97 | # ) 98 | 99 | ################################### 100 | ## catkin specific configuration ## 101 | ################################### 102 | ## The catkin_package macro generates cmake config files for your package 103 | ## Declare things to be passed to dependent projects 104 | ## INCLUDE_DIRS: uncomment this if your package contains header files 105 | ## LIBRARIES: libraries you create in this project that dependent projects also need 106 | ## CATKIN_DEPENDS: catkin_packages dependent projects also need 107 | ## DEPENDS: system dependencies of this project that dependent projects also need 108 | catkin_package( 109 | # INCLUDE_DIRS include 110 | # LIBRARIES pf_localization 111 | # CATKIN_DEPENDS ros_cpp ros_py std_msg 112 | # DEPENDS system_lib 113 | ) 114 | 115 | ########### 116 | ## Build ## 117 | ########### 118 | 119 | ## Specify additional locations of header files 120 | ## Your package locations should be listed before other locations 121 | include_directories( 122 | include/ 123 | # ${BULLET_INCLUDE_DIRS} 124 | # ${catkin_INCLUDE_DIRS} 125 | 126 | include ${catkin_INCLUDE_DIRS} 127 | # ./include/pf_localization 128 | # ${catkin_INCLUDE_DIRS} 129 | ) 130 | 131 | ## Declare a C++ library 132 | # add_library(${PROJECT_NAME} 133 | # src/${PROJECT_NAME}/pf_localization.cpp 134 | # ) 135 | 136 | ## Add cmake target dependencies of the library 137 | ## as an example, code may need to be generated before libraries 138 | ## either from message generation or dynamic reconfigure 139 | # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 140 | 141 | ## Declare a C++ executable 142 | ## With catkin_make all packages are built within a single CMake context 143 | ## The recommended prefix ensures that target names across packages don't collide 144 | # add_executable(${PROJECT_NAME}_node src/pf_localization_node.cpp) 145 | 146 | ## Rename C++ executable without prefix 147 | ## The above recommended prefix causes long target names, the following renames the 148 | ## target back to the shorter version for ease of user use 149 | ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" 150 | # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") 151 | 152 | ## Add cmake target dependencies of the executable 153 | ## same as for the library above 154 | # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 155 | 156 | ## Specify libraries to link a library or executable target against 157 | # target_link_libraries(${PROJECT_NAME}_node 158 | # ${catkin_LIBRARIES} 159 | # ) 160 | 161 | ############# 162 | ## Install ## 163 | ############# 164 | 165 | # all install targets should use catkin DESTINATION variables 166 | # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html 167 | 168 | ## Mark executable scripts (Python etc.) for installation 169 | ## in contrast to setup.py, you can choose the destination 170 | # install(PROGRAMS 171 | # scripts/my_python_script 172 | # DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 173 | # ) 174 | 175 | ## Mark executables for installation 176 | ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html 177 | # install(TARGETS ${PROJECT_NAME}_node 178 | # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 179 | # ) 180 | 181 | ## Mark libraries for installation 182 | ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html 183 | # install(TARGETS ${PROJECT_NAME} 184 | # ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 185 | # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 186 | # RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} 187 | # ) 188 | 189 | ## Mark cpp header files for installation 190 | # install(DIRECTORY include/${PROJECT_NAME}/ 191 | # DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} 192 | # FILES_MATCHING PATTERN "*.h" 193 | # PATTERN ".svn" EXCLUDE 194 | # ) 195 | 196 | ## Mark other files for installation (e.g. launch and bag files, etc.) 197 | # install(FILES 198 | # # myfile1 199 | # # myfile2 200 | # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 201 | # ) 202 | 203 | ############# 204 | ## Testing ## 205 | ############# 206 | 207 | ## Add gtest based cpp test target and link libraries 208 | # catkin_add_gtest(${PROJECT_NAME}-test test/test_pf_localization.cpp) 209 | # if(TARGET ${PROJECT_NAME}-test) 210 | # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) 211 | # endif() 212 | 213 | ## Add folders to be run by python nosetests 214 | # catkin_add_nosetests(test) 215 | 216 | 217 | MESSAGE(STATUS "This is BINARY dir " ${pf_localization_BINARY_DIR}) 218 | MESSAGE(STATUS "This is SOURCE dir " ${pf_localization_SOURCE_DIR}) 219 | 220 | # SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../src/pf_localization/bin) 221 | 222 | add_executable(localization_node src/localization_node.cpp 223 | src/MCL_localization.cpp 224 | src/load_map.cpp 225 | src/load_log.cpp) 226 | 227 | target_link_libraries(localization_node ${catkin_LIBRARIES}) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Homework of Probabilistic Robotics 2 | 3 | ## Task Description 4 |   The goal of this homework is to become familiar with robot localization and particle filtering.You will be implementing a global localization for a lost robot (global meaning that you do not know the initial pose of the robot). You may implement this using any programming language (there is no real-time-ness requirement).Feel free to utilize any techniques that we have discussed in class,as well as extension discussed in Probabilistic Robotics or elsewhere.In addition to the readings for lecture, Chapters 5 and 6 of Probabilistic Robotics may be helpful for this assignment. 5 | 6 |   Your lost robot is operating in a building with nothing but odometry and a laser range finder.Fortunately, you have a map of and a deep understanding of particle filtering to help it localize.The data directory that you received with this handout has the following files: 7 | 8 | + instruct.txt –Format description for the map and the data logs. 9 | + robotdataN.log.gz –Five data logs (odometry and laser data). 10 | + wean.dat.gz –Map for localization. 11 | + wean.gif –Image of map (just for your info). 12 | + robotmovie1.gif –Animation of data log 1 (just for your info). 13 | 14 | 15 | ## 1.Prerequisites 16 | + Ubuntu 64-bit 16.04. [ROS Kinetic](http://wiki.ros.org/kinetic/Installation/Ubuntu). 17 | 18 | ## 2.Build on ROS 19 | + Clone this repository to your catkin workspace and catkin_make. 20 | ``` 21 | cd ${YOUR_WORKSPACE_PATH}/src 22 | git clone https://github.com/Mesywang/Particle-Filter-Localization.git 23 | ``` 24 | + Change the name of the folder that you just cloned to "pf_localization" , which matches the name of ROS package. 25 | ``` 26 | cd ../ 27 | catkin_make 28 | ``` 29 | + Modify the **value** of the parameter **package_path_param** in line 16 of launch/mcl_localization.launch to the path where the pf_localization package is located. E.g : **value="/home/wsy/catkin_ws/src/"** 30 | 31 | + Run the Simulation : roslaunch pf_localization mcl_localization.launch 32 | 33 | + The result 34 | 35 |   As shown in the following GIF, the pink arrows represent the set of particles, and the motion of black robot model represents the pose of the actual robot estimated by the MCL algorithm in the map. 36 |
Monte Carlo Localization
37 |
38 | 39 |
40 | -------------------------------------------------------------------------------- /data/log/robotdata1.log.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/log/robotdata1.log.gz -------------------------------------------------------------------------------- /data/log/robotdata2.log.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/log/robotdata2.log.gz -------------------------------------------------------------------------------- /data/log/robotdata3.log.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/log/robotdata3.log.gz -------------------------------------------------------------------------------- /data/log/robotdata4.log.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/log/robotdata4.log.gz -------------------------------------------------------------------------------- /data/log/robotdata5.log.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/log/robotdata5.log.gz -------------------------------------------------------------------------------- /data/map/instruct.txt: -------------------------------------------------------------------------------- 1 | 2 | MAP FILE FORMAT: 3 | 4 | It is pretty self-explanatory: 5 | -1 = don't know 6 | any value in [0;1] is a probability for occupancy: 7 | 1 = occupied with probability 1 8 | 0 = unoccupied with probability 1 9 | 0.5 = occupied with probability 0.5 10 | The function in bee-map.c should make reading the map pretty easy. 11 | 12 | 13 | LOG DATA FILE FORMAT: 14 | 15 | In general, x and y coordinates are in centimeters, thetas are in 16 | radians, range values are in centimeters 17 | 18 | The laser on the robot is approximately 25 cm offset forward from the 19 | true center of the robot. 20 | 21 | Entry Type #1 (odometry): 22 | 23 | O x y theta ts 24 | 25 | x y theta - coordinates of the robot in standard odometry frame 26 | ts - timestamp of odometry reading (0 at start of run) 27 | 28 | Entry Type #2 (laser) 29 | 30 | L x y theta xl yl thetal r1 ... r180 ts 31 | 32 | x y theta - coodinates of the robot in standard odometry frame when 33 | laser reading was taken (interpolated) 34 | xl yl thetal - coordinates of the *laser* in standard odometry frame 35 | when the laser reading was taken (interpolated) 36 | r1 .. r180 - 180 range readings of laser in cm. The 180 readings span 37 | 180 degrees *STARTING FROM THE RIGHT AND GOING LEFT* Just like angles, 38 | the laser readings are in counterclockwise order. 39 | ts - timestamp of laser reading 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /data/map/robotmovie1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/map/robotmovie1.gif -------------------------------------------------------------------------------- /data/map/wean.dat.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/map/wean.dat.gz -------------------------------------------------------------------------------- /data/map/wean.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/data/map/wean.gif -------------------------------------------------------------------------------- /img/MCL.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mesywang/Particle-Filter-Localization/5d05ce9462d71628dcd127a051b088e147030d04/img/MCL.gif -------------------------------------------------------------------------------- /include/MCL_localization.h: -------------------------------------------------------------------------------- 1 | #ifndef MCL_LOCALIZATION_H 2 | #define MCL_LOCALIZATION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "load_map.h" 8 | #include "load_log.h" 9 | #include "geometry_msgs/PoseArray.h" 10 | #include "geometry_msgs/PoseStamped.h" 11 | #include "tf/tf.h" 12 | #include 13 | 14 | 15 | #define PARTICLES_NUM 3000 16 | #define pi 3.1415926536 17 | #define ODOM_DATA 0 18 | #define LIDAR_DATA 1 19 | #define LASER_BEAM_NUM 180 20 | 21 | 22 | typedef struct 23 | { 24 | float x; 25 | float y; 26 | float theta; 27 | }robot_state; 28 | 29 | 30 | typedef struct 31 | { 32 | float x; 33 | float y; 34 | float theta; 35 | float weight; 36 | }particle_state; 37 | 38 | 39 | typedef struct 40 | { 41 | float x_front; 42 | float y_front; 43 | float theta_front; 44 | float x_rear; 45 | float y_rear; 46 | float theta_rear; 47 | }motion_model; 48 | 49 | 50 | typedef struct 51 | { 52 | float* readings; 53 | }lidar_measure; 54 | 55 | 56 | class PFLocalization 57 | { 58 | public: 59 | 60 | PFLocalization(map_type* mapdata,vector& logdata,ros::NodeHandle node); 61 | ~PFLocalization(); 62 | void InitParticles(); 63 | void MCLAlgorithm(); 64 | void CalRobotPose(); 65 | void Visualize(); 66 | 67 | 68 | private: 69 | particle_state SampleMotionModelOdometry(particle_state particle); 70 | float SampleStandardNormalDistribution(float var); 71 | float MeasurementBeamModel(particle_state particle); 72 | float MeasurementScoreModel(particle_state particle); 73 | float ProbMeasurementHit(float zkt_star, float zkt); 74 | float ProbMeasurementShort(float zkt_star, float zkt); 75 | float ProbMeasurementMaxVal(float zkt); 76 | float ProbMeasurementRandVal(float zkt); 77 | void LowVarianceSampler(); 78 | 79 | 80 | ros::Publisher publish_particlecloud_; 81 | ros::Publisher publish_pose_; 82 | ros::NodeHandle node_; 83 | map_type* map_; 84 | vector log_data_; 85 | vector particles_; 86 | geometry_msgs::PoseArray particles_ros_; 87 | geometry_msgs::PoseStamped robot_ros_; 88 | robot_state robot_pose_; 89 | motion_model motion_; 90 | lidar_measure measurement_; 91 | 92 | float alpha1_,alpha2_,alpha3_,alpha4_; 93 | float sigmahit_, lambdashort_; 94 | float z_hit_,z_short_,z_rand_,z_max_; 95 | float threshold_, map_threshold_, obstacle_threshold_, lidar_offset_; 96 | int numParticles_, ray_tracing_step_, resolution_, lidar_range_max_; 97 | 98 | }; 99 | 100 | 101 | #endif 102 | 103 | 104 | -------------------------------------------------------------------------------- /include/load_log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOAD_LOG_H 2 | #define LOAD_LOG_H 3 | 4 | 5 | #include "load_map.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define total_readings 180 13 | 14 | using namespace std; 15 | 16 | 17 | typedef struct 18 | { 19 | int data_type; // 0-里程计数据,1-激光雷达数据 20 | float x_robot,y_robot,theta_robot; // 机器人位姿 21 | float x_lidar,y_lidar,theta_lidar; // 激光雷达位姿 22 | float* readings; // 激光雷达读数 23 | float time_stamp; // 时间戳 24 | }log_data; 25 | 26 | 27 | class LoadLog 28 | { 29 | public: 30 | LoadLog(const char* log_str); 31 | ~LoadLog(); 32 | int ReadFromData(const char* logfile, vector& logfile_data); 33 | vector GetLog(); 34 | void ShowLogData(); 35 | 36 | private: 37 | vector log; 38 | }; 39 | 40 | 41 | #endif -------------------------------------------------------------------------------- /include/load_map.h: -------------------------------------------------------------------------------- 1 | #ifndef LOAD_MAP_H 2 | #define LOAD_MAP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "nav_msgs/MapMetaData.h" 9 | #include "nav_msgs/OccupancyGrid.h" 10 | 11 | 12 | 13 | typedef struct 14 | { 15 | int resolution; //地图分辨率 16 | int size_x,size_y; //地图尺寸 17 | int min_x,min_y,max_x,max_y; //地图有效区域 18 | float offset_x, offset_y; //地图偏移量 19 | float** prob; //地图概率值 20 | } map_type; 21 | 22 | 23 | class LoadMap 24 | { 25 | public: 26 | LoadMap(const char* map_str); //LoadMap类构造函数 27 | ~LoadMap(); //LoadMap类析构函数 28 | int ReadFromData(const char* map_name); //从wean.data中读取数据 29 | void PublishMap(ros::NodeHandle node_); //将地图转换格式,并发布topic在rviz中显示 30 | map_type* GetMap(); //将地图传递到类外 31 | 32 | private: 33 | map_type* map; 34 | ros::Publisher map_pub; 35 | ros::Publisher map_meta_pub; 36 | }; 37 | 38 | #endif 39 | 40 | 41 | -------------------------------------------------------------------------------- /include/localization_node.h: -------------------------------------------------------------------------------- 1 | #ifndef LOCALIZATION_NODE_H 2 | #define LOCALIZATION_NODE_H 3 | 4 | #include 5 | #include 6 | #include "MCL_localization.h" 7 | #include "load_map.h" 8 | #include "load_log.h" 9 | 10 | 11 | 12 | 13 | #endif -------------------------------------------------------------------------------- /launch/mcl_localization.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | pf_localization 4 | 0.0.0 5 | The pf_localization package 6 | 7 | 8 | 9 | 10 | wsy 11 | 12 | 13 | 14 | 15 | 16 | TODO 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | catkin 52 | roscpp 53 | rospy 54 | std_msgs 55 | tf 56 | sensor_msgs 57 | nav_msgs 58 | 59 | roscpp 60 | rospy 61 | std_msgs 62 | 63 | roscpp 64 | rospy 65 | std_msgs 66 | tf 67 | sensor_msgs 68 | nav_msgs 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /rviz/localization.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /Status1 9 | - /Map1 10 | - /PoseArray1 11 | - /Pose1 12 | Splitter Ratio: 0.5 13 | Tree Height: 775 14 | - Class: rviz/Selection 15 | Name: Selection 16 | - Class: rviz/Tool Properties 17 | Expanded: 18 | - /2D Pose Estimate1 19 | - /2D Nav Goal1 20 | - /Publish Point1 21 | Name: Tool Properties 22 | Splitter Ratio: 0.588679016 23 | - Class: rviz/Views 24 | Expanded: 25 | - /Current View1 26 | Name: Views 27 | Splitter Ratio: 0.5 28 | - Class: rviz/Time 29 | Experimental: false 30 | Name: Time 31 | SyncMode: 0 32 | SyncSource: "" 33 | Toolbars: 34 | toolButtonStyle: 2 35 | Visualization Manager: 36 | Class: "" 37 | Displays: 38 | - Alpha: 0.5 39 | Cell Size: 1 40 | Class: rviz/Grid 41 | Color: 160; 160; 164 42 | Enabled: true 43 | Line Style: 44 | Line Width: 0.0299999993 45 | Value: Lines 46 | Name: Grid 47 | Normal Cell Count: 0 48 | Offset: 49 | X: 0 50 | Y: 0 51 | Z: 0 52 | Plane: XY 53 | Plane Cell Count: 10 54 | Reference Frame: 55 | Value: true 56 | - Alpha: 0.699999988 57 | Class: rviz/Map 58 | Color Scheme: raw 59 | Draw Behind: false 60 | Enabled: true 61 | Name: Map 62 | Topic: map 63 | Unreliable: false 64 | Use Timestamp: false 65 | Value: true 66 | - Alpha: 1 67 | Arrow Length: 0.300000012 68 | Axes Length: 0.300000012 69 | Axes Radius: 0.00999999978 70 | Class: rviz/PoseArray 71 | Color: 255; 0; 255 72 | Enabled: true 73 | Head Length: 0.0700000003 74 | Head Radius: 0.0299999993 75 | Name: PoseArray 76 | Shaft Length: 0.230000004 77 | Shaft Radius: 0.00999999978 78 | Shape: Arrow (Flat) 79 | Topic: /particle_cloud 80 | Unreliable: false 81 | Value: true 82 | - Alpha: 1 83 | Axes Length: 1 84 | Axes Radius: 0.100000001 85 | Class: rviz/Pose 86 | Color: 0; 255; 127 87 | Enabled: true 88 | Head Length: 0.300000012 89 | Head Radius: 0.100000001 90 | Name: Pose 91 | Shaft Length: 1 92 | Shaft Radius: 0.0500000007 93 | Shape: Arrow 94 | Topic: /robot_pose 95 | Unreliable: false 96 | Value: true 97 | - Class: rviz/Axes 98 | Enabled: true 99 | Length: 1 100 | Name: Axes 101 | Radius: 0.100000001 102 | Reference Frame: 103 | Value: true 104 | - Alpha: 1 105 | Class: rviz/RobotModel 106 | Collision Enabled: false 107 | Enabled: true 108 | Links: 109 | A_infrared_sensor: 110 | Alpha: 1 111 | Show Axes: false 112 | Show Trail: false 113 | Value: true 114 | A_motor: 115 | Alpha: 1 116 | Show Axes: false 117 | Show Trail: false 118 | Value: true 119 | A_pillar: 120 | Alpha: 1 121 | Show Axes: false 122 | Show Trail: false 123 | Value: true 124 | A_wheel: 125 | Alpha: 1 126 | Show Axes: false 127 | Show Trail: false 128 | Value: true 129 | All Links Enabled: true 130 | B_infrared_sensor: 131 | Alpha: 1 132 | Show Axes: false 133 | Show Trail: false 134 | Value: true 135 | B_motor: 136 | Alpha: 1 137 | Show Axes: false 138 | Show Trail: false 139 | Value: true 140 | B_pannel: 141 | Alpha: 1 142 | Show Axes: false 143 | Show Trail: false 144 | Value: true 145 | B_pillar: 146 | Alpha: 1 147 | Show Axes: false 148 | Show Trail: false 149 | Value: true 150 | B_wheel: 151 | Alpha: 1 152 | Show Axes: false 153 | Show Trail: false 154 | Value: true 155 | C_infrared_sensor: 156 | Alpha: 1 157 | Show Axes: false 158 | Show Trail: false 159 | Value: true 160 | C_motor: 161 | Alpha: 1 162 | Show Axes: false 163 | Show Trail: false 164 | Value: true 165 | C_pannel: 166 | Alpha: 1 167 | Show Axes: false 168 | Show Trail: false 169 | Value: true 170 | C_pillar: 171 | Alpha: 1 172 | Show Axes: false 173 | Show Trail: false 174 | Value: true 175 | C_wheel: 176 | Alpha: 1 177 | Show Axes: false 178 | Show Trail: false 179 | Value: true 180 | Expand Joint Details: false 181 | Expand Link Details: false 182 | Expand Tree: false 183 | Lidar_pannel: 184 | Alpha: 1 185 | Show Axes: false 186 | Show Trail: false 187 | Value: true 188 | Link Tree Style: Links in Alphabetic Order 189 | Pi_pannel: 190 | Alpha: 1 191 | Show Axes: false 192 | Show Trail: false 193 | Value: true 194 | alarm_base: 195 | Alpha: 1 196 | Show Axes: false 197 | Show Trail: false 198 | Value: true 199 | alarm_light: 200 | Alpha: 1 201 | Show Axes: false 202 | Show Trail: false 203 | Value: true 204 | arduino_board: 205 | Alpha: 1 206 | Show Axes: false 207 | Show Trail: false 208 | Value: true 209 | base_imu_link: 210 | Alpha: 1 211 | Show Axes: false 212 | Show Trail: false 213 | Value: true 214 | base_link: 215 | Alpha: 1 216 | Show Axes: false 217 | Show Trail: false 218 | Value: true 219 | battery: 220 | Alpha: 1 221 | Show Axes: false 222 | Show Trail: false 223 | Value: true 224 | blue_A_pillar: 225 | Alpha: 1 226 | Show Axes: false 227 | Show Trail: false 228 | Value: true 229 | blue_B_pillar: 230 | Alpha: 1 231 | Show Axes: false 232 | Show Trail: false 233 | Value: true 234 | blue_C_pillar: 235 | Alpha: 1 236 | Show Axes: false 237 | Show Trail: false 238 | Value: true 239 | drop_sensor: 240 | Alpha: 1 241 | Show Axes: false 242 | Show Trail: false 243 | Value: true 244 | laser_frame: 245 | Alpha: 1 246 | Show Axes: false 247 | Show Trail: false 248 | Value: true 249 | left_blue_A_pillar: 250 | Alpha: 1 251 | Show Axes: false 252 | Show Trail: false 253 | Value: true 254 | left_blue_B_pillar: 255 | Alpha: 1 256 | Show Axes: false 257 | Show Trail: false 258 | Value: true 259 | left_blue_pillar: 260 | Alpha: 1 261 | Show Axes: false 262 | Show Trail: false 263 | Value: true 264 | pi_board: 265 | Alpha: 1 266 | Show Axes: false 267 | Show Trail: false 268 | Value: true 269 | right_blue_A_pillar: 270 | Alpha: 1 271 | Show Axes: false 272 | Show Trail: false 273 | Value: true 274 | right_blue_B_pillar: 275 | Alpha: 1 276 | Show Axes: false 277 | Show Trail: false 278 | Value: true 279 | right_blue_pillar: 280 | Alpha: 1 281 | Show Axes: false 282 | Show Trail: false 283 | Value: true 284 | white_A_pillar: 285 | Alpha: 1 286 | Show Axes: false 287 | Show Trail: false 288 | Value: true 289 | white_B_pillar: 290 | Alpha: 1 291 | Show Axes: false 292 | Show Trail: false 293 | Value: true 294 | white_C_pillar: 295 | Alpha: 1 296 | Show Axes: false 297 | Show Trail: false 298 | Value: true 299 | white_D_pillar: 300 | Alpha: 1 301 | Show Axes: false 302 | Show Trail: false 303 | Value: true 304 | white_E_pillar: 305 | Alpha: 1 306 | Show Axes: false 307 | Show Trail: false 308 | Value: true 309 | white_F_pillar: 310 | Alpha: 1 311 | Show Axes: false 312 | Show Trail: false 313 | Value: true 314 | white_G_pillar: 315 | Alpha: 1 316 | Show Axes: false 317 | Show Trail: false 318 | Value: true 319 | Name: RobotModel 320 | Robot Description: robot_description 321 | TF Prefix: "" 322 | Update Interval: 0 323 | Value: true 324 | Visual Enabled: true 325 | Enabled: true 326 | Global Options: 327 | Background Color: 48; 48; 48 328 | Default Light: true 329 | Fixed Frame: map 330 | Frame Rate: 30 331 | Name: root 332 | Tools: 333 | - Class: rviz/Interact 334 | Hide Inactive Objects: true 335 | - Class: rviz/MoveCamera 336 | - Class: rviz/Select 337 | - Class: rviz/FocusCamera 338 | - Class: rviz/Measure 339 | - Class: rviz/SetInitialPose 340 | Topic: /initialpose 341 | - Class: rviz/SetGoal 342 | Topic: /move_base_simple/goal 343 | - Class: rviz/PublishPoint 344 | Single click: true 345 | Topic: /clicked_point 346 | Value: true 347 | Views: 348 | Current: 349 | Class: rviz/ThirdPersonFollower 350 | Distance: 115.186485 351 | Enable Stereo Rendering: 352 | Stereo Eye Separation: 0.0599999987 353 | Stereo Focal Distance: 1 354 | Swap Stereo Eyes: false 355 | Value: false 356 | Focal Point: 357 | X: 48.4389687 358 | Y: 15.5593414 359 | Z: 4.05411301e-05 360 | Focal Shape Fixed Size: true 361 | Focal Shape Size: 0.0500000007 362 | Invert Z Axis: false 363 | Name: Current View 364 | Near Clip Distance: 0.00999999978 365 | Pitch: 1.54479635 366 | Target Frame: 367 | Value: ThirdPersonFollower (rviz) 368 | Yaw: 4.71675205 369 | Saved: ~ 370 | Window Geometry: 371 | Displays: 372 | collapsed: false 373 | Height: 1056 374 | Hide Left Dock: false 375 | Hide Right Dock: true 376 | QMainWindow State: 000000ff00000000fd00000004000000000000016a00000396fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006100fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000002800000396000000d700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f00000393fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000002800000393000000ad00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073d0000003efc0100000002fb0000000800540069006d006501000000000000073d0000030000fffffffb0000000800540069006d00650100000000000004500000000000000000000005cd0000039600000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 377 | Selection: 378 | collapsed: false 379 | Time: 380 | collapsed: false 381 | Tool Properties: 382 | collapsed: false 383 | Views: 384 | collapsed: true 385 | Width: 1853 386 | X: 1987 387 | Y: 24 388 | -------------------------------------------------------------------------------- /src/MCL_localization.cpp: -------------------------------------------------------------------------------- 1 | #include "MCL_localization.h" 2 | 3 | 4 | using namespace std; 5 | 6 | //PFLocalization类的构造函数 7 | PFLocalization::PFLocalization(map_type* mapdata,vector& logdata,ros::NodeHandle node) 8 | { 9 | 10 | this->map_ = mapdata; //地图 11 | this->log_data_ = logdata; //里程计和雷达数据 12 | this->numParticles_ = PARTICLES_NUM; //粒子数 13 | this->map_threshold_ = 0.95; //初始撒随机粒子时的地图阈值,保证粒子都在地图有效区域内 14 | this->node_ = node; //ROS句柄 15 | this->resolution_ = 10; //地图分辨率10cm 16 | this->lidar_offset_ = 25; //激光雷达安装偏移量25cm 17 | this->lidar_range_max_ = 1000; //雷达最大有效数据1000cm 18 | this->ray_tracing_step_ = 1; //ray tracing步长 19 | this->obstacle_threshold_ = 0.2; //地图概率小于此值则认为是障碍物 20 | 21 | 22 | this->alpha1_ = 0.025; //里程计运动模型参数--旋转 23 | this->alpha2_ = 0.025; //里程计运动模型参数--旋转 24 | this->alpha3_ = 0.4; //里程计运动模型参数--平移 25 | this->alpha4_ = 0.4; //里程计运动模型参数--平移 26 | 27 | 28 | this->sigmahit_ = 2; //BeamModel中高斯分布的方差 29 | this->lambdashort_ = 1.5; //BeamModel中指数分布的参数 30 | 31 | this->z_hit_ = 0.8; //BeamModel中高斯分布Phit的权重 32 | this->z_short_ = 0.2; //BeamModel中指数分布Pshort的权重 33 | this->z_rand_ = 0.0; //BeamModel中均匀分布Prand的权重 34 | this->z_max_ = 0.0; //BeamModel中均匀分布Pmax的权重 35 | 36 | //发布粒子集位姿 37 | publish_particlecloud_ = node_.advertise("particle_cloud", 1, true); 38 | this->particles_ros_.header.stamp = ros::Time::now(); 39 | this->particles_ros_.header.frame_id = "map"; 40 | this->particles_ros_.poses.resize(numParticles_); 41 | 42 | //发布机器人位姿 43 | publish_pose_ = node_.advertise("robot_pose", 1, true); 44 | this->robot_ros_.header.stamp = ros::Time::now(); 45 | this->robot_ros_.header.frame_id = "map"; 46 | } 47 | 48 | //PFLocalization类的析构函数 49 | PFLocalization::~PFLocalization() 50 | { 51 | 52 | } 53 | 54 | 55 | //粒子集初始化函数 56 | void PFLocalization::InitParticles() 57 | { 58 | int count = 1; 59 | while (count <= numParticles_) 60 | { 61 | particle_state particle_temp; 62 | 63 | particle_temp.x = rand() / (float)RAND_MAX * (map_->max_x - map_->min_x) + map_->min_x; //初始化粒子X坐标 64 | particle_temp.y = rand() / (float)RAND_MAX * (map_->max_y - map_->min_y) + map_->min_y; //初始化粒子Y坐标 65 | 66 | // particle_temp.x = rand() / (float)RAND_MAX * (500 - 340) + 340; //初始化粒子X坐标 67 | // particle_temp.y = rand() / (float)RAND_MAX * (430 - map_->min_y) + map_->min_y; //初始化粒子Y坐标 68 | 69 | if (map_->prob[(int) particle_temp.x][(int) particle_temp.y] <= map_threshold_) //若随机出的粒子不在地图有效范围内,则重新生成粒子 70 | continue; 71 | 72 | count++; //当有效粒子数 = 粒子总数时,跳出循环 73 | 74 | particle_temp.theta = rand() / (float)RAND_MAX * 2 * pi; //初始化粒子角度theta 75 | // particle_temp.theta = rand() / (float)RAND_MAX * (1/4 * pi - (-5/4 * pi)) - 5/4 * pi ; //初始化粒子角度theta 76 | 77 | //将粒子角度转换到 -pi ~ pi 之间 78 | if(particle_temp.theta > pi) 79 | particle_temp.theta -= 2 * pi; 80 | if(particle_temp.theta < -pi) 81 | particle_temp.theta += 2 * pi; 82 | 83 | particle_temp.weight = 1.0/numParticles_; // 初始化粒子权重为1/NUM 84 | 85 | particles_.push_back(particle_temp); //存入粒子集 86 | } 87 | 88 | geometry_msgs::Pose pose_ros; 89 | geometry_msgs::Quaternion q; 90 | for(int i = 0; i < numParticles_; i++) 91 | { 92 | q = tf::createQuaternionMsgFromRollPitchYaw(0,0,particles_[i].theta); //单位:rad 93 | pose_ros.position.x = 0.1 * particles_[i].x; //单位:dm -> m 94 | pose_ros.position.y = 0.1 * particles_[i].y; 95 | pose_ros.position.z = 0.0; 96 | pose_ros.orientation = q; 97 | 98 | particles_ros_.poses[i] = pose_ros; 99 | } 100 | publish_particlecloud_.publish(particles_ros_); //发布粒子集状态 101 | } 102 | 103 | 104 | //蒙特卡洛定位算法函数 105 | void PFLocalization::MCLAlgorithm() 106 | { 107 | particle_state particle_state_temp; 108 | vector particles_temp; 109 | particles_temp.resize(numParticles_); 110 | 111 | for (int i = 1;i < log_data_.size(); i++) //此层循环遍历数据集 112 | { 113 | //取数据集中相邻两帧数据,作为里程计运动模型的相邻两次状态 114 | motion_.x_front = log_data_[i-1].x_robot; 115 | motion_.y_front = log_data_[i-1].y_robot; 116 | motion_.theta_front = log_data_[i-1].theta_robot; 117 | motion_.x_rear = log_data_[i].x_robot; 118 | motion_.y_rear =log_data_[i].y_robot; 119 | motion_.theta_rear = log_data_[i].theta_robot; 120 | 121 | for(int j = 0; j < numParticles_; j++) 122 | { 123 | particle_state_temp = SampleMotionModelOdometry(particles_[j]); //从里程计运动模型中采样 124 | // particles_temp.push_back(particle_state_temp); 125 | particles_temp[j] = particle_state_temp; 126 | } 127 | 128 | // cout << "Reading (" << i << "/" << log_data_.size() << ") log data! "; 129 | // cout << "Robot Position (X,Y,theta): (" << log_data_[i].x_robot << "," << log_data_[i].y_robot << "," << log_data_[i].theta_robot << ")" << std::endl; 130 | 131 | particles_ = particles_temp; 132 | 133 | if(log_data_[i].data_type == ODOM_DATA) //此帧数据只包括odometry 134 | { 135 | Visualize(); //显示实时更新的粒子集状态 136 | 137 | continue; 138 | } 139 | else if(log_data_[i].data_type == LIDAR_DATA) //此帧数据包括lidar 140 | { 141 | measurement_.readings = log_data_[i].readings; //当前帧雷达数据首地址 142 | 143 | double total_weight = 0; 144 | for(int j = 0; j < numParticles_; j++) 145 | { 146 | // float weight = MeasurementBeamModel(particles_[j]); //根据光束模型计算每个粒子的权重,运行速度太慢且定位效果不好 147 | float weight = MeasurementScoreModel(particles_[j]); //根据似然法计算每个粒子的权重 148 | // cout << " each weight is : " << weight << endl; 149 | particles_[j].weight = weight; 150 | total_weight += particles_[j].weight; //粒子权重总和 151 | } 152 | 153 | for(int j = 0; j < numParticles_; j++) 154 | { 155 | particles_[j].weight /= total_weight; //所有粒子权重归一化 156 | // avg_weight += particles_[j].weight/numParticles_; 157 | // cout << " Normalized weight is : " << particles_[j].weight << endl; 158 | } 159 | 160 | float avg_weight = total_weight / numParticles_; 161 | // cout << " The average weight is : " << avg_weight << endl; 162 | 163 | LowVarianceSampler(); //低方差重采样算法 164 | 165 | CalRobotPose(); //计算机器人位姿,并发布topic 166 | } 167 | else 168 | { 169 | cout << " ERROR:The Log Data Is Error!!!" << endl; 170 | } 171 | 172 | Visualize(); //显示实时更新的粒子集状态 173 | 174 | // sleep(1); //1s 175 | 176 | if(i <= 20) 177 | usleep(300000); //前20帧数据延迟大一些,更清晰的观察粒子的重采样情况 178 | else 179 | usleep(40000); 180 | } 181 | 182 | } 183 | 184 | 185 | //概率机器人P103---基于里程计运动模型中采样算法 186 | particle_state PFLocalization::SampleMotionModelOdometry(particle_state particle) 187 | { 188 | float deltarot1 = atan2(motion_.y_rear - motion_.y_front,motion_.x_rear - motion_.x_front) - motion_.theta_rear; 189 | float deltatrans1 = sqrt(pow((motion_.x_rear - motion_.x_front),2) + pow((motion_.y_rear - motion_.y_front),2)); 190 | float deltarot2 = motion_.theta_rear - motion_.theta_front - deltarot1; 191 | 192 | float deltarot1_hat = deltarot1 - SampleStandardNormalDistribution(alpha1_*deltarot1 + alpha2_*deltatrans1); 193 | float deltatrans1_hat = deltatrans1 - SampleStandardNormalDistribution(alpha3_*deltatrans1 + alpha4_*(deltarot1 + deltarot2)); 194 | float deltarot2_hat = deltarot2 - SampleStandardNormalDistribution(alpha1_*deltarot2 + alpha2_*deltatrans1); 195 | 196 | particle_state particle_temp; 197 | //地图是以dm为单位,初始化的粒子位置是基于地图生成的,所以也是dm单位,而里程计数据单位是cm,需在这里进行单位转换 198 | particle_temp.x = particle.x + (deltatrans1_hat * cos(particle.theta + deltarot1_hat)) / resolution_; 199 | particle_temp.y = particle.y + (deltatrans1_hat * sin(particle.theta + deltarot1_hat)) / resolution_; 200 | particle_temp.theta = particle.theta + deltarot1_hat + deltarot2_hat; 201 | particle_temp.weight = particle.weight; 202 | 203 | return particle_temp; 204 | } 205 | 206 | 207 | //从标准正态分布中采样 208 | float PFLocalization::SampleStandardNormalDistribution(float var) 209 | { 210 | float sum = 0; 211 | for (int i = 0;i < 12; i++) 212 | //LO + static_cast (rand()) /( static_cast (RAND_MAX/(HI-LO))) 213 | sum += (rand() - RAND_MAX / 2) / (float)RAND_MAX * 2; 214 | return (var / 6.0) * sum; 215 | } 216 | 217 | 218 | 219 | //根据光束模型计算每个粒子的权重 220 | float PFLocalization::MeasurementBeamModel(particle_state particle) 221 | { 222 | double q = 1,p = 1; 223 | robot_state lidar_pose; 224 | 225 | float step_x,step_y,zkt_star = 0, zkt = 0; 226 | 227 | //计算激光雷达在地图坐标系下的位姿 228 | lidar_pose.x = particle.x + (lidar_offset_ * cos(particle.theta)) / resolution_; //(单位:dm) 229 | lidar_pose.y = particle.y + (lidar_offset_ * sin(particle.theta)) / resolution_; //(单位:dm) 230 | lidar_pose.theta = particle.theta; 231 | 232 | //若雷达位姿在地图有效区域外,则终止此次计算,该粒子权重为0 233 | if(map_->prob[(int)lidar_pose.x][(int)lidar_pose.y] <= map_threshold_) 234 | return 0.0; 235 | 236 | for (int i = 0; i < LASER_BEAM_NUM; i++) 237 | { 238 | zkt = measurement_.readings[i]; //单位:dm,从log文件读取时就已经转换成dm了 239 | 240 | //此光束无效 241 | if (zkt > (lidar_range_max_ / resolution_)) 242 | continue; 243 | 244 | //计算第i个激光束在世界坐标系下的角度 245 | float step_theta = ((double)i / 180.0) * pi + lidar_pose.theta; 246 | 247 | /******************************* Ray Tracing **********************************/ 248 | int step = 1; 249 | int invaild_flag = 0; 250 | while(1) 251 | { 252 | //减90度是因为激光雷达第一个激光点角度与机器人坐标系相差90度 253 | step_x = lidar_pose.x + step * cos( (step_theta - pi/2.0)); 254 | step_y = lidar_pose.y + step * sin( (step_theta - pi/2.0)); 255 | 256 | //若激光束打到地图以外区域,则此激光束权值为0 257 | if(step_x >= map_->max_x || step_y >= map_->max_y || step_x < map_->min_x || step_y < map_->min_y || map_->prob[(int)step_x][(int)step_y] < 0) 258 | { 259 | invaild_flag = 1; 260 | break; 261 | } 262 | else if(map_->prob[(int)step_x][(int)step_y] <= obstacle_threshold_) //遇到障碍 263 | { 264 | zkt_star = step; //Ray Tracing的结果,单位:dm 265 | break; 266 | } 267 | 268 | step += ray_tracing_step_; 269 | } 270 | if(invaild_flag == 1) 271 | continue; 272 | 273 | p = z_hit_ * ProbMeasurementHit(zkt_star,zkt) + z_short_ * ProbMeasurementShort(zkt_star,zkt) 274 | + z_max_ * ProbMeasurementMaxVal(zkt) + z_rand_ * ProbMeasurementRandVal(zkt); 275 | 276 | // q *= p; //累积乘法会导致q趋于无穷小 277 | q += p; 278 | 279 | /******************************************************************************/ 280 | } 281 | return q; 282 | } 283 | 284 | 285 | //计算高斯分布函数的概率 286 | float PFLocalization::ProbMeasurementHit(float zkt_star, float zkt) 287 | { 288 | if (zkt < 0 || zkt > (lidar_range_max_ / resolution_)) 289 | return 0; 290 | else 291 | { 292 | float q; 293 | q = (1.0 / sqrt(2*pi*sigmahit_*sigmahit_)) * exp((-1/2*((zkt - zkt_star)*(zkt - zkt_star)))/(sigmahit_*sigmahit_)); 294 | return q; 295 | } 296 | } 297 | 298 | 299 | //计算指数分布函数的概率 300 | float PFLocalization::ProbMeasurementShort(float zkt_star, float zkt) 301 | { 302 | if(zkt < 0 || zkt < zkt_star) 303 | return 0; 304 | else 305 | { 306 | float q,eeta; 307 | eeta = 1 / (1 - exp(-1.0 * lambdashort_ * zkt_star)); 308 | q = eeta * lambdashort_ * exp(-1.0 * lambdashort_ * zkt); 309 | return q; 310 | } 311 | } 312 | 313 | 314 | //计算均匀分布函数的概率 315 | float PFLocalization::ProbMeasurementRandVal(float zkt) 316 | { 317 | if(zkt < 0 || zkt >= (lidar_range_max_ / resolution_)) 318 | return 0; 319 | else 320 | return 1.0 / (lidar_range_max_ / resolution_); 321 | } 322 | 323 | 324 | //计算是否为测量最大值的概率 325 | float PFLocalization::ProbMeasurementMaxVal(float zkt) 326 | { 327 | if(zkt == (lidar_range_max_ / resolution_)) 328 | return 1; 329 | else 330 | return 0; 331 | } 332 | 333 | 334 | //根据得分模型计算每个粒子的权重 335 | float PFLocalization::MeasurementScoreModel(particle_state particle) 336 | { 337 | robot_state lidar_pose; 338 | float laser_end_x,laser_end_y,score = 0, zkt = 0; 339 | 340 | //计算激光雷达在map坐标系下的位姿 341 | lidar_pose.x = particle.x + (lidar_offset_ * cos(particle.theta)) / resolution_; //(单位:dm) 342 | lidar_pose.y = particle.y + (lidar_offset_ * sin(particle.theta)) / resolution_; //(单位:dm) 343 | lidar_pose.theta = particle.theta; 344 | 345 | //若雷达位姿在地图有效区域外,则终止此次计算,该粒子权重为0 346 | if(map_->prob[(int)lidar_pose.x][(int)lidar_pose.y] <= map_threshold_) 347 | return 0.0; 348 | 349 | for (int i = 0; i < LASER_BEAM_NUM; i++) 350 | { 351 | zkt = measurement_.readings[i]; //第i个激光束的测距,单位:dm,从log文件读取时就已经转换成dm了 352 | 353 | //若超出设置的lidar最大有效值,则此光束无效 354 | if (zkt > (lidar_range_max_ / resolution_)) 355 | continue; 356 | 357 | //计算第i个激光束在世界坐标系下的角度 358 | float step_theta = ((double)i / 180.0) * pi + lidar_pose.theta - pi/2.0; 359 | 360 | laser_end_x = lidar_pose.x + zkt * cos(step_theta); //计算此激光束末端在map坐标系下的X坐标 361 | laser_end_y = lidar_pose.y + zkt * sin(step_theta); //计算此激光束末端在map坐标系下的Y坐标 362 | 363 | //若激光束末端在地图未知区域或无效区域,则跳过此次得分计算 364 | if(laser_end_x >= map_->max_x || laser_end_y >= map_->max_y || laser_end_x < map_->min_x || laser_end_y < map_->min_y 365 | || map_->prob[(int)laser_end_x][(int)laser_end_y] < 0) 366 | continue; 367 | 368 | // if(map_->prob[(int)laser_end_x][(int)laser_end_y] >= 0 && map_->prob[(int)laser_end_x][(int)laser_end_y] < 0.15) 369 | // score++; 370 | 371 | score += map_->prob[(int)laser_end_x][(int)laser_end_y] < 0.15 ? 1 : 0; //累加,计算此帧lidar数据的得分 372 | } 373 | 374 | return score; //返回当前帧lidar数据的得分,用此来表示粒子权重 375 | } 376 | 377 | 378 | 379 | //概率机器人P78---低方差重采样算法 380 | void PFLocalization::LowVarianceSampler() 381 | { 382 | vector particles_temp = particles_; 383 | float r = (rand() / (float)RAND_MAX) * (1.0 / (float)numParticles_); //初始化一个0~1之间的随机数 384 | float c = particles_[0].weight; 385 | int i = 0; 386 | 387 | for (int m = 0;m < numParticles_; m++) 388 | { 389 | float u = r + (float) m/ numParticles_; //移动随机数 390 | while (u > c && i < numParticles_ - 1) 391 | { 392 | i++; //移动到下一个粒子 393 | c += particles_temp[i].weight; 394 | } 395 | particles_[m] = particles_temp[i]; //复制被选择的粒子 396 | particles_[m].weight = 1.0 / numParticles_; //重采样后粒子权重重置 397 | // cout << " each weight is : " << particles_[m].weight << endl; 398 | } 399 | } 400 | 401 | 402 | //计算机器人位姿,并发布状态 403 | void PFLocalization::CalRobotPose() 404 | { 405 | float total_x = 0.0; 406 | float total_y = 0.0; 407 | float total_theta = 0.0; 408 | 409 | for(int i = 0; i < numParticles_; i++) 410 | { 411 | total_x += particles_[i].x; 412 | total_y += particles_[i].y; 413 | total_theta += particles_[i].theta; 414 | } 415 | robot_pose_.x = total_x / numParticles_; 416 | robot_pose_.y = total_y / numParticles_; 417 | robot_pose_.theta = total_theta / numParticles_; 418 | 419 | cout << "Robot pose: X: " << robot_pose_.x << ", Y: " << robot_pose_.y << ", Theta: " << robot_pose_.theta << endl; 420 | 421 | //显示机器人位姿 422 | geometry_msgs::Pose pose_ros; 423 | geometry_msgs::Quaternion q; 424 | q = tf::createQuaternionMsgFromRollPitchYaw(0,0,robot_pose_.theta); 425 | pose_ros.position.x = 0.1 * robot_pose_.x; //单位:dm -> m 426 | pose_ros.position.y = 0.1 * robot_pose_.y; 427 | pose_ros.position.z = 0.0; 428 | pose_ros.orientation = q; 429 | robot_ros_.pose = pose_ros; 430 | 431 | publish_pose_.publish(robot_ros_); //发布机器人状态 432 | 433 | 434 | static tf::TransformBroadcaster br; 435 | tf::Transform transform; 436 | transform.setOrigin( tf::Vector3(0.1 * robot_pose_.x, 0.1 * robot_pose_.y, 0.0) ); 437 | tf::Quaternion tf_q; 438 | tf_q.setRPY(0, 0, robot_pose_.theta); 439 | transform.setRotation(tf_q); 440 | br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "map", "base_link")); //发布base_link和map的tf关系 441 | 442 | } 443 | 444 | 445 | //显示实时更新的粒子集状态 446 | void PFLocalization::Visualize() 447 | { 448 | geometry_msgs::Pose pose_ros; 449 | geometry_msgs::Quaternion q; 450 | for(int i = 0; i < numParticles_; i++) 451 | { 452 | q = tf::createQuaternionMsgFromRollPitchYaw(0,0,particles_[i].theta); 453 | pose_ros.position.x = 0.1 * particles_[i].x; //单位:dm -> m 454 | pose_ros.position.y = 0.1 * particles_[i].y; 455 | pose_ros.position.z = 0.0; 456 | pose_ros.orientation = q; 457 | 458 | particles_ros_.poses[i] = pose_ros; 459 | } 460 | publish_particlecloud_.publish(particles_ros_); //发布粒子集状态 461 | } -------------------------------------------------------------------------------- /src/load_log.cpp: -------------------------------------------------------------------------------- 1 | #include "load_log.h" 2 | 3 | 4 | 5 | LoadLog::LoadLog(const char* log_str) 6 | { 7 | ReadFromData(log_str,this->log); 8 | } 9 | 10 | LoadLog::~LoadLog() 11 | { 12 | 13 | } 14 | 15 | int LoadLog::ReadFromData(const char* logfile, vector& logfile_data) 16 | { 17 | ifstream file (logfile); 18 | string logline; 19 | log_data logdata_indv; 20 | 21 | if(file.is_open()) 22 | { 23 | while(getline(file,logline)) 24 | { 25 | istringstream iss(logline); 26 | char c; 27 | iss >> c; 28 | int j = 0; 29 | switch(c) 30 | { 31 | case 'L': 32 | logdata_indv.data_type = 1; //此行数据包括odom和lidar 33 | iss >> logdata_indv.x_robot >> logdata_indv.y_robot >> logdata_indv.theta_robot; //读取机器人位姿,单位:cm;cm;rad 34 | iss >> logdata_indv.x_lidar >> logdata_indv.y_lidar >> logdata_indv.theta_lidar; //读取激光雷达位姿,单位:cm;cm;rad 35 | logdata_indv.readings = (float*) malloc(sizeof(float) * total_readings); 36 | while (j < total_readings) 37 | { 38 | iss >> logdata_indv.readings[j]; 39 | logdata_indv.readings[j] /= 10.0; //lidar读数换算成dm,因为地图的分辨率是1dm,统一单位方便后面计算 40 | j++; 41 | // cout << logdata_indv.readings[j] << endl; 42 | } 43 | iss >> logdata_indv.time_stamp; //读取时间戳 44 | break; 45 | case 'O': //此行数据只包括odom 46 | logdata_indv.data_type = 0; 47 | iss >> logdata_indv.x_robot >> logdata_indv.y_robot >> logdata_indv.theta_robot; 48 | iss >> logdata_indv.time_stamp; 49 | break; 50 | default: 51 | break; 52 | } 53 | logfile_data.push_back(logdata_indv); //存入vector中 54 | } 55 | } 56 | else 57 | { 58 | fprintf(stderr, "ERROR: Could not open file %s\n", logfile); 59 | return -1; 60 | } 61 | file.close(); 62 | } 63 | 64 | 65 | vector LoadLog::GetLog() 66 | { 67 | return this->log; 68 | } 69 | 70 | 71 | void LoadLog::ShowLogData() 72 | { 73 | log_data indv; 74 | for (int i = 0;i < this->log.size();i++) 75 | { 76 | indv = this->log[i]; 77 | cout << indv.data_type << endl; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/load_map.cpp: -------------------------------------------------------------------------------- 1 | #include "load_map.h" 2 | 3 | 4 | 5 | LoadMap::LoadMap(const char* map_str) 6 | { 7 | this->map = (map_type*) malloc(sizeof(map_type)); //为地图存储分配内存 8 | ReadFromData(map_str); 9 | } 10 | 11 | LoadMap::~LoadMap() 12 | { 13 | 14 | } 15 | 16 | int LoadMap::ReadFromData(const char* map_name) 17 | { 18 | int x, y, count; 19 | float temp; 20 | char line[256]; 21 | FILE *fp; 22 | 23 | if((fp = fopen(map_name, "rt")) == NULL) //打开文件 24 | { 25 | fprintf(stderr, "ERROR: Could not open file %s\n", map_name); 26 | return -1; 27 | } 28 | fprintf(stderr, "# Reading map: %s\n", map_name); 29 | 30 | //fgets从指定的流 stream 读取一行存入line[],当读取255个字符时,或读取到换行符时,或到达文件末尾时,它会停止 31 | while((fgets(line, 256, fp) != NULL) 32 | && (strncmp("global_map[0]", line , 13) != 0)) 33 | { 34 | if(strncmp(line, "robot_specifications->resolution", 32) == 0) 35 | //从字符串line[]中读取%d格式输入,并存入map->resolution中 36 | if(sscanf(&line[32], "%d", &(map->resolution)) != 0) 37 | printf("# Map resolution: %d cm\n", map->resolution); 38 | if(strncmp(line, "robot_specifications->autoshifted_x", 35) == 0) 39 | if(sscanf(&line[35], "%g", &(map->offset_x)) != 0) 40 | { 41 | map->offset_x = map->offset_x; 42 | printf("# Map offsetX: %g cm\n", map->offset_x); 43 | } 44 | if(strncmp(line, "robot_specifications->autoshifted_y", 35) == 0) 45 | { 46 | if (sscanf(&line[35], "%g", &(map->offset_y)) != 0) 47 | { 48 | map->offset_y = map->offset_y; 49 | printf("# Map offsetY: %g cm\n", map->offset_y); 50 | } 51 | } 52 | } 53 | 54 | if(sscanf(line,"global_map[0]: %d %d", &map->size_y, &map->size_x) != 2) //如果成功则返回成功匹配和赋值的个数 55 | { 56 | fprintf(stderr, "ERROR: corrupted file %s\n", map_name); 57 | fclose(fp); 58 | return -1; 59 | } 60 | printf("# Map size: %d %d\n", map->size_x, map->size_y); 61 | 62 | 63 | map->prob = (float **)calloc(map->size_x, sizeof(float *)); //为二维数组分配内存,calloc同时将数组初始化为0 64 | for(int i = 0; i < map->size_x; i++) 65 | { 66 | map->prob[i] = (float *)calloc(map->size_y, sizeof(float)); 67 | } 68 | 69 | map->min_x = map->size_x; 70 | map->max_x = 0; 71 | map->min_y = map->size_y; 72 | map->max_y = 0; 73 | count = 0; 74 | for(x = 0; x < map->size_x; x++) 75 | { 76 | for(y = 0; y < map->size_y; y++, count++) 77 | { 78 | if(count % 10000 == 0) 79 | fprintf(stderr, "\r# Reading ... (%.2f%%)", count / (float)(map->size_x * map->size_y) * 100); 80 | fscanf(fp,"%e", &temp); //每次读取地图一个像素点的概率值 81 | 82 | if(temp < 0.0) 83 | map->prob[x][y] = -1; //地图状态未知 84 | else 85 | { 86 | if(x < map->min_x) //min~max指的是地图有效区域,默认min,max赋的都是最大值,其值在这里进行更新 87 | map->min_x = x; 88 | else if(x > map->max_x) 89 | map->max_x = x; 90 | if(y < map->min_y) 91 | map->min_y = y; 92 | else if(y > map->max_y) 93 | map->max_y = y; 94 | map->prob[x][y] = temp; //地图概率赋值 95 | } 96 | } 97 | } 98 | //显示地图加载进度 99 | fprintf(stderr, "\r# Reading ... (%.2f%%)\n\n",count / (float)(map->size_x * map->size_y) * 100); 100 | fclose(fp); 101 | return 0; 102 | } 103 | 104 | 105 | void LoadMap::PublishMap(ros::NodeHandle node_) 106 | { 107 | nav_msgs::OccupancyGrid ros_map; 108 | ros_map.header.stamp = ros::Time::now(); 109 | ros_map.header.frame_id = "map"; 110 | ros_map.info.map_load_time = ros::Time::now(); 111 | ros_map.info.resolution = this->map->resolution * 0.01; //rviz中单位为m,1Ocm -> 0.1m 112 | ros_map.info.width = this->map->size_x; 113 | ros_map.info.height = this->map->size_y; 114 | ros_map.info.origin.position.x = 0.0; 115 | ros_map.info.origin.position.y = 0.0; 116 | ros_map.info.origin.position.z = 0.0; 117 | ros_map.info.origin.orientation.x = 0.0; 118 | ros_map.info.origin.orientation.y = 0.0; 119 | ros_map.info.origin.orientation.z = 0.0; 120 | ros_map.info.origin.orientation.w = 1.0; 121 | 122 | ros_map.data.resize(ros_map.info.width * ros_map.info.height); 123 | 124 | for(int x=0; x < ros_map.info.width; x++) 125 | { 126 | for(int y=0; y < ros_map.info.height; y++) 127 | { 128 | // ros_map.data[x*ros_map.info.width + y] = (unsigned int)(map->prob[y][x] * 255); 129 | ros_map.data[y*ros_map.info.height + x] = (uint8_t)(this->map->prob[x][y] * 255); 130 | } 131 | } 132 | 133 | map_pub = node_.advertise("map", 1 ,true); 134 | map_meta_pub = node_.advertise("map_metadata", 1 ,true); 135 | 136 | map_meta_pub.publish(ros_map.info); 137 | map_pub.publish(ros_map); 138 | } 139 | 140 | 141 | map_type* LoadMap::GetMap() 142 | { 143 | return this->map; 144 | } 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/localization_node.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "localization_node.h" 3 | #include "nav_msgs/MapMetaData.h" 4 | #include "nav_msgs/OccupancyGrid.h" 5 | #include "geometry_msgs/PoseArray.h" 6 | #include "tf/tf.h" 7 | #include "stdlib.h" 8 | 9 | using namespace std; 10 | 11 | 12 | int main(int argc, char** argv) 13 | { 14 | 15 | ros::init(argc, argv,"localization_node"); //初始化节点 16 | // ros::NodeHandle n; 17 | ros::NodeHandle n; 18 | 19 | string package_path; 20 | string map_local_path = "pf_localization/data/map/wean.dat"; 21 | string log_local_path = "pf_localization/data/log/robotdata1.log"; 22 | 23 | n.getParam("/pf_localization/package_path_param",package_path); 24 | string complete_map_path = package_path + map_local_path; 25 | string complete_log_path = package_path + log_local_path; 26 | const char* complete_map_path_c = complete_map_path.c_str(); 27 | const char* complete_log_path_c = complete_log_path.c_str(); 28 | 29 | LoadMap load_map(complete_map_path_c); //加载map 30 | LoadLog load_log(complete_log_path_c); //加载odometry和lidar数据 31 | 32 | // LoadMap load_map("/home/wsy/catkin_ws/src/pf_localization/data/map/wean.dat"); //加载map 33 | // LoadLog load_log("/home/wsy/catkin_ws/src/pf_localization/data/log/robotdata1.log"); //加载odometry和lidar数据 34 | 35 | map_type* map = load_map.GetMap(); //获取地图 36 | vector logfile_data = load_log.GetLog(); //获取数据集 37 | 38 | load_map.PublishMap(n); //发布map,rviz订阅此topic 39 | 40 | PFLocalization pf_localization(map, logfile_data, n); 41 | pf_localization.InitParticles(); //粒子集初始化 42 | sleep(5); //粒子初始化后延时5秒后开始跑数据集 43 | pf_localization.MCLAlgorithm(); //运行蒙特卡洛定位算法 44 | 45 | return 0; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /urdf/three_wheels_robot.urdf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | --------------------------------------------------------------------------------