Java对接ansible自动运维化平台

article/2025/10/2 19:14:15

Java对接ansible自动运维化平台实现文件采集分发

经过大量查阅,网上使用Java对接ansible自动运维化平台的示例代码几乎没有,为了方便自己后期巩固以及有需要的小伙伴,特以记录!!!

此次对接主要为以下两个功能:

文件采集(对文件进行批量操作,包括批量从多台主机中采集共性文件如日志文件)

文件分发(对文件进行批量操作,包括批量从多台主机中分发共性文件如日志文件)

场景说明及ansible yum安装

因ansible没有Windows的安装包,所以为了方便测试,搭建了一套Linux环境进行后续工作。

此次采用yum方式安装,在采用yum方式安装Ansible,首先安装EPEL源。

yum install -y http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

查看EPEL源中的Ansible版本

yum info ansible

直接安装此版本,如果有其他要求,请调整源,安装其他ansible版本

yum install -y ansible

安装完成之后,查看ansible版本信息

ansible --version

配置Ansible服务器清单

清单文件/etc/ansible/hosts,在此文件中编写节点主机的对应IP地址和端口在这里插入图片描述
我这里只是做一个演示,其中IP后面可以添加节点真实的SSH的端口,在定义的内容上面有一个[]列表,里面的内容为自定义内容,方面为了操作绑定的节点主机,我习惯称之为分组列表

简单的认证一下,Ping一下添加的主机

在这里插入图片描述

成功安装ansible !!

Java代码实现文件分发

顾名思义,文件分发就是把本机的文件分发到多个主机。这时候就需要 Apache POI(大家可以去导入对应的包)来创建本机的文件了(ansible Host配置文件也通过POI创建)

POI创建文件工具类

package com.tiduyun.cmp.operation.utils;import com.tiduyun.cmp.common.model.operation.HostInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** @author huyuan@tiduyun.com ansible创建文件*/
@Slf4j
@Component
public class AnsibleCreateFileUtils {private final static String filename = "hosts";public static String passWordConnect(List<HostInfo> hostInfo, String hostGroup , String directory) throws IOException{/** 在本地新建一个文件夹 里面创建一个文件 向里面写入内容 */// 创建文件夹对象 创建文件对象File folder = new File(directory);// 如果文件夹不存在 就创建一个空的文件夹if (!folder.exists()) {log.info("创建了文件夹{}" , folder);folder.mkdirs();}File file = new File(directory, filename);// 如果文件不存在 就创建一个空的文件if (!file.exists()) {try {log.info("创建了文件{}" , file);file.createNewFile();} catch (IOException e) {log.error("error data{}" , e);}}// 写入数据// 创建文件字节输出流FileOutputStream fos = new FileOutputStream(file);try {List<String> list = new ArrayList<>();for (HostInfo data : hostInfo) {// 开始写String string = data.getHost() + " ansible_ssh_pass=" + data.getPasswd() + " ansible_ssh_user="+ data.getAccount() + " ansible_ssh_port=" + data.getPort();list.add(string);}String splicingData = StringUtils.join(list, "\n");String str = "[" + hostGroup + "]" + "\n" + splicingData;byte[] bytes = str.getBytes();// 将byte数组中的所有数据全部写入fos.write(bytes);fos.flush();log.info("文件内容{}" , str);// 删除文件// deleteFile(file);// 关闭流} catch (IOException e) {log.error("error data{}" , e);throw e;}finally {if (fos != null) {fos.close();}}return directory;}public static void deleteFile(File file) {if (file.exists()) {// 判断路径是否存在if (file.isFile()) {// boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。file.delete();} else {// 不是文件,对于文件夹的操作// 保存 路径D:/1/新建文件夹2 下的所有的文件和文件夹到listFiles数组中File[] listFiles = file.listFiles();// listFiles方法:返回file路径下所有文件和文件夹的绝对路径for (File file2 : listFiles) {/** 递归作用:由外到内先一层一层删除里面的文件 再从最内层 反过来删除文件夹*    注意:此时的文件夹在上一步的操作之后,里面的文件内容已全部删除*         所以每一层的文件夹都是空的  ==》最后就可以直接删除了*/deleteFile(file2);}}file.delete();} else {log.error("该file路径不存在!!");}}
}

创建主机组配置文件

:ansible分为两种连接方式,这里采用的是密钥连接,生成的文件已拼接密钥!!!后续的采集与分发都要用到这个。(如有不懂的小伙伴,可以去查找一下ansible的连接方式)

    @Overridepublic void ansibleCreateHost(HostInfo hostInfo, String Key) {ParamCheckUtils.notNull(hostInfo, "hostInfo");List<HostInfo> HostIp = Arrays.asList(hostInfo);for (HostInfo data : HostIp) {String ansiblePassWd = data.getPasswd();String PassWd = hostInfoService.decode(ansiblePassWd);data.setPasswd(PassWd);}try {AnsibleCreateFileUtils.passWordConnect(HostIp, ansibleConfigurationItemVo.getHostGroup(),ansibleConfigurationItemVo.getDirectory());} catch (IOException e) {log.error("Failed to create host configuration{}", e);}}

实现文件分发

主机配置文件已经配置好,接下来就是执行ansible对应的命令,通过Java拼接ansible命令。

执行命令工具类

package com.tiduyun.cmp.operation.utils;import lombok.extern.slf4j.Slf4j;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;import static cn.hutool.db.DbUtil.close;/*** @author huyuan@tiduyun.com ansible执行命令工具类* upload 上传文件* createRemoteDirectory  创建远程目录*/
@Slf4j
public class AnsibleExecuteTheOrderUtils {private final static String commandBin = "/bin/sh";private final static String commandC = "-c";/***  创建远程目录*/public static void createRemoteDirectory(String hostGroup, String remotePath, String directory) throws IOException {Runtime run = Runtime.getRuntime();String[] cmds = new String[3];cmds[0] = commandBin;cmds[1] = commandC;cmds[2] ="ansible " + hostGroup + " -m command -a " + "\"mkdir " + remotePath + "\"" + " -i " + directory + "/hosts";// 执行CMD命令Process p = run.exec(cmds);log.info("ansible远程执行命令为{}", cmds[2]);BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8")));try {String lineMes;while ((lineMes = br.readLine()) != null)log.info(lineMes);// 打印输出信息try {// 检查命令是否执行失败。if (p.waitFor() != 0) {if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束log.error("命令执行失败");}} catch (InterruptedException e) {log.error("error data{}", e);}} catch (IOException e) {log.error("fail to carry out command{}", e);throw e;} finally {if (br != null) {br.close();}}}/***  文件分发*/public static void upload(String hostGroup, String localPath, String remotePath, String directory)throws IOException {Runtime run = Runtime.getRuntime();String[] cmds = new String[3];cmds[0] = commandBin;cmds[1] = commandC;cmds[2] = "ansible " + hostGroup + " -m copy -a " + "\"src=" + localPath + " dest=" + remotePath + "\"" + " -i "+ directory + "/hosts";// 执行CMD命令Process p = run.exec(cmds);log.info("ansible命令为{}", cmds[2]);BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8")));try {String lineMes;while ((lineMes = br.readLine()) != null)log.info("ansible输出信息为 :" + lineMes);// 打印输出信息try {// 检查命令是否执行失败。if (p.waitFor() != 0) {if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束log.error("命令执行失败");}} catch (InterruptedException e) {log.error("error data{}", e);}} catch (IOException e) {log.error("fail to carry out command{}", e);throw e;} finally {if (br != null) {br.close();}}}/***  文件采集*/public static void fileCollection(String hostGroup, String remotePath, String localPath , String directory) throws IOException {Runtime run = Runtime.getRuntime();String[] cmds = new String[3];cmds[0] = commandBin;cmds[1] = commandC;cmds[2] = "ansible " + hostGroup + " -m fetch -a " + "\"src=" + remotePath + " dest=" + localPath + " force=yes backup=yes\"" + " -i "+ directory + "/hosts";// 执行CMD命令Process p = run.exec(cmds);log.info("ansible远程采集文件命令为{}", cmds[2]);BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8")));try {String lineMes;while ((lineMes = br.readLine()) != null)log.info(lineMes);// 打印输出信息try {// 检查命令是否执行失败。if (p.waitFor() != 0) {if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束log.error("命令执行失败");}} catch (InterruptedException e) {log.error("error data{}", e);}} catch (IOException e) {log.error("fail to carry out command{}", e);throw e;} finally {if (br != null) {br.close();}}}public static void ExecuteTheOrder(String command) throws IOException {log.info("start execute cmd {}", command);String[] cmd = new String[] {"/bin/bash", "-c", command};Runtime run = Runtime.getRuntime();Process p = run.exec(cmd); // 执行CMD命令BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8")));try {String lineMes;while ((lineMes = br.readLine()) != null)log.info("输出信息为 {}", lineMes);// 打印输出信息try {// 检查命令是否执行失败。if (p.waitFor() != 0) {if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束log.error("命令执行失败");}} catch (InterruptedException e) {log.error("error data{}", e);}} catch (IOException e) {log.error("fail to carry out command{}", e);throw e;} finally {if (br != null) {br.close();}}}public static void disconnect() {try {close();} catch (Exception ex) {// Ignore because disconnection is quietly}}// public void execute(String command) throws Exception {// log.info("start execute cmd {}", command);// try (Session session = sshClient.startSession()) {// Session.Command exec = session.exec(command);//// Integer readLineCount = 0;// InputStream in = exec.getInputStream();// log.info(IOUtils.readFully(in).toString());// String errorMessage = IOUtils.readFully(exec.getErrorStream(), LoggerFactory.DEFAULT).toString();// log.info(errorMessage);// if (exec.getExitStatus() != null && exec.getExitStatus() != 0) {// throw new RuntimeException(// "exec " + command + " error,error message is " + errorMessage + ",error code " + exec.getExitStatus());// }// log.info("exec result code {}", exec.getExitStatus());//// }//// }
}

接下来就是调用

package com.tiduyun.cmp.operation.service.impl;import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.tiduyun.cmp.common.model.flow.UploadFile;
import com.tiduyun.cmp.common.model.operation.ComponentInfo;
import com.tiduyun.cmp.common.model.operation.HostInfo;
import com.tiduyun.cmp.common.provider.service.ExceptionBuildService;
import com.tiduyun.cmp.operation.constant.OperationExceptionCode;
import com.tiduyun.cmp.operation.constant.StartCmdSeparate;
import com.tiduyun.cmp.operation.model.AnsibleConfigurationItemVo;
import com.tiduyun.cmp.operation.model.vo.FileQueryVo;
import com.tiduyun.cmp.operation.service.AnsibleTaskRecordService;
import com.tiduyun.cmp.operation.service.ComposerDeployService;
import com.tiduyun.cmp.operation.service.HostInfoService;
import com.tiduyun.cmp.operation.service.UploadFileService;
import com.tiduyun.cmp.operation.utils.AnsibleExecuteTheOrderUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@Slf4j
@Service
public class AnsibleDeployServiceImpl implements ComposerDeployService {@Value(value = "${cmp.operation.commandHeader:cmd /c}")private String commandHeader;@Value(value = "${cmp.operation.filePath:/data/cmp/file}")private String filePath;@Value(value = "${cmp.operation.remoteFilePath:/tmp}")private String remoteFilePath;@Autowiredprivate AnsibleTaskRecordService ansibleTaskRecordService;@Autowiredprivate AnsibleConfigurationItemVo ansibleConfigurationItemVo;@Autowiredprivate UploadFileService uploadFileService;@Autowiredprivate HostInfoService hostInfoService;@Autowiredprivate ExceptionBuildService exceptionBuildService;@Overridepublic void deploy(HostInfo hostInfo, ComponentInfo componentInfo, String cpmposerName) {ansibleTaskRecordService.ansibleCreateHost(hostInfo, null);try {String remotePath = StringUtils.join(remoteFilePath, "/", cpmposerName, "-", componentInfo.getName(), "-",RandomUtil.randomString(3));log.info("remote file path = {}", remotePath);List<Integer> fileIds = getFileIds(componentInfo.getFileUrl());if (CollectionUtils.isNotEmpty(fileIds)) {FileQueryVo uploadFileQueryVo = new FileQueryVo();uploadFileQueryVo.setIds(fileIds);List<UploadFile> uploadFiles = uploadFileService.query(uploadFileQueryVo);for (UploadFile uploadFile : uploadFiles) {String path = StringUtils.join(filePath, uploadFile.getFilePath());File file = new File(path);if (!file.exists()) {log.error("file url is {}", file.getPath());throw exceptionBuildService.buildException(OperationExceptionCode.FILE_NOT_EXIST,new Object[] {uploadFile.getFileName()});}// 创建远程目录AnsibleExecuteTheOrderUtils.createRemoteDirectory(ansibleConfigurationItemVo.getHostGroup(),StringUtils.join(remotePath), ansibleConfigurationItemVo.getDirectory());// 分发文件AnsibleExecuteTheOrderUtils.upload(ansibleConfigurationItemVo.getHostGroup(), path,StringUtils.join(remotePath, "/", uploadFile.getFileName()),ansibleConfigurationItemVo.getDirectory());}}List<String> startCmds = getStartCmds(componentInfo.getStartCmd());if (CollectionUtils.isNotEmpty(startCmds)) {String cdCmd = StringUtils.join("cd ", remotePath);String execCmd = StringUtils.join(startCmds, ";");execCmd = StringUtils.join(cdCmd, ";", execCmd);log.info("execCmd= " + execCmd);// sshClient.execute(execCmd);AnsibleExecuteTheOrderUtils.ExecuteTheOrder(execCmd);} else {log.error("parse startCmd fail {}", componentInfo.getStartCmd());}} catch (Exception e) {log.error("主机[{}]部署[{}]组件失败,主机ID[{}],组件ID[{}]:", hostInfo.getHost(), componentInfo.getName(),hostInfo.getId(), componentInfo.getId(), e);throw exceptionBuildService.buildException(OperationExceptionCode.EXECUTE_CMD_ERROR,new Object[] {e.getMessage()});} finally {AnsibleExecuteTheOrderUtils.disconnect();}}@Overridepublic boolean isSupport(HostInfo hostInfo) {return true;}private List<Integer> getFileIds(String fileIds) {List<Integer> ids = new ArrayList<>();if (fileIds == null) {return null;}String[] split = StringUtils.split(fileIds, ",");for (String s : split) {ids.add(Integer.parseInt(s));}return ids;}private List<String> getStartCmds(String startCmd) {List<String> cmd = new ArrayList<>();if (startCmd == null) {return cmd;}String[] split = StrUtil.split(startCmd, StartCmdSeparate.SIGN);cmd.addAll(Arrays.asList(split));return cmd;}public static Boolean needCd(String s) {String[] splits = StrUtil.split(s, "&&");int maxIndex = splits.length - 1;String cmd = splits[maxIndex];if (StrUtil.startWith(cmd, "cd")) {return false;} else {return true;}}
}

文件采集

同上,调用两个工具类

@Overridepublic void fileCollection(HostInfo hostInfo, String remotePath, String localPath) {ansibleCreateHost(hostInfo, null);try {log.info("remote file path = {}", remotePath);log.info("local file path = {}", localPath);// 文件采集AnsibleExecuteTheOrderUtils.fileCollection(ansibleConfigurationItemVo.getHostGroup(), remotePath,localPath , ansibleConfigurationItemVo.getDirectory());} catch (Exception e) {log.error("主机[{}]文件采集失败,主机ID[{}]:", hostInfo.getHost(), hostInfo.getId(), e);throw exceptionBuildService.buildException(OperationExceptionCode.EXECUTE_CMD_ERROR,new Object[] {e.getMessage()});} finally {AnsibleExecuteTheOrderUtils.disconnect();}}

以上代码如大家有需要,请自行更改!!!


http://chatgpt.dhexx.cn/article/mWeiFtZ6.shtml

相关文章

java开发和运维的区别

前言 在大型系统中,为了减少数据库压力通常会引入缓存机制,一旦引入缓存又很容易造成缓存和数据库数据不一致,导致用户看到的是旧数据。 为了减少数据不一致的情况,更新缓存和数据库的机制显得尤为重要,接下来带领大家踩踩坑。 Spring 所有的答案在文末展示 Spring 概述…

JAVA开发运维(云基础设备监控)

在大型的商用系统中&#xff0c;经常需要监控云设备的健康状态&#xff0c;性能情况&#xff0c;流量数据等。及时发现系统问题&#xff0c;及时修复&#xff0c;以确保系统的高可用。检查云资源的工作内容主要包括基础监控、主动拨测、用户体验、APM监控、指标体系、业务分析、…

JAVA开发运维(CI/CD)

CI :持续性集成 CD&#xff1a;持续性部署 SIT&#xff1a;系统集成测试 UAT&#xff1a;用户验收测试 研发流程的变化&#xff0c;因为用户永远一开始不知道自己想要什么样的东西&#xff0c;导致了软件无法从一而终的进行设计&#xff0c;用户需要能立刻运行的软件和功能。 &…

Hi,运维,你懂Java吗--No.4:JVM-概述

作为运维&#xff0c;你不一定要会写Java代码&#xff0c;但是一定要懂Java在生产跑起来之后的各种机制。 本文为《Hi&#xff0c;运维&#xff0c;你懂Java吗》系列文章 第四篇&#xff0c;敬请关注后续系列文章 欢迎关注 龙叔运维&#xff08;公众号&#xff09; 持续分享运…

JAVA开发与运维(docker运维常规操作)

使用docker来安装软件步骤。 1、先装好docker环境。 1.4 docker环境及部署 1.4.1 版本 Client: Docker Engine – Community version: 20.10.12 Server: Docker Engine – Community version: 20.10.12 1.4.2 部署 1.用yum拉取阿里云镜像: yum-config-manager --add-repo …

JAVA开发运维(扁鹊见齐桓公之系统运维之痛)

引言&#xff1a; 扁鹊见蔡桓公&#xff0c;立有间&#xff0c;扁鹊曰&#xff1a;“君有疾在腠理&#xff0c;不治将恐深。”桓侯曰&#xff1a;“寡人无疾。”扁鹊出&#xff0c;桓侯曰&#xff1a;“医之好治不病以为功&#xff01;” 居十日&#xff0c;扁鹊复见&#xff0…

死磕原理!java运维工程师做什么

前言 今天中午收到我司安全部发了一封邮件:Jackson存在安全漏洞。查了一下,这件事并不算很新鲜了(已经过了5天的样子),本文来聊聊吧。 说起来还蛮戏剧化:阿里云向Jackson官方提交了一个Jackson序列化安全漏洞。众所周知,在国内关于JSON库使用有两大主要阵营:国际著名…

java项目运维手册

1、生成dump文件 jmap -dump:live,formatb,fileheap-dump-1829.bin 32171 2、查看子文件夹磁盘空间占用情况 du -sh * 3、实时监控gc情况&#xff1a; jstat -gcutil pid interval(ms) 4、开启jmx远程监控&#xff1a; 执行foo.jar启动命令 java -Dcom.sun.management.j…

java运维调优的神器Arthas---入门

Arthas Arthas是一款阿里巴巴开源的 Java 线上诊断工具,功能非常强大,可以解决很多线上不方便解决的问题 一、安装启动arthas 1.下载arthas-boot.jar 官网&#xff1a;https://arthas.aliyun.com/doc/quick-start.html Maven下载&#xff1a; com.taobao.arthas arthas-sp…

JAVA开发运维(系统上到生产环境准备工作)

一、前言 java项目在开发环境开发完成&#xff0c;在测试环境测试没有问题后&#xff0c;就需要发布到生产环境&#xff0c;如果系统是对公众的&#xff0c;那就需要很多工作了。比如服务器申请&#xff0c;域名申请&#xff0c;渗透测试&#xff0c;漏洞扫描&#xff0c;公网…

Hi,运维,你懂Java吗-No.1:初识Java

作为运维&#xff0c;你不一定要会写Java代码&#xff0c;但是一定要懂Java在生产跑起来之后的各种机制。 本文为《Hi&#xff0c;运维&#xff0c;你懂Java吗》系列文章 第一篇&#xff0c;敬请关注后续系列文章 欢迎关注 龙叔运维&#xff08;公众号&#xff09; 持续分享运…

JAVA开发运维(软件一体化可观测平台的功能)

软件可观测是软件度量的一种。旨在对软件的数字体验、业务运营、网络性能、应用性能、基础设施、IT流程进行监控和数据刻画。使开发人员和运维人员更好的对软件进行优化维护。 一、数字体验&#xff1a; 用户会话 了解用户使用路径&#xff0c;追查使用过程中影响用户体验的…

JAVA开发运维(DevOps过程)

DevOps开发运维的一套方法论。这边文章主要借鉴万达的DevOps的建设过程。谈谈DevOps主要解决那些问题和怎么解决。 DevOps的是一种IT项目开发管理方法论&#xff0c;它旨在提供全面的持续集成、持续交付等能力&#xff0c;并持在续进行过程度量和改进&#xff0c;不断提升 IT 运…

Java项目运维总结记录

一、常见故障 1.1、Java项目CPU耗用突增100%分析定位 1&#xff09;执行top命令确认当前占用cpu的的java进程&#xff1b;以下是一些快捷键&#xff1a; ?&#xff1a;显示在top当中可以输入的命令 P&#xff1a;以CPU的使用资源排序显示 M&#xff1a;以内存的使用资源排序…

初级车道线检测

前言 上一期的无人驾驶技术入门&#xff0c;我们以障碍物的跟踪为例&#xff0c;介绍了卡尔曼滤波器的原理、公式和代码的编写。接下来的几期无人驾驶技术入门&#xff0c;我会带大家接触无人驾驶技术的另一个重要的领域——计算机视觉。 在无人驾驶技术入门&#xff08;五&a…

c++ 车道线检测

利用概率霍夫变换&#xff0c;进行车道线的简单检测 1、首先编写一个头文件&#xff08;也可以在源文件中写一个类&#xff0c;一样的&#xff09; #ifndef LANE_H //头文件的写法格式 if not define #define LANE_H //_LANE_H_ 为头文件Lane.h的大写&#xff0c;防止被重复…

简述车道线检测

针对车道线检测的任务&#xff0c;我们需要弄清楚几个问题&#xff1a; 1. 车道线的表示形式&#xff1f; 输出类型&#xff1a;掩码/点集/矢量线条实例化&#xff1a;每个车道线是否形成实例分类&#xff1a;是否对车道线进行了分类&#xff08;单白、双黄等&#xff09;提前定…

PaddleSeg:车道线检测

向AI转型的程序员都关注了这个号&#x1f447;&#x1f447;&#x1f447; 0.1 任务 要求参赛者利用提供的训练数据&#xff0c;设计一个车道线检测和分类模型&#xff0c;来检测测试数据中车道线的具体位置和类别。样例示范&#xff1a; 0.2 数据集描述 本次赛题数据集包括x张…

3D车道线检测杂记

3D车道线检测相关论文学习 单目 一、3D-LaneNet: End-to-End 3D Multiple Lane Detection-ICCV2019 0 前言 这篇文章是第一篇通过单目前视相机直接预测3D车道线的论文。 一般想得到3D车道线信息有两种途径&#xff1a; 一种是利用离线高精度地图以及准确的自车定位信息&a…

车道线检测-LSTR-论文学习笔记

论文&#xff1a;《End-to-end Lane Shape Prediction with Transformers》代码&#xff1a;https://github.com/liuruijin17/LSTR地址&#xff1a;https://arxiv.org/abs/2011.04233v2内容&#xff1a;使用 CNNTransformer&#xff0c;基于三次曲线拟合车道线 整体结构 模型输…