自动驾驶算法(五):Informed RRT*算法讲解与代码实现(基于采样的路径规划) 与比较

目录

1 RRT*与Informed RRT*

2 Informed RRT*代码解析

3 完整代码

4 算法比较


1 RRT*与Informed RRT*

        上篇博客我们介绍了RRT*算法:我们在找到一个路径以后我们还会反复的搜索。

        Informed RRT*算法提出的动机(motivation)是能否增加渐近最优的速度呢?

        根据这篇论文的思想:Informed RRT*: Optimal Sampling-based Path Planning Focused via Direct Sampling of an Admissible Ellipsoidal Heuristic。

        我们通过限制采样范围来增加渐进最优的速度。也就是我们在找到路径后限制采样的范围(椭圆),它的长轴就是我们找到路径的cbest,短轴就是如上图,cmin是起点和终点连起来的距离。感兴趣的同学可以去看一下论文。通过cbest越来越小采样的范围也就越来越小。最后我们就会找到路径。我们看一下椭圆是怎么找的,和RRT*相比仅仅改变了采样的Sample函数:

2 Informed RRT*代码解析

        start_time = time.time()self.start = Node(start[0], start[1])self.goal = Node(goal[0], goal[1])self.node_list = [self.start]# max length we expect to find in our 'informed' sample space,# starts as infinitecBest = float('inf')path = None# Computing the sampling spacecMin = math.sqrt(pow(self.start.x - self.goal.x, 2)+ pow(self.start.y - self.goal.y, 2))xCenter = np.array([[(self.start.x + self.goal.x) / 2.0],[(self.start.y + self.goal.y) / 2.0], [0]])a1 = np.array([[(self.goal.x - self.start.x) / cMin],[(self.goal.y - self.start.y) / cMin], [0]])e_theta = math.atan2(a1[1], a1[0])

        这里先初始化cbest为无穷大,将path置为空路径。求出起始点和终点的距离cMin

        Xcenter创建了一个 numpy 数组 xCenter,其中包含了 self.start(导航起始点) 和 self.goal(导航终止点) 之间的中心点的 x 和 y 坐标,以及一个零。

        a1 = np.array([[(self.goal.x - self.start.x) / cMin], [(self.goal.y - self.start.y) / cMin], [0]]): 创建了一个 numpy 数组 a1,它表示了从 self.start 指向 self.goal 的单位向量,除以距离 cMin

        e_theta = math.atan2(a1[1], a1[0]): 使用 math.atan2 计算了单位向量 a1 与 x 轴之间的角度 e_theta(以弧度表示)。

        用数学公式表示如下:

        C = np.array([[math.cos(e_theta), -math.sin(e_theta), 0],[math.sin(e_theta), math.cos(e_theta),  0],[0,                 0,                  1]])

        和RRT与RRT*相比,只改变了采样函数,我们看采样函数:

    def informed_sample(self, cMax, cMin, xCenter, C):if cMax < float('inf'):r = [cMax / 2.0,math.sqrt(cMax ** 2 - cMin ** 2) / 2.0,math.sqrt(cMax ** 2 - cMin ** 2) / 2.0]L = np.diag(r)xBall = self.sample_unit_ball()rnd = np.dot(np.dot(C, L), xBall) + xCenterrnd = [rnd[(0, 0)], rnd[(1, 0)]]else:rnd = self.sample()return rnd

    def sample_unit_ball():a = random.random()b = random.random()if b < a:a, b = b, asample = (b * math.cos(2 * math.pi * a / b),b * math.sin(2 * math.pi * a / b))return np.array([[sample[0]], [sample[1]], [0]])

        这段代码定义了一个名为 `sample_unit_ball` 的函数,它用于生成一个单位球内的随机点。

具体来说,代码中的操作如下:

1. `a = random.random()`: 生成一个在 [0, 1) 范围内的随机浮点数并将其赋给变量 `a`。

2. `b = random.random()`: 同样地,生成一个在 [0, 1) 范围内的随机浮点数并将其赋给变量 `b`。

3. `if b < a: a, b = b, a`: 如果 `b` 小于 `a`,则交换它们的值,确保 `a` 始终大于等于 `b`。

4. `sample = (b * math.cos(2 * math.pi * a / b), b * math.sin(2 * math.pi * a / b))`: 这里使用生成的随机数 `a` 和 `b` 计算了一个点 `sample`,它位于极坐标系中的角度 `2πa/b` 处,并且距离原点的距离为 `b`。

5. `return np.array([[sample[0]], [sample[1]], [0]])`: 将 `sample` 转换为一个列向量,并添加一个零作为第三个元素,最终返回一个二维的列向量。

总的来说,这个函数用于在单位球内生成一个随机点,它的坐标通过随机生成的参数 `a` 和 `b` 以及一些三角函数计算得到。

        rnd = np.dot(np.dot(C, L), xBall) + xCenter这里是先把单位圆压缩成椭圆,得到新采样的点。

3 完整代码

import copy
import math
import random
import timeimport matplotlib.pyplot as plt
from scipy.spatial.transform import Rotation as Rot
import numpy as npshow_animation = Trueclass RRT:# randArea采样范围[-2--18] obstacleList设置的障碍物 maxIter最大迭代次数 expandDis采样步长为2.0 goalSampleRate 以10%的概率将终点作为采样点def __init__(self, obstacleList, randArea,expandDis=2.0, goalSampleRate=10, maxIter=200):self.start = Noneself.goal = Noneself.min_rand = randArea[0]self.max_rand = randArea[1]self.expand_dis = expandDisself.goal_sample_rate = goalSampleRateself.max_iter = maxIterself.obstacle_list = obstacleList# 存储RRT树self.node_list = None# start、goal 起点终点坐标def rrt_planning(self, start, goal, animation=True):start_time = time.time()self.start = Node(start[0], start[1])self.goal = Node(goal[0], goal[1])# 将起点加入node_list作为树的根结点self.node_list = [self.start]path = Nonefor i in range(self.max_iter):# 进行采样rnd = self.sample()# 取的距离采样点最近的节点下标n_ind = self.get_nearest_list_index(self.node_list, rnd)# 得到最近节点nearestNode = self.node_list[n_ind]# 将Xrandom和Xnear连线方向作为生长方向# math.atan2() 函数接受两个参数,分别是 y 坐标差值和 x 坐标差值。它返回的值是以弧度表示的角度,范围在 -π 到 π 之间。这个角度表示了从 nearestNode 指向 rnd 的方向。theta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)# 生长 : 输入参数为角度、下标、nodelist中最近的节点  得到生长过后的节点newNode = self.get_new_node(theta, n_ind, nearestNode)# 检查是否有障碍物 传入参数为新生城路径的两个节点noCollision = self.check_segment_collision(newNode.x, newNode.y, nearestNode.x, nearestNode.y)if noCollision:# 没有碰撞把新节点加入到树里面self.node_list.append(newNode)if animation:self.draw_graph(newNode, path)# 是否到终点附近if self.is_near_goal(newNode):# 是否这条路径与障碍物发生碰撞if self.check_segment_collision(newNode.x, newNode.y,self.goal.x, self.goal.y):lastIndex = len(self.node_list) - 1# 找路径path = self.get_final_course(lastIndex)pathLen = self.get_path_len(path)print("current path length: {}, It costs {} s".format(pathLen, time.time()-start_time))if animation:self.draw_graph(newNode, path)return pathdef rrt_star_planning(self, start, goal, animation=True):start_time = time.time()self.start = Node(start[0], start[1])self.goal = Node(goal[0], goal[1])self.node_list = [self.start]path = NonelastPathLength = float('inf')for i in range(self.max_iter):rnd = self.sample()n_ind = self.get_nearest_list_index(self.node_list, rnd)nearestNode = self.node_list[n_ind]# steertheta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)newNode = self.get_new_node(theta, n_ind, nearestNode)noCollision = self.check_segment_collision(newNode.x, newNode.y, nearestNode.x, nearestNode.y)if noCollision:nearInds = self.find_near_nodes(newNode)newNode = self.choose_parent(newNode, nearInds)self.node_list.append(newNode)# 重联self.rewire(newNode, nearInds)if animation:self.draw_graph(newNode, path)if self.is_near_goal(newNode):if self.check_segment_collision(newNode.x, newNode.y,self.goal.x, self.goal.y):lastIndex = len(self.node_list) - 1tempPath = self.get_final_course(lastIndex)tempPathLen = self.get_path_len(tempPath)if lastPathLength > tempPathLen:path = tempPathlastPathLength = tempPathLenprint("current path length: {}, It costs {} s".format(tempPathLen, time.time()-start_time))return pathdef informed_rrt_star_planning(self, start, goal, animation=True):start_time = time.time()self.start = Node(start[0], start[1])self.goal = Node(goal[0], goal[1])self.node_list = [self.start]# max length we expect to find in our 'informed' sample space,# starts as infinitecBest = float('inf')path = None# Computing the sampling spacecMin = math.sqrt(pow(self.start.x - self.goal.x, 2)+ pow(self.start.y - self.goal.y, 2))xCenter = np.array([[(self.start.x + self.goal.x) / 2.0],[(self.start.y + self.goal.y) / 2.0], [0]])a1 = np.array([[(self.goal.x - self.start.x) / cMin],[(self.goal.y - self.start.y) / cMin], [0]])e_theta = math.atan2(a1[1], a1[0])# 论文方法求旋转矩阵(2选1)# first column of identity matrix transposed# id1_t = np.array([1.0, 0.0, 0.0]).reshape(1, 3)# M = a1 @ id1_t# U, S, Vh = np.linalg.svd(M, True, True)# C = np.dot(np.dot(U, np.diag(#     [1.0, 1.0, np.linalg.det(U) * np.linalg.det(np.transpose(Vh))])),#            Vh)# 直接用二维平面上的公式(2选1)C = np.array([[math.cos(e_theta), -math.sin(e_theta), 0],[math.sin(e_theta), math.cos(e_theta),  0],[0,                 0,                  1]])for i in range(self.max_iter):# Sample space is defined by cBest# cMin is the minimum distance between the start point and the goal# xCenter is the midpoint between the start and the goal# cBest changes when a new path is foundrnd = self.informed_sample(cBest, cMin, xCenter, C)n_ind = self.get_nearest_list_index(self.node_list, rnd)nearestNode = self.node_list[n_ind]# steertheta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)newNode = self.get_new_node(theta, n_ind, nearestNode)noCollision = self.check_segment_collision(newNode.x, newNode.y, nearestNode.x, nearestNode.y)if noCollision:nearInds = self.find_near_nodes(newNode)newNode = self.choose_parent(newNode, nearInds)self.node_list.append(newNode)self.rewire(newNode, nearInds)if self.is_near_goal(newNode):if self.check_segment_collision(newNode.x, newNode.y,self.goal.x, self.goal.y):lastIndex = len(self.node_list) - 1tempPath = self.get_final_course(lastIndex)tempPathLen = self.get_path_len(tempPath)if tempPathLen < cBest:path = tempPathcBest = tempPathLenprint("current path length: {}, It costs {} s".format(tempPathLen, time.time()-start_time))if animation:self.draw_graph_informed_RRTStar(xCenter=xCenter,cBest=cBest, cMin=cMin,e_theta=e_theta, rnd=rnd, path=path)return pathdef sample(self):# 取得1-100的随机数,如果比10大的话(以10%的概率取到终点)if random.randint(0, 100) > self.goal_sample_rate:# 在空间里随机采样一个点rnd = [random.uniform(self.min_rand, self.max_rand), random.uniform(self.min_rand, self.max_rand)]else:  # goal point sampling# 终点作为采样点rnd = [self.goal.x, self.goal.y]return rnddef choose_parent(self, newNode, nearInds):# 圈里是否有候选节点if len(nearInds) == 0:return newNodedList = []for i in nearInds:dx = newNode.x - self.node_list[i].xdy = newNode.y - self.node_list[i].yd = math.hypot(dx, dy)theta = math.atan2(dy, dx)# 检测是否碰到障碍物if self.check_collision(self.node_list[i], theta, d):# 计算距离dList.append(self.node_list[i].cost + d)else:# 无穷大dList.append(float('inf'))# 找到路径最小的的点 父节点minCost = min(dList)minInd = nearInds[dList.index(minCost)]if minCost == float('inf'):print("min cost is inf")return newNodenewNode.cost = minCostnewNode.parent = minIndreturn newNodedef find_near_nodes(self, newNode):n_node = len(self.node_list)# 动态变化 搜索到越后面的话半径越小r = 50.0 * math.sqrt((math.log(n_node) / n_node))# 找到节点d_list = [(node.x - newNode.x) ** 2 + (node.y - newNode.y) ** 2for node in self.node_list]# 比半径小near_inds = [d_list.index(i) for i in d_list if i <= r ** 2]return near_indsdef informed_sample(self, cMax, cMin, xCenter, C):if cMax < float('inf'):r = [cMax / 2.0,math.sqrt(cMax ** 2 - cMin ** 2) / 2.0,math.sqrt(cMax ** 2 - cMin ** 2) / 2.0]L = np.diag(r)xBall = self.sample_unit_ball()rnd = np.dot(np.dot(C, L), xBall) + xCenterrnd = [rnd[(0, 0)], rnd[(1, 0)]]else:rnd = self.sample()return rnd@staticmethoddef sample_unit_ball():a = random.random()b = random.random()if b < a:a, b = b, asample = (b * math.cos(2 * math.pi * a / b),b * math.sin(2 * math.pi * a / b))return np.array([[sample[0]], [sample[1]], [0]])@staticmethoddef get_path_len(path):pathLen = 0for i in range(1, len(path)):node1_x = path[i][0]node1_y = path[i][1]node2_x = path[i - 1][0]node2_y = path[i - 1][1]pathLen += math.sqrt((node1_x - node2_x)** 2 + (node1_y - node2_y) ** 2)return pathLen@staticmethoddef line_cost(node1, node2):return math.sqrt((node1.x - node2.x) ** 2 + (node1.y - node2.y) ** 2)@staticmethoddef get_nearest_list_index(nodes, rnd):# 遍历所有节点 计算采样点和节点的距离dList = [(node.x - rnd[0]) ** 2+ (node.y - rnd[1]) ** 2 for node in nodes]# 获得最近的距离所对应的索引minIndex = dList.index(min(dList))return minIndexdef get_new_node(self, theta, n_ind, nearestNode):newNode = copy.deepcopy(nearestNode)# 坐标newNode.x += self.expand_dis * math.cos(theta)newNode.y += self.expand_dis * math.sin(theta)# 代价newNode.cost += self.expand_dis# 父亲节点newNode.parent = n_indreturn newNodedef is_near_goal(self, node):# 计算距离d = self.line_cost(node, self.goal)if d < self.expand_dis:return Truereturn Falsedef rewire(self, newNode, nearInds):n_node = len(self.node_list)# 新节点 和圆圈的候选点for i in nearInds:nearNode = self.node_list[i]# 以newnode作为父节点 计算两条节点之间的距离d = math.sqrt((nearNode.x - newNode.x) ** 2+ (nearNode.y - newNode.y) ** 2)s_cost = newNode.cost + dif nearNode.cost > s_cost:theta = math.atan2(newNode.y - nearNode.y,newNode.x - nearNode.x)if self.check_collision(nearNode, theta, d):nearNode.parent = n_node - 1nearNode.cost = s_cost@staticmethoddef distance_squared_point_to_segment(v, w, p):# Return minimum distance between line segment vw and point pif np.array_equal(v, w):return (p - v).dot(p - v)  # v == w casel2 = (w - v).dot(w - v)  # i.e. |w-v|^2 -  avoid a sqrt# Consider the line extending the segment,# parameterized as v + t (w - v).# We find projection of point p onto the line.# It falls where t = [(p-v) . (w-v)] / |w-v|^2# We clamp t from [0,1] to handle points outside the segment vw.t = max(0, min(1, (p - v).dot(w - v) / l2))projection = v + t * (w - v)  # Projection falls on the segmentreturn (p - projection).dot(p - projection)def check_segment_collision(self, x1, y1, x2, y2):# 遍历所有的障碍物for (ox, oy, size) in self.obstacle_list:dd = self.distance_squared_point_to_segment(np.array([x1, y1]),np.array([x2, y2]),np.array([ox, oy]))if dd <= size ** 2:return False  # collisionreturn Truedef check_collision(self, nearNode, theta, d):tmpNode = copy.deepcopy(nearNode)end_x = tmpNode.x + math.cos(theta) * dend_y = tmpNode.y + math.sin(theta) * dreturn self.check_segment_collision(tmpNode.x, tmpNode.y, end_x, end_y)def get_final_course(self, lastIndex):path = [[self.goal.x, self.goal.y]]while self.node_list[lastIndex].parent is not None:node = self.node_list[lastIndex]path.append([node.x, node.y])lastIndex = node.parentpath.append([self.start.x, self.start.y])return pathdef draw_graph_informed_RRTStar(self, xCenter=None, cBest=None, cMin=None, e_theta=None, rnd=None, path=None):plt.clf()# for stopping simulation with the esc key.plt.gcf().canvas.mpl_connect('key_release_event',lambda event: [exit(0) if event.key == 'escape' else None])if rnd is not None:plt.plot(rnd[0], rnd[1], "^k")if cBest != float('inf'):self.plot_ellipse(xCenter, cBest, cMin, e_theta)for node in self.node_list:if node.parent is not None:if node.x or node.y is not None:plt.plot([node.x, self.node_list[node.parent].x], [node.y, self.node_list[node.parent].y], "-g")for (ox, oy, size) in self.obstacle_list:plt.plot(ox, oy, "ok", ms=30 * size)if path is not None:plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')plt.plot(self.start.x, self.start.y, "xr")plt.plot(self.goal.x, self.goal.y, "xr")plt.axis([-2, 18, -2, 15])plt.grid(True)plt.pause(0.01)@staticmethoddef plot_ellipse(xCenter, cBest, cMin, e_theta):  # pragma: no covera = math.sqrt(cBest ** 2 - cMin ** 2) / 2.0b = cBest / 2.0angle = math.pi / 2.0 - e_thetacx = xCenter[0]cy = xCenter[1]t = np.arange(0, 2 * math.pi + 0.1, 0.1)x = [a * math.cos(it) for it in t]y = [b * math.sin(it) for it in t]rot = Rot.from_euler('z', -angle).as_matrix()[0:2, 0:2]fx = rot @ np.array([x, y])px = np.array(fx[0, :] + cx).flatten()py = np.array(fx[1, :] + cy).flatten()plt.plot(cx, cy, "xc")plt.plot(px, py, "--c")def draw_graph(self, rnd=None, path=None):plt.clf()# for stopping simulation with the esc key.plt.gcf().canvas.mpl_connect('key_release_event',lambda event: [exit(0) if event.key == 'escape' else None])if rnd is not None:plt.plot(rnd.x, rnd.y, "^k")for node in self.node_list:if node.parent is not None:if node.x or node.y is not None:plt.plot([node.x, self.node_list[node.parent].x], [node.y, self.node_list[node.parent].y], "-g")for (ox, oy, size) in self.obstacle_list:# self.plot_circle(ox, oy, size)plt.plot(ox, oy, "ok", ms=30 * size)plt.plot(self.start.x, self.start.y, "xr")plt.plot(self.goal.x, self.goal.y, "xr")if path is not None:plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')plt.axis([-2, 18, -2, 15])plt.grid(True)plt.pause(0.01)class Node:def __init__(self, x, y):self.x = xself.y = yself.cost = 0.0self.parent = Nonedef main():print("Start rrt planning")# create obstacles# obstacleList = [#     (3,  3,  1.5),#     (12, 2,  3),#     (3,  9,  2),#     (9,  11, 2),# ]# 设置障碍物 (圆点、半径)obstacleList = [(5, 5, 1), (3, 6, 2), (3, 8, 2), (3, 10, 2), (7, 5, 2),(9, 5, 2), (8, 10, 1)]# Set params# 采样范围 设置的障碍物 最大迭代次数rrt = RRT(randArea=[-2, 18], obstacleList=obstacleList, maxIter=200)# 传入的是起点和终点#path = rrt.rrt_planning(start=[0, 0], goal=[15, 12], animation=show_animation)#path = rrt.rrt_star_planning(start=[0, 0], goal=[15, 12], animation=show_animation)path = rrt.informed_rrt_star_planning(start=[0, 0], goal=[15, 12], animation=show_animation)print("Done!!")if show_animation and path:plt.show()if __name__ == '__main__':main()

4 算法比较

        RRT:路径较差,非最优,在本例程中为6ms。

        RRT*:路径渐进最优,在本例程中为34ms。

        Informed RRT*:路径渐进最优,在本例程中为109ms。

        看官姥爷看自己需求用奥。

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

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

相关文章

【数据结构】树家族

目录 树的相关术语树家族二叉树霍夫曼树二叉查找树 BST平衡二叉树 AVL红黑树伸展树替罪羊树 B树B树B* 树 当谈到数据结构中的树时&#xff0c;我们通常指的是一种分层的数据结构&#xff0c;它由节点&#xff08;nodes&#xff09;组成&#xff0c;这些节点之间以边&#xff08…

基于级联延迟信号消除的锁相环(CDSC_PLL)技术MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 基于级联型延迟信号消除&#xff08;CDSC&#xff09;的锁相环技术&#xff08;CDSC-PLL&#xff09;&#xff0c;该锁相环克服了传统dq 锁相环在电网电压畸变或不对称时存在较大稳态误差的缺点。CDSC-PLL是在…

Ansible中的任务执行控制

循环 简单循环 {{item}} 迭代变量名称 loop: - value1 - value2 - ... //赋值列表{{item}} //迭代变量名称循环散列或字典列表 - name: create filehosts: host1tasks:- name: file moudleservice:name: "{{ item.name }}"state: "{{…

FRI及相关SNARKs的Fiat-Shamir安全

1. 引言 本文主要参考&#xff1a; Alexander R. Block 2023年论文 Fiat-Shamir Security of FRI and Related SNARKsAlbert Garreta 2023年9月在ZK Summit 10上分享 ZK10: Fiat-Shamir security of FRI and related SNARKs - Albert Garreta (Nethermind) 评估参数用的Sage…

Git客户端软件 Tower mac中文版特点说明

Tower mac是一款Mac OS X系统上的Git客户端软件&#xff0c;它提供了丰富的功能和工具&#xff0c;帮助用户更加方便地管理和使用Git版本控制系统。 Tower mac软件特点 1. 界面友好&#xff1a;Tower的界面友好&#xff0c;使用户能够轻松地掌握软件的使用方法。 2. 多种Git操…

edge浏览器的隐藏功能

1. edge://version 查看版本信息 2. edge://flags 特性界面 具体到某一特性&#xff1a;edge://flags/#overlay-scrollbars 3. edge://settings设置界面 详情可参考chrome: 4. edge://extensions 扩展程序页面 5. edge://net-internals 网络事件信息 6. edge://component…

excel制作透视表

场景描述&#xff1a; 有一张excel表&#xff0c;存在多条记录&#xff0c;现在需要把相同名称的商品的数量求和&#xff0c;放在一起展示 操作步骤&#xff1a; 删除最后一行数据 选中不显示分类汇总 以表格形式展示

STM32-HAL库08-TIM的输出比较模式(输出PWM的另一种方式)

STM32-HAL库08-TIM的输出比较模式&#xff08;输出PWM的另一种方式&#xff09; 一、所用材料&#xff1a; STM32F103C6T6最小系统板 STM32CUBEMX&#xff08;HAL库软件&#xff09; MDK5 示波器或者逻辑分析仪 二、所学内容&#xff1a; 通过定时器TIM的输出比较模式得到预…

第21期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

【计算机网络实验/wireshark】tcp建立和释放

wireshark开始捕获后&#xff0c;浏览器打开xg.swjtu.edu.cn&#xff0c;网页传输完成后&#xff0c;关闭浏览器&#xff0c;然后停止报文捕获。 若捕获不到dns报文&#xff0c;先运行ipconfig/flushdns命令清空dns缓存 DNS报文 设置了筛选条件&#xff1a;dns 查询报文目的…

BO(Business Object)是一种用于表示业务对象的设计模式

BO是 Business Object 的缩写&#xff0c;是一种用于表示业务对象的设计模式。在Java中&#xff0c;BO的主要作用是 封装业务逻辑&#xff0c;实现业务流程的可重用性和可维护性。 BO主要有以下几个作用&#xff1a; 实现业务逻辑的封装&#xff1a;将业务逻辑封装在BO对象中&a…

【完美世界】石昊拒绝云曦相认,爱而不得,云曦悲伤无助

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料《完美世界云曦篇》最新一集&#xff0c;为了云曦&#xff0c;石昊不远十万里&#xff0c;亲自送她回家&#xff0c;这份感情之真挚&#xff0c;绝对毋庸置疑。然而&#xff0c;令人感到不解的是&…

【KVM】软件虚拟化和硬件虚拟化

前言 大家好&#xff0c;我是秋意零。 今天介绍的内容是虚拟化技术以及软件虚拟化和硬件虚拟化。 &#x1f47f; 简介 &#x1f3e0; 个人主页&#xff1a; 秋意零&#x1f525; 账号&#xff1a;全平台同名&#xff0c; 秋意零 账号创作者、 云社区 创建者&#x1f9d1; 个…

HCIA数据通信——路由协议

数据通信——网络层&#xff08;OSPF基础特性&#xff09;_咕噜跳的博客-CSDN博客 数据通信——网络层&#xff08;RIP与BGP&#xff09;_咕噜跳的博客-CSDN博客 上述是之前写的理论知识部分&#xff0c;懒得在实验中再次提及了。这次做RIP协议以及OSPF协议。不过RIP协议不常用…

Mac连接linux的办法(自带终端和iterm2)

1. 使用Mac自带终端Terminal 1.1 点击右上角的聚焦搜索&#xff0c;再输入终端 1.2 查找linux系统的ip地址 在虚拟机里输入如下命令&#xff0c;找到蓝色区域的就是ip地址 ip addr 如果没有显示ip地址&#xff0c;可以重新安装一下虚拟机&#xff0c;之后确保以太网的连接是打…

【PC电脑windows-学习样例tusb_serial_device-ESP32的USB模拟串口程序+VScode建立工程+usb组件添加+-基础样例学习】

【PC电脑windows-学习样例tusb_serial_device-ESP32的USB模拟串口程序-基础样例学习】 1、概述2、实验环境3-1、 物品说明3-2、所遇问题&#xff1a;ESP32 cannot open source file "tinyusb.h"或者“tinyusb.h:No such file or directory ....”3-3、解决问题&#…

京东大数据平台(京东数据分析):9月京东牛奶乳品排行榜

鲸参谋监测的京东平台9月份牛奶乳品市场销售数据已出炉&#xff01; 9月份&#xff0c;牛奶乳品市场销售呈大幅上涨。鲸参谋数据显示&#xff0c;今年9月&#xff0c;京东平台牛奶乳品市场的销量为2000万&#xff0c;环比增长约65%&#xff0c;同比增长约3%&#xff1b;销售额为…

JVM字节码文件浅谈

文章目录 版权声明java虚拟机的组成字节码文件打开字节码文件的姿势字节码文件的组成魔数&#xff08;基本信息&#xff09;主副版本号&#xff08;基本信息&#xff09;主版本号不兼容的错误解决方法基本信息常量池方法 字节码文件的常用工具javap -v命令jclasslib插件阿里art…

搭建Qt5.7.1+kylinV10开发环境、运行环境

1.下载Qt源码 Index of / 2.编译Qt 解压缩qt-everywhere-opensource-src-5.7.1.tar.gz 进入到qt-everywhere-opensource-src-5.7.1/qtbase/mkspecs这个目录下&#xff0c; 2.1找到以下目录 复制他&#xff0c;然后改名linux-x86-arrch64&#xff0c;博主这里名字取的有些问…

循环链表(单循环、双循环)(数据结构与算法)

循环链表&#xff1a;循环单链表、循环双链表 1. 循环单链表 循环单链表&#xff08;Circular Singly Linked List&#xff09;是一种特殊类型的单链表&#xff0c;其中最后一个节点的指针指向头节点&#xff0c;形成一个循环。 循环单链表与普通单链表的主要区别在于&#xf…