史上最详细的轨迹优化教程-机器人避障及轨迹平滑实现(干货满满)

有一些朋友问我到底如何用优化方法实现轨迹优化(避障+轨迹平滑等),今天就出一个干货满满的教程,绝对是面向很多工业化场景的讲解,为了便于理解,我选用二维平面并给出详细代码实现,三维空间原理相似。

本教程禁止转载,主要是有问题可以联系我探讨,我的邮箱 fanzexuan135@163.com

下面提供的代码实际运行结果如下图(下面会有数学和代码的详解)

在这里插入图片描述

轨迹优化教程

本教程将详细介绍如何在二维平面上优化一条轨迹,使其避免碰撞并保持平滑。我们将从数学原理出发,逐步推导并给出代码实现。同时,我也会介绍一些常用的优化器库。

问题定义

假设我们在一个二维平面上有一组障碍物,我们的目标是在起点和终点之间规划一条轨迹,使其满足以下条件:

  1. 轨迹不与任何障碍物相交(避免碰撞)
  2. 轨迹尽可能平滑,没有急转弯(保持平滑)
  3. 轨迹尽可能短,减少不必要的绕路(最小化长度)

我们可以将这个问题形式化为一个优化问题:

min ⁡ x L ( x ) + λ s S ( x ) + λ c C ( x ) \min_{\mathbf{x}} \quad L(\mathbf{x}) + \lambda_s S(\mathbf{x}) + \lambda_c C(\mathbf{x}) xminL(x)+λsS(x)+λcC(x)

其中:

  • x \mathbf{x} x是轨迹的参数化表示,例如一系列的路径点坐标
  • L ( x ) L(\mathbf{x}) L(x)表示轨迹的长度
  • S ( x ) S(\mathbf{x}) S(x)表示轨迹的平滑度,可以用轨迹的曲率或加速度等量度
  • C ( x ) C(\mathbf{x}) C(x)表示轨迹与障碍物之间的碰撞代价
  • λ s \lambda_s λs λ c \lambda_c λc是平滑项和碰撞项的权重系数

轨迹参数化

为了优化轨迹,我们首先需要选择一种方式来参数化轨迹。常见的方法有:

  1. 路径点参数化:用一系列的路径点 ( x i , y i ) (x_i, y_i) (xi,yi)来表示轨迹,路径点之间可以用直线或曲线连接
  2. 样条曲线参数化:用贝塞尔曲线、B样条曲线等来表示轨迹,控制点的坐标作为优化变量
  3. 函数参数化:用一个显式或隐式的函数 f ( x , y ) = 0 f(x,y)=0 f(x,y)=0来表示轨迹,函数的参数作为优化变量

这里我们选择路径点参数化,因为它简单直观,易于实现。假设我们用 n n n个路径点来表示轨迹,优化变量为:

x = [ x 1 , y 1 , x 2 , y 2 , … , x n , y n ] T \mathbf{x} = [x_1, y_1, x_2, y_2, \dots, x_n, y_n]^T x=[x1,y1,x2,y2,,xn,yn]T

其中 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) ( x n , y n ) (x_n, y_n) (xn,yn)分别为起点和终点的坐标,是固定的。

目标函数构建

接下来,我们要为每一项设计合适的目标函数。

长度项

轨迹的长度可以用路径点之间的欧氏距离之和来近似:

L ( x ) = ∑ i = 1 n − 1 ( x i + 1 − x i ) 2 + ( y i + 1 − y i ) 2 L(\mathbf{x}) = \sum_{i=1}^{n-1} \sqrt{(x_{i+1}-x_i)^2 + (y_{i+1}-y_i)^2} L(x)=i=1n1(xi+1xi)2+(yi+1yi)2

这个函数是非线性的,但我们可以用泰勒展开来线性化:

L ( x + Δ x ) ≈ L ( x ) + ∇ L ( x ) T Δ x = L ( x ) + ∑ i = 1 n − 1 x i + 1 − x i d i Δ x i + y i + 1 − y i d i Δ y i \begin{aligned} L(\mathbf{x}+\Delta \mathbf{x}) &\approx L(\mathbf{x}) + \nabla L(\mathbf{x})^T \Delta \mathbf{x} \\ &= L(\mathbf{x}) + \sum_{i=1}^{n-1} \frac{x_{i+1}-x_i}{d_i} \Delta x_i + \frac{y_{i+1}-y_i}{d_i} \Delta y_i \end{aligned} L(x+Δx)L(x)+L(x)TΔx=L(x)+i=1n1dixi+1xiΔxi+diyi+1yiΔyi

其中 d i = ( x i + 1 − x i ) 2 + ( y i + 1 − y i ) 2 d_i = \sqrt{(x_{i+1}-x_i)^2 + (y_{i+1}-y_i)^2} di=(xi+1xi)2+(yi+1yi)2 。这样长度项就变成了关于 Δ x \Delta \mathbf{x} Δx的线性函数。

平滑项

轨迹的平滑度可以用相邻路径点之间的角度变化来度量。假设我们希望轨迹尽可能接近一条直线,即相邻线段的夹角接近180°,我们可以定义平滑项为:

S ( x ) = ∑ i = 2 n − 1 ( 1 − cos ⁡ θ i ) S(\mathbf{x}) = \sum_{i=2}^{n-1} (1-\cos\theta_i) S(x)=i=2n1(1cosθi)

其中 θ i \theta_i θi是第 i − 1 i-1 i1条线段和第 i i i条线段的夹角:

cos ⁡ θ i = ( x i − x i − 1 ) ( x i + 1 − x i ) + ( y i − y i − 1 ) ( y i + 1 − y i ) d i − 1 d i \cos\theta_i = \frac{(x_i-x_{i-1})(x_{i+1}-x_i) + (y_i-y_{i-1})(y_{i+1}-y_i)}{d_{i-1}d_i} cosθi=di1di(xixi1)(xi+1xi)+(yiyi1)(yi+1yi)

这个函数也是非线性的,我们可以用泰勒展开来线性化:

S ( x + Δ x ) ≈ S ( x ) + ∇ S ( x ) T Δ x = S ( x ) + ∑ i = 2 n − 1 sin ⁡ θ i ( ∂ θ i ∂ x i − 1 Δ x i − 1 + ∂ θ i ∂ y i − 1 Δ y i − 1 + ∂ θ i ∂ x i Δ x i + ∂ θ i ∂ y i Δ y i + ∂ θ i ∂ x i + 1 Δ x i + 1 + ∂ θ i ∂ y i + 1 Δ y i + 1 ) \begin{aligned} S(\mathbf{x}+\Delta \mathbf{x}) &\approx S(\mathbf{x}) + \nabla S(\mathbf{x})^T \Delta \mathbf{x} \\ &= S(\mathbf{x}) + \sum_{i=2}^{n-1} \sin\theta_i (\frac{\partial \theta_i}{\partial x_{i-1}} \Delta x_{i-1} + \frac{\partial \theta_i}{\partial y_{i-1}} \Delta y_{i-1} + \frac{\partial \theta_i}{\partial x_i} \Delta x_i + \frac{\partial \theta_i}{\partial y_i} \Delta y_i + \frac{\partial \theta_i}{\partial x_{i+1}} \Delta x_{i+1} + \frac{\partial \theta_i}{\partial y_{i+1}} \Delta y_{i+1}) \end{aligned} S(x+Δx)S(x)+S(x)TΔx=S(x)+i=2n1sinθi(xi1θiΔxi1+yi1θiΔyi1+xiθiΔxi+yiθiΔyi+xi+1θiΔxi+1+yi+1θiΔyi+1)

其中 sin ⁡ θ i \sin\theta_i sinθi cos ⁡ θ i \cos\theta_i cosθi可以用上面的公式计算,而 ∂ θ i ∂ x i \frac{\partial \theta_i}{\partial x_i} xiθi等偏导数项可以通过求导得到,这里不再赘述。

碰撞项

碰撞项的设计是最复杂的,因为它需要考虑轨迹与障碍物之间的位置关系。一种简单的方法是把轨迹离散成一系列点,检查每个点是否在障碍物内部,如果是则给予惩罚。设障碍物 O j O_j Oj的签名距离函数为 d j ( x , y ) d_j(x,y) dj(x,y)(在障碍物外部为正,内部为负),我们可以定义碰撞项为:

C ( x ) = ∑ i = 1 n ∑ j = 1 m max ⁡ ( 0 , − d j ( x i , y i ) ) 2 C(\mathbf{x}) = \sum_{i=1}^n \sum_{j=1}^m \max(0, -d_j(x_i,y_i))^2 C(x)=i=1nj=1mmax(0,dj(xi,yi))2

这个函数是光滑的,梯度为:

∇ C ( x ) = ∑ i = 1 n ∑ j = 1 m 2 max ⁡ ( 0 , − d j ( x i , y i ) ) ∇ d j ( x i , y i ) \nabla C(\mathbf{x}) = \sum_{i=1}^n \sum_{j=1}^m 2\max(0, -d_j(x_i,y_i)) \nabla d_j(x_i,y_i) C(x)=i=1nj=1m2max(0,dj(xi,yi))dj(xi,yi)

其中 ∇ d j ( x , y ) = [ ∂ d j ∂ x , ∂ d j ∂ y ] T \nabla d_j(x,y) = [\frac{\partial d_j}{\partial x}, \frac{\partial d_j}{\partial y}]^T dj(x,y)=[xdj,ydj]T d j d_j dj ( x , y ) (x,y) (x,y)处的梯度。

优化求解

将三项合并,我们得到完整的目标函数:

min ⁡ Δ x ∇ L ( x ) T Δ x + λ s ∇ S ( x ) T Δ x + λ c ∇ C ( x ) T Δ x \min_{\mathbf{\Delta x}} \quad \nabla L(\mathbf{x})^T \Delta \mathbf{x} + \lambda_s \nabla S(\mathbf{x})^T \Delta \mathbf{x} + \lambda_c \nabla C(\mathbf{x})^T \Delta \mathbf{x} ΔxminL(x)TΔx+λsS(x)TΔx+λcC(x)TΔx

这是一个线性最小二乘问题,可以用最小二乘法求解:

Δ x = − ( ∇ L ( x ) + λ s ∇ S ( x ) + λ c ∇ C ( x ) ) \Delta \mathbf{x} = -(\nabla L(\mathbf{x}) + \lambda_s \nabla S(\mathbf{x}) + \lambda_c \nabla C(\mathbf{x})) Δx=(L(x)+λsS(x)+λcC(x))

然后我们用梯度下降法来更新优化变量:

x ← x + α Δ x \mathbf{x} \leftarrow \mathbf{x} + \alpha \Delta \mathbf{x} xx+αΔx

其中 α \alpha α是步长,可以用线搜索或信赖域方法来自适应调节。

我们重复这个过程,直到 Δ x \Delta \mathbf{x} Δx的norm小于一个阈值,或者达到最大迭代次数。

工程实践及代码实现

我们可以使用scipy.optimize库来实现这个优化问题。scipy.optimize提供了多种优化算法,这里我们使用BFGS算法,它是一种拟牛顿法,利用梯度信息来近似Hessian矩阵,具有超线性收敛速度。

以下是使用scipy.optimize库实现的代码:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize

# 障碍物签名距离函数
def signed_distance(x, y, obstacles):
    d = np.inf
    for obs in obstacles:
        d = min(d, np.sqrt((x-obs[0])**2 + (y-obs[1])**2) - obs[2])
    return d

# 计算路径长度
def path_length(xy):
    x, y = xy.reshape(2, -1)
    dx = x[1:] - x[:-1]
    dy = y[1:] - y[:-1]
    d = np.sqrt(dx**2 + dy**2)
    L = np.sum(d)
    return L

# 计算路径平滑度
def path_smoothness(xy):
    x, y = xy.reshape(2, -1)
    dx1 = x[1:-1] - x[:-2]
    dy1 = y[1:-1] - y[:-2] 
    dx2 = x[2:] - x[1:-1]
    dy2 = y[2:] - y[1:-1]
    
    cos_theta = (dx1*dx2 + dy1*dy2) / np.sqrt((dx1**2 + dy1**2) * (dx2**2 + dy2**2))
    cos_theta = np.clip(cos_theta, -1, 1)
    S = np.sum(1 - cos_theta)
    return S

# 计算路径与障碍物的碰撞代价
def path_collision(xy, obstacles):
    x, y = xy.reshape(2, -1)
    C = 0
    for i in range(len(x)):
        d = signed_distance(x[i], y[i], obstacles)
        if d < 0:
            C += d**2
    return C

# 总代价函数
def total_cost(xy, obstacles, lambda_smooth, lambda_collision):
    L = path_length(xy)
    S = path_smoothness(xy)
    C = path_collision(xy, obstacles)
    J = L + lambda_smooth * S + lambda_collision * C
    return J

# 优化主函数
def optimize_path(x0, y0, obstacles, lambda_smooth, lambda_collision):
    xy0 = np.concatenate([x0, y0])
    
    res = minimize(total_cost, xy0, args=(obstacles, lambda_smooth, lambda_collision), 
                   method='BFGS', options={'gtol': 1e-6, 'disp': True})
    
    xy = res.x
    x = xy[:len(x0)]
    y = xy[len(x0):]
    
    return x, y

# 测试
if __name__ == '__main__':
    # 障碍物列表,每个障碍物用(x,y,r)表示圆心和半径
    obstacles = [(-2, 0, 1), (0, 2, 1.5), (2, -1, 1)]
    
    # 起点和终点
    start = (-4, -2)
    end = (4, 2)
    
    # 初始路径(直线)
    x0 = np.linspace(start[0], end[0], 20)
    y0 = np.linspace(start[1], end[1], 20)
    
    # 优化参数
    lambda_smooth = 1.0
    lambda_collision = 10.0
    
    # 优化
    x, y = optimize_path(x0, y0, obstacles, lambda_smooth, lambda_collision)
    
    # 绘图
    fig, ax = plt.subplots(figsize=(8,6))
    
    for obs in obstacles:
        circle = plt.Circle((obs[0], obs[1]), obs[2], color='r', alpha=0.5)
        ax.add_patch(circle)
    
    ax.plot(x0, y0, 'g--', label='Initial path')    
    ax.plot(x, y, 'b-', label='Optimized path', zorder=10)
    ax.plot(start[0], start[1], 'go', label='Start')
    ax.plot(end[0], end[1], 'ro', label='End')
    
    ax.set_xlim(-5, 5)
    ax.set_ylim(-4, 4)
    ax.set_aspect('equal')
    ax.legend()
    
    plt.show()

运行这段代码,你应该可以看到优化后的轨迹(蓝色实线)成功避开了所有障碍物(红色圆圈),同时也比初始轨迹(绿色虚线)更加平滑。

使用scipy.optimize库可以大大简化优化问题的实现,同时也提供了多种优化算法供选择。这使得我们可以更加专注于问题的建模和求解,而不必过多关注优化算法的细节。

当然,对于更加复杂的优化问题,我们可能需要使用其他的优化库或算法,如IPOPT、NLopt、OSQP等。选择合适的优化工具需要根据问题的特点和需求来权衡。

问题描述

给定一个二维平面,平面上分布着一些圆形障碍物。我们的目标是规划一条从起点到终点的轨迹,使其满足以下要求:

  1. 轨迹不与任何障碍物相交(避免碰撞)
  2. 轨迹尽可能平滑,没有急转弯(保持平滑)
  3. 轨迹尽可能短,减少不必要的绕路(最小化长度)

数学建模

我们可以将这个问题形式化为一个优化问题:

min ⁡ x , y L ( x , y ) + λ s S ( x , y ) + λ c C ( x , y ) \min_{\mathbf{x},\mathbf{y}} \quad L(\mathbf{x},\mathbf{y}) + \lambda_s S(\mathbf{x},\mathbf{y}) + \lambda_c C(\mathbf{x},\mathbf{y}) x,yminL(x,y)+λsS(x,y)+λcC(x,y)

其中:

  • x , y \mathbf{x},\mathbf{y} x,y分别是轨迹上点的x坐标和y坐标向量
  • L ( x , y ) L(\mathbf{x},\mathbf{y}) L(x,y)表示轨迹的长度
  • S ( x , y ) S(\mathbf{x},\mathbf{y}) S(x,y)表示轨迹的平滑度,可以用轨迹的曲率或加速度等量度
  • C ( x , y ) C(\mathbf{x},\mathbf{y}) C(x,y)表示轨迹与障碍物之间的碰撞代价
  • λ s \lambda_s λs λ c \lambda_c λc是平滑项和碰撞项的权重系数

接下来,我们将分别定义这三个代价函数。

长度代价

轨迹的长度可以用相邻路径点之间的欧氏距离之和来表示:

L ( x , y ) = ∑ i = 1 n − 1 ( x i + 1 − x i ) 2 + ( y i + 1 − y i ) 2 L(\mathbf{x},\mathbf{y}) = \sum_{i=1}^{n-1} \sqrt{(x_{i+1}-x_i)^2 + (y_{i+1}-y_i)^2} L(x,y)=i=1n1(xi+1xi)2+(yi+1yi)2

其中 n n n是路径点的数量。

平滑代价

轨迹的平滑度可以用相邻线段之间的夹角来度量。假设我们希望轨迹尽可能接近一条直线,即相邻线段的夹角接近180°,我们可以定义平滑代价为:

S ( x , y ) = ∑ i = 2 n − 1 ( 1 − cos ⁡ θ i ) S(\mathbf{x},\mathbf{y}) = \sum_{i=2}^{n-1} (1-\cos\theta_i) S(x,y)=i=2n1(1cosθi)

其中 θ i \theta_i θi是第 i − 1 i-1 i1条线段和第 i i i条线段的夹角,可以用向量的点乘计算:

cos ⁡ θ i = ( x i − x i − 1 , y i − y i − 1 ) ⋅ ( x i + 1 − x i , y i + 1 − y i ) ( x i − x i − 1 ) 2 + ( y i − y i − 1 ) 2 ( x i + 1 − x i ) 2 + ( y i + 1 − y i ) 2 \cos\theta_i = \frac{(x_i-x_{i-1},y_i-y_{i-1})\cdot(x_{i+1}-x_i,y_{i+1}-y_i)}{\sqrt{(x_i-x_{i-1})^2+(y_i-y_{i-1})^2}\sqrt{(x_{i+1}-x_i)^2+(y_{i+1}-y_i)^2}} cosθi=(xixi1)2+(yiyi1)2 (xi+1xi)2+(yi+1yi)2 (xixi1,yiyi1)(xi+1xi,yi+1yi)

碰撞代价

为了计算轨迹与障碍物之间的碰撞代价,我们首先定义障碍物的签名距离函数(signed distance function, SDF)。对于第 j j j个障碍物,其SDF为:

d j ( x , y ) = ( x − x j ) 2 + ( y − y j ) 2 − r j d_j(x,y) = \sqrt{(x-x_j)^2+(y-y_j)^2} - r_j dj(x,y)=(xxj)2+(yyj)2 rj

其中 ( x j , y j ) (x_j,y_j) (xj,yj) r j r_j rj分别是障碍物的圆心坐标和半径。SDF的值在障碍物外部为正,内部为负,边界上为零。

有了SDF,我们可以将碰撞代价定义为:

C ( x , y ) = ∑ i = 1 n ∑ j = 1 m max ⁡ ( 0 , − d j ( x i , y i ) ) 2 C(\mathbf{x},\mathbf{y}) = \sum_{i=1}^n \sum_{j=1}^m \max(0, -d_j(x_i,y_i))^2 C(x,y)=i=1nj=1mmax(0,dj(xi,yi))2

其中 m m m是障碍物的数量。这个函数的含义是,对于每个路径点,我们计算其到所有障碍物的SDF值,如果SDF为负(即路径点在障碍物内部),就累加一个惩罚项,惩罚项的大小与SDF的平方成正比。

基于梯度的数值优化

将长度、平滑和碰撞三项代价相加,我们得到了完整的优化目标函数。接下来,我们需要选择一个优化算法来求解这个问题。这里我们使用scipy.optimize库中的BFGS算法,它是一种拟牛顿法,利用梯度信息来近似Hessian矩阵,具有超线性收敛速度。

为了使用BFGS算法,我们需要将优化变量从二维数组 ( x , y ) (\mathbf{x},\mathbf{y}) (x,y)转化为一维向量 z \mathbf{z} z:

z = [ x T , y T ] T \mathbf{z} = [\mathbf{x}^T, \mathbf{y}^T]^T z=[xT,yT]T

相应地,我们也需要将代价函数改写为关于 z \mathbf{z} z的函数。这一点在代码实现中也体现了出来。

让我来分析一下这段代码:

  1. 我们首先定义了几个辅助函数,用于计算路径的长度、平滑度和碰撞代价。这些函数对应了前面提到的三个代价项。注意,为了适应scipy.optimize的接口,我们将路径表示为一个一维向量xy,其中前半部分是x坐标,后半部分是y坐标。

  2. 在total_cost函数中,我们将三个代价项加权求和,得到总的代价函数。这个函数将作为优化的目标函数。

  3. optimize_path函数是优化的主函数。它先将起点和终点拼接成一个初始路径,然后调用scipy.optimize.minimize函数进行优化。我们选择了BFGS算法,并设置了一些优化选项,如终止条件的梯度阈值gtol和显示选项disp。

  4. 优化得到的结果存储在res.x中,我们将其拆分为x坐标和y坐标,作为函数的返回值。

  5. 在测试部分,我们设置了障碍物、起点和终点,以及一条初始路径(直线)。我们还设置了平滑代价和碰撞代价的权重系数。然后,我们调用optimize_path函数进行优化,并将结果绘制出来。

运行这段代码,你应该可以看到优化后的轨迹(蓝色实线)成功避开了所有障碍物(红色圆圈),同时也比初始轨迹(绿色虚线)更加平滑。

关于轨迹优化,以下是一些值得参考的文献:

  • Zucker M, Ratliff N, Dragan A D, et al. CHOMP: Covariant Hamiltonian optimization for motion planning[J]. The International Journal of Robotics Research, 2013, 32(9-10): 1164-1193.[6]
  • Schulman J, Duan Y, Ho J, et al. Motion planning with sequential convex optimization and convex collision checking[J]. The International Journal of Robotics Research, 2014, 33(9): 1251-1270.[7]
  • Oleynikova H, Taylor Z, Fehr M, et al. Continuous-time trajectory optimization for online UAV replanning[C]//2016 IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS). IEEE, 2016: 5332-5339.[8]
  • Mukadam M, Yan X, Boots B. Gaussian process motion planning[C]//2016 IEEE International Conference on Robotics and Automation (ICRA). IEEE, 2016: 9-15.[9]

这些文献提供了更多关于轨迹优化的理论和应用细节,感兴趣的读者可以进一步阅读。

常用优化器库

在实际应用中,我们通常不需要从头实现优化算法,而是可以使用现成的优化器库。以下是一些常用的优化器库:

  • scipy.optimize: SciPy的优化子模块,提供了多种优化算法,如BFGS,L-BFGS-B,SLSQP,trust-constr等。
  • IPOPT: 大规模非线性优化器,支持稀疏矩阵,适用于大型问题。
  • NLopt: 非线性优化库,支持多种局部和全局优化算法。
  • Ceres Solver: 来自Google的非线性最小二乘优化库,支持自动求导,常用于机器人,计算机视觉等领域。
  • GTSAM: 来自佐治亚理工的图优化库,常用于SLAM,状态估计等问题。
  • g2o: 来自图斯勒的通用图优化框架,常用于SLAM,BA等。
  • OSQP: 二次规划优化器,适用于凸二次规划问题。

这些优化器库通常采用了比我们上面示例更加高效和鲁棒的算法,并提供了方便的接口。感兴趣的读者可以进一步了解和学习。

小结

优化是机器人领域中一个重要而广泛的话题。从运动规划到控制,从定位到建图,从感知到决策,优化无处不在。掌握优化的基本原理和常用方法,对于从事机器人相关研究和开发的人员来说是必不可少的。

实际的无人机或是机器人轨迹要复杂的多,我们可能需要在更高维度的空间中规划轨迹,可能需要考虑运动学和动力学约束,可能需要处理更复杂的环境和任务需求。这就需要我们在问题建模,约束处理,求解算法等方面有更深入的理解和更巧妙的设计。优化也不是万能的,有时候我们还需要与其他方法(如采样,搜索,学习等)结合,才能更好地解决问题。

如有任何问题和建议,欢迎交流和指正。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL数据操作与查询- 聚合函数和分组查询

一、聚合函数 聚合函数主要用来进行数据 汇总 。 1、sum 返回选取的某列的总和。 语法&#xff1a; select sum(字段名) from 表名 where 条件表达式 2、max 返回选取的某列的最大值。 语法&#xff1a; select max(字段名) from 表名 where 条件表达式 3、min 返…

LoadBalance客户端负载均衡

1. 前言Ribbon Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。简单的说&#xff0c;Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时&#xff0…

VMware虚拟机linux无法使用ifconfig的解决方法

在有些linux系统中&#xff0c;输入ifconfig会报错&#xff0c;这是为什么呢&#xff1f; 如果出现 那是说明&#xff0c;你的linux内没有对应的命令。 具体可输入 ls /sbin 查看,发现其中确实没有ifconfig命令 这个解决很简单&#xff0c;在命令行输入 sudo apt-get inst…

DDPAI盯盯拍记录仪删除后的恢复方法(前后双路)

DDPAI盯盯拍行车记录仪的口碑相当不错&#xff0c;其产品一直以行车记录仪为主&#xff0c;曾经使用过比较早的产品&#xff0c;体验还不错。下面来看下这个DDPAI的视频恢复方法。 故障存储: 64G存储卡 /文件系统&#xff1a;FAT32 故障现象: 在发生事故后在记录仪上看到了…

OpenCV目标识别

一 图像轮廓 具有相同颜色或强度的连续点的曲线。 图像轮廓的作用 可以用于图像分析 物体的识别与检测 注意 为了检测的准确性&#xff0c;需要先对图像进行二值化或Canny操作。 画轮廓时会修改输入的图像。 轮廓查找的API findContours(img,mode,ApproximationMode,...)…

北航第六次数据结构与程序设计作业(查找与排序)选填题

一、 顺序查找的平均查找长度ASL&#xff08;1 2 …… n&#xff09;/ n (n 1&#xff09;/ 2 二、 这半查找法的平均查找次数和判定树的深度有关系。若查找一个不存在的元素&#xff0c;说明进行了深度次比较。 注意&#xff0c;判定树不是满二叉树&#xff0c;因此深…

创新案例 | 3个关键策略:乳制品品牌认养一头牛如何通过私域流量运营获取1400万会员

探索认养一头牛如何运用创新的私域流量运营策略&#xff0c;在竞争激烈的乳制品市场中脱颖而出&#xff0c;实现会员数量的飞速增长至1400万。本文深入分析了其数据驱动的广告投放、高效的会员运营体系和创新的用户互动机制&#xff0c;为企业提供提升用户粘性和品牌忠诚度的宝…

Postgre 调优工具pgBadger部署

一&#xff0c;简介&#xff1a; pgBadger&#xff08;日志分析器&#xff09;类似于oracle的AWR报告&#xff08;基于1小时&#xff0c;一天&#xff0c;一周&#xff0c;一月的报告&#xff09;&#xff0c;以图形化的方式帮助DBA更方便的找到隐含问题。 pgbadger是为了提高…

嵌入式数据库的一般架构

嵌入式数据库的架构与应用对象紧密相关&#xff0c;其架构是以内存、文件和网络等三种方式为主。 1.基于内存的数据库系统 基于内存的数据库系统中比较典型的产品是每个McObject公司的eXtremeDB嵌入式数据库&#xff0c;2013年3月推出5.0版&#xff0c;它采用内存数据结构&…

【Java】过滤器/拦截器

文章目录 两者区别request链路全过程 在实际开发中&#xff0c;过滤器和拦截器都是经常使用的技术&#xff0c;但一被提及到其区别时&#xff0c;整个人就愣住了&#xff0c;好像没有认真地对两者进行区别和总结&#xff0c;这两者之间也确实很容易混淆&#xff0c;因此结合了很…

python读取excel导入数据库

一、环境准备&#xff0c;安装包 pip install pandas openpyxl sqlalchemy二、数据准备 三、代码编写 from sqlalchemy import create_engine import pandas as pdclass GDPDataImporter:def __init__(self, db_type, dbapi, host, port, database, username, password):&quo…

【Git】基础操作

初识Git 版本控制的方式&#xff1a; 集中式版本控制工具&#xff1a;版本库是集中存放在中央服务器的&#xff0c;team里每个人work时从中央服务器下载代码&#xff0c;是必须联网才能工作&#xff0c;局域网或者互联网。个人修改之后要提交到中央版本库 例如&#xff1a;SVM和…

Python(二)---数据类型与变量、以及运算符

文章目录 前言1.Python程序的构成1.1.代码的组织和缩进1.2.使用\行连接符 2.对象和引用、标识符规则2.1.对象2.2.引用2.3.标识符规则 3.变量和简单赋值语句3.1.变量的声明和赋值3.2.删除变量和垃圾回收机制3.3.常量3.4.链式赋值3.5.系列解包赋值 4.最基本内置数据类型4.1.数字和…

rclone 上传资料到 onedrive 遇到限速问题解决

原因分析 可能和脚本参数设置有关系,我的参数是: rclone copy "F:\阿里云盘\6666\局域网" "od:影视" --ignore-existing -u -v -P --transfers20 --ignore-errors --buffer-size128M --check-first --checkers10 --drive-acknowledge-abuse差不多8G大小的…

C#——值类型和引用类型的区别详情

值类型和引用类型的区别 值类型 值类型&#xff1a; 常用的基本数据类型都是值类型&#xff1a;bool 、char、int、 double、 float、long 、 byte 、ulong、uint、枚举类型、 结构体类型等特点: 在赋值的过程当中&#xff0c;把值的本身赋值给另一个变量&#xff0c;再修改…

关于STM32上用HID HOST调鼠标数据的解析

一、前言 关于这章主要是基于我前面的那篇文章 链接: 关于怎么用Cubemx生成的USBHID设备实现读取一体的鼠标键盘设备&#xff08;改进版&#xff09; https://blog.csdn.net/qq_29187987/article/details/139535648?spm1001.2014.3001.5501 引用的文章的简介 引用的这篇文…

FFmpeg编解码的那些事(3)-视频硬解码的基础知识

目录 前言&#xff1a; 1.iso/os x平台 2.windows平台 3.linux平台 4.Tips&#xff1a; 5.结论&#xff1a; 前言&#xff1a; 视频硬解码的过程就是把视频提取成图片变显示出来&#xff0c;就是播放器播放视频的过程&#xff0c;就可以理解为解码的过程。 在不同的系统…

微信同声传译小程序插件使用教程

微信同声传译小程序插件 —— 机器翻译、智能语音 案例可搜索“一起学英语鸭”小程序查看&#xff0c; 实现效果如下图&#xff1a; 插件功能 语音转文字 语音合成 文本翻译 step 1&#xff1a;添加插件 在使用前&#xff0c;需要登录官网 设置 → 第三方服务 → 添加插件…

UniApp+Vue3使用Vant-微信小程序组件

第一步&#xff1a;打开创建好的UniappVue3的项目 第二步&#xff1a;下载Vant-Weapp npm i vant/weapp -S --production 第三步&#xff1a;修改目录名称 wxcomponents 必须是wxcomponents 第四步&#xff1a;将下载好的vant中的dist目录剪切到当前wxcomponents目录下 第五…

(超详细)基于动态顺序表实现简单的通讯录项目

前言&#xff1a; 我们在上一章节用c语言实现了线性表中的的动态顺序表&#xff0c;那么顺序表就只是顺序表吗&#xff1f;当然不是&#xff0c;使用顺序表结构可以实现很多项目&#xff0c;许多项目的数据结构都会用到顺序表&#xff0c;本章节我们就要使用顺序表实现一个简易…