对于我想为一堆模拟机器人发布 odom tfs 的特定情况,我尝试将“/tf”主题重新映射到机器人特定主题,例如 /robot1_tf、/robot2_tf 等。更准确地说,在一个控制循环的某个地方,在一个通用过程中模拟我的所有机器人,我这样做是为了喂养 amcl:
tf::TransformBroadcaster* tfBroadcaster;
void ROSInterface::sendOdomData(const Vector<OdomData> &odomData)
{
for (uint i = 0; i < odomData.size(); i++) // process odomData for every simulated robot
{
// Publish the odom message.
nav_msgs::Odometry odom;
odom.header.stamp = ros::Time::now();
odom.header.frame_id = "odom";
odom.child_frame_id = "base_link";
odom.pose.pose.position.x = odomData[i].x;
odom.pose.pose.position.y = odomData[i].y;
odom.pose.pose.position.z = 0.0;
odom.pose.pose.orientation = tf::createQuaternionMsgFromYaw(odomData[i].theta);
odom.twist.twist.linear.x = odomData[i].vx;
odom.twist.twist.linear.y = odomData[i].vy;
odom.twist.twist.angular.z = odomData[i].omega;
odomPub[i].publish(odom);
// Publish the odom transform with the TransformBroadcaster.
geometry_msgs::TransformStamped odom_tf;
odom_tf.header = odom.header;
odom_tf.child_frame_id = "base_link";
odom_tf.transform.translation.x = odomData[i].x;
odom_tf.transform.translation.y = odomData[i].y;
odom_tf.transform.translation.z = 0.0;
odom_tf.transform.rotation = odom.pose.pose.orientation;
tfBroadcaster->sendTransform(odom_tf);
}
}
看看我如何为每个机器人都有一个 odomPub 向量?我可以像这样轻松设置:
std::vector<ros::Publisher> odomPub;
for (uint i = 0; i < robots.size(); i++)
odomPub.push_back(nh->advertise<nav_msgs::Odometry>(std::string(robots[i].name + "/odom"), 1));
但是对于 TransformBroadcaster 而言,这是毫无意义的,所有已发布的变换都在一个“/tf”主题中网格在一起,无法将它们彼此区分开。
经过大量谷歌搜索后,我想到了一个想法,我可以为每个模拟机器人使用单独的节点句柄,并使用特定的映射实例化每个节点句柄,如下所示:
ros::M_string remappings;
remappings.insert(std::make_pair("/tf", "/robot1_tf"));
remappings.insert(std::make_pair("tf", "/robot1_tf"));
nh1 = new ros::NodeHandle("devices", remappings);
但是没有。它不这样做。 TransformBroadcaster 仍然在“/tf”上发布,就好像映射不在那里一样。不过,该映射确实适用于我自己宣传的主题。我有什么做得不对的地方吗?有没有简单的方法来解决这个问题或以其他方式重新映射 /tf ?
到目前为止,我能够成功重新映射 /tf 的唯一方法是在启动文件中使用重新映射,
<remap from="/tf" to="/$(arg robot_id)_tf"/>
但这绝对是一场噩梦。模拟机器人的数量可以随时改变,理想情况下甚至在运行时也是如此。即使我重新启动整个模拟,我也必须动态创建一个启动文件,为每个机器人启动一个单独的节点,以便我可以单独重新映射它们的 /tf ,甚至不用说将 odom 数据从模拟过程获取到每个启动的节点都有单独的进程。
欢迎任何想法。
但是对于 TransformBroadcaster 而言,这是毫无意义的,所有已发布的变换都在一个“/tf”主题中网格在一起,无法将它们彼此区分开。
TF的总体设计是它代表/管理整个系统中所有已建立的坐标系。因此,在这里您需要通过为伸手机器人提供自己的
frame_id
来区分 TF 网络中的机器人:
odom.child_frame_id = std::string(robots[i].name + "/base_link"; // match frame_id used in TF
...
odom_tf.child_frame_id = std::string(robots[i].name + "/base_link"; // separate frames in TF
这与 turtle_tf2_demo 生成多个海龟机器人的方式类似。
如果您尝试将每个模拟机器人的 TF 消息隔离到其自己的主题(就像使用 Odometry 正确完成的那样),您将遇到其他 ROS 工具(例如 rviz)都无法解决的问题设置为处理
/tf
和 /tf_static
以外主题的 TF 数据。当然,您可以重新映射主题名称,但是您永远不会同时拥有模拟机器人的所有的 TF 数据(我假设这就是您想要的)。