单线激光雷达和视觉信息融合案例

很多智能应用场景涉及到激光雷达和相机视觉信息融合,一般都是指多线激光雷达,至少也得16线激光吧;但多线激光雷达动不动数万的价格,让很多技术人员无法尝试.我尝试使用微型机器人或扫地机器人常用的单线激光雷达,与相机视觉做个信息融合实践,以作为多线激光雷达和视觉信息融合的补充,共大家参考实践.

本实践基于ROS系统开发,硬件平台采用一款搭载Jetson Nano的四轮机器人.平台搭载一款单线激光雷达,可实现 12M 测距范围, 8000 次/秒的测量频率,扫描频率15HZ;同时还搭载一款深度相机,具有1080P RGB普通相机功能,又有深度相机功能(因为要实践和单线激光雷达做融合,深度相机有此功能咱也不使用).

另外,本实例主要出于熟悉雷达和视觉融合流程考虑,只写了可视化调试代码,并没有实际的业务逻辑代码.
        先规划下我们都有哪些工作要做?
    一. 获取单线激光雷达外参,并发布激光的外参到tf;
    二. 获取相机外参,并发布相机的外参到tf;
    三. 对相机内参做标定,并获取相机的内参参数.这样可以把相机坐标系下的3D坐标点无畸变地投影到相机的像素平面;
    四. 对相机和单线激光雷达联合标定,目的就是建立单线激光雷达坐标系下3D点云的point和相机坐标系下3D点的对应关系.

        这里分两种情况:
       1>. 如果你对激光雷达和相机坐标转换精度要求不高,且你在第一步和第二步测量计算的外参精度很有把握,你可以不做专门的联合标定,直接利用第一,二步发布的tf做坐标系间的变换;
       2>. 对于商品化产品,一般对激光雷达和相机坐标转换精度要求较高,且考虑到装配误差,需要做独立的联合标定, 详细的联合标定方法请参考其他文献;
    五. 相机内参数据获取,及单线激光雷达数据和相机数据的时间线对齐;
    六. 将单线激光雷达的LaserScan数据从极坐标系表示转换到激光雷达笛卡尔坐标系表示,并生成需要的点云(PointCloud)数据L(1xn);
    七. 对上述单线激光雷达坐标系下的点云数据L(1xn),做水平插值,以保证点云密度,生成新的点云数据L(1xm).
   八. 对激光雷达笛卡尔坐标系下的单行点云数据L(1xm)做垂直方向扩展,向下扩展到地面和墙面交界处k,向上扩展约0.5米高j,生成激光雷达笛卡尔坐标系下的点云数据L((k+1+j)xm);
    九. 将上一步生成的点云(PointCloud)数据L((k+1+j)xm)利用一,二步发布的tf变换到相机坐标系下,生成相机坐标系下的点云数据C((k+1+j)xm),同时利用获取的相机内参信息,将点云映射到相机像素平面坐标(x,y),并从像素平面(current_image_frame)获取对应颜色RGB值;
    十. 根据以上各参数和生成的数据,进行点云数据和视觉信息融合;

        1>. 二维相机图像像素数据 --融合到--> 三维空间的点云数据;
        2>. 三维空间的点云数据 --融合到--> 二维相机图像像素数据;
    十一. 需要细节优化,性能调整的地方.

为了便于对比,我固定选用家里的一个局部场景做演示,这是一个标准的H户型的主次卧和卫生间中间的过廊场景,左右分别是主次卧的卧室门,中间是卫生间门.

直接通过相机,在rqt_image_view查看是这样的:


        通过单线激光雷达,在rviz中查看laser scan是这个样的(我在图中用白色选中的倒U形区域,我们称为ROI,这也是相机的可视区域):

融合视觉信息的雷达扫描效果如下图(未做优化):

放大效果(中间门的中间有条黑色竖条,是我的激光雷达在交接处无数据,后期会优化处理):

下面按照前面的规划,一步一步来完成,涉及ROS包,节点的创建,已经ROS初始化等基本操作的内容将忽略.(代码中设计的类名,我统一改为YourClass)

一. 获取激光雷达外参,并发布激光的外参到tf;
    从硬件和结构人员那里获取激光雷达在整体机器人上的结构参数,就是相对于机器人整体参考系的坐标信息,在适当的launch文件执行时发布激光雷达的位姿到tf:

<node pkg="tf" type="static_transform_publisher" name="base_foot_print_to_laser" args="0.1 0 0.1 -1.57 0 0 base_footprint laser_link 40"/>

二. 获取相机外参,并发布相机的外参到tf;
    同上面激光雷达类似:

<node pkg="tf" type="static_transform_publisher" name="base_footprint_to_camera" args="0.10 0 0.10 1.57 3.14 1.57 base_footprint camera_link 40"/>

三. 对相机内参做标定,并获取相机的内参参数.这样可以把相机坐标系下的3D坐标点无畸变地投影到相机的像素平面;
        相机内参标定的方法,相关书籍和网上都有说明,这里不再阐述.通过相机标定会得到相机内参矩阵和畸变系数,畸变系数可以消除相机凸透镜的畸变效应,有了相机内参的信息就可以把相机坐标系下的3维点投影到相机的2维成像像素平面。

ROS系统中一般标定好的相机内参,可以通过订阅"/camera/camera_info"主题(topic)获取.

//订阅相机内参:
node_h.subscribe("/camera/camera_info", 1, &YourClass::intrinsicValueCallback);

回调函数获取摄像机内参参数及畸变系数,详细查看ROS文档sensor_msgs::CameraInfo定义.
这里我们获取到一些相机内参信息,后面从相机获取到图像数据时,会利用camera_intrinsic_value和distortion_coefficients对图像做去畸变,而fx,fy,cx,cy则用来对相机坐标系下的点云point做投影计算,映射到成像像素平面. 此处变量是对象属性,需要预先定义好.

void YourClass::intrinsicValueCallback(const sensor_msgs::CameraInfo::ConstPtr &intrinsic_value_msg){image_frame_size.height = intrinsic_value_msg->height;image_frame_size.width = intrinsic_value_msg->width;// 相机内参camera_intrinsic_value = cv::Mat(3, 3, CV_64F);//相机内参变换矩阵3x3,把3D点投影到2D像素平面时使用.  后面获取相机图像时,会用来去畸变for (int row = 0; row < 3; row++){for (int col = 0; col < 3; col++){camera_intrinsic_value.at<double>(row, col) = intrinsic_value_msg->K[row * 3 + col];}}// 相机畸变参数. For "plumb_bob"模式, the 5 parameters are: (k1, k2, t1, t2, k3).//k 1  ,k 2  ,k 3  表示径向畸变参数;t 1 ,t 2  表示切向畸变参数;distortion_coefficients = cv::Mat(1, 5, CV_64F);for (int col = 0; col < 5; col++){distortion_coefficients.at<double>(col) = intrinsic_value_msg->D[col];}//Projection/camera matrix 3x4// 投影系数,获取投影矩阵3x4的数组的fx,fy,cx,cy元素,fx = static_cast<float>(intrinsic_value_msg->P[0]);fy = static_cast<float>(intrinsic_value_msg->P[5]);cx = static_cast<float>(intrinsic_value_msg->P[2]);cy = static_cast<float>(intrinsic_value_msg->P[6]);intrinsics_sub_.shutdown(); //关闭不再使用的intrinsics subscriber
}

四. 对相机和单线激光雷达联合标定,目的就是建立单线激光雷达坐标系下3D点云的point和相机坐标系下3D点的对应关系.
        考虑到这里是单线激光雷达, 不方便利用现有的联合标定工具包做联合标的,所以只能在单线激光雷达和相机的测量计算精度上下功夫, 
确保在第一步和第二步发布到tf的外参尽量精准可靠;
        具体使用时,只需要利用ROS系统的TF功能,即可做单线激光雷达坐标系下3D点云的point和相机坐标系下3D点的坐标变换;
    举例如下:
    1. 获取目标frame和源frame之间的变换关系transform

tf::StampedTransform transform;
try
{transform_listener.lookupTransform("camera", "laser", ros::Time(0), transform);
}
catch (tf::TransformException ex)
{    }

2. 利用transform做坐标系变换

tf::Vector3 tf_point(pc_point.x, pc_point.y, pc_point.z); //点云点转tf点类型
tf::Vector3 tf_point_transformed = transform * tf_point; //对单个点做坐标系变换
return pcl::PointXYZ(tf_point_transformed.x(), tf_point_transformed.y(),tf_point_transformed.z());//tf点类型转点云点

五. 相机内参数据获取,及单线激光雷达数据和相机数据的时间线对齐;
        因为单线激光雷达和相机信息融合,依赖于相机内参,所以程序启动后,需要等待订阅的相机内参数据获取. 代码见第三步;
        又因为单线激光雷达主题消息和相机图像主题消息,发布的频率不一致,我们需要对两种类型主题消息的stamp做比对;以保证
        激光雷达扫描数据和相机图像数据均反应同一时刻的状态,这部分代码比较简单,故略;

//订阅相机主题消息
node_h.subscribe("/camera/image_raw", 1, &YourClass::cameraImageCallback);
//相机主题消息处理函数
void YourClass::cameraImageCallback(const sensor_msgs::Image::ConstPtr &image_msg){...cv_bridge::CvImagePtr cv_image = cv_bridge::toCvCopy(image_msg, "bgr8");cv::Mat image = cv_image->image;//使用上面第三步获取的相机内参信息,利用openCV对图像做去畸变处理. current_image_frame 类型cv::Mat cv::undistort(image, current_image_frame, camera_intrinsic_value,distortion_coefficients);...
}//订阅激光雷达扫描主题消息
node_h.subscribe("scan", 1, &YourClass::laserScanCallback);
//激光雷达扫描主题消息处理函数
void YourClass::laserScanCallback(const sensor_msgs::LaserScan::ConstPtr &laserScan_msg)
{
...略
}

六. 将单线激光雷达的LaserScan数据从极坐标系表示转换到激光雷达笛卡尔坐标系表示,并生成需要的点云(PointCloud)数据L(1xn);
七. 对上述单线激光雷达坐标系下的点云数据L(1xn),做水平插值,以保证点云密度,生成新的点云数据L(1xm).
    Note: 第六步和第七步合并处理

        单线激光雷达的数据类型为sensor_msgs::LaserScan,这不同于多线激光雷达的点云数据类型PointCloud.  sensor_msgs::LaserScan的数据实际上是极坐标的形式. 我们需要代码转换为PointCloud格式.
    进行之前,需要先介绍下我使用的这款单线激光雷达的数据特性.
    测距范围: 0.15米--12米;
    每次测量旋转一周的数据数量是: 720个;
    起始角度: -179.013169 度
    结束角度: 180.013264 度
    相邻测量点角度差: 0.499341度  约0.5度
    激光雷达的数据采用极坐标的形式发送,极坐标0度,朝向小车正后方,90度朝向在小车右侧,180度朝向小车前方,270度朝向小车左侧,激光雷达逆时针旋转,
    测距数据从序列为:[-179.013169,180.013264],实际数据是弧度值.
    而小车的载体坐标系是,X轴正向指向小车正前方,Y轴正向小车正左方,Z轴正向垂直向上.

如下图所示,小车的载体坐标系Oxyz,激光雷达的坐标系O'x'y'z',激光雷达的极坐标系:


    所以这里需要两个坐标系变换: 
    1. 激光雷达数据的极坐标系旋转到和激光雷达三维坐标系一致的方位; 
    2. 旋转后的激光雷达极坐标系数据转换为激光雷达的三维笛卡尔积坐标系数据;
    
    现在,等待相机内参,相机图像数据和激光雷达数据都准备好后,就可以开始:
    已知数据:
    1. 单线激光雷达坐标系和相机坐标系的变换tf: camera_lidar_tf;
    2. 相机内参信息:  image_frame_size, camera_intrinsic_value, distortion_coefficients, fx,fy,cx,cy;
    3. 来自相机的去畸变图像数据: cv::Mat current_image_frame;
    4. 来自单线激光雷达的扫描数据: sensor_msgs::LaserScan::ConstPtr laserScan_msg;
    5. 宏定义:
    #define H_LIDAR_INTERPOLATION  (true) //是否对雷达数据水平方向插值
    #define H_LIDAR_INTERPOLATION_IN_RADIAN  (0.00349)  //弧度
    #define H_LIDAR_INTERPOLATION_IN_RADIAN_3  (0.002)  //弧度
    #define V_LIDAR_INTERPOLATION  (true) //是否对雷达数据垂直方向插值
    #define V_UP_LIDAR_INTERPOLATION_IN_PIXEL  (99)  //向上垂直插值个数
    #define V_DOWN_LIDAR_INTERPOLATION_IN_PIXEL  (30)  //向下垂直插值个数

代码: 激光雷达扫描数据转点云数据,同时做了过滤,插值,点云转换,按像素平面顺序重排:

//激光雷达扫描数据转点云数据,同时做了过滤,插值,点云转换,按像素平面顺序重排:pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_msg(new pcl::PointCloud<pcl::PointXYZ>);cloud_msg->header.frame_id = laserScan_msg->header.frame_id;//"laser_link";cloud_msg->height = 1; //垂直扩展前高度为1cloud_msg->width = laserScan_msg->ranges.size(); //水平插值前宽度int pointCount = laserScan_msg->ranges.size();std::vector<pcl::PointXYZ>  pointVector; float lastLeftRadian = -1;float lastRightRadian = -1;//range data [m] (Note: values < range_min or > range_max should be discarded)for(int i = 0; i < pointCount; i++) //遍历激光雷达扫描数据{   float distance = laserScan_msg->ranges[i];//过滤无效距离if( distance < laserScan_msg->range_min || distance > laserScan_msg->range_max )continue;/*小车载体坐标系是前向X轴正向,左侧Y轴正向;而雷达的极坐标系为后侧为0轴,前向180度.逆时针旋转,到180度时,从-179开始累加1直到0轴所以,返回的雷达数据中,前方摄像头可视区域内,雷达数据存储顺序是[-179,-150] + [150,180],单位度.两个坐标系相差180度或一个PI,我们对雷达角度信息加一个PI,以使两个坐标系起始轴一致.*/float radian = laserScan_msg->angle_min+laserScan_msg->angle_increment*i + M_PI;float degree = RAD2DEG(radian);/*雷达数据转换到和载体坐标系一致后,有效区域数据顺序为:[330,360]和[0,30],为了和摄像头拍摄的像素平面X-Y坐标一致,需要重新排序为:[30,0] + [360,330]*///过滤雷达数据,只保留相机拍摄区数据. 左前方雷达数据,倒序存放为[30,0]if( degree > 0 and degree <= 45 ){float x = cos(radian) * distance;//这里可以对cos(),sin()值缓存优化float y = sin(radian) * distance;cloud_msg->points.insert(cloud_msg->points.begin(),pcl::PointXYZ(x, y, 0));//z坐标0,是激光雷达本地坐标系z坐标//水平插值.  Note: 这里暂时采用较粗暴的插值方法,只是按极坐标角度方向插值,后期要做优化.
#ifdef H_LIDAR_INTERPOLATIONfloat step = H_LIDAR_INTERPOLATION_IN_RADIAN;if( distance > 3.0f )step = H_LIDAR_INTERPOLATION_IN_RADIAN_3;if( lastLeftRadian >= 0 && (radian-lastLeftRadian) > step ){for(float rad = lastLeftRadian; rad < radian; rad+= step){float x2 = cos(rad) * distance; //这里可以对cos(),sin()值缓存优化float y2 = sin(rad) * distance;cloud_msg->points.insert(cloud_msg->points.begin(),pcl::PointXYZ(x2, y2, 0));//z坐标0,是激光雷达本地坐标系z坐标}}lastLeftRadian = radian;
#endif}//过滤雷达数据,只保留相机拍摄区数据. 右前方雷达数据,倒序存放为[360,330]if( degree >= 315 && degree <= 360 ){float x = cos(radian) * distance; //这里可以对cos(),sin()值缓存优化float y = sin(radian) * distance;pointVector.insert(pointVector.begin(),pcl::PointXYZ(x, y, 0));//z坐标0,是激光雷达本地坐标系z坐标//水平插值  Note: 这里暂时采用较粗暴的插值方法,只是按极坐标角度方向插值,后期要做优化.
#ifdef H_LIDAR_INTERPOLATIONif( lastRightRadian >= 0 && (radian-lastRightRadian) > H_LIDAR_INTERPOLATION_IN_RADIAN ){for(float rad = lastRightRadian; rad < radian; rad+= H_LIDAR_INTERPOLATION_IN_RADIAN){float x2 = cos(rad) * distance; //这里可以对cos(),sin()值缓存优化float y2 = sin(rad) * distance;pointVector.insert(pointVector.begin(),pcl::PointXYZ(x2, y2, 0));//z坐标0,是激光雷达本地坐标系z坐标}}lastRightRadian = radian;
#endif}}//调整雷达数据顺序和计算笛卡尔坐标后,按相机像素图像顺序,从左到右,保存到cloud_msg->points. 方便后面直接映射到相机像素平面for( int i = 0; i < pointVector.size(); i++ )cloud_msg->points.push_back(pcl::PointXYZ(pointVector.at(i).x, pointVector.at(i).y, pointVector.at(i).z));//z坐标暂存变换后极坐标角度cloud_msg->width = cloud_msg->points.size(); //水平插值后宽度

八. 对激光雷达笛卡尔坐标系下的单行点云数据L(1xm)做垂直方向扩展,向下扩展到地面和墙面交界处k,向上扩展约0.5米高j,生成激光雷达笛卡尔坐标系下的点云数据L((k+1+j)xm);

#ifdef V_LIDAR_INTERPOLATIONpcl::PointCloud<pcl::PointXYZ>::Ptr point_cloud(new pcl::PointCloud<pcl::PointXYZ>);point_cloud->header.frame_id = laserScan_msg->header.frame_id;//"laser_link";point_cloud->height = V_UP_LIDAR_INTERPOLATION_IN_PIXEL + V_DOWN_LIDAR_INTERPOLATION_IN_PIXEL + 1;point_cloud->width = cloud_msg->width;//向上扩展99行for( int up = 1; up <= V_UP_LIDAR_INTERPOLATION_IN_PIXEL; up++ ){for(int i = 0; i < cloud_msg->points.size(); i++){point_cloud->points.push_back(pcl::PointXYZ(cloud_msg->points[i].x, cloud_msg->points[i].y, up/200.0));//这里z坐标应该为正数}}//这是原始激光雷达数据水平插值后单行点云数据, L(1xm)for(int i = 0; i < cloud_msg->points.size(); i++){point_cloud->points.push_back(pcl::PointXYZ(cloud_msg->points[i].x, cloud_msg->points[i].y, cloud_msg->points[i].z));//这里z坐标应该为0}//向下扩展30行for( int down = 1; down <= V_DOWN_LIDAR_INTERPOLATION_IN_PIXEL; down++ ){for(int i = 0; i < cloud_msg->points.size(); i++){point_cloud->points.push_back(pcl::PointXYZ(cloud_msg->points[i].x, cloud_msg->points[i].y, -down/200.0));//这里z坐标开始为负数}}cloud_msg->height = point_cloud->height;cloud_msg->width = point_cloud->width;cloud_msg->points.clear();for(int i = 0; i < point_cloud->points.size(); i++) cloud_msg->points.push_back(point_cloud->points[i]);
#endif

到这里我们生成了激光雷达本地坐标系下的一系列点云数据L((k+1+j)xm), 针对上述代码是130行,m列,m值大小不固定.

九. 将上一步生成的点云(PointCloud)数据L((k+1+j)xm)利用一,二步发布的tf变换到相机坐标系下,生成相机坐标系下的点云数据C((k+1+j)xm),同时利用获取的相机内参信息,将点云映射到相机像素平面坐标(x,y),并从像素平面(current_image_frame)获取对应颜色RGB值;

//定义新的带3D坐标和RGB的点云数据
pcl::PointXYZRGB colored_3d_point;pcl::PointCloud<pcl::PointXYZRGB>::Ptr outColorPointCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
outColorPointCloud->points.clear();std::vector<pcl::PointXYZ> cam_cloud(cloud_msg->points.size());for(int i = 0; i < cloud_msg->points.size(); i++)
{//把点云里面的3D坐标从激光雷达坐标系,变换到摄像机的坐标系cam_cloud[i] = transformPoint(cloud_msg->points[i], camera_lidar_tf);// 再使用相机内参将三维空间点投影到像素平面int col = int(cam_cloud[i].x * fx / cam_cloud[i].z + cx);int row = int(cam_cloud[i].y * fy / cam_cloud[i].z + cy);//根据映射后的坐标,从当前摄像机的当前图像帧current_image_frame上获取颜色值,并保存对应的3D位置信息if ((col >= 0) && (col < image_frame_size.width) && (row >= 0) && (row < image_frame_size.height) ) {//生成的点云坐标,在rviz中显示和小车前后位置相反,系前面极坐标旋转导致,这里强制把x,y(分别乘-1)相对原点做旋转colored_3d_point.x = -cloud_msg->points[i].x;  //乘-1colored_3d_point.y = -cloud_msg->points[i].y; //乘-1colored_3d_point.z = cloud_msg->points[i].z;cv::Vec3b rgb_pixel = current_image_frame.at<cv::Vec3b>(row, col);colored_3d_point.r = rgb_pixel[2] * 2;colored_3d_point.g = rgb_pixel[1] * 2;colored_3d_point.b = rgb_pixel[0] * 2;outColorPointCloud->points.push_back(colored_3d_point);}
}
//转换为ROS的点云类型
sensor_msgs::PointCloud2 out_colored_cloud_msg;
pcl::toROSMsg(*outColorPointCloud, out_colored_cloud_msg);
out_colored_cloud_msg.header = laserScan_msg->header;//发布"colored_point_cloud" 主题消息
fusion_cloud_pub_.publish(out_colored_cloud_msg);

十. 根据以上各参数和生成的数据,进行点云数据和视觉信息融合;
    1>. 二维相机图像像素数据 --融合到--> 三维空间的点云数据;
    因为本案例仅仅出于演示单线激光雷达和视觉融合目的,不涉及具体功能逻辑,做到发布融合视觉信息的点云到rviz显示,已经达到目的;
    执行rviz,查看topic: PointCloud2的"colored_point_cloud",即可.
    2>. 三维空间的点云数据 --融合到--> 二维相机图像像素数据;
    此步骤与上述相似,只需将已经获得的摄像机坐标系下的点云数据,映射并修改相机像素平面(current_image_frame)的坐标(x,y)像素值即可.
    只是处理时,点云数据建议做下稀疏处理,因为上面水平插值和垂直扩展后,点云数据太密了.

看看在rviz中效果图(未优化):

一.相机视觉效果(rqt_image_view):

2.单线激光雷达扫描图(rviz):

3.单线雷达和视觉融合效果(rviz: 正视图):

4.单线雷达和视觉融合效果(rviz: 左视图,因为水平插值未优化,有明显片状切片现象):

5.单线雷达和视觉融合效果(rviz: 右视图,因为水平插值未优化,有明显片状切片现象):

6.单线雷达和视觉融合效果(rviz: 后视图):

7.单线雷达和视觉融合效果(rviz: 俯视图,俯视角度问题,雷达点和视觉点云看起来有些错位):

十一. 下一步细节优化,性能调整计划.
    1. 由极坐标转笛卡尔坐标过程中的cos()和sin()值,因为每次处理雷达扫描都是720个一样的值,可以缓存做优化,避免每次都重复计算;

2. 单线激光雷达水平插值算法优化,从在极坐标系下基于角度插值改为雷达笛卡尔坐标系下的基于3D坐标插值,这样最终融合视觉信息后,在rviz中不会呈现片状切片.
    3. 水平插值和垂直扩展算法,可以改进为根据景物距离采用不同的插值和扩展密度;
    4. 下一步试试单线激光雷达和视觉融合是否可以应用到SLAM和导航中,以及配合AMCL做定位.

最后:

        应大家要求,我上传了配到的: 单线激光雷达和视觉信息融合 ROS实践功能包

当时随便写着玩玩,代码也写的比较乱, 本不想拿出来献丑, 很多朋友要求,就发出来.

十五. 单线激光雷达和视觉信息融合相关推荐

  1. 无人驾驶汽车系统入门(二十五)——基于欧几里德聚类的激光雷达点云分割及ROS实现

    无人驾驶汽车系统入门(二十五)--基于欧几里德聚类的激光雷达点云分割及ROS实现 上一篇文章中我们介绍了一种基于射线坡度阈值的地面分割方法,并且我们使用pcl_ros实现了一个简单的节点,在完成了点云 ...

  2. 多传感器融合定位十五-多传感器时空标定(综述)

    多传感器融合定位十五-多传感器时空标定 1. 多传感器标定简介 1.1 标定内容及方法 1.2 讲解思路 2. 内参标定 2.1 雷达内参标定 2.2 IMU内参标定 2.3 编码器内参标定 2.4 ...

  3. 麒麟信安携异构融合云金融信创解决方案亮相第十五届湖南地区金融科技交流会

    为推动金融科技在湖南地区的创新发展,更好地助力金融服务实体经济,6月23日下午,在中国人民银行长沙中心支行科技处的支持下,"第十五届湖南地区金融科技交流会"于长沙顺利召开.会议上, ...

  4. 解析激光雷达中时序融合的研究现状和发展方向

    本文转自:计算机视觉联盟 在自动驾驶领域,基于激光雷达 (LiDAR) 的3D物体检测和运动行为预测是一种普遍的方案.目前绝大部分关于激光雷达的物体检测算法都是基于单帧的.激光雷达的多帧时序数据,提供 ...

  5. 蚂蚁金服十五年技术架构演进之路

    来自:蚂蚁金服科技 蚂蚁金服过去十五年,通过技术重塑了支付服务.小微贷款服务.我们认为 Blockchain (区块链).Artificial intelligence(人工智能).Security( ...

  6. 第十五届全国大学生智能汽车竞赛 人工智能创意组总决赛

    第十五届全国大学生智能汽车竞赛 人工智能创意组总决赛 一.赛题背景 近年来,随着人工智能特别是深度学习的发展,如何通过自学习实现避障已成为一大研究热点.实现自主学习是机器人实现智能化的重要一步,有利于 ...

  7. 网易云信荣获第十五届中国企业年终评选「IT行业优秀技术奖」!

    近日,由 51CTO主办的<中国企业"IT 印象◆释放 IT 动能,加速数智时代"年终评选>暨2020年第十五届中国企业年终评选获奖结果正式发布,系列获奖新技术.新成果 ...

  8. 二十五.SLAM中Mapping和Localization区别和思考

    专栏系列文章如下: 一:Tixiao Shan最新力作LVI-SAM(Lio-SAM+Vins-Mono),基于视觉-激光-惯导里程计的SLAM框架,环境搭建和跑通过程_goldqiu的博客-CSDN ...

  9. 推荐系统系列教程之十五:一网打尽协同过滤、矩阵分解和线性模型

    编者按:之前推出了<推荐系统系列教程>,反响不错,前面已经推出了十四期,今天按约推出第十五期:一网打尽协同过滤.矩阵分解和线性模型.希望朋友们多点"在看",多多转发,我 ...

最新文章

  1. react测试组件_测试驱动的开发,功能和React组件
  2. python语言程序设计西安电子科技大学答案-徐悦甡 | 个人信息 | 西安电子科技大学个人主页...
  3. new Integer 和 Integer.valueOf 有什么不同
  4. postfix导致maillog填满磁盘空间的巨坑!
  5. artDialog对话框在PHP下的简单应用-artDialog弹出层篇
  6. 得到指定进程所有窗口。显示 影藏 置顶。
  7. SmartSql使用教程(1)——初探,建立一个简单的CURD接口服务
  8. Android模拟器环境下SD卡内容的管理
  9. a标签下载文件文档流 xlsxlsxdoc,docx,pdf
  10. matlab 系数矩阵存储,用Matlab对矩阵进行LU分解法 值得收藏
  11. Docker升级Wekan
  12. go decimal 使用方法
  13. linux系统下使用润乾报表设计器
  14. 控制元素到达可视区域内触发动效
  15. Mac更新系统后提示xcrun error
  16. “科林明伦杯”哈尔滨理工大学第十届程序设计竞赛
  17. Linux磁盘空间被占用的释放方法
  18. 下载PHYSICAL REVIEW JOURNALS的TEX模板
  19. java/php/net/python校园招聘管理系统设计
  20. Pyhon初探(1)

热门文章

  1. PS学习笔记_Day1 文件操作
  2. 计算机主机装机步骤,电脑组装步骤详细教程之裸机点亮 再将DIY硬件装入主机箱...
  3. Peakcoo软硬件方案设计——汽车胎压表气压计PCBA方案
  4. WordPress多本小说主题–WNovel主题,现已更新至1.2版本
  5. WindowsXP中的文件加密功能及其使用
  6. IPCC 政府间气候变化专门委员会(IPCC)是联合国评估气候变化相关科学的机构
  7. 我看被和谐新闻的心路历程
  8. ajax open方法和表单
  9. 硬件系列(三)--------wifi打印机之使用socket打印(无sdk)
  10. broadcom 802.11n linux驱动下载,broadcom 802.11n驱动下载