3、传感器基础:常见传感器类型与数据格式
做QNX系统上的传感器融合,第一步不是写代码,而是先搞懂你手头的传感器到底在输出什么。我见过太多人一上来就调算法,结果发现IMU的数据单位都搞错了——这种事真的很耽误时间。
今天咱们就把四种核心传感器掰开揉碎了讲。IMU、GPS、激光雷达、摄像头,这四样东西在自动驾驶和机器人项目里几乎是标配。我会结合自己踩过的坑,把数据格式和协议说清楚。
3.1 IMU:惯性测量单元
IMU这玩意儿,说白了就是测加速度和角速度的。它内部通常有三个轴:X、Y、Z。我习惯把IMU想象成一个「六自由度」的小盒子——三个轴测直线运动,三个轴测旋转运动。
核心数据输出:
- 加速度计:单位 m/s² 或 g(1g = 9.8 m/s²)
- 陀螺仪:单位 rad/s 或 °/s
- 磁力计(部分型号):单位 μT(微特斯拉)
我在项目中遇到过一个问题:某款IMU的加速度计默认输出是g,但算法库要求的是m/s²。如果你没做单位转换,融合出来的姿态角会直接飘掉。嗯,这里要注意——数据单位必须统一。
常见的IMU数据格式是这样的:
// 典型的IMU数据帧(二进制格式)
typedef struct {
uint32_t timestamp; // 微秒级时间戳
float accel_x; // 加速度 X 轴
float accel_y; // 加速度 Y 轴
float accel_z; // 加速度 Z 轴
float gyro_x; // 角速度 X 轴
float gyro_y; // 角速度 Y 轴
float gyro_z; // 角速度 Z 轴
} imu_data_t;
通信协议方面,IMU最常用的是I²C和SPI。我个人建议在QNX上用SPI,因为它的实时性更好。I²C虽然接线简单,但速率上限低,高频率采样时容易丢数据。
避坑指南:我曾经在调试时发现IMU数据突然跳变,查了半天发现是SPI时钟线没做阻抗匹配。高频信号线上加个22Ω的电阻,问题就解决了。
3.2 GPS:全球定位系统
GPS大家都不陌生,但做传感器融合时,你关心的不是经纬度本身,而是它的更新频率和精度。普通GPS模块输出10Hz,RTK(实时动态差分)能做到20Hz甚至更高。
GPS的数据格式,最经典的就是NMEA 0183协议。你打开串口助手,看到的那些以$GPGGA、$GPRMC开头的字符串,就是它。
// NMEA GGA 语句示例
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
// 解析字段说明:
// 123519 — UTC时间 12:35:19
// 4807.038,N — 纬度 48°07.038' N
// 01131.000,E — 经度 11°31.000' E
// 1 — 定位质量(0=无效,1=单点定位,2=差分)
// 08 — 卫星颗数
// 0.9 — HDOP(水平精度因子)
// 545.4,M — 海拔高度(米)
你想想看,如果GPS的更新频率只有10Hz,而IMU是1000Hz,那融合的时候就必须做时间对齐。我习惯的做法是:用IMU的时间戳作为主时钟,GPS数据到达时打上最近的IMU时间戳。
注意:GPS数据在室内基本不可用。如果你做的是室内机器人,别指望GPS。我见过有人把激光雷达和GPS硬凑在一起做融合,结果定位误差直接飙到米级。
3.3 激光雷达:LiDAR
激光雷达输出的是一堆点云数据。每个点包含三维坐标和反射强度。常见的LiDAR有16线、32线、64线、128线。线数越多,点云越密,但数据量也越大。
数据格式方面,主流有两种:
- PCD格式:Point Cloud Data,文本或二进制存储
- ROS bag格式:在QNX上可以通过libpcap解析
我实际项目中用得最多的是Velodyne的VLP-16。它的数据包格式是这样的:
// Velodyne VLP-16 数据包结构(简化版)
typedef struct {
uint8_t azimuth; // 方位角(0-255,对应0°-360°)
uint16_t distance; // 距离(单位:2mm)
uint8_t intensity; // 反射强度(0-255)
} lidar_point_t;
// 每个数据包包含12个这样的点
typedef struct {
lidar_point_t points[12];
uint32_t timestamp; // 微秒级时间戳
} lidar_packet_t;
这里有个坑:激光雷达的数据量非常大。64线LiDAR每秒能产生超过200万个点。在QNX上处理时,我建议用双缓冲机制——一个缓冲区接收数据,另一个缓冲区做处理,避免数据覆盖。
个人经验:我曾经在QNX上直接在主线程里解析LiDAR数据,结果导致系统响应延迟飙升。后来改成用pthread创建独立线程,配合消息队列传递数据,问题就解决了。
3.4 摄像头:Camera
摄像头输出的是图像帧。常见的格式有YUV、RGB、Bayer RAW。在QNX上,摄像头驱动通常通过V4L2(Video for Linux 2)接口来操作。
数据格式这块,我重点说三个:
| 格式 | 特点 | 适用场景 |
|---|---|---|
| YUV420 | 压缩率高,带宽小 | 视频流传输 |
| RGB888 | 每个像素3字节,色彩还原好 | 图像处理算法 |
| Bayer RAW | 原始传感器数据,未处理 | ISP调试、高精度视觉 |
摄像头的数据量同样惊人。1080p@30fps的RGB流,每秒数据量是1920×1080×3×30 ≈ 186MB。你想想看,如果系统带宽不够,丢帧是必然的。
我建议的做法是:在QNX上使用共享内存(shm)来传递图像数据。这样多个进程可以同时访问同一帧数据,避免拷贝开销。
关键点:摄像头和LiDAR的时间同步是最难的。因为摄像头曝光时间不确定,LiDAR是逐点扫描。我常用的方法是:在摄像头帧的VSYNC信号上打时间戳,然后和LiDAR的PPS信号做对齐。
3.5 传感器数据协议总结
最后,我把四种传感器的关键参数整理成一个表格,方便你对照:
| 传感器 | 典型更新频率 | 数据量/秒 | 常用接口 | 时间同步方式 |
|---|---|---|---|---|
| IMU | 100-1000 Hz | 几KB | SPI / I²C | 硬件中断 |
| GPS | 10-20 Hz | 几百字节 | UART | PPS脉冲 |
| LiDAR | 10-20 Hz | 几十MB | Ethernet | PTP / 硬件时间戳 |
| 摄像头 | 30-60 Hz | 几百MB | MIPI / USB | VSYNC + 软件对齐 |
做传感器融合,说白了就是把这些不同频率、不同格式、不同时间基准的数据,统一到一个坐标系和时间轴上。下一章我会讲具体的同步策略,但今天的内容是基础——基础不牢,地动山摇。
最后提醒一句:别相信传感器手册上写的「典型值」。实际项目中,IMU的噪声、GPS的漂移、LiDAR的盲区、摄像头的畸变,都会让你怀疑人生。拿到传感器后,先做标定,再做融合。