单线激光雷达(SICK)驱动安装及时空标定

news2024/11/26 19:43:40

一、引言

1、AGV需要同时具备定位、避障与导航的功能,其中避障对于雷达本身的分辨率、精度要求并不是很高,只需要能够根据预设定的雷达扫描范围准确避开障碍物即可,故本文以TIM240(SICK激光类雷达)为例介绍实现多雷达时空标定的问题。
2、多个避障雷达可能会被安装在车体各个位置,并且不一定有重叠区域,所以通过提取特征点再进行ICP或NDT配准的方法获取相对位姿变换关系的方式不可行,由于机械结构本身已经按照设计图纸预先设置好雷达的安装位置,所以雷达之间的相对位置关系相对准确,故可以直接利用已有参数可以进行雷达空间标定,将两个雷达数据统一到同一个坐标系下进行,需要用到PCL库。
3、坐标变换实际使用过程当中确实可以用tf进行静态坐标变换,但是相比较直接手动写代码进行转换延迟较高,故采用手动根据相对位置关系进行坐标变换的方式。
4、进行时空标定步骤主要分为:(1)驱动安装及雷达参数配置(2)雷达时间同步(3)雷达空间同步(Rp+T,这里的旋转矩阵和平移矩阵的定义后文会给出)

二、整体思路与流程

1、SICK激光雷达驱动安装
新买的SICK雷达不仅需要安装驱动,还需要进配置文件修改参数才能正常工作
(1)安装驱动

mkdir -p ./sick_scan_ws
cd ./sick_scan_ws

mkdir ./src
pushd ./src
git clone https://ghproxy.com/https://github.com/SICKAG/libsick_ldmrs.git
git clone https://ghproxy.com/https://github.com/SICKAG/msgpack11.git
git clone https://ghproxy.com/https://github.com/SICKAG/sick_scan_xd.git
popd

mkdir -p ./build/msgpack11
pushd ./build/msgpack11
cmake -G "Unix Makefiles" -D CMAKE_CXX_FLAGS=-fPIC -D CMAKE_BUILD_TYPE=Release -D MSGPACK11_BUILD_TESTS=0 ../../src/msgpack11
make
sudo make install
popd

source /opt/ros/melodic/setup.bash # replace noetic by your ros distro
catkin_make_isolated --install --cmake-args -DROS_VERSION=1 -Wno-dev
source ./devel_isolated/setup.bash

(2)雷达配置文件修改
1)首先需要设置PC机的以太网ip端口号如:192.168.0.2
2)SICK官网下载sopas软件,在Windows端使用(用Win10系统,Win11有问题)
3)进入软件,修改登录权限为授权的用户,密码是client
在这里插入图片描述
4)修改雷达ip(与PC机同一网段即可,192.168.0.1以及Cola dialect改为cola binary格式。此处修改雷达ip是为了保证两个雷达能同时使用
在这里插入图片描述
(3)硬件组装
需要交换机、电源模块、电源线(普通的家庭供电线就可以,火线为棕灰色、地线为黄绿双色、零线为蓝色,分别对应接到电源模块的L端、FG端、N端)、供电线、网线,如下图连接示意图
在这里插入图片描述
(4)修改雷达启动文件(launch),需要创建两个启动文件,分别对应不同ip的雷达,我这里设置一个ip为192.168.0.1,一个为192.168.0.3,因为PC的ip为192.168.0.2

<?xml version="1.0"?>
<!--
     **********************************************
     Launch File for TIM 240 scanner
     **********************************************
     Start and stop angle is given in [rad]

     Check IP-address, if you scanner is not found after roslaunch.
-->

<!-- You can launch a TIM_240-scanner on a specific ip address (e.g. 192.68.0.71) using the following example call:

     roslaunch sick_scan sick_tim_240.launch hostname:=192.168.0.71

-->
<!-- Using node option required="true" will close roslaunch after node exits -->

<launch>
    <arg name="hostname" default="192.168.0.1"/>
    <arg name="port" default="2111"/>
    <arg name="cloud_topic" default="cloud1"/>
    <arg name="frame_id" default="cloud1"/>
    <arg name="sw_pll_only_publish" default="true"/>
    <arg name="nodename" default="sick_tim_240_1"/>
    <arg name="add_transform_xyz_rpy" default="0,0,0,0,0,0"/>
    <arg name="add_transform_check_dynamic_updates" default="false"/> <!-- Note: dynamical updates of parameter add_transform_xyz_rpy can decrease the performance and is therefor deactivated by default -->
    <node name="$(arg nodename)" pkg="sick_scan" type="sick_generic_caller" respawn="false" output="screen" required="true">
        <param name="scanner_type" type="string" value="sick_tim_240"/>
        <!-- datagram shows values from -90 ... 120 (211 values) -->
        <!-- -120 deg -->
        <param name="min_ang" type="double" value="-2.094395102"/>
        <!-- 120 deg -->
        <param name="max_ang" type="double" value="+2.094395102"/>
        <!-- corresponds to 1.0/(14.4 * 360) -->
        <param name="time_increment" type="double" value="0.00019290123"/>
        <param name="use_binary_protocol" type="bool" value="True"/>
        <param name="intensity" type="bool" value="true"/>
        <param name="intensity_resolution_16bit" type="bool" value="true"/>
        <param name="hostname" type="string" value="$(arg hostname)"/>
        <param name="cloud_topic" type="string" value="$(arg cloud_topic)"/>
        <param name="frame_id" type="str" value="$(arg frame_id)"/>
        <param name="port" type="string" value="$(arg port)"/>
        <param name="timelimit" type="int" value="5"/>
        <param name="sw_pll_only_publish" type="bool" value="$(arg sw_pll_only_publish)"/>
        <param name="use_generation_timestamp" type="bool" value="true"/> <!-- Use the lidar generation timestamp (true, default) or send timestamp (false) for the software pll converted message timestamp -->
        <param name="min_intensity" type="double" value="0.0"/> <!-- Set range of LaserScan messages to infinity, if intensity < min_intensity (default: 0) -->

        
        <!-- Optional range filter configuration: If the range of a scan point is less than range_min or greater than range_max, the point can be filtered. -->
        <!-- Depending on parameter range_filter_handling, the following filter can be applied for points with a range not within [range_min, range_max],   -->
        <!-- see enumeration RangeFilterResultHandling in range_filter.h:                                           -->
        <!--   0: RANGE_FILTER_DEACTIVATED,  do not apply range filter (default)                                    -->
        <!--   1: RANGE_FILTER_DROP,         drop point, if range is not within [range_min, range_max]              -->
        <!--   2: RANGE_FILTER_TO_ZERO,      set range to 0, if range is not within [range_min, range_max]          -->
        <!--   3: RANGE_FILTER_TO_RANGE_MAX, set range to range_max, if range is not within [range_min, range_max]  -->
        <!--   4: RANGE_FILTER_TO_FLT_MAX,   set range to FLT_MAX, if range is not within [range_min, range_max]    -->
        <!--   5: RANGE_FILTER_TO_NAN        set range to NAN, if range is not within [range_min, range_max]        -->
        <!-- Note: Range filter applies only to Pointcloud messages, not to LaserScan messages.                     -->
        <!-- Using range_filter_handling 4 or 5 requires handling of FLT_MAX and NAN values in an application.      -->
        <param name="range_min" type="double" value="0.0"/>
        <param name="range_max" type="double" value="100.0"/>
        <param name="range_filter_handling" type="int" value="0"/>
        
        <!-- Apply an additional transform to the cartesian pointcloud, default: "0,0,0,0,0,0" (i.e. no transform) -->
        <!-- Note: add_transform_xyz_rpy is specified by 6D pose x, y, z, roll, pitch, yaw in [m] resp. [rad] -->
        <!-- It transforms a 3D point in cloud coordinates to 3D point in user defined world coordinates: --> 
        <!-- add_transform_xyz_rpy := T[world,cloud] with parent "world" and child "cloud", i.e. P_world = T[world,cloud] * P_cloud -->
        <!-- The additional transform applies to cartesian lidar pointclouds and visualization marker (fields) -->
        <!-- It is NOT applied to polar pointclouds, radarscans, ldmrs objects or other messages -->
        <param name="add_transform_xyz_rpy" type="string" value="$(arg add_transform_xyz_rpy)" /> 
        <param name="add_transform_check_dynamic_updates" type="bool" value="$(arg add_transform_check_dynamic_updates)" />

        <param name="start_services" type="bool" value="True" />                  <!-- Start ros service for cola commands, default: true -->
        <param name="message_monitoring_enabled" type="bool" value="True" />      <!-- Enable message monitoring with reconnect+reinit in case of timeouts, default: true -->
        <param name="read_timeout_millisec_default" type="int" value="5000"/>     <!-- 5 sec read timeout in operational mode (measurement mode), default: 5000 milliseconds -->
        <param name="read_timeout_millisec_startup" type="int" value="120000"/>   <!-- 120 sec read timeout during startup (sensor may be starting up, which can take up to 120 sec.), default: 120000 milliseconds -->
        <param name="read_timeout_millisec_kill_node" type="int" value="150000"/> <!-- 150 sec pointcloud timeout, ros node will be killed if no point cloud published within the last 150 sec., default: 150000 milliseconds -->
        <param name="client_authorization_pw" type="string" value="F4724744"/>    <!-- Default password for client authorization -->

        <!-- Configuration of ROS quality of service: -->
        <!-- On ROS-1, parameter "ros_qos" sets the queue_size of ros publisher -->
        <!-- On ROS-2, parameter "ros_qos" sets the QoS of ros publisher to one of the following predefined values: -->
        <!-- 0: rclcpp::SystemDefaultsQoS(), 1: rclcpp::ParameterEventsQoS(), 2: rclcpp::ServicesQoS(), 3: rclcpp::ParametersQoS(), 4: rclcpp::SensorDataQoS() -->
        <!-- See e.g. https://docs.ros2.org/dashing/api/rclcpp/classrclcpp_1_1QoS.html#ad7e932d8e2f636c80eff674546ec3963 for further details about ROS2 QoS -->
        <!-- Default value is -1, i.e. queue_size=10 on ROS-1 resp. qos=rclcpp::SystemDefaultsQoS() on ROS-2 is used.-->
        <param name="ros_qos" type="int" value="-1"/>  <!-- Default QoS=-1, i.e. do not overwrite, use queue_size=10 on ROS-1 resp. qos=rclcpp::SystemDefaultsQoS() on ROS-2 -->

    </node>
</launch>

2、SICK激光雷达时间同步
主要使用ros官方提供的软同步方式(message_filters
使用方式如下:

ros::Publisher pointcloud_pub;
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, sensor_msgs::PointCloud2> testSyncPolicy;

void callback(const sensor_msgs::PointCloud2ConstPtr &point1, const sensor_msgs::PointCloud2ConstPtr &point2)  //回调中包含多个消息
{
	//需要处理的内容//
}

int main (int argc, char **argv)
{
    ros::init (argc, argv, "lidar2base");
    ros::NodeHandle n;
    pointcloud_pub = n.advertise<sensor_msgs::LaserScan>("/scan", 1000);
    //发布的话题
    message_filters::Subscriber<sensor_msgs::PointCloud2> pointcloud1_sub(n, "/cloud1", 1);// 需要同步的topic1
    message_filters::Subscriber<sensor_msgs::PointCloud2> pointcloud2_sub(n, "/cloud2", 1);// 需要同步的topic2 
    message_filters::Synchronizer<testSyncPolicy> sync(testSyncPolicy(10), pointcloud1_sub, pointcloud2_sub);// 同步
    sync.registerCallback(boost::bind(&callback, _1, _2));

    ros::spin();
    ros::shutdown();
    return 0;
}

3、SICK激光雷达空间同步
主要是通过雷达之间的旋转和平移参数将两个雷达数据统一到一个坐标系下再使用PCL库对雷达数据进行融合,这里编写了相关函数

//坐标变换将激光雷达坐标系下的点转换到小车坐标系下
void transPoint(pcl::PointCloud<pcl::PointXYZ>::Ptr lidarCloud,
                 pcl::PointCloud<pcl::PointXYZ>::Ptr carCloud,
                 double yaw,double pitch,double roll,double x,double y,double z)
{
    Eigen::Matrix4f transform=Eigen::Matrix4f::Identity();
    Eigen::Matrix4f transformYaw;
    Eigen::Matrix4f transformPitch;
    Eigen::Matrix4f transformRoll;
    
    //航向角
    transformYaw<<cos(yaw),-sin(yaw),0,0,\
            sin(yaw),cos(yaw),0,0,\
            0,0,1,0,\
            0,0,0,1;
    //俯仰角
    transformPitch<<cos(pitch),0,sin(pitch),0,\
            0,1,0,0,\
            -sin(pitch),0,cos(pitch),0,\
            0,0,0,1;
    //横滚角
    transformRoll<<1,0,0,0,\
            0,cos(roll),-sin(roll),0,\
            0,sin(roll),cos(roll),0,\
            0,0,0,1;
            
    //旋转矩阵
    transform=transformRoll*transformPitch*transformYaw;
    
    //平移矩阵
    transform(0,3)=x;
    transform(1,3)=y;
    transform(2,3)=z;
    
    //坐标转换
    pcl::transformPointCloud(*lidarCloud,*carCloud,transform);
}

如下图:
需求是通过上述函数将P点从激光雷达坐标系变换到车体坐标系,函数的输入分别为偏航角yaw, pitch, roll,x,y,z
在这里插入图片描述
偏航角yaw的获取方式是:以车坐标系为右手基准坐标系,大拇指指向为z轴正方向,激光雷达系为目标坐标系,顺时针旋转z轴的角度分量为yaw的值,图中x1Oy1为车体坐标系。
同理俯仰角pictch为绕y轴旋转角度分量,横滚角roll为绕x轴的分量。
平移分量(x, y, z)是激光雷达相对于车体坐标原点的x,y,z的坐标。

在这里插入图片描述4、雷达数据融合
需要将ros的PointCloud2类型数据转换为PCL数据类型,而后将PCL数据类型转为ros的LaserScan数据类型。
1)数据融合主要在SICK激光雷达时间同步这一部分的回调函数中写:

    pcl::PointCloud<pcl::PointXYZ>::Ptr colorcloud1(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::fromROSMsg(*point1, *colorcloud1);
    pcl::PointCloud<pcl::PointXYZ>::Ptr colorcloud2(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::fromROSMsg(*point2, *colorcloud2);
    
    pcl::PointCloud<pcl::PointXYZ>::Ptr output_points1(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr output_points2(new pcl::PointCloud<pcl::PointXYZ>);

    transPoint(colorcloud1, output_points1, yaw1, pitch1, roll1, x1, y11, z1);
    transPoint(colorcloud2, output_points2, yaw2, pitch2, roll2, x2, y2, z2);
    pcl::PointCloud<pcl::PointXYZ> output = *output_points1 + *output_points2;
    
    
    //pcl::toROSMsg((*output_points1 + *output_points2), output);
    //以实时发布的方式发布
    
    sensor_msgs::LaserScan output2 = PointCloudToLaserscan(output);
    output2.header.frame_id = "base_link";
    pointcloud_pub.publish(output2);

2)PCL转LaserScan单独写了一个函数

sensor_msgs::LaserScan PointCloudToLaserscan(pcl::PointCloud<pcl::PointXYZ>& _pointcloud)
{
  float angle_min, angle_max, range_min, range_max, angle_increment;
  
  //需要自行调整的参数
  angle_min = -2.094395102;
  angle_max =  2.094395102;
  range_min = 0.05;
  range_max = 10;
  //角度分辨率,分辨率越小,转换后的误差越小
  angle_increment = 0.0174444444;

  //计算扫描点个数
  unsigned int beam_size = ceil((angle_max - angle_min) / angle_increment);

  sensor_msgs::LaserScan output;
  output.header.stamp = ros::Time::now();
  output.header.frame_id = "base_link";
  output.angle_min = angle_min;
  output.angle_max = angle_max;
  output.range_min = range_min;
  output.range_max = range_max;
  output.angle_increment = angle_increment;
  output.time_increment = 0.0;
  output.scan_time = 0.0;
  
  //先将所有数据用nan填充
  output.ranges.assign(beam_size, std::numeric_limits<float>::quiet_NaN());
  //output.intensities.assign(beam_size, std::numeric_limits<float>::quiet_NaN());

  for (auto point : _pointcloud.points)
  {
    float range = hypot(point.x, point.y);
    float angle = atan2(point.y, point.x);
    int index = (int)((angle - output.angle_min) / output.angle_increment);
    if (index >= 0 && index < beam_size)
    {
      //如果当前内容为nan,则直接赋值
      if (isnan(output.ranges[index]))
      {
        output.ranges[index] = range;
      }
      //否则,只有距离小于当前值时,才可以重新赋值
      else
      {
        if (range < output.ranges[index])
        {
          output.ranges[index] = range;
        }
      }
      //output.intensities[index] = point.intensity;
    }
  }
  return output;
}

5、创建启动脚本
首先启动两个雷达,其次启动坐标转换的脚本,即可实现两个雷达的时空同步

#!/bin/bash
gnome-terminal --tab -- bash -c "roslaunch sick_scan sick_tim_240_1.launch"
sleep 1s
gnome-terminal --tab -- bash -c "roslaunch sick_scan sick_tim_240_2.launch "
sleep 2s
gnome-terminal --tab -- bash -c "source /home/lixushi/catkin_ws/devel/setup.bash; roslaunch lidar2base lidar2base.launch"

如下图为雷达实际摆放位置
在这里插入图片描述

转换前的点云图
在这里插入图片描述
转换后的点云图
在这里插入图片描述

三、参考文献

[1] https://blog.csdn.net/m0_68312479/article/details/128266483
[2] https://blog.csdn.net/hltt3838/article/details/123067620
[3] https://blog.csdn.net/m0_68312479/article/details/128266483
[4] https://adamshan.blog.csdn.net/article/details/105930565?spm=1001.2014.3001.5502
[5] https://mp.weixin.qq.com/s?__biz=MzU1NjEwMTY0Mw==&mid=2247556040&idx=1&sn=fa1f7cc63f7aeeaed69242add86efd75&chksm=fbc862acccbfebba12fd29cef32b7a34ac296f54e8ef6f19485aff81ebddd794a3364419c90b&scene=27

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/393367.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Canal实战 canal的坑 CanalParseException: column size is not match for table 错误

时序表结构方案设计 异常现象截图 canal的坑&#xff1a;CanalParseException: column size is not match for table (表结构缓存异常阻塞问题) 背景 早期的canal版本(<1.0.24)&#xff0c;在处理表结构的DDL变更时采用了一种简单的策略&#xff0c;在内存里维护了一个当…

Django实践-04静态资源和Ajax请求

文章目录Django实践-04静态资源和Ajax请求Django实践-04静态资源配置创建静态资源目录修改settings.py文件Django实践-04用Ajax实现投票功能修改项目的urls.py文件修改polls/views.py文件修改显示老师信息的模板页&#xff0c;引入jQuery库来实现事件处理、Ajax请求和DOM操作。…

AUTOSAR知识点Com(三):CanIf发送

1、概述 CanIf的发送请求函数CanIf_Transmit()是上层模块传输L-PDU的通用接口。上层通信层模块需要通过CanIf的服务启动传输&#xff0c;无法直接访问CanDrv。如果CanDrv能够将L-PDU数据写入CAN硬件传输对象中&#xff0c;则发起的传输请求成功完成。上层模块使用API服务CanIf_…

https介绍

目录 一.简介 1.1定义 1.2补充知识 1.2.1 对称加密与非对称加密 1.2.2. 数据摘要 && 数据指纹 二.HTTPS 的⼯作过程探究 2.1只使⽤对称加密 2.2只使用非对称加密 2.3非对称加密与对称加密 2.4中间人攻击 三.证书 3.1CA认证 3.2数据签名 3.3 ⾮对称加密 …

电池保护板 - 问题归纳

电池保护板 - 问题归纳简介充电锂电池磷酸铁锂电池放电总结最近更新日期&#xff1a;2023-03-07简介 电池充放电过程中&#xff0c;如果电压、电流或温度等参数不稳定或超出电池的安全范围&#xff0c;就会对电池造成损害&#xff0c;甚至可能引发安全事故。为了保护电池的安全…

融云入选中国信通院《高质量数字化转型产品及服务全景图》

企业数字化转型正在进入“深水区”。 3 月 3 日&#xff0c;“中国信息通信研究院&#xff08;以下简称中国信通院&#xff09;高质量数字化转型创新发展大会暨中国信通院‘铸基计划’年度峰会”在京召开&#xff0c;深度展示了中国信通院在数字化转型领域的工作成果&#xff…

计算机专业毕业设计基于Spring Boot 学生在线考试系统

目录 一、学生端 1.1 登录 1.2 注册 1.3 学生首页 1.4 学生查看任务中心的试卷&#xff08;已答卷/未答卷&#xff09; 1.5 学生查看固定试卷以及开始做题 1.6 学生查看时段试卷以及开始做题 1.7 学生查看试卷中心 1.8 学生查看考试记录以及查看试卷 1.9 学生查看…

HTTP与HTTPS协议的嵌套访问

概述 HTTP和HTTPS是两种不同的协议。HTTP使用80端口&#xff0c;而HTTPS使用443端口。HTTP是明文传输&#xff0c;而HTTPS为了保障数据传输的安全性&#xff0c;通过SSL证书实现加密传输。 分析 问题1&#xff1a;HTTP和HTTPS可以互相访问吗&#xff1f; 可以互相访问&#…

端口复用(bind error: Address already in use 问题)

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 端口复用专栏&#xff1a;《Linux从小白到大神》《网络编程》 在前面讲解TCP状态转换中提到过一个2MSL…

非栈上的格式化字符串利用例题讲解

题目自取&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1te_oc3GuWTlDDS5q-NhtKQ?pwdcjz9 提取码&#xff1a;cjz9 开始&#xff1a; 一个非常明显的格式化字符串漏洞&#xff0c;但是值得注意的是&#xff0c;此时的buf变量不在栈上&#xff0c;因此我们之前把pr…

SpringBoot 项目中集成 Prometheus 和 Grafana

项目上线后&#xff0c;除了能保障正常运行以外&#xff0c;也需要服务运行的各个指标进行监控&#xff0c;例如 服务器CPU、内存使用占比&#xff0c;Full GC 执行时间等&#xff0c;针对一些指标出现异常&#xff0c;可以加入一些报警机制能及时反馈给开发运维。这样&#xf…

如何打造一个高品质的酒店品牌形象?VR全景营销是关键!

数字化改革早已不是新鲜“字眼”&#xff0c;酒店行业作为竞争激烈的红海市场&#xff0c;运用“数字化”升级改造成为宣传推广的重点方向之一。VR全景酒店&#xff0c;运用沉浸式全景展示&#xff0c;使其在竞争激烈的酒店行业中进行差异化竞争&#xff0c;使消费者在线上全面…

SpringCloud:统一网关Gateway

目录 1、网关介绍 2、搭建网关服务 3、路由断言工厂 4、路由过滤器 5、全局过滤器GlobalFilter 6、过滤器执行顺序 7、跨域问题处理 1、网关介绍 网关(Gateway)又称网间连接器、协议转换器。网关在网络层以上实现网络互连&#xff0c;是复杂的网络互 连设备&#xff0…

Spark SQL快速入门

在spark上运用SQL处理结构化数据 1、SparkSQL快速入门 1.1 什么是SparkSQL SparkSQL 是Spark的一个模块&#xff0c;用于处理海量结构化数据 限定&#xff1a;结构化数据 1.2 为什么学习SparkSQL SparkSQL是非常成熟的&#xff0c;海量结构化数据处理框架 学习SparkSQL主要…

动态代理—Java

代理可以理解为请一个中间人帮忙处理一些事情。代理支持任意接口类型的实现类对象做代理&#xff0c;也可以直接为接本身做代理。可以为被代理对象的所有方法做代理。可以在不改变方法源码的情况下&#xff0c;实现对方法功能的增强。简化了编程工作、提高了软件系统的可扩展性…

QT入门Display Widgets之QLine、QLcdNumber、QTextBrowser

目录 一、QLine界面相关 1、布局介绍 2、界面基本属性 二、QLCDNumber的介绍 1、界面布局 2、定时器代码测试 三、QTextBrowser 此文为作者原创&#xff0c;创作不易&#xff0c;转载请标明出处&#xff01; 一、QLine界面相关 1、布局介绍 先看下界面中创建个Q…

递归(java)

1.递归应用场景 看个实际应用场景&#xff0c;迷宫问题(回溯)&#xff0c; 递归(Recursion) 2.递归的概念 简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。 3.递归调用机制 我列举两个小案例,来帮助大家…

Stream——数字类型的字符串排序

文章目录前言什么是数字类型的字符串一个简单的坑demo拯救坑代码对象集合中的数字类型排序(有坑)对象集合中的数字类型排序 解决扩展将数字类型字符串数组转换为Integer集合总结前言 想到给数据进行排序&#xff0c;一开始头脑中想到的就是sorted()&#xff0c;本篇文章重点说…

SSL安全证书有什么优缺点?

在将SSL实施到您的站点之前&#xff0c;了解SSL的优点和缺点非常重要。下面就给大家分析下安装SSL证书有什么优缺点&#xff1a;优点&#xff1a;1. 加密信息确保您在线发送的数据只被指定的接收者读取&#xff0c;而不被其他人读取。SSL对原始数据进行了一些更改&#xff0c;因…

秒懂SpringBoot之Filter与HandlerInterceptor异同

[版权申明] 非商业目的注明出处可自由转载 出自&#xff1a;shusheng007 文章目录概述前置知识Filter原理及使用场景使用执行顺序设置Filter作用范围HandlerInterceptor原理及使用场景使用实现org.springframework.web.servlet.HandlerInterceptor 接口配置二者异同共同点不同点…