Hi matthieu,
sorry for posting another post but i have made some changes.
this is the code launch file i am using for velodyne to generate 3D map and for localization as well
<launch> <!-- Hand-held 3D lidar mapping example using only a Velodyne PUCK (no camera). Prerequisities: rtabmap should be built with libpointmatcher Example: $ roslaunch rtabmap_ros test_velodyne.launch $ rosrun rviz rviz -f map $ Show TF and /rtabmap/cloud_map topics --> <arg name="rtabmapviz" default="false"/> <arg name="rviz" default="true"/> <arg name="localization" default="true"/> <arg name="use_imu" default="false"/> <!-- Assuming IMU fixed to lidar with /velodyne -> /imu_link TF --> <arg name="imu_topic" default="/imu/data"/> <arg name="scan_20_hz" default="false"/> <!-- If we launch the velodyne with "rpm:=1200" argument --> <arg name="use_sim_time" default="false"/> <param if="$(arg use_sim_time)" name="use_sim_time" value="true"/> <arg name="frame_id" default="velodyne"/> <include file="$(find velodyne_pointcloud)/launch/VLP16_points.launch"> <arg if="$(arg scan_20_hz)" name="rpm" value="1200"/> <arg unless="$(arg scan_20_hz)" name="rpm" value="600"/> </include> <!-- IMU orientation estimation and publish tf accordingly to os1_sensor frame --> <node if="$(arg use_imu)" pkg="rtabmap_ros" type="imu_to_tf" name="imu_to_tf"> <remap from="imu/data" to="$(arg imu_topic)"/> <param name="fixed_frame_id" value="$(arg frame_id)_stabilized"/> <param name="base_frame_id" value="$(arg frame_id)"/> </node> <group ns="rtabmap"> <node pkg="rtabmap_ros" type="icp_odometry" name="icp_odometry" output="screen"> <remap from="scan_cloud" to="/velodyne_points"/> <param name="frame_id" type="string" value="$(arg frame_id)"/> <param name="odom_frame_id" type="string" value="odom"/> <param if="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="25"/> <param unless="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="15"/> <remap if="$(arg use_imu)" from="imu" to="$(arg imu_topic)"/> <param if="$(arg use_imu)" name="guess_frame_id" type="string" value="$(arg frame_id)_stabilized"/> <param if="$(arg use_imu)" name="wait_imu_to_init" type="bool" value="true"/> <!-- ICP parameters --> <param name="publish_tf" type="bool" value="true"/> <param name="Icp/PointToPlane" type="string" value="true"/> <param name="Icp/Iterations" type="string" value="10"/> <param name="Icp/VoxelSize" type="string" value="0.2"/> <param name="Icp/DownsamplingStep" type="string" value="1"/> <!-- cannot be increased with ring-like lidar --> <param name="Icp/Epsilon" type="string" value="0.001"/> <param name="Icp/PointToPlaneK" type="string" value="20"/> <param name="Icp/PointToPlaneRadius" type="string" value="0"/> <param name="Icp/MaxTranslation" type="string" value="2"/> <param name="Icp/MaxCorrespondenceDistance" type="string" value="1"/> <param name="Icp/PM" type="string" value="true"/> <param name="Icp/PMOutlierRatio" type="string" value="0.7"/> <param name="Icp/CorrespondenceRatio" type="string" value="0.01"/> <!-- Odom parameters --> <param name="Odom/ScanKeyFrameThr" type="string" value="0.9"/> <param name="Odom/Strategy" type="string" value="0"/> <param name="OdomF2M/ScanSubtractRadius" type="string" value="0.2"/> <param name="OdomF2M/ScanMaxSize" type="string" value="15000"/> </node> <node pkg="rtabmap_ros" type="rtabmap" name="rtabmap" output="screen"> <param name="frame_id" type="string" value="$(arg frame_id)"/> <param name="subscribe_depth" type="bool" value="false"/> <param name="subscribe_rgb" type="bool" value="false"/> <param name="subscribe_scan_cloud" type="bool" value="true"/> <param name="approx_sync" type="bool" value="false"/> <param if="$(arg localization)" name="Mem/IncrementalMemory" type="string" value="false"/> <param unless="$(arg localization)" name="Mem/IncrementalMemory" type="string" value="true"/> <param name="Mem/InitWMWithAllNodes" type="string" value="$(arg localization)"/> <remap from="scan_cloud" to="assembled_cloud"/> <!-- RTAB-Map's parameters --> <param name="Rtabmap/DetectionRate" type="string" value="1"/> <param name="RGBD/NeighborLinkRefining" type="string" value="false"/> <param name="RGBD/ProximityBySpace" type="string" value="true"/> <param name="RGBD/ProximityMaxGraphDepth" type="string" value="0"/> <param name="RGBD/ProximityPathMaxNeighbors" type="string" value="1"/> <param name="RGBD/AngularUpdate" type="string" value="0.05"/> <param name="RGBD/LinearUpdate" type="string" value="0.05"/> <param name="Mem/NotLinkedNodesKept" type="string" value="false"/> <param name="Mem/STMSize" type="string" value="30"/> <!-- param name="Mem/LaserScanVoxelSize" type="string" value="0.1"/ --> <!-- param name="Mem/LaserScanNormalK" type="string" value="10"/ --> <!-- param name="Mem/LaserScanRadius" type="string" value="0"/ --> <param name="Reg/Strategy" type="string" value="1"/> <param name="Grid/CellSize" type="string" value="0.1"/> <param name="Grid/RangeMax" type="string" value="20"/> <param name="Grid/ClusterRadius" type="string" value="1"/> <param name="Grid/GroundIsObstacle" type="string" value="true"/> <!-- ICP parameters --> <param name="Icp/VoxelSize" type="string" value="0.2"/> <param name="Icp/PointToPlaneK" type="string" value="20"/> <param name="Icp/PointToPlaneRadius" type="string" value="0"/> <param name="Icp/PointToPlane" type="string" value="true"/> <param name="Icp/Iterations" type="string" value="10"/> <param name="Icp/Epsilon" type="string" value="0.001"/> <param name="Icp/MaxTranslation" type="string" value="3"/> <param name="Icp/MaxCorrespondenceDistance" type="string" value="1"/> <param name="Icp/PM" type="string" value="true"/> <param name="Icp/PMOutlierRatio" type="string" value="0.7"/> <param name="Icp/CorrespondenceRatio" type="string" value="0.4"/> </node> <node if="$(arg rtabmapviz)" name="rtabmapviz" pkg="rtabmap_ros" type="rtabmapviz" output="screen"> <param name="frame_id" type="string" value="$(arg frame_id)"/> <param name="odom_frame_id" type="string" value="odom"/> <param name="subscribe_odom_info" type="bool" value="true"/> <param name="subscribe_scan_cloud" type="bool" value="true"/> <param name="approx_sync" type="bool" value="false"/> <remap from="scan_cloud" to="/velodyne_points"/> </node> <node if="$(arg rviz)" name="rviz" pkg="rviz" type="rviz" output="screen"> <param name="frame_id" type="string" value="$(arg frame_id)"/> <param name="odom_frame_id" type="string" value="odom"/> <param name="subscribe_odom_info" type="bool" value="true"/> <param name="subscribe_scan_cloud" type="bool" value="true"/> <param name="approx_sync" type="bool" value="false"/> <remap from="scan_cloud" to="/velodyne_points"/> </node> <node pkg="nodelet" type="nodelet" name="point_cloud_assembler" args="standalone rtabmap_ros/point_cloud_assembler" output="screen"> <remap from="cloud" to="/velodyne_points"/> <remap from="odom" to="odom"/> <param if="$(arg scan_20_hz)" name="max_clouds" type="int" value="20" /> <param unless="$(arg scan_20_hz)" name="max_clouds" type="int" value="10" /> <param name="fixed_frame_id" type="string" value="" /> </node> </group> </launch> <launch> <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen"> <param name="base_global_planner" value="navfn/NavfnROS"/> <param name="base_local_planner" value="dwa_local_planner/DWAPlannerROS"/> <rosparam file="$(find rtabmap_ros)/launch/azimut3/config/costmap_common_params.yaml" command="load" ns="global_costmap" /> <rosparam file="$(find rtabmap_ros)/launch/azimut3/config/costmap_common_params.yaml" command="load" ns="local_costmap" /> <rosparam file="$(find rtabmap_ros)/launch/azimut3/config/local_costmap_params_3d.yaml" command="load" /> <rosparam file="$(find rtabmap_ros)/launch/azimut3/config/global_costmap_params.yaml" command="load" /> <rosparam file="$(find rtabmap_ros)/launch/azimut3/config/base_local_planner_params.yaml" command="load" /> <remap from="odom" to="/rtabmap/odom" /> <remap from="map" to="/rtabmap/cloud_map"/> </node> </launch> #include <ros/ros.h> #include <ros/console.h> #include <serial/serial.h> #include <signal.h> #include <string> #include <sstream> #define DELTAT(_nowtime,_thentime) ((_thentime>_nowtime)?((0xffffffff-_thentime)+_nowtime):(_nowtime-_thentime)) // // cmd_vel subscriber // // Define following to enable cmdvel debug output #define _CMDVEL_DEBUG // Define following to enable motor test mode // Runs both motors in forward direction at 10% of configured maximum (rpms in close_loop mode, power in open_loop mode) // If configured correctly robot should translate forward in a straight line //#define _CMDVEL_FORCE_RUN #include <geometry_msgs/Twist.h> #include <nav_msgs/Odometry.h> #include <tf/transform_broadcaster.h> // // odom publisher // // Define following to enable odom debug output #define _ODOM_DEBUG // Define following to publish additional sensor information #define _ODOM_SENSORS // Define following to enable service for returning covariance //#define _ODOM_COVAR_SERVER #define NORMALIZE(_z) atan2(sin(_z), cos(_z)) #include <tf/tf.h> #include <geometry_msgs/Quaternion.h> #include <tf/transform_broadcaster.h> #include <geometry_msgs/TransformStamped.h> #include <nav_msgs/Odometry.h> #ifdef _ODOM_SENSORS #include <std_msgs/Float32.h> #include <roboteq_diff_msgs/Duplex.h> #endif #ifdef _ODOM_COVAR_SERVER #include "roboteq_diff_msgs/OdometryCovariances.h" #include "rogoteq_diff_msgs/RequestOdometryCovariances.h" #endif void mySigintHandler(int sig) { ROS_INFO("Received SIGINT signal, shutting down..."); ros::shutdown(); } uint32_t millis() { ros::WallTime walltime = ros::WallTime::now(); // return (uint32_t)((walltime._sec*1000 + walltime.nsec/1000000.0) + 0.5); // return (uint32_t)(walltime.toNSec()/1000000.0+0.5); return (uint32_t)(walltime.toNSec()/1000000); } class MainNode { public: MainNode(); public: // // cmd_vel subscriber // void cmdvel_callback( const geometry_msgs::Twist& twist_msg); void cmdvel_setup(); void cmdvel_loop(); void cmdvel_run(); // // odom publisher // void odom_setup(); void odom_stream(); void odom_loop(); //void odom_hs_run(); void odom_ms_run(); void odom_ls_run(); void odom_publish(); #ifdef _ODOM_COVAR_SERVER void odom_covar_callback(const roboteq_diff_msgs::RequestOdometryCovariancesRequest& req, roboteq_diff_msgs::RequestOdometryCovariancesResponse& res); #endif int run(); protected: ros::NodeHandle nh; serial::Serial controller; uint32_t starttime; uint32_t hstimer; uint32_t mstimer; uint32_t lstimer; // // cmd_vel subscriber // ros::Subscriber cmdvel_sub; // // odom publisher // geometry_msgs::TransformStamped tf_msg; tf::TransformBroadcaster odom_broadcaster; nav_msgs::Odometry odom_msg; ros::Publisher odom_pub; #ifdef _ODOM_SENSORS std_msgs::Float32 voltage_msg; ros::Publisher voltage_pub; roboteq_diff_msgs::Duplex current_msg; ros::Publisher current_pub; std_msgs::Float32 energy_msg; ros::Publisher energy_pub; std_msgs::Float32 temperature_msg; ros::Publisher temperature_pub; #endif // buffer for reading encoder counts int odom_idx; char odom_buf[24]; // toss out initial encoder readings char odom_encoder_toss; int32_t odom_encoder_left; int32_t odom_encoder_right; float odom_x; float odom_y; float odom_yaw; float odom_last_x; float odom_last_y; float odom_last_yaw; uint32_t odom_last_time; #ifdef _ODOM_SENSORS float voltage; float current_right; float current_left; float energy; float temperature; uint32_t current_last_time; #endif // settings bool pub_odom_tf; std::string odom_frame; std::string base_frame; std::string cmdvel_topic; std::string odom_topic; std::string port; int baud; bool open_loop; double wheel_circumference; double track_width; int encoder_ppr; int encoder_cpr; double max_amps; int max_rpm; }; MainNode::MainNode() : starttime(0), hstimer(0), mstimer(0), odom_idx(0), odom_encoder_toss(5), odom_encoder_left(0), odom_encoder_right(0), odom_x(0.0), odom_y(0.0), odom_yaw(0.0), odom_last_x(0.0), odom_last_y(0.0), odom_last_yaw(0.0), odom_last_time(0), #ifdef _ODOM_SENSORS voltage(0.0), current_right(0.0), current_left(0.0), energy(0.0), temperature(0.0), current_last_time(0), #endif pub_odom_tf(true), open_loop(false), baud(115200), wheel_circumference(0), track_width(0), encoder_ppr(0), encoder_cpr(0), max_amps(0.0), max_rpm(0) { // CBA Read local params (from launch file) ros::NodeHandle nhLocal("~"); nhLocal.param("pub_odom_tf", pub_odom_tf, true); ROS_INFO_STREAM("pub_odom_tf: " << pub_odom_tf); nhLocal.param<std::string>("odom_frame", odom_frame, "odom"); ROS_INFO_STREAM("odom_frame: " << odom_frame); nhLocal.param<std::string>("base_frame", base_frame, "base_link"); ROS_INFO_STREAM("base_frame: " << base_frame); nhLocal.param<std::string>("cmdvel_topic", cmdvel_topic, "cmd_vel"); ROS_INFO_STREAM("cmdvel_topic: " << cmdvel_topic); nhLocal.param<std::string>("odom_topic", odom_topic, "odom"); ROS_INFO_STREAM("odom_topic: " << odom_topic); nhLocal.param<std::string>("port", port, "/dev/ttyACM0"); ROS_INFO_STREAM("port: " << port); nhLocal.param("baud", baud, 115200); ROS_INFO_STREAM("baud: " << baud); nhLocal.param("open_loop", open_loop, false); ROS_INFO_STREAM("open_loop: " << open_loop); nhLocal.param("wheel_circumference", wheel_circumference, 0.3192); ROS_INFO_STREAM("wheel_circumference: " << wheel_circumference); nhLocal.param("track_width", track_width, 0.4318); ROS_INFO_STREAM("track_width: " << track_width); nhLocal.param("encoder_ppr", encoder_ppr, 900); ROS_INFO_STREAM("encoder_ppr: " << encoder_ppr); nhLocal.param("encoder_cpr", encoder_cpr, 3600); ROS_INFO_STREAM("encoder_cpr: " << encoder_cpr); nhLocal.param("max_amps", max_amps, 5.0); ROS_INFO_STREAM("max_amps: " << max_amps); nhLocal.param("max_rpm", max_rpm, 100); ROS_INFO_STREAM("max_rpm: " << max_rpm); } // // cmd_vel subscriber // void MainNode::cmdvel_callback( const geometry_msgs::Twist& twist_msg) { // wheel speed (m/s) float right_speed = twist_msg.linear.x + track_width * twist_msg.angular.z / 2.0; float left_speed = twist_msg.linear.x - track_width * twist_msg.angular.z / 2.0; #ifdef _CMDVEL_DEBUG ROS_DEBUG_STREAM("cmdvel speed right: " << right_speed << " left: " << left_speed); #endif std::stringstream right_cmd; std::stringstream left_cmd; if (open_loop) { // motor power (scale 0-1000) int32_t right_power = right_speed / wheel_circumference * 60.0 / max_rpm * 1000.0; int32_t left_power = left_speed / wheel_circumference * 60.0 / max_rpm * 1000.0; #ifdef _CMDVEL_DEBUG ROS_DEBUG_STREAM("cmdvel power right: " << right_power << " left: " << left_power); #endif right_cmd << "!G 2 " << right_power << "\r"; left_cmd << "!G 1 " << left_power << "\r"; } else { // motor speed (rpm) int32_t right_rpm = right_speed / wheel_circumference * 60.0; int32_t left_rpm = left_speed / wheel_circumference * 60.0; #ifdef _CMDVEL_DEBUG ROS_DEBUG_STREAM("cmdvel rpm right: " << right_rpm << " left: " << left_rpm); #endif right_cmd << "!S 2 " << right_rpm << "\r"; left_cmd << "!S 1 " << left_rpm << "\r"; } #ifndef _CMDVEL_FORCE_RUN controller.write(right_cmd.str()); controller.write(left_cmd.str()); controller.flush(); #endif } void MainNode::cmdvel_setup() { // stop motors controller.write("!G 2 0\r"); controller.write("!G 1 0\r"); controller.write("!S 1 0\r"); controller.write("!S 2 0\r"); controller.flush(); // disable echo controller.write("^ECHOF 1\r"); controller.flush(); // enable watchdog timer (1000 ms) controller.write("^RWD 1000\r"); // set motor operating mode (1 for closed-loop speed) if (open_loop) { // open-loop speed mode controller.write("^MMOD 1 0\r"); controller.write("^MMOD 2 0\r"); } else { // closed-loop speed mode controller.write("^MMOD 1 1\r"); controller.write("^MMOD 2 1\r"); } // set motor amps limit (A * 10) std::stringstream right_ampcmd; std::stringstream left_ampcmd; right_ampcmd << "^ALIM 1 " << (int)(max_amps * 10) << "\r"; left_ampcmd << "^ALIM 2 " << (int)(max_amps * 10) << "\r"; controller.write(right_ampcmd.str()); controller.write(left_ampcmd.str()); // set max speed (rpm) for relative speed commands std::stringstream right_rpmcmd; std::stringstream left_rpmcmd; right_rpmcmd << "^MXRPM 1 " << max_rpm << "\r"; left_rpmcmd << "^MXRPM 2 " << max_rpm << "\r"; controller.write(right_rpmcmd.str()); controller.write(left_rpmcmd.str()); // set max acceleration rate (2000 rpm/s * 10) controller.write("^MAC 1 20000\r"); controller.write("^MAC 2 20000\r"); // set max deceleration rate (2000 rpm/s * 10) controller.write("^MDEC 1 20000\r"); controller.write("^MDEC 2 20000\r"); // set PID parameters (gain * 10) controller.write("^KP 1 10\r"); controller.write("^KP 2 10\r"); controller.write("^KI 1 80\r"); controller.write("^KI 2 80\r"); controller.write("^KD 1 0\r"); controller.write("^KD 2 0\r"); // set encoder mode (18 for feedback on motor1, 34 for feedback on motor2) controller.write("^EMOD 1 18\r"); controller.write("^EMOD 2 34\r"); // set encoder counts (ppr) std::stringstream right_enccmd; std::stringstream left_enccmd; right_enccmd << "^EPPR 1 " << encoder_ppr << "\r"; left_enccmd << "^EPPR 2 " << encoder_ppr << "\r"; controller.write(right_enccmd.str()); controller.write(left_enccmd.str()); controller.flush(); ROS_INFO_STREAM("Subscribing to topic " << cmdvel_topic); cmdvel_sub = nh.subscribe(cmdvel_topic, 1000, &MainNode::cmdvel_callback, this); } void MainNode::cmdvel_loop() { } void MainNode::cmdvel_run() { #ifdef _CMDVEL_FORCE_RUN if (open_loop) { controller.write("!G 2 100\r"); controller.write("!G 1 100\r"); } else { std::stringstream right_cmd; std::stringstream left_cmd; right_cmd << "!S 2 " << (int)(max_rpm * 0.1) << "\r"; left_cmd << "!S 1 " << (int)(max_rpm * 0.1) << "\r"; controller.write(right_cmd.str()); controller.write(left_cmd.str()); } controller.flush(); #endif } // // odom publisher // #ifdef _ODOM_COVAR_SERVER void MainNode::odom_covar_callback(const roboteq_diff_msgs::RequestOdometryCovariancesRequest& req, roboteq_diff_msgs::RequestOdometryCovariancesResponse& res) { res.odometry_covariances.pose.pose.covariance[0] = 0.001; res.odometry_covariances.pose.pose.covariance[7] = 0.001; res.odometry_covariances.pose.pose.covariance[14] = 1000000; res.odometry_covariances.pose.pose.covariance[21] = 1000000; res.odometry_covariances.pose.pose.covariance[28] = 1000000; res.odometry_covariances.pose.pose.covariance[35] = 1000; res.odometry_covariances.twist.twist.covariance[0] = 0.001; res.odometry_covariances.twist.twist.covariance[7] = 0.001; res.odometry_covariances.twist.twist.covariance[14] = 1000000; res.odometry_covariances.twist.twist.covariance[21] = 1000000; res.odometry_covariances.twist.twist.covariance[28] = 1000000; res.odometry_covariances.twist.twist.covariance[35] = 1000; } #endif /* position.pose.covariance = boost::assign::list_of(1e-3) (0) (0) (0) (0) (0) (0) (1e-3) (0) (0) (0) (0) (0) (0) (1e6) (0) (0) (0) (0) (0) (0) (1e6) (0) (0) (0) (0) (0) (0) (1e6) (0) (0) (0) (0) (0) (0) (1e3) ; position.twist.covariance = boost::assign::list_of(1e-3) (0) (0) (0) (0) (0) (0) (1e-3) (0) (0) (0) (0) (0) (0) (1e6) (0) (0) (0) (0) (0) (0) (1e6) (0) (0) (0) (0) (0) (0) (1e6) (0) (0) (0) (0) (0) (0) (1e3) ; To estimate velocity covariance you should know TICKSMM (128) and SIPCYCLE (100) parameters of your robot (written in your robots flash memory and not accessible with Aria). First parameter tells you how many encoder impulses (count) gets generated by your robot's forward movement of 1 mm. Second parameter tells you number of milliseconds between two consecutive Server Information Packets from your robot. The values in the parentheses are for P3-DX (ARCOS). So an error in determining velocity could come from missing an encoder impulse in a cycle. This would result in 1/TICKSMM/SIPCYCLE velocity error (mm/ms or m/s) for one wheel. For P3-DX parameters above, this value is 7.8125e-05. Note that you would err by the same absolute amount of velocity in the next cycle. Gearbox also plays a role in velocity error, but you would need to measure to find the exact amount. As a rule of thumb, I would at least double the previous amount in order to include gearbox error. Now that we have determined maximum error of a single wheel's (transversal) velocity, i.e. we expect 99.7% of errors to be less than this number, we can determine sigma = max_err/3 and C = sigma^2. Translational and rotational velocities are determined from left and right wheel velocities like this: v = (v_R + v_L)/2 w = (v_R - v_L)/(2d) So the covariance for transversal velocity would be (1/2)^2 2C and the covariance for rotational velocity would be (1/(2d))^2 2C. The d parameter iS 1/DiffConvFactor and is accessible from Aria (ArRobot::getDiffConvFactor()). */ void MainNode::odom_setup() { if ( pub_odom_tf ) { ROS_INFO("Broadcasting odom tf"); // odom_broadcaster.init(nh); // ??? } ROS_INFO_STREAM("Publishing to topic " << odom_topic); odom_pub = nh.advertise<nav_msgs::Odometry>(odom_topic, 1000); #ifdef _ODOM_COVAR_SERVER ROS_INFO("Advertising service on roboteq/odom_covar_srv"); odom_covar_server = nh.advertiseService("roboteq/odom_covar_srv", &MainNode::odom_covar_callback, this); #endif #ifdef _ODOM_SENSORS ROS_INFO("Publishing to topic roboteq/voltage"); voltage_pub = nh.advertise<std_msgs::Float32>("roboteq/voltage", 1000); ROS_INFO("Publishing to topic roboteq/current"); current_pub = nh.advertise<roboteq_diff_msgs::Duplex>("roboteq/current", 1000); ROS_INFO("Publishing to topic roboteq/energy"); energy_pub = nh.advertise<std_msgs::Float32>("roboteq/energy", 1000); ROS_INFO("Publishing to topic roboteq/temperature"); temperature_pub = nh.advertise<std_msgs::Float32>("roboteq/temperature", 1000); #endif tf_msg.header.seq = 0; tf_msg.header.frame_id = odom_frame; tf_msg.child_frame_id = base_frame; odom_msg.header.seq = 0; odom_msg.header.frame_id = odom_frame; odom_msg.child_frame_id = base_frame; odom_msg.pose.covariance.assign(0); odom_msg.pose.covariance[0] = 0.001; odom_msg.pose.covariance[7] = 0.001; odom_msg.pose.covariance[14] = 1000000; odom_msg.pose.covariance[21] = 1000000; odom_msg.pose.covariance[28] = 1000000; odom_msg.pose.covariance[35] = 1000; odom_msg.twist.covariance.assign(0); odom_msg.twist.covariance[0] = 0.001; odom_msg.twist.covariance[7] = 0.001; odom_msg.twist.covariance[14] = 1000000; odom_msg.twist.covariance[21] = 1000000; odom_msg.twist.covariance[28] = 1000000; odom_msg.twist.covariance[35] = 1000; #ifdef _ODOM_SENSORS // voltage_msg.header.seq = 0; // voltage_msg.header.frame_id = 0; // current_msg.header.seq = 0; // current_msg.header.frame_id = 0; // energy_msg.header.seq = 0; // energy_msg.header.frame_id = 0; // temperature_msg.header.seq = 0; // temperature_msg.header.frame_id = 0; #endif // start encoder streaming odom_stream(); odom_last_time = millis(); #ifdef _ODOM_SENSORS current_last_time = millis(); #endif } void MainNode::odom_stream() { #ifdef _ODOM_SENSORS // start encoder and current output (30 hz) // doubling frequency since one value is output at each cycle // controller.write("# C_?CR_?BA_# 17\r"); // start encoder, current and voltage output (30 hz) // tripling frequency since one value is output at each cycle controller.write("# C_?CR_?BA_?V_# 11\r"); #else // // start encoder output (10 hz) // controller.write("# C_?CR_# 100\r"); // start encoder output (30 hz) controller.write("# C_?CR_# 33\r"); // // start encoder output (50 hz) // controller.write("# C_?CR_# 20\r"); #endif controller.flush(); } void MainNode::odom_loop() { uint32_t nowtime = millis(); // if we haven't received encoder counts in some time then restart streaming if( DELTAT(nowtime,odom_last_time) >= 1000 ) { odom_stream(); odom_last_time = nowtime; } // read sensor data stream from motor controller if (controller.available()) { char ch = 0; if ( controller.read((uint8_t*)&ch, 1) == 0 ) return; if (ch == '\r') { odom_buf[odom_idx] = 0; #ifdef _ODOM_DEBUG //ROS_DEBUG_STREAM( "line: " << odom_buf ); #endif // CR= is encoder counts if ( odom_buf[0] == 'C' && odom_buf[1] == 'R' && odom_buf[2] == '=' ) { int delim; for ( delim = 3; delim < odom_idx; delim++ ) { if ( odom_encoder_toss > 0 ) { --odom_encoder_toss; break; } if (odom_buf[delim] == ':') { odom_buf[delim] = 0; odom_encoder_right = (int32_t)strtol(odom_buf+3, NULL, 10); odom_encoder_left = (int32_t)strtol(odom_buf+delim+1, NULL, 10); #ifdef _ODOM_DEBUG ROS_DEBUG_STREAM("encoder right: " << odom_encoder_right << " left: " << odom_encoder_left); #endif odom_publish(); break; } } } #ifdef _ODOM_SENSORS // V= is voltages else if ( odom_buf[0] == 'V' && odom_buf[1] == '=' ) { int count = 0; int start = 2; for ( int delim = 2; delim <= odom_idx; delim++ ) { if (odom_buf[delim] == ':' || odom_buf[delim] == 0) { odom_buf[delim] = 0; /* switch (count) { case 0: // odom_internal_voltage = (float)strtol(odom_buf+start, NULL, 10) / 10.0; break; case 1: voltage = (float)strtol(odom_buf+start, NULL, 10) / 10.0; break; case 2: // odom_aux_voltage = (float)strtol(odom_buf+start, NULL, 1000.0); break; } */ if ( count == 1 ) { voltage = (float)strtol(odom_buf+start, NULL, 10) / 10.0; #ifdef _ODOM_DEBUG //ROS_DEBUG_STREAM("voltage: " << voltage); #endif break; } start = delim + 1; count++; } } } // BA= is motor currents else if ( odom_buf[0] == 'B' && odom_buf[1] == 'A' && odom_buf[2] == '=' ) { int delim; for ( delim = 3; delim < odom_idx; delim++ ) { if (odom_buf[delim] == ':') { odom_buf[delim] = 0; current_right = (float)strtol(odom_buf+3, NULL, 10) / 10.0; current_left = (float)strtol(odom_buf+delim+1, NULL, 10) / 10.0; #ifdef _ODOM_DEBUG //ROS_DEBUG_STREAM("current right: " << current_right << " left: " << current_left); #endif // determine delta time in seconds float dt = (float)DELTAT(nowtime,current_last_time) / 1000.0; current_last_time = nowtime; energy += (current_right + current_left) * dt / 3600.0; break; } } } #endif odom_idx = 0; } else if ( odom_idx < (sizeof(odom_buf)-1) ) { odom_buf[odom_idx++] = ch; } } } //void MainNode::odom_hs_run() //{ //} void MainNode::odom_ms_run() { #ifdef _ODOM_SENSORS // current_msg.header.seq++; // current_msg.header.stamp = ros::Time::now(); current_msg.a = current_right; current_msg.b = current_left; current_pub.publish(current_msg); #endif } void MainNode::odom_ls_run() { #ifdef _ODOM_SENSORS // voltage_msg.header.seq++; // voltage_msg.header.stamp = ros::Time::now(); voltage_msg.data = voltage; voltage_pub.publish(voltage_msg); // energy_msg.header.seq++; // energy_msg.header.stamp = ros::Time::now(); energy_msg.data = energy; energy_pub.publish(energy_msg); // temperature_msg.header.seq++; // temperature_msg.header.stamp = ros::Time::now(); temperature_msg.data = temperature; temperature_pub.publish(temperature_msg); #endif } void MainNode::odom_publish() { // determine delta time in seconds uint32_t nowtime = millis(); float dt = (float)DELTAT(nowtime,odom_last_time) / 1000.0; odom_last_time = nowtime; #ifdef _ODOM_DEBUG /* ROS_DEBUG("right: "); ROS_DEBUG(odom_encoder_right); ROS_DEBUG(" left: "); ROS_DEBUG(odom_encoder_left); ROS_DEBUG(" dt: "); ROS_DEBUG(dt); ROS_DEBUG(""); */ #endif // determine deltas of distance and angle float linear = ((float)odom_encoder_right / (float)encoder_cpr * wheel_circumference + (float)odom_encoder_left / (float)encoder_cpr * wheel_circumference) / 2.0; // float angular = ((float)odom_encoder_right / (float)encoder_cpr * wheel_circumference - (float)odom_encoder_left / (float)encoder_cpr * wheel_circumference) / track_width * -1.0; float angular = ((float)odom_encoder_right / (float)encoder_cpr * wheel_circumference - (float)odom_encoder_left / (float)encoder_cpr * wheel_circumference) / track_width; #ifdef _ODOM_DEBUG /* ROS_DEBUG("linear: "); ROS_DEBUG(linear); ROS_DEBUG(" angular: "); ROS_DEBUG(angular); ROS_DEBUG(""); */ #endif // Update odometry odom_x += linear * cos(odom_yaw); // m odom_y += linear * sin(odom_yaw); // m odom_yaw = NORMALIZE(odom_yaw + angular); // rad #ifdef _ODOM_DEBUG //ROS_DEBUG_STREAM( "odom x: " << odom_x << " y: " << odom_y << " yaw: " << odom_yaw ); #endif // Calculate velocities float vx = (odom_x - odom_last_x) / dt; float vy = (odom_y - odom_last_y) / dt; float vyaw = (odom_yaw - odom_last_yaw) / dt; #ifdef _ODOM_DEBUG //ROS_DEBUG_STREAM( "velocity vx: " << odom_x << " vy: " << odom_y << " vyaw: " << odom_yaw ); #endif odom_last_x = odom_x; odom_last_y = odom_y; odom_last_yaw = odom_yaw; #ifdef _ODOM_DEBUG /* ROS_DEBUG("vx: "); ROS_DEBUG(vx); ROS_DEBUG(" vy: "); ROS_DEBUG(vy); ROS_DEBUG(" vyaw: "); ROS_DEBUG(vyaw); ROS_DEBUG(""); */ #endif geometry_msgs::Quaternion quat = tf::createQuaternionMsgFromYaw(odom_yaw); if ( pub_odom_tf ) { tf_msg.header.seq++; tf_msg.header.stamp = ros::Time::now(); tf_msg.transform.translation.x = odom_x; tf_msg.transform.translation.y = odom_y; tf_msg.transform.translation.z = 0.0; tf_msg.transform.rotation = quat; odom_broadcaster.sendTransform(tf_msg); } odom_msg.header.seq++; odom_msg.header.stamp = ros::Time::now(); odom_msg.pose.pose.position.x = odom_x; odom_msg.pose.pose.position.y = odom_y; odom_msg.pose.pose.position.z = 0.0; odom_msg.pose.pose.orientation = quat; odom_msg.twist.twist.linear.x = vx; odom_msg.twist.twist.linear.y = vy; odom_msg.twist.twist.linear.z = 0.0; odom_msg.twist.twist.angular.x = 0.0; odom_msg.twist.twist.angular.y = 0.0; odom_msg.twist.twist.angular.z = vyaw; odom_pub.publish(odom_msg); } int MainNode::run() { ROS_INFO("Beginning setup..."); serial::Timeout timeout = serial::Timeout::simpleTimeout(1000); controller.setPort(port); controller.setBaudrate(baud); controller.setTimeout(timeout); // TODO: support automatic re-opening of port after disconnection while ( ros::ok() ) { ROS_INFO_STREAM("Opening serial port on " << port << " at " << baud << "..." ); try { controller.open(); if ( controller.isOpen() ) { ROS_INFO("Successfully opened serial port"); break; } } catch (serial::IOException e) { ROS_WARN_STREAM("serial::IOException: " << e.what()); } ROS_WARN("Failed to open serial port"); sleep( 5 ); } cmdvel_setup(); odom_setup(); starttime = millis(); hstimer = starttime; mstimer = starttime; lstimer = starttime; // ros::Rate loop_rate(10); ROS_INFO("Beginning looping..."); while (ros::ok()) { cmdvel_loop(); odom_loop(); uint32_t nowtime = millis(); //ROS_INFO_STREAM("loop nowtime: " << nowtime << " lstimer: " << lstimer << " delta: " << DELTAT(nowtime,lstimer) << " / " << (nowtime-lstimer)); //uint32_t delta = DELTAT(nowtime,lstimer); //ROS_INFO_STREAM("loop nowtime: " << nowtime << " lstimer: " << lstimer << " delta: " << delta << " / " << (nowtime-lstimer)); // // Handle 50 Hz publishing // if (DELTAT(nowtime,hstimer) >= 20) // Handle 30 Hz publishing if (DELTAT(nowtime,hstimer) >= 33) { hstimer = nowtime; // odom_hs_run(); } // Handle 10 Hz publishing if (DELTAT(nowtime,mstimer) >= 100) { mstimer = nowtime; cmdvel_run(); odom_ms_run(); } // Handle 1 Hz publishing if (DELTAT(nowtime,lstimer) >= 1000) { lstimer = nowtime; odom_ls_run(); } ros::spinOnce(); // loop_rate.sleep(); } if ( controller.isOpen() ) controller.close(); ROS_INFO("Exiting"); return 0; } int main(int argc, char **argv) { ros::init(argc, argv, "main_node"); MainNode node; // Override the default ros sigint handler. // This must be set after the first NodeHandle is created. signal(SIGINT, mySigintHandler); return node.run(); } |
Administrator
|
From the Tf tree, you have two disjoint odometries. I would start by using your odometry. Remove icp_odometry node, and link with a static_transform_publisher the velodyne on the base_link.
For rtabmap node, use your odometry and set frame_id to base_link. Don't use the assembled cloud at first to make a simple working example. Feed velodyne_points to rtabmap directly. cheers, Mathieu |
Hi Matthieu merry christmas!!
I was able to get my transform viewframes to work
And everything looks to work fine and my robot can navigate using the navigation stack
However, the problem i am getting now is that my global costmap is just a white square and i can only navigate within the white square
image for reference:
however in rtabmap database viewer i am able to see a very nice 2d map that i want to use for my global costmap
image for reference:
these are my move_base.launch yaml files
costmap_common_params.yaml:
footprint: [[ 0.3, 0.3], [-0.3, 0.3], [-0.3, -0.3], [ 0.3, -0.3]] footprint_padding: 0.02 #robot_radius: 0.38 #robot_radius: ir_of_robot inflation_layer: inflation_radius: 0.7 # 2xfootprint, it helps to keep the global planned path farther from obstacles transform_tolerance: 2 global_costmap: global_frame: map robot_base_frame: base_footprint update_frequency: 5 publish_frequency: 5 always_send_full_costmap: false static_map: true local_costmap: global_frame: odom robot_base_frame: base_footprint update_frequency: 5 publish_frequency: 5 static_map: false rolling_window: true width: 5.0 height: 5.0 resolution: 0.025 origin_x: -2.0 origin_y: -2.0 #observation_sources: laser_scan_sensor point_cloud_sensor observation_sources: point_cloud_sensor laser_scan_sensor: { data_type: LaserScan, topic: base_scan, expected_update_rate: 0.2, marking: true, clearing: true} # assuming receiving a cloud from rtabmap/obstacles_detection node point_cloud_sensor: { sensor_frame: base_footprint, data_type: PointCloud2, topic: /velodyne_points, expected_update_rate: 2, marking: true, clearing: true, min_obstacle_height: -99999.0, max_obstacle_height: 0.5} DWAPlannerROS: # Current limits based on AZ3 standalone configuration. acc_lim_x: 0.75 acc_lim_y: 0.75 acc_lim_theta: 4 # min_vel_x and max_rotational_vel were set to keep the ICR at # minimal distance of 0.48 m. # Basically, max_rotational_vel * rho_min <= min_vel_x max_vel_x: 1 min_vel_x: 0.5 max_vel_theta: 0.5 min_vel_theta: -0.5 min_in_place_vel_theta: 0.5 holonomic_robot: true xy_goal_tolerance: 0.25 yaw_goal_tolerance: 0.25 latch_xy_goal_tolerance: true # make sure that the minimum velocity multiplied by the sim_period is less than twice the tolerance on a goal. Otherwise, the robot will prefer to rotate in place just outside of range of its target position rather than moving towards the goal. sim_time: 1.5 # set between 1 and 2. The higher he value, the smoother the path (though more samples would be required). sim_granularity: 0.025 angular_sim_granularity: 0.05 vx_samples: 12 vtheta_samples: 20 meter_scoring: true pdist_scale: 0.7 # The higher will follow more the global path. gdist_scale: 0.8 occdist_scale: 0.01 publish_cost_grid_pc: false #move_base controller_frequency: 10.0 #The robot can move faster when higher. #global planner NavfnROS: allow_unknown: true visualize_potential: false |
Administrator
|
Do you mean that the global costmap is wrong, or /rtabmap/grid_map? The tf tree is also wrong, in icp_odometry, set frame_id to base_footprint. Invert also base_link and base_footprint. You should have a tf tree like this:
map -> odom -> base_footprint -> base_link -> velodyne |
Hi matthieu,
i realised why my global costmap was not showing, it was because in my move_base.launch i did not include remap /map to /rtabmap/grid_map, so once i did that everything was working well. However when trying to change icp frame_id to base_footprint it did not manage to launch properly. but so far my robot is able to navigate through with the current settings |
In reply to this post by matlabbe
Also i was wondering, rtabmap takes my sensor as ground level but actually my velodyne sensor is 1.8meters above the ground, how do i define how high up my sensor is?
|
Administrator
|
As I said, the " The tf tree is also wrong", you should fix it. If icp_odom is using base_footprint, and TF base_footprint -> velodyne is 1.8 meters, the odometry will be at ground level.
|
Thank you matthieu,
i managed to get my tf_frames to be correct this time
<launch> <!-- Hand-held 3D lidar mapping example using only a Velodyne PUCK (no camera). Prerequisities: rtabmap should be built with libpointmatcher Example: $ roslaunch rtabmap_ros test_velodyne.launch $ rosrun rviz rviz -f map $ Show TF and /rtabmap/cloud_map topics --> <arg name="rtabmapviz" default="false"/> <arg name="rviz" default="true"/> <arg name="localization" default="true"/> <arg name="use_imu" default="false"/> <!-- Assuming IMU fixed to lidar with /velodyne -> /imu_link TF --> <arg name="imu_topic" default="/imu/data"/> <arg name="scan_20_hz" default="false"/> <!-- If we launch the velodyne with "rpm:=1200" argument --> <arg name="use_sim_time" default="false"/> <param if="$(arg use_sim_time)" name="use_sim_time" value="true"/> <arg name="frame_id" default="velodyne"/> <include file="$(find velodyne_pointcloud)/launch/VLP16_points.launch"> <arg if="$(arg scan_20_hz)" name="rpm" value="1200"/> <arg unless="$(arg scan_20_hz)" name="rpm" value="600"/> </include> <node pkg="tf" type="static_transform_publisher" name="base_footprint_to_base_link" args="0 0 0 0.0 0.0 0.0 /base_footprint /base_link 1000" /> <node pkg="tf" type="static_transform_publisher" name="base_link_to_velodyne" args="0 0 1.8 0.0 0.0 0.0 /base_link /velodyne 1000" /> <group ns="rtabmap"> <node pkg="rtabmap_ros" type="icp_odometry" name="icp_odometry" output="screen"> <remap from="scan_cloud" to="/velodyne_points"/> <param name="frame_id" type="string" value="base_footprint"/> <param name="odom_frame_id" type="string" value="odom"/> <param if="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="25"/> <param unless="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="15"/> <remap if="$(arg use_imu)" from="imu" to="$(arg imu_topic)"/> <param if="$(arg use_imu)" name="guess_frame_id" type="string" value="$(arg frame_id)_stabilized"/> <param if="$(arg use_imu)" name="wait_imu_to_init" type="bool" value="true"/> <!-- ICP parameters --> <param name="publish_tf" type="bool" value="true"/> <param name="tf_delay" type="double" value="0.02"/> <param name="Icp/PointToPlane" type="string" value="true"/> <param name="Icp/Iterations" type="string" value="10"/> <param name="Icp/VoxelSize" type="string" value="0.4"/> <param name="Icp/DownsamplingStep" type="string" value="1"/> <!-- cannot be increased with ring-like lidar --> <param name="Icp/Epsilon" type="string" value="0.001"/> <param name="Icp/PointToPlaneK" type="string" value="20"/> <param name="Icp/PointToPlaneRadius" type="string" value="0"/> <param name="Icp/MaxTranslation" type="string" value="2"/> <param name="Icp/MaxCorrespondenceDistance" type="string" value="1"/> <param name="Icp/PM" type="string" value="true"/> <param name="Icp/PMOutlierRatio" type="string" value="0.7"/> <param name="Icp/CorrespondenceRatio" type="string" value="0.01"/> <!-- Odom parameters --> <param name="Odom/ScanKeyFrameThr" type="string" value="0.9"/> <param name="Odom/Strategy" type="string" value="0"/> <param name="OdomF2M/ScanSubtractRadius" type="string" value="0.2"/> <param name="OdomF2M/ScanMaxSize" type="string" value="15000"/> </node> <node pkg="rtabmap_ros" type="rtabmap" name="rtabmap" output="screen" args=""> <param name="use_action_for_goal" type="bool" value="true"/> <remap from="move_base" to="/move_base"/> <param name="tf_delay" type="double" value="0.02"/> <param name="frame_id" type="string" value="$(arg frame_id)"/> <param name="subscribe_depth" type="bool" value="false"/> <param name="subscribe_rgb" type="bool" value="false"/> <param name="subscribe_scan_cloud" type="bool" value="true"/> <param name="approx_sync" type="bool" value="false"/> <param if="$(arg localization)" name="Mem/IncrementalMemory" type="string" value="false"/> <param unless="$(arg localization)" name="Mem/IncrementalMemory" type="string" value="true"/> <param name="Mem/InitWMWithAllNodes" type="string" value="$(arg localization)"/> <remap from="scan_cloud" to="assembled_cloud"/> <!-- RTAB-Map's parameters --> <param name="Rtabmap/DetectionRate" type="string" value="1"/> <param name="RGBD/NeighborLinkRefining" type="string" value="false"/> <param name="RGBD/ProximityBySpace" type="string" value="true"/> <param name="RGBD/ProximityMaxGraphDepth" type="string" value="0"/> <param name="RGBD/ProximityPathMaxNeighbors" type="string" value="1"/> <param name="RGBD/AngularUpdate" type="string" value="0.05"/> <param name="RGBD/LinearUpdate" type="string" value="0.05"/> <param name="Mem/NotLinkedNodesKept" type="string" value="false"/> <param name="Mem/STMSize" type="string" value="30"/> <!-- param name="Mem/LaserScanVoxelSize" type="string" value="0.1"/ --> <!-- param name="Mem/LaserScanNormalK" type="string" value="10"/ --> <!-- param name="Mem/LaserScanRadius" type="string" value="0"/ --> <param name="Reg/Strategy" type="string" value="1"/> <param name="Grid/CellSize" type="string" value="0.1"/> <param name="Grid/RangeMax" type="string" value="50"/> <param name="Grid/ClusterRadius" type="string" value="1"/> <param name="Grid/GroundIsObstacle" type="string" value="false"/> <!-- ICP parameters --> <param name="Icp/VoxelSize" type="string" value="0.2"/> <param name="Icp/PointToPlaneK" type="string" value="20"/> <param name="Icp/PointToPlaneRadius" type="string" value="0"/> <param name="Icp/PointToPlane" type="string" value="true"/> <param name="Icp/Iterations" type="string" value="10"/> <param name="Icp/Epsilon" type="string" value="0.001"/> <param name="Icp/MaxTranslation" type="string" value="3"/> <param name="Icp/MaxCorrespondenceDistance" type="string" value="1"/> <param name="Icp/PM" type="string" value="true"/> <param name="Icp/PMOutlierRatio" type="string" value="0.7"/> <param name="Icp/CorrespondenceRatio" type="string" value="0.4"/> </node> <node if="$(arg rtabmapviz)" name="rtabmapviz" pkg="rtabmap_ros" type="rtabmapviz" output="screen"> <param name="frame_id" type="string" value="$(arg frame_id)"/> <param name="odom_frame_id" type="string" value="odom"/> <param name="subscribe_odom_info" type="bool" value="true"/> <param name="subscribe_scan_cloud" type="bool" value="true"/> <param name="approx_sync" type="bool" value="false"/> <remap from="scan_cloud" to="/velodyne_points"/> </node> <node if="$(arg rviz)" name="rviz" pkg="rviz" type="rviz" output="screen"> <param name="frame_id" type="string" value="$(arg frame_id)"/> <param name="odom_frame_id" type="string" value="odom"/> <param name="subscribe_odom_info" type="bool" value="true"/> <param name="subscribe_scan_cloud" type="bool" value="true"/> <param name="approx_sync" type="bool" value="false"/> <remap from="scan_cloud" to="/velodyne_points"/> </node> <node pkg="nodelet" type="nodelet" name="point_cloud_assembler" args="standalone rtabmap_ros/point_cloud_assembler" output="screen"> <remap from="cloud" to="/velodyne_points"/> <remap from="odom" to="odom"/> <param if="$(arg scan_20_hz)" name="max_clouds" type="int" value="20" /> <param unless="$(arg scan_20_hz)" name="max_clouds" type="int" value="10" /> <param name="fixed_frame_id" type="string" value="" /> </node> </group> </launch> |
also, is there a way to ensure my map isnt rotated about the x and y axis, for example i want the 3D map to always be in the XY axis because sometimes the map made is rotated and this causes me to have really distorted map
|
Administrator
|
If velodyne point clouds are published at 10 Hz, odom will not publish faster than 10 Hz. You could launch velodyne at 1200 rpm and enable scan_20_hz (you then could get 20 Hz odom if you computer is able to process all scans under 50 ms).
It could be also possible to adjust your local obstacle layer in the costmap to wait 100 ms for transform. For the rotation, do you use a ground robot? If so, adjust TF between base_link and velodyne to include the orientation of the velodyne. Regards, Mathieu |
Free forum by Nabble | Edit this page |