自动驾驶算法(一):Dijkstra算法讲解与代码实现

news2025/2/24 3:33:06

目录

0 本节关键词:栅格地图、算法、路径规划

1 Dijkstra算法详解

2 Dijkstra代码详解


0 本节关键词:栅格地图、算法、路径规划

1 Dijkstra算法详解

        用于图中寻找最短路径。节点是地点,边是权重。

        从起点开始逐步扩展,每一步为一个节点找到最短路径:

        While True:
                1.从未访问的节点选择距离最小的节点收录(贪心思想)
                2.收录节点后遍历该节点的邻接节点,更新距离

        我们举例子说明一下,在机器人路径规划中,通常用open list、closed list表达:

        open list 表示从该节点到起点已经有路径的节点

        closed list 表示已经找到最短路径的节点

        Step1:从起点开始,将起点放入open list中,选择距离最短的节点进行收录。

open list    1(0)(min)
closed list

|
|

open list    
closed list  1(0)

        Step2:遍历1号节点的邻接节点(4、2号节点)

open list    2(2)(1-->2) 4(1)(1-->4)(min)
closed list  1(0)

|
|

open list    2(2)(1-->2)
closed list  1(0)  4(1)(1-->4)

        4号节点收录后我们需要对其邻接节点更新距离。(3、6、7号节点)

        Step3:3号节点我们找到1->4->3路径,6号节点我们找到1->4->6路径,7号节点我们找到1->4->7路径。

open list    2(2)(1-->2)(min) 3(3)(1-->4-->3) 6(9)(1-->4-->6) 7(5)(1-->4-->7)
closed list  1(0)

|
|

open list    3(3)(1-->4-->3) 6(9)(1-->4-->6) 7(5)(1-->4-->7)
closed list  1(0)  4(1)(1-->4) 2(2)(1-->2)

        Step4:遍历2的邻接节点,我们发现4号节点已经在close list中(不需要被更新),我们更新5号节点。

open list    3(3)(1-->4-->3)(min) 6(9)(1-->4-->6) 7(5)(1-->4-->7) 5(13)(1->2-->5)
closed list  1(0)

|
|

open list    6(9)(1-->4-->6) 7(5)(1-->4-->7) 5(13)(1->2-->5)
closed list  1(0)  4(1)(1-->4) 2(2)(1-->2) 3(3)(1-->4-->3)

        Step5:遍历3的邻接节点,(3-->1无需更新,更新3-->6  1436(8) (因为我们有到3的最短距离)),而我们已经为6号节点找到路径6(9)(146),更新6号节点的路径。

open list    6(9)(1-->4-->6)(1->4->3-->6 7) 7(5)(1-->4-->7)(min) 5(13)(1->2-->5)
closed list  1(0)  4(1)(1-->4) 2(2)(1-->2) 3(3)(1-->4-->3)

|
|

open list    6(8)(1->4->3-->6) 5(13)(1->2-->5)
closed list  1(0)  4(1)(1-->4) 2(2)(1-->2) 3(3)(1-->4-->3) 7(5)(1-->4-->7)

        Step6:遍历7的邻接节点(6号节点)(1 4 7 6 = 6)比之前的8小,对6号距离再次更新。

open list    6(8)(1->4->3-->6)(1->4->7-->6 6)(min)  5(13)(1->2-->5) 
closed list  1(0)  4(1)(1-->4) 2(2)(1-->2) 3(3)(1-->4-->3) 7(5)(1-->4-->7) 

|
|

open list    5(13)(1->2-->5) 
closed list  1(0)  4(1)(1-->4) 2(2)(1-->2) 3(3)(1-->4-->3) 7(5)(1-->4-->7)  6(6)(1-->4-->7-->6)

        Step7:遍历6的邻接节点(6号节点)结束

        栅格地图初介绍:

        假设图中灰色的是障碍物,红色的是机器人。在避障时,我们的常用做法是通过膨胀障碍物,将机器人视为质点来规划路径,然后对地图进行栅格化,将地图弄成一块一块的。最后将栅格地图转化为有权地图。我们可以把栅格地图的每个栅格看作是有权图的节点,机器人的运动范围可以看作是有权图的节点和节点之间的连接。

2 Dijkstra代码详解

        这里我们先配置下代码环境,最好是在python3.9下,我们创建conda虚拟环境:

conda create -n nav python=3.9

        安装所需库:

pip install numpy scipy matplotlib pandas cvxpy pytest -i https://pypi.tuna.tsinghua.edu.cn/simple

        我们的代码如下:

"""

Grid based Dijkstra planning

author: Atsushi Sakai(@Atsushi_twi)

"""

import matplotlib.pyplot as plt
import math

show_animation = True


class Dijkstra:

    def __init__(self, ox, oy, resolution, robot_radius):
        """
        Initialize map for planning

        ox: x position list of Obstacles [m]
        oy: y position list of Obstacles [m]
        resolution: grid resolution [m]
        rr: robot radius[m]
        """

        self.min_x = None
        self.min_y = None
        self.max_x = None
        self.max_y = None
        self.x_width = None
        self.y_width = None
        self.obstacle_map = None

        self.resolution = resolution
        self.robot_radius = robot_radius
        self.calc_obstacle_map(ox, oy)
        self.motion = self.get_motion_model()

    class Node:
        def __init__(self, x, y, cost, parent_index):
            self.x = x  # index of grid
            self.y = y  # index of grid
            self.cost = cost  # g(n)
            self.parent_index = parent_index  # index of previous Node

        def __str__(self):
            return str(self.x) + "," + str(self.y) + "," + str(
                self.cost) + "," + str(self.parent_index)

    def planning(self, sx, sy, gx, gy):
        """
        dijkstra path search

        input:
            s_x: start x position [m]
            s_y: start y position [m]
            gx: goal x position [m]
            gx: goal x position [m]

        output:
            rx: x position list of the final path
            ry: y position list of the final path
        """

        start_node = self.Node(self.calc_xy_index(sx, self.min_x),
                               self.calc_xy_index(sy, self.min_y), 0.0, -1)   # round((position - minp) / self.resolution)
        goal_node = self.Node(self.calc_xy_index(gx, self.min_x),
                              self.calc_xy_index(gy, self.min_y), 0.0, -1)

        open_set, closed_set = dict(), dict()     # key - value: hash表
        open_set[self.calc_index(start_node)] = start_node

        while 1:
            c_id = min(open_set, key=lambda o: open_set[o].cost)  # 取cost最小的节点
            current = open_set[c_id]

            # show graph
            if show_animation:  # pragma: no cover
                plt.plot(self.calc_position(current.x, self.min_x),
                         self.calc_position(current.y, self.min_y), "xc")
                # 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 len(closed_set.keys()) % 10 == 0:
                    plt.pause(0.001)

            # 判断是否是终点
            if current.x == goal_node.x and current.y == goal_node.y:
                print("Find goal")
                goal_node.parent_index = current.parent_index
                goal_node.cost = current.cost
                break

            # Remove the item from the open set
            del open_set[c_id]

            # Add it to the closed set
            closed_set[c_id] = current

            # expand search grid based on motion model
            for move_x, move_y, move_cost in self.motion:
                node = self.Node(current.x + move_x,
                                 current.y + move_y,
                                 current.cost + move_cost, c_id)
                n_id = self.calc_index(node)

                if n_id in closed_set:
                    continue

                if not self.verify_node(node):
                    continue

                if n_id not in open_set:
                    open_set[n_id] = node  # Discover a new node
                else:
                    if open_set[n_id].cost >= node.cost:
                        # This path is the best until now. record it!
                        open_set[n_id] = node

        rx, ry = self.calc_final_path(goal_node, closed_set)

        return rx, ry

    def calc_final_path(self, goal_node, closed_set):
        # generate final course
        rx, ry = [self.calc_position(goal_node.x, self.min_x)], [
            self.calc_position(goal_node.y, self.min_y)]
        parent_index = goal_node.parent_index
        while parent_index != -1:
            n = closed_set[parent_index]
            rx.append(self.calc_position(n.x, self.min_x))
            ry.append(self.calc_position(n.y, self.min_y))
            parent_index = n.parent_index

        return rx, ry

    def calc_position(self, index, minp):
        pos = index * self.resolution + minp
        return pos

    def calc_xy_index(self, position, minp):
        return round((position - minp) / self.resolution)

    def calc_index(self, node):
        return node.y * self.x_width + node.x

    def verify_node(self, node):
        px = self.calc_position(node.x, self.min_x)
        py = self.calc_position(node.y, self.min_y)

        if px < self.min_x:
            return False
        if py < self.min_y:
            return False
        if px >= self.max_x:
            return False
        if py >= self.max_y:
            return False

        if self.obstacle_map[node.x][node.y]:
            return False

        return True

    def calc_obstacle_map(self, ox, oy):
        ''' 第1步:构建栅格地图 '''
        self.min_x = round(min(ox))
        self.min_y = round(min(oy))
        self.max_x = round(max(ox))
        self.max_y = round(max(oy))
        print("min_x:", self.min_x)
        print("min_y:", self.min_y)
        print("max_x:", self.max_x)
        print("max_y:", self.max_y)

        self.x_width = round((self.max_x - self.min_x) / self.resolution)
        self.y_width = round((self.max_y - self.min_y) / self.resolution)
        print("x_width:", self.x_width)
        print("y_width:", self.y_width)

        # obstacle map generation
        # 初始化地图
        self.obstacle_map = [[False for _ in range(self.y_width)]
                             for _ in range(self.x_width)]
        # 设置障碍物
        for ix in range(self.x_width):
            x = self.calc_position(ix, self.min_x)
            for iy in range(self.y_width):
                y = self.calc_position(iy, self.min_y)
                for iox, ioy in zip(ox, oy):
                    d = math.hypot(iox - x, ioy - y)
                    if d <= self.robot_radius:
                        self.obstacle_map[ix][iy] = True
                        break

    @staticmethod
    def get_motion_model():
        # dx, dy, cost
        motion = [[1, 0, 1],
                  [0, 1, 1],
                  [-1, 0, 1],
                  [0, -1, 1],
                  [-1, -1, math.sqrt(2)],
                  [-1, 1, math.sqrt(2)],
                  [1, -1, math.sqrt(2)],
                  [1, 1, math.sqrt(2)]]

        return motion

def main():
    # start and goal position
    sx = -5.0  # [m]
    sy = -5.0  # [m]
    gx = 50.0  # [m]
    gy = 50.0  # [m]
    grid_size = 2.0  # [m]
    robot_radius = 1.0  # [m]

    # set obstacle positions
    ox, oy = [], []
    for i in range(-10, 60):
        ox.append(i)
        oy.append(-10.0)
    for i in range(-10, 60):
        ox.append(60.0)
        oy.append(i)
    for i in range(-10, 61):
        ox.append(i)
        oy.append(60.0)
    for i in range(-10, 61):
        ox.append(-10.0)
        oy.append(i)
    for i in range(-10, 40):
        ox.append(20.0)
        oy.append(i)
    for i in range(0, 40):
        ox.append(40.0)
        oy.append(60.0 - i)

    if show_animation:  # pragma: no cover
        plt.plot(ox, oy, ".k")
        plt.plot(sx, sy, "og")
        plt.plot(gx, gy, "xb")
        plt.grid(True)
        plt.axis("equal")

    dijkstra = Dijkstra(ox, oy, grid_size, robot_radius)
    rx, ry = dijkstra.planning(sx, sy, gx, gy)

    if show_animation:  # pragma: no cover
        plt.plot(rx, ry, "-r")
        plt.pause(0.01)
        plt.show()

if __name__ == '__main__':
    main()

        先执行一下看看效果:

        我们现在来详解一下:

        我们从main函数开始:

    # 1. 设置起点和终点
    sx = -5.0  # [m]
    sy = -5.0  # [m]
    gx = 50.0  # [m]
    gy = 50.0  # [m]
    # 2. 设置珊格的大小和机器人的半径
    grid_size = 2.0  # [m]
    robot_radius = 1.0  # [m]

    # 3. 设置障碍物的位置(图中的黑点就是)
    ox, oy = [], []
    # 3.1 设置外围的四堵墙  (-10,-10)  --> (60,-10) 最下面的一条线
    for i in range(-10, 60):
        ox.append(i)
        oy.append(-10.0)
    # 3.1 设置外围的四堵墙  (60,-10)  -->  (60,60)  最右面的一条线
    for i in range(-10, 60):
        ox.append(60.0)
        oy.append(i)
    # 3.1 设置外围的四堵墙  (-10,60)  -->  (61,60)  最上面的一条线
    for i in range(-10, 61):
        ox.append(i)
        oy.append(60.0)
    # 3.1 设置外围的四堵墙  (-10,-10)  -->  (-10,61) 最左面的一条线
    for i in range(-10, 61):
        ox.append(-10.0)
        oy.append(i)

    # 3.2 障碍物
    for i in range(-10, 40):
        ox.append(20.0)
        oy.append(i)
    for i in range(0, 40):
        ox.append(40.0)
        oy.append(60.0 - i)

    # 4 画图 起点、终点、障碍物都画出来
    if show_animation:  # pragma: no cover
        plt.plot(ox, oy, ".k")
        plt.plot(sx, sy, "og")
        plt.plot(gx, gy, "xb")
        plt.grid(True)
        plt.axis("equal")

        这段代码就设置了边框和障碍物区域并把他们可视化了:

        就是我图中画的区域。

    #生成了Dijkstra的对象 调用其中的方法(障碍物信息、珊格大小、机器人半径)
    dijkstra = Dijkstra(ox, oy, grid_size, robot_radius)

        生成了Dijkstra的对象。我们来看这个类的构造函数,进入一个类首先执行构造函数:

    def __init__(self, ox, oy, resolution, robot_radius):
        """
        Initialize map for planning

        ox: x position list of Obstacles [m]
        oy: y position list of Obstacles [m]
        resolution: grid resolution [m]
        rr: robot radius[m]
        """

        self.min_x = None
        self.min_y = None
        self.max_x = None
        self.max_y = None
        self.x_width = None
        self.y_width = None
        self.obstacle_map = None

        # 珊格大小
        self.resolution = resolution
        # 机器人半径
        self.robot_radius = robot_radius
        # 构建珊格地图
        self.calc_obstacle_map(ox, oy)
        self.motion = self.get_motion_model()

        我们先来看是怎么创建珊格地图的:

    def calc_obstacle_map(self, ox, oy):
        ''' 第1步:构建栅格地图 '''

        # 1. 获得地图的边界值
        self.min_x = round(min(ox))
        self.min_y = round(min(oy))
        self.max_x = round(max(ox))
        self.max_y = round(max(oy))
        print("min_x:", self.min_x)
        print("min_y:", self.min_y)
        print("max_x:", self.max_x)
        print("max_y:", self.max_y)

        # 2.计算x、y方向珊格个数
        self.x_width = round((self.max_x - self.min_x) / self.resolution)
        self.y_width = round((self.max_y - self.min_y) / self.resolution)
        print("x_width:", self.x_width)
        print("y_width:", self.y_width)

        # obstacle map generation
        # 3.初始化地图 都设置为false 表示还没有设置障碍物
        self.obstacle_map = [[False for _ in range(self.y_width)]
                             for _ in range(self.x_width)]
        # 4.设置障碍物 遍历每一个栅格
        for ix in range(self.x_width):
            # 通过下标计算珊格位置
            x = self.calc_position(ix, self.min_x)
            for iy in range(self.y_width):
                y = self.calc_position(iy, self.min_y)
                # 遍历障碍物
                for iox, ioy in zip(ox, oy):
                    # 计算障碍物到珊格的距离
                    d = math.hypot(iox - x, ioy - y)
                    # 膨胀障碍物 如果距离比机器人半径小 机器人不能通行
                    if d <= self.robot_radius:
                        # 设置为true
                        self.obstacle_map[ix][iy] = True
                        break

        首先我们获得了地图的边界值,算出了每一个方向上有多少珊格数量。

        比如我们的长是100m(self.max_x - self.min_x = 100),珊格大小为3,那么我们每一行不就是有33个珊格啦~。

        我们初始化obstacle_map,这个大小为珊格长 * 珊格宽的大小,我们将他们初始化为false表示这个地方没有障碍物。

        然后我们遍历每一个珊格for ix in range(self.x_width)、for iy in range(self.y_width)。我们来看看calc_position这个方法做了什么。

    def calc_position(self, index, minp):
        pos = index * self.resolution + minp
        return pos

        其实就计算了珊格所在位置的真实(x,y)坐标,比如我们的self.minx = 10,ix = 0,那么他的pos = 0 * 2 + 10 = 10,比如我们的self.minx = 10,ix = 1,那么他的pos = 1 * 2 + 10 = 12。我们遍历所有障碍物体的坐标,计算障碍物体(真实坐标)与这个机器人的距离,如果这个距离比机器人自身的大小小的话,我们将这个地方的珊格标志置为false表示有东西。

        那么,在完成这个函数calc_obstacle_map时候,我们有了一张珊格地图,里面充斥着false和true,如果为true的话,那么机器人是过不去的,这块也就是设置成了障碍物区域。

        我们接着往下看构造函数:

        self.calc_obstacle_map(ox, oy)
        self.motion = self.get_motion_model()

         self.motion = self.get_motion_model()这段代码建立了机器人的运动模型和运动代价:

    def get_motion_model():
        # dx, dy, cost
        motion = [[1, 0, 1],                    #x增加1,y不变 代价为1
                  [0, 1, 1],
                  [-1, 0, 1],
                  [0, -1, 1],
                  [-1, -1, math.sqrt(2)],
                  [-1, 1, math.sqrt(2)],
                  [1, -1, math.sqrt(2)],
                  [1, 1, math.sqrt(2)]]

        return motion

        这里也就是机器人向左走(x+1,y+0)代价为1,斜着走代价为根号2。到此为止,我们构造函数讲解完了。我们返回主函数。

open_set

        这里开始正式进入路径规划了。传入的参数为起点坐标和终点坐标:

自动驾驶算法(一):Dijkstra算法讲解与代码实现

        我们先看下node类。

    class Node:
        def __init__(self, x, y, cost, parent_index):
            self.x = x  # index of grid
            self.y = y  # index of grid
            self.cost = cost  # g(n)
            self.parent_index = parent_index  # index of previous Node

        def __str__(self):
            return str(self.x) + "," + str(self.y) + "," + str(
                self.cost) + "," + str(self.parent_index)

        首先执行构造函数,我们发现就是把珊格的(x,y)坐标(并非真实坐标是珊格的)还有cost(后文说)以及父节点的ID赋值了。(so easy)        

        我们在看一下calc_xy_index函数:

    def calc_xy_index(self, position, minp):
        return round((position - minp) / self.resolution)

        它就是计算出真实世界的点点属于哪一个珊格的某一维度的坐标,我们举个例子:

self.calc_xy_index(sx, self.min_x)  sx = 30  minx = 20

        这就代表我们的地图边界的 x 坐标为20,这个点的坐标x=30,我们用(30-20)/2 = 5,那么这个珊格坐标的x方向的坐标就是5。

        start_node = self.Node(self.calc_xy_index(sx, self.min_x),
                               self.calc_xy_index(sy, self.min_y), 0.0, -1)   # round((position - minp) / self.resolution)
        goal_node = self.Node(self.calc_xy_index(gx, self.min_x),
                              self.calc_xy_index(gy, self.min_y), 0.0, -1)

        因此,这段代码的含义就是我们计算出了起始和终止点的珊格坐标,并且将代价置为0,且他们的父节点为-1(没有父亲节点)。封装成了node。

        下面进入算法部分,我们看流程图:

        代码部分和流程图是一样的:

        1.首先我们把起点放入openlist中:

        # 设置openlist closelist 基于哈希表
        open_set, closed_set = dict(), dict()     # key - value: hash表
        # 将startnode放进openset里面 索引为一维数组
        open_set[self.calc_index(start_node)] = start_node

        看一下calc_index函数:这里将珊格地图映射成了一个一维数组,返回数组的ID,类似C++中的二维数组降维。这里openset是一个字典,里面的key是珊格地图点的ID,value是这个珊格节点。

    def calc_index(self, node):
        return node.y * self.x_width + node.x

        2.while True进入循环

        while 1:

        2.1 取openlist cost最小的节点作为当前节点

            c_id = min(open_set, key=lambda o: open_set[o].cost)
            current = open_set[c_id]

        2.2.1 判断当前是否为终点,如果是终点,如果是终点的话把终点的cost修改为当前点的cost值,且终点的父亲节点为当前点的父亲节点。

            # 判断是否是终点
            if current.x == goal_node.x and current.y == goal_node.y:
                print("Find goal")
                # 当前节点的信息赋值给终点
                goal_node.parent_index = current.parent_index
                goal_node.cost = current.cost
                break

        2.2.2 不是最终节点的话从openlist删除加入到closelist

            # 把当前节点从openset里面删掉
            del open_set[c_id]

            # 加入到closed set
            closed_set[c_id] = current

        2.3 遍历其9个邻接运动节点

            for move_x, move_y, move_cost in self.motion:

        2.3.1 封装邻接节点

node = self.Node(current.x + move_x,
                                 current.y + move_y,
                                 current.cost + move_cost, c_id)

        这个点到邻接节点的移动就是 x +-( 1或-1或+根号2或-根号2),然后移动上下的话它的代价值需要+1,斜着移动需要 + 根号2,这样递归的进行我们就求出来所有点的代价值了,同样这个新走的点是通过我们这个点走过来的,因此新点的父节点就是我们这个点。

        2.3.2 求当前节点的一维索引判断是否收录到closelist并判断是否可行,如果收录了,那么已经有最小路径了不需要我们再去处理了,还需要判断这个节点是否在珊格地图标记为false点上(珊格地图就是这么用的....)如果这个地方有障碍物那么我们也走不了。

                # 求当前节点的key
                n_id = self.calc_index(node)

                # 是否已经收录到close set里面
                if n_id in closed_set:
                    continue

                # 邻接节点是否可行
                if not self.verify_node(node):
                    continue

        2.3.3 如果不在openset里面我们就将她作为一个新节点加入,如果在openset比较值是否是最优更新,是否和之前的最优路径有重叠。

                if n_id not in open_set:
                    open_set[n_id] = node  # Discover a new node
                else:
                    if open_set[n_id].cost >= node.cost:
                        # This path is the best until now. record it!
                        open_set[n_id] = node

        到这里我们的算法就结束了。我们迭代找到最终点后算法就break掉了~。

        最后我们计算路径:

    def calc_final_path(self, goal_node, closed_set):
        # generate final course
        rx, ry = [self.calc_position(goal_node.x, self.min_x)], [
            self.calc_position(goal_node.y, self.min_y)]
        parent_index = goal_node.parent_index
        while parent_index != -1:
            n = closed_set[parent_index]
            rx.append(self.calc_position(n.x, self.min_x))
            ry.append(self.calc_position(n.y, self.min_y))
            parent_index = n.parent_index

        return rx, ry

        我们将最终节点的珊格坐标还原成真实的三维坐标,并向前找他们的父亲节点直到起始节点(parent_index= -1),我们就出来这个路径了。

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

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

相关文章

MacOS安装homebrew

文章目录 官网脚本无法正常下载安装使用HomebrewCN国内安装脚本进行安装找到一份合适的安装脚步执行安装脚本 Homebrew自己的安装位置使用Homebrew安装tree指令验证安装是否成功Homebrew把软件程序都安装到哪里了 Homebrew安装需要依赖Git&#xff0c;请先确保Git已安装成功 Ho…

烧脑玄幻小说,情节超乎想象,深陷其中无法自拔,快来一探究竟

《时空穿越守则》 这本小说讲述了一个主角穿越不同世界&#xff0c;通过积攒点数提升技能&#xff0c;并带回物资发家致富的故事。主角的穿梭能力让他可以在不同的世界中自如穿梭&#xff0c;这种独特的设定和故事情节让人耳目一新。 《惊悚乐园》 这部作品堪称封神之作&#x…

「免费活动」敏捷武林上海站 | 与 Scrum.org CEO 面对面

活动介绍 过去的几年里&#xff0c;外界的风云变幻为我们的生活增添了一些不一样的色彩。在VUCA世界的浪潮里&#xff0c;每一个人都成为自己生活里的冒险家。面对每一次的变化&#xff0c;勇于探索未知&#xff0c;迎接挑战&#xff0c;努力追逐更好的自己。 七月&#xff0…

NI USB-4431对标国产化4路同步采集卡解决方案

102.4 kS/s , 100 dB , 0.8 Hz AC/DC耦合&#xff0c;4输入/单输出声音与振动设备 USB-4431专为声音和振动应用而设计。输入通道集成了用于加速度计和麦克风的集成电路压电式(IEPE)信号调理功能。四个USB-4431的输入通道可同步对输入信号进行数字化。模拟输出(AO)通道是激励响…

vue2+ant-design-vue a-form-model组件二次封装(form表单组件)FormModel 表单

一、效果图 二、参数配置 1、代码示例 <t-antd-form:ref-obj.sync"formOpts.ref":formOpts"formOpts":widthSize"1":labelCol"{ span:2}":wrapperCol"{ span:22}"handleEvent"handleEvent" />2. 配置参数…

【ROS系列】坐标系转换介绍和对齐

一、坐标系简介 本篇文章介绍&#xff1a;ECEF、ENU、UTM、WGS-84坐标系&#xff08;LLA) 1.1、ECEF坐标系 ECEF坐标系也叫地心地固直角坐标系。 原点&#xff1a;地球的质心&#xff0c; x轴&#xff1a;原点延伸通过本初子午线&#xff08;0度经度&#xff09;和赤道&am…

MySQL数据库干货_16—— SQL99标准中的查询

SQL99标准中的查询 MySQL5.7 支持部分的SQL99 标准。 SQL99中的交叉连接(CROSS JOIN) 示例&#xff1a; 使用交叉连接查询 employees 表与 departments 表。 select * from employees cross join departments;SQL99中的自然连接(NATURAL JOIN) 自然连接 连接只能发生在两…

软考中项集成如何画图?计算题怎么考的?

2023下半年软考集成一共考6个批次&#xff0c;10月28日、29日软考集成考了第一、二、三、四批次&#xff0c;11月4日软考集成再考第五批和第六批。 先说一下通过10.28-29得出的软考机考注意事项&#xff1a; 1、草稿纸不能自带&#xff0c;考试现场会发放草稿纸&#xff0c;草…

大数据信息抽取

随着互联网的广泛应用和技术的不断进步&#xff0c;海量数据被产生、存储和共享。这些数据中包含着宝贵的的信息和知识&#xff0c;二大数据信息抽取是正是为了把这些数据中关键、有用的信息提取出来。 大数据信息抽取就是指通过自动化的方式&#xff0c;从大数据中提取有异议…

政务服务技能竞赛中用到的软件和硬件

政务服务技能竞赛包括争上游、抢先机、秀风采、比擂台几个环节&#xff0c;用到选手端平板、评委端平板、主持人平板、抢答器等设备、抢答器等。分别计算团队分和个人分。答题规则和计分方案均较为复杂&#xff0c;一般竞赛软件无法实现&#xff0c;要用到高端竞赛软件&#xf…

【数据结构】顺序表:简单而实用(比较水

前言 最近开始学习数据结构 就重新拾起写博客的习惯 来记录一下 今天就来学一下顺序表和链表 小提示&#xff1a;引用的部分可看可不看 以及 这篇文章使用的是C语言 引入&#xff1a;线性表 在学习顺序表之前 我们先来了解一下线性表 线性表&#xff08;linear list&#xf…

2023-2024-1 高级语言程序设计-函数

6-1 求m到n之和 本题要求实现一个计算m~n&#xff08;m<n&#xff09;之间所有整数的和的简单函数。 函数接口定义&#xff1a; int sum( int m, int n ); 其中m和n是用户传入的参数&#xff0c;保证有m<n。函数返回的是m~n之间所有整数的和。 裁判测试程序样例&…

模板引擎技术---FreeMarker

什么是模板引擎 模板引擎是一种用于生成动态内容的工具&#xff0c;它将数据和静态模板结合起来&#xff0c;生成最终的文档&#xff08;通常是HTML、XML、JSON等格式&#xff09;&#xff0c;这些文档可以被浏览器或其他客户端解析并展示给用户。模板引擎的主要目的是将数据和…

jquery变焦放大效果

实现效果&#xff1a; jquery变焦放大效果,一般商城网站的商品都会有这样的效果&#xff0c;点击或者鼠标放在图片上时&#xff0c;会展示出一个比较大的图片&#xff0c;让我们对商品观看的更清楚&#xff0c;青柠资源网推荐下载&#xff01; 下载地址 qnziyw点cn/wysc/wytx…

自动化测试实战篇:UI自动化测试用例管理平台搭建

用到的工具&#xff1a;python3 django2 mysql RabbitMQ celery selenium python3和selenium这个网上很多教程&#xff0c;我不在这一一说明&#xff1b; 平台功能介绍&#xff1a; 项目管理&#xff1a;用于管理项目。每个项目可以设置多个环境&#xff0c;例如开发环境…

2023年11月1日蜻蜓C影视追剧系统v1.2.2更新-与时俱进调整微信登录授权获取方式-修复无法登陆授权

2023年11月1日蜻蜓C影视追剧系统v1.2.2更新-与时俱进调整微信登录授权获取方式-修复无法登陆授权 问题背景&#xff1a; 小程序用户头像昵称获取规则调整公告官方 微信团队2022-05-09 更新时间&#xff1a;2022年11月9日 由于 PC/macOS 平台「头像昵称填写能力」存在兼容性问…

“Lazada API揭秘:按关键字搜索商品,轻松掌握电商未来!“

Lazada的API可以按关键字搜索商品。请求参数包括&#xff1a; key&#xff1a;调用key&#xff0c;必须以GET方式拼接在URL中。secret&#xff1a;调用密钥。qshoe&&#xff1a;要搜索的关键字。start_price&end_price&&#xff1a;价格范围&#xff0c;可按价格筛…

HackTheBox-Starting Point--Tier 1---Pennyworth

文章目录 一 题目二 实验过程 一 题目 Tags Web、Common Applications、Jenkins、Java、Reconnaissance、Remote Code Execution、Default Credentials译文&#xff1a;Web、常见应用、Jenkins、Java、侦察、远程代码执行、默认凭证Connect To attack the target machine, y…

行情分析——加密货币市场大盘走势(11.1)

大饼短期内处于震荡&#xff0c;目前在吸血山寨。对于做中长线的也是可以秉持“做多大饼&#xff0c;做空山寨“的原则。目前大饼依然保持逢低做多即可&#xff0c;短期内依然不容易下跌。稳健的朋友&#xff0c;大家可以不做大饼。 以太目前也是处在震荡向上过程&#xff0c;以…

浅谈电动汽车充电桩检测技术的实现

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a; 关键词&#xff1a;电动直流和交流充电桩是我国电动汽车充电桩中运行量较大的一种。为了保持正常运行和使用&#xff0c;应高度重视检测、运行和维护工作。因此&#xff0c;有关部门应做好充电桩的检测工作…