项目实训 - 智能车系统 - 第八周记录
日期:4.11 – 4.17
项目进度
本周工作进展:
- 完成了雷达驱动的编写(未测试)
- 完成imu驱动的编写(未测试)
- 与可视化部分进行对接
1、速腾rslidar驱动编写(二次开发)
通过阅读ROS二次开发的rslidar驱动后,决定延用之前移植项目的做法,在ros二次开发的版本的基础上进行修改,将发布的数据修改我们自定义的类型,并且发布数据。
另一个节点rs_to_velodyne订阅上面发布的消息,将数据转换成velodyne的点云格式,然后进行发布,被项目订阅。
具体移植过程不做记录,这里记录一下开发过程中的一些问题。
可能产生的问题记录
由于封校原因,暂时无法拿到雷达,所以无法直接对编写好的驱动进行测试,所以记录了一下移植后可能产生的问题:
- height和width的问题
- rslidar发布的点云数据是有序的(height!=1 ),但是阅读lio-sam后,发现lio-sam获取的数据是无序的(height == 1 ),二者出现了差异
- 我的做法是在驱动中直接将点云数据按照heght=1的情况进行存储,然后发布
- point.at的问题
- 也是上面的问题,由于点云存储方式的不同,定位到每个点的时候,也可能出现乱序的情况
- 时间戳的起始问题
- 获取数据包中的时间信息,进行解析
- 时间数据流的大端法和小端法
- 这个是传入的数据流的存储方式问题,根据雷达手册暂时无法区分
- 需要具体测试
- 从参数服务器中获取tf参数
- 这个貌似是没用的,不需要tf的相关信息
- 将ros的时间戳改成系统时间戳 input
- 这里在ros的二次开发版本中,是使用ROS::time获取当前时间戳
- 我的做法是获取了系统时间戳(system time),通过c++标准库实现
- getPacket传入的参数是不是指针
- 配置文件可能不全
- 去网上搜到了一些配置文件,需要实际调试
时间戳处理问题
要注意的是,传出的数据包中,实际上包含了两个时间戳的信息:
- 整个数据包的时间戳(获取系统时间戳)
- 数据包中,点云中包含的timestamp信息
希望得到的时间戳
- double类型,以sec为单位
- 小数点后精确到us
分析雷达返回的时间格式
- 后4byte数据可以直接拼出us部分
- ms * 1e6 + us
- 前6byte无法简单地得到sec部分,需要调用c标准库time.h
文档:https://en.cppreference.com/w/c/chrono/time_t
简单学习后,总结如下信息:
-
头文件 <time.h>
-
时间戳类:time_t,给类型返回一个整数,代表标准UTC时间戳,精确到sec
-
tm,一个结构体,成员如下:
-
mktime:函数,将一个tm类型变量转换成time_t类型
demo实例:
#include <stdio.h>
#include <time.h>
#include <locale.h>
int main()
{
// struct tm my_time = { .tm_year=112, // = year 2012
// .tm_mon=9, // = 10th month
// .tm_mday=9, // = 9th day
// .tm_hour=8, // = 8 hours
// .tm_min=10, // = 10 minutes
// .tm_sec=20 // = 20 secs
// };
struct tm my_time;
my_time.tm_year = 112;
my_time.tm_mon = 9;
my_time.tm_mday = 9;
my_time.tm_hour = 8;
my_time.tm_min = 10;
my_time.tm_sec = 21;
printf("Today is %s", asctime(&my_time));
printf("(DST is %s)\n", my_time.tm_isdst ? "in effect" : "not in effect");
//my_time.tm_mon -= 100; // tm_mon is now outside its normal range
time_t timet = mktime(&my_time); // tm_isdst is not set to -1; today's DST status is used
// printf("100 months ago was %s", asctime(&my_time));
// printf("(DST was %s)\n", my_time.tm_isdst ? "in effect" : "not in effect");
printf("%ld\n", timet);
}
输出结果:
可以使用如上函数来处理得到需要的sec信息
定义了一个raw_time结构体,存储原始时间数据
/**
* @brief 自定义用于解析数据包中的时间信息
* raw data
*
*/
struct raw_time
{
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint16_t msecond;
uint16_t usecond;
}
接口
/**
* @brief convert raw time info to timestamp(double, uint: s)
*
* @param raw_time_data
* @return double
*/
double RawData::get_timestamp(raw_time raw_time_data)
{
//处理sec
struct tm cur_time;
cur_time.tm_year = raw_time_data.year + 100;
cur_time.tm_mon = raw_time_data.month - 1;
cur_time.tm_mday = raw_time_data.day;
cur_time.tm_hour = raw_time_data.hour;
cur_time.tm_min = raw_time_data.minute;
cur_time.tm_sec = raw_time_data.second;
uint32_t sec = mktime(&tm);
//处理nsec
uint32_t nsec = raw_time_data.msecond * 1e6 + raw_time_data.usecond;
//得到double类型时间戳
return (static_cast<double>(sec) + 1e-9*static_cast<double>(nsec));
}
测试基本没啥问题
- 问题:需要判断雷达得到的数据流是大端法还是小端法
- 会丢失一点us的精度
2、imu驱动编写
这个部分有学长提供的imu手册,对照着直接编写即可,不做具体记录
3、与可视化的对接
这个部分比较顺利,因为之前的接口已经定义好的,所以运行项目和可视化之后,直接就可以正常运行了,效果图如下(因为之前订阅的是registered_raw点云数据,没有经过降采样,所以看起来有点糊)
右边是rivz的运行效果,左侧是自定义效果
流程(接收端暂时还是使用ros的机制):
- rosbag启动数据包
- lio-sam项目启动,接收数据,处理后发布数据
- 可视化接收lio-sam发布的数据,继续渲染
技术难点
socket通信
bug记录
没啥大bug
其他
下周预计完全去除项目中的ros相关的东西(之前方便调试,有些变量函数都没有删除),重写一个cmake文件。这样一整个流程基本上就可以实现了。
安利一波新番 测不准的阿波连同学,萌翻了家人们。,。