一、把kidnap_pose2d做为cartographer的初始位姿
如何把kidnap_pose2d应用到cartographer?想到两种方法。
- 当前会有个cartographer_node节点正在运行,告诉这个node,把kidnap_pose2d设为当前位姿。只要想,这自然可以做到,但需要修改较多cartographer源码。举个例子,PoseGraph2D有两个变量:data_.submap_data、data_.global_submap_poses_2d,如果要用一个外界强加的位姿,这两个变量必须清空,目前没有清空代码。换个思路,把PoseGraph2D销毁再创建?——PoseGraph2D存在于MapBuilder,它们可说有着一样的生存期。一运行cartographer_node节点就会创建个MapBuilder,节点运行期间MapBuilder必须一直有效。换句话说,节点运行期间PoseGraph2D必须一直有效,所以也不可能通过运行时重创建PoseGraph2D方法来设置特定位姿。
- 销毁cartographer_node节点,然后以kidnap_pose2d为初始位姿,重新运行该节点。
相对来说,方法1更自然,效率也更高,但它要改的源码过多。第二种方法则要看销毁、运行cartographer_node节点开销。目前用的是第二种。
cartographer存在两个tf:odom-->map的tf、base_footprint-->odom的tf,kidnap_pose2d是作为odom-->map这个tf的初始值。
向cartographer设置个非0初始位姿,涉及到两处。
1.1 MapBuilder::AddTrajectoryBuilder

图1显示了MapBuilder::AddTrajectoryBuilder调用时机。1)它运行在cartographer_node主线程。2)在主线程进入“cbqueue.callAvailable(timeout)”while循环前被调用。
<cartographer>/cartographer/mapping/map_builder.cc ------ int MapBuilder::AddTrajectoryBuilder( const std::set<SensorId>& expected_sensor_ids, const proto::TrajectoryBuilderOptions& trajectory_options, LocalSlamResultCallback local_slam_result_callback) { const int trajectory_id = trajectory_builders_.size(); ... if (trajectory_options.has_initial_trajectory_pose()) { const auto& initial_trajectory_pose = trajectory_options.initial_trajectory_pose(); pose_graph_->SetInitialTrajectoryPose( trajectory_id, initial_trajectory_pose.to_trajectory_id(), transform::ToRigid3(initial_trajectory_pose.relative_pose()), common::FromUniversal(initial_trajectory_pose.timestamp()));
cartographer支持设置初始位姿,用的应该是修改lua配置。可我还没能正确用起来。
} if (ros::kidnap_pose2d.valid) { const tpose2d& pose2d = ros::kidnap_pose2d; int to_trajectory_id = 0; const cartographer::transform::Rigid3d relative_pose(Eigen::Vector3d(pose2d.x, pose2d.y, 0), Eigen::AngleAxisd(pose2d.yaw, Eigen::Vector3d(0, 0, 1))); int64_t timestamp = 0; pose_graph_->SetInitialTrajectoryPose( trajectory_id, to_trajectory_id, relative_pose, common::FromUniversal(timestamp));
这是现在在用的设置初始位姿方法。同样是调用pose_graph_->SetInitialTrajectoryPose,然后按要求把kidnap_pose2d转换为它须要的参数。
SDL_Log("use kidnap_pose: (%.5f, %.5f)-deg(%.5f)", pose2d.x, pose2d.y, RAD2DEG(pose2d.yaw)); } ... }
cartographer支持设置初始位姿,用的应该是修改lua配置。可我还没能正确用起来,也就用了种替代方法。将来等会了,应该会回到lua配置。
1.2 PoseGraph2D::ComputeLocalToGlobalTransform

图2显示了MapBuilder::AddTrajectoryBuilder调用时机:AddSensorData --> PoseGraph2D::AddNode --> PoseGraph2D::GetLocalToGlobalTransform --> 本函数
示例中是第一次调用ComputeLocalToGlobalTransform,因而此时data_.trajectory_nodes、data_.global_submap_poses_2d(即参数global_submap_poses)都是空。
<cartographer>/cartographer/mapping/internal/2d/pose_graph_2d.cc ------ transform::Rigid3d PoseGraph2D::ComputeLocalToGlobalTransform( const MapById<SubmapId, optimization::SubmapSpec2D>& global_submap_poses, const int trajectory_id) const { auto begin_it = global_submap_poses.BeginOfTrajectory(trajectory_id); auto end_it = global_submap_poses.EndOfTrajectory(trajectory_id); if (begin_it == end_it) { const auto it = data_.initial_trajectory_poses.find(trajectory_id); if (it != data_.initial_trajectory_poses.end()) { return it->second.relative_pose;
上面是我改成的代码,下面注释掉是cartographer官方源码。当在data_.trajectory_nodes是空时,也就是图2显示那种情况,GetInterpolatedGlobalTrajectoryPose会抛出异常。
/* return GetInterpolatedGlobalTrajectoryPose(it->second.to_trajectory_id, it->second.time) * it->second.relative_pose; */ } else { return transform::Rigid3d::Identity(); } } ,,, }
经过修改后,time字段就失去了作用。