Trapezoidal Decomposition梯形分解算法(TCD)

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
TODO:写完再整理

文章目录

  • 系列文章目录
  • 前言
  • Trapezoidal Decomposition梯形分解算法(TCD)
    • 原理
      • (1)第一种原理
      • (2)第二种原理
      • (3)第三种原理
    • 优缺点


前言

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!

本文先对**Trapezoidal Decomposition梯形分解算法(TCD)**做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章


提示:以下是本篇文章正文内容

Trapezoidal Decomposition梯形分解算法(TCD)

原理

Trapezoidal Decomposition是最简单的精确细胞分解方法。该方法先使机器人沿着空间的边缘行走一圈,构建整个区域地图,然后用一条竖直的切割线从左至右扫描过整个区域,当切割线与多边形障碍物的顶点相遇时,就会分解出一个子区域,这样整个自由空间就被分解为诸多子区域,每个子区域都是梯形。梯形分解法的时间复杂度通常为O(n log n)。机器人在每个子区域中进行往复运动对子区域进行覆盖。

(1)第一种原理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

(2)第二种原理

梯形分解方法假设一条垂直线段(称为slice)从左到右扫过由多边形障碍物填充的有界环境。单元格cell是通过一系列打开和关闭操作形成的

事件类型
当slice遇到一个事件时,有三种类型的事件:IN、OUT和MIDDLE。
1、在IN事件中,当前单元格关闭(从而完成其构造),两个新单元格打开(从而启动其构造)(图2)。

2、OUT事件正好相反:两个单元格关闭,一个新单元格打开(图3)。
IN事件可以看作是一个单元格分解成两个单元格,而OUT事件是两个单元格合并成一个单元格。

3、在MIDDLE事件中,当前单元格关闭,一个新单元格形成。这些操作的结果是自由空间被分解成梯形单元格。
在这里插入图片描述
在这里插入图片描述

(3)第三种原理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过cell的面积最大化来确定cell的真正边界
在这里插入图片描述

代码实现

# Find a path avoiding obstacles using Vertical Cell Decomposition
# Author -- Shikhar Dev Guptaimport sys
from helpers.graph import *
from helpers.geometry import *;
import matplotlib.pyplot as plt# Check for empty lines
file_handler = open("input_file","r");
raw_data = file_handler.read();
raw_data = raw_data.split("\n");if(len(raw_data) <2):print("Incorrect format of the input file");exit;def parse_input_line(line):temp2 = [];line = [i.strip() for i in line.split(",")];vertex = [];for index,i in enumerate(line):if(i[0] == "("):i = i[1:];if(i[len(i)-1] == ")"):i= i[:-1];vertex.append(int(i));if(index%2 != 0):temp2.append(vertex);vertex = [];return temp2;	# Draw the obstacles and point the source and the destination----------------------------------------------
def draw_problem():bnd_x = [i.x for i in boundary];bnd_x.append(boundary[0].x);bnd_y = [i.y for i in boundary];bnd_y.append(boundary[0].y);poly_x = [];poly_y = []# Draw the boundaryplt.plot(bnd_x, bnd_y);for index, i in enumerate(obstacles):poly_x.append([p[0] for p in i]);poly_y.append([p[1] for p in i]);plt.fill( poly_x[index], poly_y[index], color="#512DA8");plt.plot(source.x, source.y, marker="o");plt.plot(dest.x, dest.y, marker="o");plt.annotate('Source', xy=(source.x, source.y), xytext=(source.x+5, source.y-6) );plt.annotate('Destination', xy=(dest.x, dest.y), xytext=(dest.x-4, dest.y-10) );# Extract vertices----------------------------------------------
temp = parse_input_line(raw_data[0]);
boundary = [point(i[0], i[1]) for i in temp];# Extract source and dest
temp = parse_input_line(raw_data[len(raw_data)-1]);
source = point(temp[0][0], temp[0][1]);
dest = point(temp[1][0], temp[1][1]);# Extract obstacles
obstacles = [];
for i in raw_data[1:len(raw_data)-1]:obstacles.append(parse_input_line(i) );#sort by x-values
sorted_vertices = [];
for index,i in enumerate(obstacles):for j in i:j.append(index);sorted_vertices.append(j);
sorted_vertices.sort(key=lambda x: x[0]);# Draw the problem
draw_problem();new_sorted_vertices = [];for i in sorted_vertices:temp = point(i[0], i[1], i[2]);new_sorted_vertices.append(temp);new_obstacles = [];
for index, i in enumerate(obstacles):temp_obs = [];for j in i:temp = point(j[0], j[1], index);temp_obs.append(temp);new_obstacles.append(temp_obs);	#-----------------------------------------------------------
# Find vertical lines
open_line_segments = [];y_limit_lower = boundary[0].y;
y_limit_upper = boundary[2].y;for pt in new_sorted_vertices:curr_line_segment = [ point(pt.x, y_limit_lower), point(pt.x, y_limit_upper) ]; lower_obs_pt = curr_line_segment[0];upper_obs_pt = curr_line_segment[1];upper_gone = False;lower_gone = False;break_now = False;# Find intersection points with the vertical proposed lines. the intersection function returns false if segments are same, so no need to worry about same segment checkingfor index,obs in enumerate(new_obstacles):# Add the first point again for the last line segment of a polygon.obs.append( obs[0] );for vertex_index in range(len(obs)-1 ):res = segment_intersection( curr_line_segment[0], curr_line_segment[1], obs[vertex_index],  obs[vertex_index+1]);if (res!=-1):if ( index == pt.obstacle ):if pt.equals( res ) == False:if ( res.y > pt.y ):upper_gone = True;elif ( res.y < pt.y ):lower_gone = True;	else:	if pt.equals( res ) == False:if ( upper_gone is False ):if ( (res.y > pt.y) and res.y < (upper_obs_pt.y) ):upper_obs_pt = res;if ( lower_gone is False ):if ( (res.y < pt.y) and (res.y > lower_obs_pt.y) ):lower_obs_pt = res;if( upper_gone is True and lower_gone is True ):break_now = True;#No need to check for current point anymore...completely blockedif(break_now is True):break;		# Draw the vertical cell linesif(lower_gone is False):plt.plot( [lower_obs_pt.x, pt.x],  [lower_obs_pt.y, pt.y] );if(upper_gone is False):plt.plot( [pt.x, upper_obs_pt.x],  [pt.y, upper_obs_pt.y] );# Add to the global segment listif (lower_gone and upper_gone):open_line_segments.append([None, None]);elif (lower_gone):open_line_segments.append([None, upper_obs_pt]);elif (upper_gone):open_line_segments.append([lower_obs_pt, None]);else:open_line_segments.append([lower_obs_pt, upper_obs_pt]);#------------------------------------------------------
# Find Polygon cells naiively. Will improve next. 
cells = [];for index1 in range(len(open_line_segments) ):curr_segment = open_line_segments[index1];curr_vertex = new_sorted_vertices[index1];break_now = False;done = [False, False, True];if( curr_segment[0] is None ):done[0] = True; if( curr_segment[1] is None ):done[1] = True;	if( curr_segment[1] is None and open_line_segments[index1][0] is None):done[2] = False;	for index2 in range(index1+1,  len(open_line_segments) ):next_segment = open_line_segments[index2];next_vertex = new_sorted_vertices[index2];			double_index1 = -2;double_index2 = -2;lines_to_check = [];trapezoids = [];double_check = False;if ( next_segment[0] is not None and next_segment[1] is not None ):double_check = True;if( done[0] is False ):if( double_check ):double_index1 = len(lines_to_check);lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[0], next_vertex]), 0]);lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[1], next_vertex]), 0]);trapezoids.append([ curr_segment[0], next_segment[0], next_vertex, curr_vertex ]);trapezoids.append([ curr_segment[0], next_vertex, next_segment[1], curr_vertex ]);elif ( next_segment[0] is not None ):lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[0], next_vertex]), 0]);trapezoids.append([ curr_segment[0], next_segment[0], next_vertex, curr_vertex ]);elif( next_segment[1] is not None ):lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[1], next_vertex]), 0]);trapezoids.append([ curr_segment[0], next_vertex, next_segment[1], curr_vertex ]);else:lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), next_vertex, 0]);trapezoids.append([ curr_segment[0], next_vertex, curr_vertex ]);if( done[1] is False ):if( double_check ):double_index2 = len(lines_to_check);lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[0], next_vertex]), 1]);lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[1], next_vertex]), 1]);trapezoids.append([ curr_vertex, next_segment[0], next_vertex , point(curr_segment[1].x, curr_segment[1].y,curr_segment[1].obstacle, 34)]);trapezoids.append([ curr_vertex, next_vertex, next_segment[1], curr_segment[1] ]);elif ( next_segment[1] is not None ):lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[1], next_vertex]), 1]);trapezoids.append([ curr_vertex, next_vertex, next_segment[1], curr_segment[1] ]);elif( next_segment[0] is not None ):lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[0], next_vertex]), 1]);trapezoids.append([ curr_vertex, next_segment[0], next_vertex , curr_segment[1] ]);else:lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), next_vertex, 1]);trapezoids.append([ curr_vertex, next_vertex, curr_segment[1] ]);if( done[2] is False ):if(double_check):double_index = len(lines_to_check);lines_to_check.append( [curr_vertex, centroid([next_segment[0], next_vertex]), 2]);trapezoids.append([ curr_vertex,next_segment[0], next_vertex ]);lines_to_check.append( [curr_vertex, centroid([next_segment[1], next_vertex]), 2]);trapezoids.append([ curr_vertex, next_vertex, next_segment[1] ]);elif ( next_segment[0] is not None ):lines_to_check.append( [curr_vertex, centroid([next_segment[0], next_vertex]), 2]);trapezoids.append([ curr_vertex,next_segment[0], next_vertex ]);elif( next_segment[1] is not None ):lines_to_check.append( [curr_vertex, centroid([next_segment[1], next_vertex]), 2]);trapezoids.append([ curr_vertex, next_vertex, next_segment[1] ]);# Will this ever occur though??else:lines_to_check.append( [curr_vertex, next_vertex, 2]);trapezoids.append([curr_vertex, next_vertex]);temp_to_remove = [];for index5,q in enumerate(lines_to_check): ok = [True, True, True];for index3,obs in enumerate(new_obstacles):# Add the last line to make closed polygonobs.append( obs[0] );for index4 in range(len(obs)-1):if (segment_intersection( q[0], q[1],  obs[index4],  obs[index4+1]) != -1):ok[q[2]] = False;if(index5 not in temp_to_remove):temp_to_remove.append(index5);if (  ok[q[2]] is True ):done[q[2]] = True;for i in range(len(lines_to_check)):if i not in temp_to_remove:cells.append(trapezoids[i]);if( done[0] == True and done[1] == True and done[2] == True ):break;to_draw =[];
for i in cells:i.append(i[0]);to_draw.append(i);#-------------------------------------------------------
# Merge overlapping Polygons
quad_cells = [i for i in cells if len(i)>3];
tri_cells = [i for i in cells if len(i)==3];
others = [i for i in cells if len(i)<3];
quads_to_remove = [];
quads_to_add = [];quads_to_remove = [];
quads_to_add = [];
for index_cell in range(len(quad_cells)):for index_cell2,cell in enumerate(quad_cells):if(index_cell != index_cell2):if(quad_cells[index_cell][0].x == cell[0].x and quad_cells[index_cell][1].x == cell[1].x):temp1 = list(quad_cells[index_cell]);temp1.append(temp1[0]);temp2 = list(cell);temp2.append(temp2[0]);area1 = polygon_area(temp1,4); area2 = polygon_area(temp2,4);new_quad=[];new_quad.append( point(temp1[0].x, min(temp1[0].y, temp2[0].y)) );new_quad.append( point(temp1[1].x, min(temp1[1].y, temp2[1].y)) );new_quad.append( point(temp1[1].x, max(temp1[2].y, temp2[2].y)) );new_quad.append( point(temp1[0].x, max(temp1[3].y, temp2[3].y)) );new_quad.append( point(temp1[0].x, min(temp1[0].y, temp2[0].y)) );area3 = polygon_area(new_quad, 4);if( area1 + area2 >= area3):#mergequads_to_remove.append(index_cell);quads_to_remove.append(index_cell2);quads_to_add.append(new_quad);quads_to_remove = list(set(quads_to_remove));
for index in sorted(quads_to_remove, reverse=True):del quad_cells[index];for i in quads_to_add:quad_cells.append(i);# Remove duplicates
to_remove = [];
for index1 in range(len(quad_cells)):for index2 in range(index1+1, len(quad_cells)):duplicate = True;for k,m in zip(quad_cells[index1], quad_cells[index2]):if k.equals(m) is False:duplicate = False;break;if(duplicate is True):if index2 not in to_remove:to_remove.append(index2);		for index in sorted(to_remove, reverse=True):del quad_cells[index];# One more pass to remove extra quads generated because of cross - segments
quads_to_remove = [];
for index1 in range(len(quad_cells)):for index2 in range(len(quad_cells)):if(index1 != index2 and quad_cells[index1][0].x == quad_cells[index2][0].x and quad_cells[index1][1].x == quad_cells[index2][1].x):if( (quad_cells[index1][0].y<= quad_cells[index2][0].y) and  (quad_cells[index1][1].y<= quad_cells[index2][1].y)and (quad_cells[index1][2].y>= quad_cells[index2][2].y) and (quad_cells[index1][3].y >= quad_cells[index2][3].y)):			quads_to_remove.append(index2);quads_to_remove = list(set(quads_to_remove) );
for index in sorted(quads_to_remove, reverse=True):del quad_cells[index];#------------------------------------------------------
# Add boundary lines
if( boundary[0].x != new_sorted_vertices[0].x):quad_cells.append([boundary[0], point(new_sorted_vertices[0].x, y_limit_lower), point(new_sorted_vertices[0].x, y_limit_upper), boundary[3]]);
if( boundary[1].x != new_sorted_vertices[len(new_sorted_vertices)-1].x):quad_cells.append([point(new_sorted_vertices[len(new_sorted_vertices)-1].x ,y_limit_lower), boundary[1], boundary[2], point(new_sorted_vertices[len(new_sorted_vertices)-1].x, y_limit_upper) ]);#-------------------------------------------------------
# Plot final cells
to_draw = quad_cells+tri_cells+others;
for i in to_draw:x = [j.x for j in i];y = [j.y for j in i];plt.plot(x, y);#----------------------------------------------------------------------
# Get the graph
graph_vertices = [];
graph_edges = [];for index1 in range(len(quad_cells)):same_boundary = [];for index2 in range(len(quad_cells)):if(index1 != index2):if( (quad_cells[index1][1].x == quad_cells[index2][0].x ) and ((quad_cells[index1][2].y in [quad_cells[index2][0].y, quad_cells[index2][3].y]) or (quad_cells[index1][1].y in [quad_cells[index2][0].y, quad_cells[index2][3].y]) ) ):same_boundary.append(index2);temp = quad_cells[index1][0:4];centroid_vertex = centroid(temp);place = centroid_vertex.find_point(graph_vertices)if( place == -1):graph_vertices.append(centroid_vertex);if(len(same_boundary)==1):temp_edge_middle = centroid([quad_cells[index1][1], quad_cells[index1][2]]);graph_vertices.append(temp_edge_middle);n = len(graph_vertices)-1;if(place != -1):graph_edges.append([place, n]);else:graph_edges.append([n-1, n]);temp = quad_cells[same_boundary[0]][0:4];curr_centroid_vertex = centroid(temp);place2 = curr_centroid_vertex.find_point(graph_vertices);if( place2 == -1 ):graph_vertices.append(curr_centroid_vertex);graph_edges.append([n, n+1]);else:graph_edges.append([n, place2]);elif(len(same_boundary)>1):n = len(graph_vertices)-1;if(place != -1):use = place;else:use = n;	for index, i in enumerate(same_boundary):temp = quad_cells[i][0:4];curr_centroid_vertex = centroid(temp);temp_edge_middle = centroid([quad_cells[i][0], quad_cells[i][3]]);graph_vertices.append(temp_edge_middle);pl1 =len(graph_vertices)-1;hmmm= curr_centroid_vertex.find_point(graph_vertices);if (hmmm == -1):graph_vertices.append(curr_centroid_vertex);pl2 =len(graph_vertices)-1;else:pl2 = hmmm;	graph_edges.append([use, pl1]);graph_edges.append([pl1, pl2]);		# Add source and dest to graph
# Find the smallest distance vertex on graph and see if its clear to traverse
# Source------------------------------
min_ind = -1; min = 9999999;
for index, i in enumerate(graph_vertices):if( check_obstruction(new_obstacles, [source, i]) is True ):dist = find_dist(i, source);if( dist < min):min = dist;min_ind = index;graph_vertices.append(source);
m = len(graph_vertices)-1;
graph_edges.append([min_ind, m]);	# Destination------------------------------------
min_ind = -1; min = 9999999;
for index, i in enumerate(graph_vertices):if( check_obstruction(new_obstacles, [dest, i]) is True ):dist = find_dist(i, dest);if( dist < min):min = dist;min_ind = index;graph_vertices.append(dest);
m = len(graph_vertices)-1;
graph_edges.append([min_ind, m]);# Convert graph in adjacency list format
graph = [];
for j in range(len(graph_vertices)):graph.append([]);for i in graph_edges:if(i[0]==j):graph[j].append(i[1]);elif(i[1]==j):graph[j].append(i[0]);	path = bfs(graph, len(graph_vertices)-2, len(graph_vertices)-1);if(path is None):print "No path found. Sorry";sys.exit();
else:print "Path found."	;# Draw everything--------------
for index,i in enumerate(graph_vertices):plt.annotate(str(index), xy=(i.x, i.y), xytext=(i.x+2, i.y-2) );# plt.plot(i.x,i.y, marker="x");for i in graph_edges:temp_x = [graph_vertices[i[0]].x, graph_vertices[i[1]].x];temp_y = [graph_vertices[i[0]].y, graph_vertices[i[1]].y];plt.plot(temp_x,temp_y);# draw path
temp_x = [graph_vertices[i].x for i in path];
temp_y = [graph_vertices[i].y for i in path];
plt.plot(temp_x,temp_y, color="#0F0F0F", linewidth=2);	#----------------------------------------------------
# output into a file
file_output = open("vertical_cell_output", "w" );
str_to_write = "";
for index in range(len(graph_vertices)):str_to_write = str_to_write + ", "+str(index)+":"+"("+ str(int(graph_vertices[index].x) )+  ", "+ str(int(graph_vertices[index].y) ) + ")";str_to_write = str_to_write[1:];total_write = str_to_write+"\n";
str_to_write="";
for i in graph:if (i == []):continue;str_to_write = str_to_write + ",(";for j in i:str_to_write = str_to_write + str(j) + ",";str_to_write = str_to_write[:-1];str_to_write = str_to_write + ")";str_to_write = str_to_write[1:];total_write = total_write+ str_to_write + "\n";str_to_write = "";
str_to_write =','.join(str(x) for x in path);total_write = total_write + str_to_write;file_output.write(total_write);
print "Output written to file.. Drawing the result";plt.show();

优缺点

产生大量的子区域,造成了大量的路径冗余


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

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

相关文章

DataX实战:从MongoDB到MySQL的数据迁移--修改源码并测试打包

在现代数据驱动的业务环境中&#xff0c;数据迁移和集成是常见的需求。DataX&#xff0c;作为阿里云开源的数据集成工具&#xff0c;提供了强大的数据同步能力&#xff0c;支持多种数据源和目标端。本文将介绍如何使用DataX将数据从MongoDB迁移到MySQL。 环境准备 安装MongoDB…

智慧医院人工智能应用场景 | 智能导诊系统源码

近年来&#xff0c;智能医疗在国内外的发展热度不断提升。图像识别、深度学习、神经网络、大模型、语音等关键技术的突破带来了人工智能技术新一轮的发展。 场景一&#xff1a;智能机器人 医疗机器人是指能够在医疗领域执行特定任务或功能的机器人&#xff0c;包括手术机器人、…

【LLaMa2入门】从零开始训练LLaMa2

目录 1 背景2 搭建环境2.1 硬件配置2.2 搭建虚拟环境2.2.1 创建虚拟环境2.2.2 安装所需的库 3 准备工作3.1 下载GitHub代码3.2 下载模型3.3 数据处理3.3.1 下载数据3.3.2 数据集tokenize预处理 4 训练4.1 修改配置4.2 开始训练4.3 多机多卡训练 5 模型推理5.1 编译5.1.1 安装gc…

Java算法专栏

专栏导读 在当今这个技术日新月异的时代&#xff0c;Java算法作为软件开发的核心&#xff0c;对于提升程序性能和解决复杂问题至关重要。本“Java算法”专栏旨在帮助读者深入理解Java编程语言中的算法原理和应用&#xff0c;通过实战案例和深入分析&#xff0c;使读者能够掌握…

软媒市场新探索:软文媒体自助发布,开启自助发稿新篇章

在繁华喧嚣的软媒市场中,每一个声音都在竭力呼喊,每一个品牌都在奋力展现。而软文,作为一种温柔而坚韧的营销力量,正逐渐崭露头角。特别是软文媒体自助发布平台的出现,更是为企业提供了一个全新的、高效的自助发稿渠道。 软媒市场自助发布平台,正如其名,是一个让企业能够自主发…

【LeetCode】每日一题 2024_9_21 边积分最高的节点(哈希)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;边积分最高的节点 代码与解题思路 func edgeScore(edges []int) (ans int) {// 直接维护哈希最大值即可mp : map[int]int{}for i, v : range edges {mp[v] i// 如果多个节点的 边积分 相…

【数据库】常用数据库简介

目录 &#x1f354; 常用的关系型数据库 &#x1f354; Mysql简介 &#x1f354; SQL 简介 SQL语句的分类 SQL 写法 SQL 常用的数据类型 &#x1f354; DDL语句 对数据库的操作 对数据表的操作 &#x1f354; DML语句 插入数据 insert into 修改数据 update 删除数…

Ubuntu下使用 python搭建服务实现从web端远程配置设备网口

1、通过文件配置Ubuntu设备网口 在Ubuntu工控机上&#xff0c;通过文件配置网口&#xff08;网络接口&#xff09;可以让网络配置在每次系统启动时自动生效。以下是常见的方法步骤&#xff1a; 1.1 使用 netplan 配置网口&#xff08;Ubuntu 18.04 及以上版本&#xff09; 编…

探索微软Copilot Agents:如何通过Wave 2 AI彻底改变工作方式

微软在最近的Copilot Wave 2发布会上&#xff0c;展示了一系列将彻底改变日常工作流程的新AI功能&#xff0c;尤其是 Copilot Agents&#xff0c;它们不仅仅是简单的工具&#xff0c;而是真正的工作助理&#xff0c;可以自动完成任务、提供智能分析并帮助你做出决策。这些新功能…

Day6:反转链表

题目&#xff1a;给你单链表的头节点head&#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 输入&#xff1a;head[1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] public ListNode reverseList() {if (head null) {return head;}ListNode cur head.next;head.next null…

Python脚本每日自动备份MySQL数据库,无需mysqldump

编写一个Python脚本&#xff0c;每天凌晨3点开始备份 脚本具有以下特点 不需要安装mysql-client&#xff0c;并且Windows Linux都可以使用支持多个数据库连接的备份每个数据库支持多个表备份日志保存下来&#xff0c;方便第二天早上查看备份结果 首先安装需要的库 pip3 ins…

调节 PWM的占空比控制舵机的角度

一、PWM工作原理 让计数器从0数到自动重装载值&#xff0c;不停计数。计数值小于输出比较寄存器时输出一种电平&#xff0c;大于输出比较寄存器时使出另一种电平。 修改定时器时钟源的速度以及预分频器等设置&#xff0c;可以修改计数器计数的速度 再加上修改自动重装载值&…

肺结节检测系统源码分享

肺结节检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

python画图|图像背景颜色设置

python画图出来的默认图形背景是白色&#xff0c;有时候并不适合大家表达想要表达的意思。 因此&#xff0c;我们很有必要掌握自己设置图形背景颜色的技巧。 【1】官网教程 首先请各位看官移步官网&#xff0c;看看官网如何设置&#xff0c;下述链接可轻松到达&#xff1a; …

如何将很多个pdf拼接在一起?很多种PDF拼接的方法

如何将很多个pdf拼接在一起&#xff1f;将多个PDF文件合并不仅能够提升信息的整合性&#xff0c;还能使文件管理更加高效。想象一下&#xff0c;你需要向同事或老师提交一份综合报告&#xff0c;其中包含了多份相关资料。如果每个文件单独存在&#xff0c;查找和传输都会变得繁…

IDEA中Quarkus框架(3.13版本)开发、调试、部署、打包等

code-with-quarkus code-with-quarkus 是使用官网生成的demo项目 这个项目使用Quarkus&#xff08;使用3.13.0版本&#xff0c;该版本支持JDK21&#xff09;&#xff0c;超音速亚原子Java框架。官网地址: https://quarkus.io/. 环境要求 OS: Windows 10.0 jdk 11 maven 3.9…

2024年研赛-华为杯数模竞赛F题论文首发+论文讲解

本届研赛助攻题目 C D F三题论文均已经全部完成。后更新计划 如图所示。 免费给大家分享 三个问题的论文部分代码 2024年华为杯-研赛分享资料&#xff08;论文部分代码&#xff09;&#xff08;已更新部分代码&#xff09;&#xff1a; 链接&#xff1a;https://pan.baidu.com…

【Pyside】pycharm2024配置conda虚拟环境

知识拓展 Pycharm 是一个由 JetBrains 开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它主要用于 Python 编程语言的开发。Pycharm 提供了代码编辑、调试、版本控制、测试等多种功能&#xff0c;以提高 Python 开发者的效率。 Pycharm 与 Python 的关系 Pycharm 是…

springboot实战学习笔记(5)(用户登录接口的主逻辑)

接着上篇博客学习。上篇博客是已经基本完成用户模块的注册接口的开发以及注册时的参数合法性校验。具体往回看了解的链接如下。 springboot实训学习笔记&#xff08;4&#xff09;(Spring Validation参数校验框架、全局异常处理器)-CSDN博客文章浏览阅读576次&#xff0c;点赞7…

Java免税商品购物商城:Spring Boot实现详解

第一章 绪论 1.1 课题开发的背景 从古至今&#xff0c;通过书本获取知识信息的方式完全被互联网络信息化&#xff0c;但是免税商品优选购物商城&#xff0c;对于购物商城工作来说&#xff0c;仍然是一项非常重要的工作。尤其是免税商品优选购物商城&#xff0c;传统人工记录模式…