ROS多消息同步(如LiDAR点云与IMU数据)

ROS的message_filters库提供了一种机制来处理来自一个或多个主题的消息,尤其是当这些消息需要以某种方式同步或以某种特定顺序处理时。例如LiDAR点云与IMU数据之间的同步。

以下是一些主要的message_filters功能:

  • Time Synchronizer: message_filters::TimeSynchronizer允许你同步两个或多个主题的消息,这些消息的时间戳必须完全相同。当所有主题都有一个新消息时,就会触发一个回调。

  • Approximate Time Policy: message_filters::sync_policies::ApproximateTime是一个更灵活的同步策略,它允许消息的时间戳有小的误差。这对于处理那些不能精确同步的设备(例如,不同的传感器可能有微小的时间差)非常有用。

  • Message Filters: message_filters还提供了一些过滤器,例如message_filters::Subscriber(订阅一个主题的消息)、message_filters::Cache(缓存一段时间内的消息)和message_filters::TimeSequencer(确保消息按时间顺序处理)。

  • Chain: message_filters::Chain允许你将多个过滤器链接在一起,创建一个处理消息的管道。

使用message_filters,你可以创建复杂的消息处理逻辑,而无需手动处理所有的细节。例如,你可以创建一个系统,该系统等待从多个传感器接收数据,然后当所有数据都准备就绪时,同步处理这些数据。

接下来给出一个使用案例,进行同步LiDAR点云与IMU。

#include <iostream>
#include <fstream>
#include <vector>
#include <csignal>  

#include "global_relocation/global_relocation.h"

#include <pcl_conversions/pcl_conversions.h>
#include <pcl/common/transforms.h>
#include <pcl/io/pcd_io.h>

#include <message_filters/subscriber.h>
#include <message_filters/time_synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>


void messageCallback(const sensor_msgs::PointCloud2ConstPtr& pointMsgIn, const sensor_msgs::ImuConstPtr& ImuMsgIn)
{
    std::cout << "hi!--Lidar: " << std::setprecision(16) << pointMsgIn->header.stamp.toSec() <<
    " IMU: " << ImuMsgIn->header.stamp.toSec() << std::endl;
}

int main(int argc, char ** argv)
{
    ros::init(argc, argv, "global_relocation");
	ros::NodeHandle nh;
    // sync the time by ros message filter
    message_filters::Subscriber<sensor_msgs::PointCloud2> subPointCloud(nh, "/os_cloud_node_1/points", 1);
    message_filters::Subscriber<sensor_msgs::Imu> subIMU(nh, "/os_cloud_node_1/imu", 1);
    typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, sensor_msgs::Imu> syncPolicy;
    typedef message_filters::Synchronizer<syncPolicy> Sync;
    message_filters::Synchronizer<syncPolicy> sync(syncPolicy(100), subPointCloud, subIMU);
    sync.registerCallback(boost::bind(&messageCallback, _1, _2));

    ROS_INFO("hello ros!");

    ros::spin();
    return 0;
}

注意:如果两个topic的频率相差很大时,需要修改syncPolicy(100)的值,比如说两个点云数据的topic都是10hz,那么我们可以设置为syncPolicy(10),但是针对10Hz的点云与100Hz的IMU数据,我们需要设置为syncPolicy(100)。

如果队列大小设置得太小,那么可能会因为消息没有足够的机会找到匹配的对而被丢弃。如果设置得太大,那么可能会消耗过多的内存。所以,选择合适的队列大小需要根据应用和硬件进行权衡。

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于ROS的激光雷达+小车+IMU的SLAM建图、定位、路径规划c++实现源码+项目说明.zip # 单线激光雷达SLAM建图与路径规划 使用的硬件:autolabor pro1小车、小觅双目相机(S1030标准版本)、IntelNUC迷你主机、显示器、2D激光雷达 Delta-1A 使用的软件:ubuntu 16.04 LTS、ROS-kinetic、小觅驱动、autolabor pro1小车驱动、Delta-1A驱动、VINS-Fusion算法、ROS-navigation导航包、gmapping建图算法、cartographer_ros建图算法 ## tf介绍 tf变换:map(地图坐标系)-->odom(里程计坐标系)——>base_link(小车基座坐标系)——>传感器坐标系(lidar、camera) ---- map-->odom 由建图算法提供 ---- odom --> base_link 由小车驱动提供 ---- base_link --> 传感器坐标系 根据传感器在小车上安装的位置确定,使用static_transform_publisher静态坐标转换来设置 所需传感器数据类型: sensor_msgs/LaserScan 使用Delta-1A驱动发布的雷达扫描话题,话题名:/scan 所需里程计信息数据类型:nav_msgs/Odometry 使用autolabor pro1发布的里程计,话题名:/wheel_odom ## 建图 采用gmapping与 cartographer_ros建图算法分别进行建图 ### gmapping建图:(雷达+小车) 打开终端,执行如下指令: $ cd ~/catkin_ws_lidar_slam/ $ source devel/setup.bash $ roslaunch autolabor_box_launch create_map_gmapping.launch ####打开小车urdf模型、驱动、键盘控制节点、打开雷达驱动、打开gmapping节点、打开rviz可视化节点 ####此时使用键盘的上下左右键控制小车移动,开始建图 ####建图完成后,开启新的终端执行如下指令,保存地图: $ rosrun map_server map_saver -f map_name ####保存地图 map_name为保存地图的名称,运行结果,会生成2个文件,后缀名分别为 .pgm .yaml ### cartographer_ros建图:(雷达+小车) ##打开终端,执行如下指令: $ cd ~/catkin_ws_lidar_slam/ $ source devel/setup.bash $ roslaunch autolabor_box_launch create_map_cartographer.launch ####此时使用键盘的上下左右键控制小车移动,开始建图 ####建图完成后,开启新的终端执行如下指令,保存地图: $ rosrun map_server map_saver -f map_name ......
假设我们已知imu和激光雷达的相对位置关系为$T_{imu-lidar}$,而imu数据包括角速度$\omega$和线加速度$a$。我们可以通过以下步骤来推算激光雷达的四元数: 1. 将imu的角速度$\omega$转换为旋转矩阵$R_{imu}$。这可以通过将$\omega$积分得到: $$R_{imu}(t) = R_{imu}(t-1) \cdot \exp([\omega(t) - b_{\omega}] \cdot \Delta t)$$ 其中$\Delta t$是时间间隔,$b_{\omega}$是角速度的偏移量。 2. 将imu的线加速度$a$转换为重力加速度在imu坐标系下的向量$g_{imu}(t)$。这可以通过以下公式计算: $$g_{imu}(t) = R_{imu}(t) \cdot g_{world} + a(t)$$ 其中$g_{world}$是重力加速度在世界坐标系下的向量。 3. 将激光雷达在imu坐标系下的位置$p_{lidar}(t)$计算为: $$p_{lidar}(t) = T_{imu-lidar} \cdot p_{imu}(t)$$ 其中$p_{imu}(t)$是imu在世界坐标系下的位置。 4. 计算激光雷达在世界坐标系下的向量$v_{lidar}(t)$。这可以通过以下公式计算: $$v_{lidar}(t) = R_{imu}(t) \cdot v_{imu}(t)$$ 其中$v_{imu}(t)$是imu在世界坐标系下的速度。 5. 根据激光雷达在世界坐标系下的向量$v_{lidar}(t)$和位置$p_{lidar}(t)$计算激光雷达的四元数$q_{lidar}(t)$。这可以通过以下公式计算: $$q_{lidar}(t) = \begin{bmatrix} \sqrt{(1 + v_{lidar,x}(t) + p_{lidar,x}(t))^2 + (v_{lidar,y}(t) + p_{lidar,y}(t))^2 + (v_{lidar,z}(t) + p_{lidar,z}(t))^2} \\ \frac{v_{lidar,z}(t) + p_{lidar,z}(t)}{\sqrt{(1 + v_{lidar,x}(t) + p_{lidar,x}(t))^2 + (v_{lidar,y}(t) + p_{lidar,y}(t))^2 + (v_{lidar,z}(t) + p_{lidar,z}(t))^2}} \\ \frac{v_{lidar,y}(t) + p_{lidar,y}(t)}{\sqrt{(1 + v_{lidar,x}(t) + p_{lidar,x}(t))^2 + (v_{lidar,y}(t) + p_{lidar,y}(t))^2 + (v_{lidar,z}(t) + p_{lidar,z}(t))^2}} \\ \frac{v_{lidar,x}(t) + p_{lidar,x}(t)}{\sqrt{(1 + v_{lidar,x}(t) + p_{lidar,x}(t))^2 + (v_{lidar,y}(t) + p_{lidar,y}(t))^2 + (v_{lidar,z}(t) + p_{lidar,z}(t))^2}}\end{bmatrix}$$ 最后,我们可以使用ROS编写一个节点来实现算法。代码示例如下: ```python import rospy from sensor_msgs.msg import Imu from geometry_msgs.msg import Quaternion, Vector3, PoseStamped import tf class LidarQuaternionNode: def __init__(self): rospy.init_node('lidar_quaternion_node') self.lidar_quaternion_pub = rospy.Publisher('/lidar_quaternion', PoseStamped, queue_size=10) self.imu_sub = rospy.Subscriber('/imu', Imu, self.imu_callback) self.tf_broadcaster = tf.TransformBroadcaster() self.T_imu_lidar = None def imu_callback(self, imu_msg): # Step 1: Calculate rotation matrix from imu angular velocity R_imu = self.calc_rotation_matrix(imu_msg.angular_velocity) # Step 2: Calculate gravity vector in imu frame g_world = Vector3(0, 0, -9.81) g_imu = R_imu * g_world + imu_msg.linear_acceleration # Step 3: Calculate lidar position in imu frame lidar_pos_imu = self.T_imu_lidar * Vector3(0, 0, 0) # Step 4: Calculate lidar velocity in world frame v_imu_world = R_imu * imu_msg.linear_acceleration + Vector3(0, 0, 9.81) lidar_vel_world = R_imu * v_imu_world # Step 5: Calculate lidar quaternion from velocity and position lidar_quat = self.calc_lidar_quaternion(lidar_pos_imu, lidar_vel_world) # Publish lidar quaternion lidar_pose = PoseStamped() lidar_pose.header.stamp = rospy.Time.now() lidar_pose.pose.orientation = lidar_quat self.lidar_quaternion_pub.publish(lidar_pose) # Broadcast lidar transform self.tf_broadcaster.sendTransform((lidar_pos_imu.x, lidar_pos_imu.y, lidar_pos_imu.z), (lidar_quat.x, lidar_quat.y, lidar_quat.z, lidar_quat.w), rospy.Time.now(), 'lidar', 'imu') def calc_rotation_matrix(self, angular_velocity): # Calculate rotation matrix from imu angular velocity dt = 1.0 / 50 R_imu = tf.transformations.identity_matrix() for i in range(3): R_imu = tf.transformations.concatenate_matrices(R_imu, tf.transformations.rotation_matrix(angular_velocity[i] * dt, (1, 0, 0))) return R_imu def calc_lidar_quaternion(self, lidar_pos_imu, lidar_vel_world): # Calculate lidar quaternion from velocity and position x = lidar_pos_imu.x + lidar_vel_world.x + 1 y = lidar_pos_imu.y + lidar_vel_world.y z = lidar_pos_imu.z + lidar_vel_world.z norm = (x ** 2 + y ** 2 + z ** 2) ** 0.5 w = norm x /= norm y /= norm z /= norm return Quaternion(x, y, z, w) def run(self): # Wait for T_imu_lidar to be set while self.T_imu_lidar is None: rospy.sleep(0.1) # Spin rospy.spin() if __name__ == '__main__': node = LidarQuaternionNode() node.T_imu_lidar = tf.transformations.quaternion_matrix([0, 0, 0.1, 1]) node.run() ``` 在这个ROS节点中,我们订阅了IMU数据,并发布了激光雷达的四元数和变换。我们还定义了两个辅助函数来计算旋转矩阵和激光雷达的四元数。在这个示例中,我们假设激光雷达的相对位置关系为$[0, 0, 0.1, 1]$,即激光雷达在imu坐标系下的位置为$(0, 0, 0.1)$,四元数为$(0, 0, 0, 1)$。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓晨的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值