【RS-M1系列-2】RS-M1扫描方式与坐标系定义

本文详细介绍了速腾聚创RS-M1激光雷达的螺旋扫描方式,包括其5通道扫描机制、交叉区域、垂直和水平分辨率的特点,以及坐标系定义。尽管通道间存在物理错开,但通过标定不影响精度。

RS-M1扫描方式

激光雷达的扫描方式对于点云数据存储以及特征提取等将产生很大影响。与传统旋转式激光雷达扫描方式不同,固态激光雷达通常为不规则的扫描方式,比如大疆的为花瓣形扫描方式,而本文将介绍速腾聚创RS-M1扫描方式为螺旋型扫描。
M1 的 5 个通道通过一个镜子进行扫描,意味着同一时刻发出去 5 个点,一行扫描结束总计 625 个点,其中每个通道对应 125 个点,总计扫描 126 行。
在这里插入图片描述

图1 M1扫描模式示意图

下图是单个通道理论的一个扫描轨迹,为螺旋式扫描,因此在局部存在交叉的情况。目前由于镜子本身的椭圆度参数特性存在差异,所以实际控制的扫描轨迹的交叉区域存在不同,因此出现了交叉区域在视场中间的情况。
螺旋扫描的方式决定了垂直方向上的分辨率并不是均匀分布,根据 126 线,25°的垂直 FOV 推算,其平均垂直分辨率是 0.2°。垂直分辨率是由振镜的慢轴(pitch 方向摆动)决定,水平分辨率则是由振镜的快轴(yaw 方向摆动)决定,每一行从左到右或者从右到左,并不是一个匀速过程,意味着水平分辨率也并不是均匀分布,根据一行 125 个点,25°水平 FOV 推算,其平均水平分辨率是0.2°
在这里插入图片描述

图2 M1螺旋扫描示意图

通道与通道之间垂直的排列方向有一定的错开,每个视场区域自己的 FoV不完全一致。物理上的排列错开,并不会影响到点云的精度,只要通道与通道之间标定对准后,扫描一面墙,在重合区域的精度是完全正常的,并不会造成扫描到的相同区域在点云上面产生错误的错位现象。
在实验室环境下测试RS-M1采集到点云将其按通道着色如图3,可见中间通道垂直视角明显大于两侧。
请添加图片描述
图3 RS-M1各通道着色点云效果

RS-M1坐标系定义

RS-M1坐标系定义为前(X)左(Y)上(Z),下列三图依次为依据XYZ三轴点云距离大小着色,由红–>紫依次增大
请添加图片描述

图4 X轴着色点云效果

请添加图片描述

图5 Y轴着色点云效果

请添加图片描述
图6 Z轴着色点云效果

<think>我们处理的是速腾聚创RoboSense M1雷达的点云解析。M1是固态激光雷达,其数据格式传统的机械式雷达不同。 主要步骤: 1. 安装驱动和依赖 2. 配置雷达参数 3. 解析数据包并转换为点云 4. 保存为PCD文件 注意:M1雷达的数据包结构不同于机械式雷达,需要按照其官方文档解析。</think>### 速腾RoboSense M1点云解析指南 RoboSense M1是固态激光雷达,其点云解析需要特殊处理。以下是完整的解决方案: #### 1. M1雷达特性数据格式 - **固态扫描模式**:非旋转式扫描120°水平视场角 - **数据包结构**: ```c struct M1_Packet { uint8_t header[8]; // 包头 (0x55, 0xAA, 0x5A, 0xA5) uint32_t timestamp; // 时间戳 (μs) uint16_t block_count; // 数据块数量 (固定为6) M1_Block blocks[6]; // 数据块 uint8_t tail[4]; // 包尾 }; struct M1_Block { uint8_t channel_id; // 通道ID (0-127) uint16_t azimuth; // 水平角度 (0.01°) uint16_t elevation; // 垂直角度 (0.01°) uint16_t distance; // 距离值 (mm) uint8_t reflectivity; // 反射率 }; ``` #### 2. Python点云解析实现 ```python #!/usr/bin/env python import struct import numpy as np import pcl from math import radians, sin, cos def parse_m1_packet(packet_data): """解析M1单帧数据包""" points = [] # 验证包头 if packet_data[0:4] != b'\x55\xaa\x5a\xa5': raise ValueError("无效的M1数据包头") # 解析时间戳和块数量 timestamp = struct.unpack('<I', packet_data[8:12])[0] block_count = struct.unpack('<H', packet_data[12:14])[0] # 解析每个数据块 (每块128个点) for block_idx in range(block_count): block_offset = 14 + block_idx * 1024 # 每个块1024字节 for point_idx in range(128): # 每块128个点 point_offset = block_offset + point_idx * 8 point_data = packet_data[point_offset:point_offset+8] # 解析点数据 channel, azimuth, elevation, distance, reflectivity = struct.unpack( '<BHHHB', point_data) # 转换为3D坐标 x, y, z = polar_to_cartesian( distance / 1000.0, # 毫米转米 azimuth / 100.0, # 0.01度转度 elevation / 100.0 ) points.append([x, y, z, reflectivity]) return points, timestamp def polar_to_cartesian(distance, azimuth, elevation): """极坐标转笛卡尔坐标""" az_rad = radians(azimuth) el_rad = radians(elevation) xy_dist = distance * cos(el_rad) x = xy_dist * sin(az_rad) y = xy_dist * cos(az_rad) z = distance * sin(el_rad) return x, y, z def save_pcd(points, filename): """保存为PCD文件""" cloud = pcl.PointCloud_PointXYZI() cloud.from_list(points) pcl.save(cloud, filename, binary=True) print(f"保存点云: {filename} (点数: {len(points)})") # 示例使用 if __name__ == "__main__": # 从文件读取数据包 with open('m1_packet.bin', 'rb') as f: packet_data = f.read() # 解析并保存 points, timestamp = parse_m1_packet(packet_data) save_pcd(points, f"m1_cloud_{timestamp}.pcd") ``` #### 3. ROS实时解析节点 ```python #!/usr/bin/env python import rospy import struct import numpy as np from robosense_msgs.msg import RobosensePacket from sensor_msgs.msg import PointCloud2, PointField from pcl import PointCloud_PointXYZI from pcl_helper import pcl_to_ros class M1PointCloudParser: def __init__(self): rospy.init_node('m1_pointcloud_parser') # 参数配置 self.frame_id = rospy.get_param('~frame_id', 'rs_m1') self.pub_cloud = rospy.Publisher('/rs_m1/points', PointCloud2, queue_size=10) rospy.Subscriber('/rslidar_packets', RobosensePacket, self.packet_callback) rospy.loginfo("M1点云解析器已启动") def packet_callback(self, packet): try: # 只处理M1数据包 if packet.packet_type != 2: # 2表示M1类型 return # 解析点云 points, timestamp = self.parse_m1_packet(packet.data) # 转换为ROS点云消息 cloud_msg = self.create_cloud_msg(points, packet.header.stamp) self.pub_cloud.publish(cloud_msg) except Exception as e: rospy.logerr(f"解析错误: {str(e)}") def parse_m1_packet(self, data): """解析M1数据包""" # 解析逻辑同上... # 返回点列表和时间戳 def create_cloud_msg(self, points, timestamp): """创建ROS PointCloud2消息""" # 创建PCL点云 pcl_cloud = PointCloud_PointXYZI() pcl_cloud.from_list(points) # 转换为ROS消息 ros_cloud = pcl_to_ros( pcl_cloud, frame_id=self.frame_id, stamp=timestamp ) # 设置字段 ros_cloud.fields = [ PointField('x', 0, PointField.FLOAT32, 1), PointField('y', 4, PointField.FLOAT32, 1), PointField('z', 8, PointField.FLOAT32, 1), PointField('intensity', 12, PointField.FLOAT32, 1) ] return ros_cloud if __name__ == '__main__': M1PointCloudParser() rospy.spin() ``` #### 4. 点云后处理技巧 **畸变校正** (因固态扫描特性) ```python def motion_compensation(points, packet_time, imu_data): """使用IMU数据进行运动补偿""" compensated_points = [] # 获取角速度和线速度 angular_vel = imu_data.angular_velocity linear_vel = imu_data.linear_acceleration # 实际应用中需要积分 for point in points: # 计算点相对于数据包开始的时间偏移 point_time = packet_time + point[4] # point[4]存储时间偏移 # 计算补偿量 time_diff = point_time - imu_data.header.stamp.to_sec() rotation = angular_vel * time_diff translation = linear_vel * time_diff # 应用补偿 x, y, z = apply_transform(point[:3], rotation, translation) compensated_points.append([x, y, z, point[3]]) return compensated_points def apply_transform(point, rot, trans): """应用旋转和平移变换""" # 简化的变换实现 x = point[0] + trans.x y = point[1] + trans.y z = point[2] + trans.z return [x, y, z] ``` **点云过滤** ```python import pcl def filter_cloud(cloud): """应用多级过滤""" # 1. 移除无效点 passthrough = cloud.make_passthrough_filter() passthrough.set_filter_field_name("z") passthrough.set_filter_limits(0.1, 10.0) # 保留0.1-10米高度 # 2. 统计离群点去除 sor = cloud.make_statistical_outlier_filter() sor.set_mean_k(50) # 使用50个邻近点分析 sor.set_std_dev_mul_thresh(1.0) # 标准差阈值 # 3. 体素网格下采样 vg = sor.filter().make_voxel_grid_filter() vg.set_leaf_size(0.02, 0.02, 0.02) # 2cm分辨率 return vg.filter() ``` #### 5. 配置启动 **驱动配置 (config.yaml)** ```yaml lidar: - driver: lidar_type: RSM1 # M1型号标识 frame_id: /rs_m1 device_ip: 192.168.1.200 msop_port: 6699 return_mode: 1 # 回波模式 (1=最强回波) lidar_scan_path: /path/to/scan_file # M1专用扫描路径文件 ``` **启动命令** ```bash # 启动M1雷达 roslaunch rslidar_sdk start.launch lidar_type:=RSM1 # 启动点云解析节点 rosrun m1_parser m1_parser_node.py _frame_id:=/rs_m1 ``` ### 常见问题解决 1. **点云密度不足**: ```yaml # 配置文件中增加分辨率参数 lidar: - driver: scan_mode: 2 # 0=标准, 1=增强, 2=高密度 ``` 2. **时间同步问题**: ```python # 在解析器中添加PTP支持 def enable_ptp_sync(): # 配置PTP主时钟 os.system('sudo ptp4l -i eth0 -m -S') # 雷达配置启用PTP ``` 3. **点云畸变校正**: - 使用IMU数据:`rosrun imu_filter madgwick_filter` - 使用轮速计信息进行运动补偿 4. **坐标系对齐**: ```xml <!-- 在URDF中添加M1雷达模型 --> <link name="rs_m1"/> <joint name="rs_m1_joint" type="fixed"> <parent link="base_link"/> <child link="rs_m1"/> <origin xyz="0.2 0 1.5" rpy="0 0 0"/> </joint> ``` 5. **性能优化**: ```python # 使用Numba加速计算 from numba import jit @jit(nopython=True) def polar_to_cartesian_numba(distance, azimuth, elevation): # 加速版的坐标转换 az_rad = np.radians(azimuth) el_rad = np.radians(elevation) xy_dist = distance * np.cos(el_rad) return ( xy_dist * np.sin(az_rad), xy_dist * np.cos(az_rad), distance * np.sin(el_rad) ) ```
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值