自动驾驶---Motion Planning之轨迹Speed优化

1 背景

        在之前的几篇文章中,不管是通过构建SL图《自动驾驶---Motion Planning之Path Boundary》,ST图《自动驾驶---Motion Planning之Speed Boundary》,又或者是构建SLT图《自动驾驶---Motion Planning之构建SLT Driving Corridor》,最终我们都是为了得到boundary的信息。

        构造优化问题求解的前提:首先确定问题的代价函数,有初值,有边界(约束),接下来就可以进行求解了。 在之前的博客《自动驾驶---Motion Planning之轨迹Path优化》中讨论过横向的优化过程。本篇博客笔者继续阐述Apollo中纵向轨迹优化的问题。

2 轨迹纵向优化

        前面也提到过,在Apollo中,横纵向的优化求解是解耦开的,因此在横纵向分别需要求解一次,最终得到得到横纵向的轨迹信息(s,s',s'',l,l',l'')------这里的 l' 是 l 关于 s 的一阶导数, s' 是 s 关于 t 的一阶导数

        整体轨迹优化器的代码位于/apollo/modules/planning/tasks/optimizers。

2.1 构造优化问题  

        二次规划问题的形式:

min(\cfrac{1}{2}x^THx+g^Tx) \\ \\ s.t. a_i^Tx = b_i, i\in{E} \\ h_j^Tx\le{t_j}, j\in{I}

        其中H是由二阶导构成的Hessian矩阵,g^T是由梯度构成的Jacobi矩阵;然后包括m个等式约束集合和n个不等约束集合。

        Speed优化问题的自变量为:x=[s_0,s_1...s_{n-1},s'_0,s'_1,s'_2...s'_{n-1},s''_0,s''_1...s''_{n-1}]

        论文中主要考虑了纵向加速度、纵向Jerk、向心加速度、接近参考速度、终点的s,s'以及s''的限制,同样的在Apollo开源项目中增加了所有点的更接近参考线的惩罚函数。

论文中的代价函数如下所示:

 Apollo开源项目中的代价函数如下所示:

J=w_{s_{ref}}\sum_{i=0}^{n-1}(s_i-s_{iref})^2+w_k*\sum_{i=0}^{n-1} (s'_i)^2*Kappa(s_i)\\\\+w_{s'}*\sum_{i=0}^{n-1}(s'_i-s'_{iref})^2+w_{s''}*\sum_{i=0}^{n-1} (s''_i)^2\\\\+w_{s'''}*\sum_{i=0}^{n-2}((s''_{i+1}-s''_{i})/\Delta s)^2+w_{send}(s_{n-1}- s_{endl})\\\\+w_{s'end}(s'_{n-1}-s'_{ends})+w_{s''end}(s''_{n-1}-s''_{endl})

2.2 约束条件

        接下来考虑的是约束条件(不等式约束+等式约束)。不等式约束主要考虑包括自车的速度,加速度,jerk以及向心加速度,等式约束主要考虑的是采样点之间s,s',s''的连续性。

2.3 求解二次规划问题

将2.1.2小节得到的代价函数依据阶次整理得到:

J=w_{s_{ref}}\sum_{i=0}^{n-1}(s_i-s_{iref})^2\\\\+w_k*\sum_{i=0}^{n-1} (s'_i)^2*Kappa(s_i)+w_{s'}*\sum_{i=0}^{n-1}(s'_i-s'_{iref})^2\\\\+w_{s''}*\sum_{i=0}^{n-1} (s''_i)^2+w_{s'''}*\sum_{i=0}^{n-2}((s''_{i+1}-s''_{i})/\Delta s)^2\\\\+w_{send}(s_{n-1}- s_{endl})+w_{s'end}(s'_{n-1}-s'_{ends})+w_{s''end}(s''_{n-1}-s''_{endl})

先看speed optimizer的Process()函数:

Status PiecewiseJerkSpeedOptimizer::Process(const PathData& path_data,
                                            const TrajectoryPoint& init_point,
                                            SpeedData* const speed_data) {
  if (reference_line_info_->ReachedDestination()) {
    return Status::OK();
  }

  ACHECK(speed_data != nullptr);
  // 获取speed初值
  SpeedData reference_speed_data = *speed_data;

  if (path_data.discretized_path().empty()) {
    std::string msg("Empty path data");
    AERROR << msg;
    return Status(ErrorCode::PLANNING_ERROR, msg);
  }
  // 得到speed decider等模块获取的st图
  StGraphData& st_graph_data = *reference_line_info_->mutable_st_graph_data();

  const auto& veh_param =
      common::VehicleConfigHelper::GetConfig().vehicle_param();

  std::array<double, 3> init_s = {0.0, st_graph_data.init_point().v(),
                                  st_graph_data.init_point().a()};
  double delta_t = 0.1;
  double total_length = st_graph_data.path_length();
  double total_time = st_graph_data.total_time_by_conf();
  int num_of_knots = static_cast<int>(total_time / delta_t) + 1;

  PiecewiseJerkSpeedProblem piecewise_jerk_problem(num_of_knots, delta_t,
                                                   init_s);
  // 设置权重系数和部分约束
  const auto& config = config_.piecewise_jerk_speed_optimizer_config();
  piecewise_jerk_problem.set_weight_ddx(config.acc_weight());
  piecewise_jerk_problem.set_weight_dddx(config.jerk_weight());

  piecewise_jerk_problem.set_x_bounds(0.0, total_length);
  piecewise_jerk_problem.set_dx_bounds(
      0.0, std::fmax(FLAGS_planning_upper_speed_limit,
                     st_graph_data.init_point().v()));
  piecewise_jerk_problem.set_ddx_bounds(veh_param.max_deceleration(),
                                        veh_param.max_acceleration());
  piecewise_jerk_problem.set_dddx_bound(FLAGS_longitudinal_jerk_lower_bound,
                                        FLAGS_longitudinal_jerk_upper_bound);

  piecewise_jerk_problem.set_dx_ref(config.ref_v_weight(),
                                    reference_line_info_->GetCruiseSpeed());

  // Update STBoundary
  std::vector<std::pair<double, double>> s_bounds;
  for (int i = 0; i < num_of_knots; ++i) {
    double curr_t = i * delta_t;
    double s_lower_bound = 0.0;
    double s_upper_bound = total_length;
    for (const STBoundary* boundary : st_graph_data.st_boundaries()) {
      double s_lower = 0.0;
      double s_upper = 0.0;
      if (!boundary->GetUnblockSRange(curr_t, &s_upper, &s_lower)) {
        continue;
      }
      switch (boundary->boundary_type()) {
        case STBoundary::BoundaryType::STOP:
        case STBoundary::BoundaryType::YIELD:
          s_upper_bound = std::fmin(s_upper_bound, s_upper);
          break;
        case STBoundary::BoundaryType::FOLLOW:
          // TODO(Hongyi): unify follow buffer on decision side
          s_upper_bound = std::fmin(s_upper_bound, s_upper - 8.0);
          break;
        case STBoundary::BoundaryType::OVERTAKE:
          s_lower_bound = std::fmax(s_lower_bound, s_lower);
          break;
        default:
          break;
      }
    }
    if (s_lower_bound > s_upper_bound) {
      std::string msg("s_lower_bound larger than s_upper_bound on STGraph!");
      AERROR << msg;
      speed_data->clear();
      return Status(ErrorCode::PLANNING_ERROR, msg);
    }
    s_bounds.emplace_back(s_lower_bound, s_upper_bound);
  }
  piecewise_jerk_problem.set_x_bounds(std::move(s_bounds));

  // Update SpeedBoundary and ref_s
  std::vector<double> x_ref;
  std::vector<double> penalty_dx;
  std::vector<std::pair<double, double>> s_dot_bounds;
  const SpeedLimit& speed_limit = st_graph_data.speed_limit();
  for (int i = 0; i < num_of_knots; ++i) {
    double curr_t = i * delta_t;
    // get path_s
    SpeedPoint sp;
    reference_speed_data.EvaluateByTime(curr_t, &sp);
    const double path_s = sp.s();
    x_ref.emplace_back(path_s);
    // get curvature
    PathPoint path_point = path_data.GetPathPointWithPathS(path_s);
    penalty_dx.push_back(std::fabs(path_point.kappa()) *
                         config.kappa_penalty_weight());
    // get v_upper_bound
    const double v_lower_bound = 0.0;
    double v_upper_bound = FLAGS_planning_upper_speed_limit;
    v_upper_bound = speed_limit.GetSpeedLimitByS(path_s);
    s_dot_bounds.emplace_back(v_lower_bound, std::fmax(v_upper_bound, 0.0));
  }
  piecewise_jerk_problem.set_x_ref(config.ref_s_weight(), std::move(x_ref));
  piecewise_jerk_problem.set_penalty_dx(penalty_dx);
  piecewise_jerk_problem.set_dx_bounds(std::move(s_dot_bounds));

  // Solve the problem
  if (!piecewise_jerk_problem.Optimize()) {
    std::string msg("Piecewise jerk speed optimizer failed!");
    AERROR << msg;
    speed_data->clear();
    return Status(ErrorCode::PLANNING_ERROR, msg);
  }

  // Extract output
  const std::vector<double>& s = piecewise_jerk_problem.opt_x();
  const std::vector<double>& ds = piecewise_jerk_problem.opt_dx();
  const std::vector<double>& dds = piecewise_jerk_problem.opt_ddx();
  for (int i = 0; i < num_of_knots; ++i) {
    ADEBUG << "For t[" << i * delta_t << "], s = " << s[i] << ", v = " << ds[i]
           << ", a = " << dds[i];
  }
  speed_data->clear();
  speed_data->AppendSpeedPoint(s[0], 0.0, ds[0], dds[0], 0.0);
  for (int i = 1; i < num_of_knots; ++i) {
    // Avoid the very last points when already stopped
    if (ds[i] <= 0.0) {
      break;
    }
    speed_data->AppendSpeedPoint(s[i], delta_t * i, ds[i], dds[i],
                                 (dds[i] - dds[i - 1]) / delta_t);
  }
  SpeedProfileGenerator::FillEnoughSpeedPoints(speed_data);
  RecordDebugInfo(*speed_data, st_graph_data.mutable_st_graph_debug());
  return Status::OK();
}

其中的二次项系数构建P矩阵的过程:

void PiecewiseJerkSpeedProblem::CalculateKernel(std::vector<c_float>* P_data,
                                                std::vector<c_int>* P_indices,
                                                std::vector<c_int>* P_indptr) {
  const int n = static_cast<int>(num_of_knots_);
  const int kNumParam = 3 * n;
  const int kNumValue = 4 * n - 1;
  std::vector<std::vector<std::pair<c_int, c_float>>> columns;
  columns.resize(kNumParam);
  int value_index = 0;

  // x(i)^2 * w_x_ref
  for (int i = 0; i < n - 1; ++i) {
    columns[i].emplace_back(
        i, weight_x_ref_ / (scale_factor_[0] * scale_factor_[0]));
    ++value_index;
  }
  // x(n-1)^2 * (w_x_ref + w_end_x)
  columns[n - 1].emplace_back(n - 1, (weight_x_ref_ + weight_end_state_[0]) /
                                         (scale_factor_[0] * scale_factor_[0]));
  ++value_index;

  // x(i)'^2 * (w_dx_ref + penalty_dx)
  for (int i = 0; i < n - 1; ++i) {
    columns[n + i].emplace_back(n + i,
                                (weight_dx_ref_ + penalty_dx_[i]) /
                                    (scale_factor_[1] * scale_factor_[1]));
    ++value_index;
  }
  // x(n-1)'^2 * (w_dx_ref + penalty_dx + w_end_dx)
  columns[2 * n - 1].emplace_back(
      2 * n - 1, (weight_dx_ref_ + penalty_dx_[n - 1] + weight_end_state_[1]) /
                     (scale_factor_[1] * scale_factor_[1]));
  ++value_index;

  auto delta_s_square = delta_s_ * delta_s_;
  // x(i)''^2 * (w_ddx + 2 * w_dddx / delta_s^2)
  columns[2 * n].emplace_back(2 * n,
                              (weight_ddx_ + weight_dddx_ / delta_s_square) /
                                  (scale_factor_[2] * scale_factor_[2]));
  ++value_index;

  for (int i = 1; i < n - 1; ++i) {
    columns[2 * n + i].emplace_back(
        2 * n + i, (weight_ddx_ + 2.0 * weight_dddx_ / delta_s_square) /
                       (scale_factor_[2] * scale_factor_[2]));
    ++value_index;
  }

  columns[3 * n - 1].emplace_back(
      3 * n - 1,
      (weight_ddx_ + weight_dddx_ / delta_s_square + weight_end_state_[2]) /
          (scale_factor_[2] * scale_factor_[2]));
  ++value_index;

  // -2 * w_dddx / delta_s^2 * x(i)'' * x(i + 1)''
  for (int i = 0; i < n - 1; ++i) {
    columns[2 * n + i].emplace_back(2 * n + i + 1,
                                    -2.0 * weight_dddx_ / delta_s_square /
                                        (scale_factor_[2] * scale_factor_[2]));
    ++value_index;
  }

  CHECK_EQ(value_index, kNumValue);

  int ind_p = 0;
  for (int i = 0; i < kNumParam; ++i) {
    P_indptr->push_back(ind_p);
    for (const auto& row_data_pair : columns[i]) {
      P_data->push_back(row_data_pair.second * 2.0);
      P_indices->push_back(row_data_pair.first);
      ++ind_p;
    }
  }
  P_indptr->push_back(ind_p);
}

构建一次项矩阵的过程:

void PiecewiseJerkSpeedProblem::CalculateOffset(std::vector<c_float>* q) {
  CHECK_NOTNULL(q);
  const int n = static_cast<int>(num_of_knots_);
  const int kNumParam = 3 * n;
  q->resize(kNumParam);
  for (int i = 0; i < n; ++i) {
    if (has_x_ref_) {
      q->at(i) += -2.0 * weight_x_ref_ * x_ref_[i] / scale_factor_[0];
    }
    if (has_dx_ref_) {
      q->at(n + i) += -2.0 * weight_dx_ref_ * dx_ref_ / scale_factor_[1];
    }
  }

  if (has_end_state_ref_) {
    q->at(n - 1) +=
        -2.0 * weight_end_state_[0] * end_state_ref_[0] / scale_factor_[0];
    q->at(2 * n - 1) +=
        -2.0 * weight_end_state_[1] * end_state_ref_[1] / scale_factor_[1];
    q->at(3 * n - 1) +=
        -2.0 * weight_end_state_[2] * end_state_ref_[2] / scale_factor_[2];
  }
}

约束矩阵的构造同Path Optimizer:

void PiecewiseJerkProblem::CalculateAffineConstraint(
    std::vector<c_float>* A_data, std::vector<c_int>* A_indices,
    std::vector<c_int>* A_indptr, std::vector<c_float>* lower_bounds,
    std::vector<c_float>* upper_bounds) {
  // 3N params bounds on x, x', x''
  // 3(N-1) constraints on x, x', x''
  // 3 constraints on x_init_
  const int n = static_cast<int>(num_of_knots_);
  const int num_of_variables = 3 * n;
  const int num_of_constraints = num_of_variables + 3 * (n - 1) + 3;
  lower_bounds->resize(num_of_constraints);
  upper_bounds->resize(num_of_constraints);

  std::vector<std::vector<std::pair<c_int, c_float>>> variables(
      num_of_variables);

  int constraint_index = 0;
  // set x, x', x'' bounds
  for (int i = 0; i < num_of_variables; ++i) {
    if (i < n) {
      variables[i].emplace_back(constraint_index, 1.0);
      lower_bounds->at(constraint_index) =
          x_bounds_[i].first * scale_factor_[0];
      upper_bounds->at(constraint_index) =
          x_bounds_[i].second * scale_factor_[0];
    } else if (i < 2 * n) {
      variables[i].emplace_back(constraint_index, 1.0);

      lower_bounds->at(constraint_index) =
          dx_bounds_[i - n].first * scale_factor_[1];
      upper_bounds->at(constraint_index) =
          dx_bounds_[i - n].second * scale_factor_[1];
    } else {
      variables[i].emplace_back(constraint_index, 1.0);
      lower_bounds->at(constraint_index) =
          ddx_bounds_[i - 2 * n].first * scale_factor_[2];
      upper_bounds->at(constraint_index) =
          ddx_bounds_[i - 2 * n].second * scale_factor_[2];
    }
    ++constraint_index;
  }
  CHECK_EQ(constraint_index, num_of_variables);

  // x(i->i+1)''' = (x(i+1)'' - x(i)'') / delta_s
  for (int i = 0; i + 1 < n; ++i) {
    variables[2 * n + i].emplace_back(constraint_index, -1.0);
    variables[2 * n + i + 1].emplace_back(constraint_index, 1.0);
    lower_bounds->at(constraint_index) =
        dddx_bound_.first * delta_s_ * scale_factor_[2];
    upper_bounds->at(constraint_index) =
        dddx_bound_.second * delta_s_ * scale_factor_[2];
    ++constraint_index;
  }

  // x(i+1)' - x(i)' - 0.5 * delta_s * x(i)'' - 0.5 * delta_s * x(i+1)'' = 0
  for (int i = 0; i + 1 < n; ++i) {
    variables[n + i].emplace_back(constraint_index, -1.0 * scale_factor_[2]);
    variables[n + i + 1].emplace_back(constraint_index, 1.0 * scale_factor_[2]);
    variables[2 * n + i].emplace_back(constraint_index,
                                      -0.5 * delta_s_ * scale_factor_[1]);
    variables[2 * n + i + 1].emplace_back(constraint_index,
                                          -0.5 * delta_s_ * scale_factor_[1]);
    lower_bounds->at(constraint_index) = 0.0;
    upper_bounds->at(constraint_index) = 0.0;
    ++constraint_index;
  }

  // x(i+1) - x(i) - delta_s * x(i)'
  // - 1/3 * delta_s^2 * x(i)'' - 1/6 * delta_s^2 * x(i+1)''
  auto delta_s_sq_ = delta_s_ * delta_s_;
  for (int i = 0; i + 1 < n; ++i) {
    variables[i].emplace_back(constraint_index,
                              -1.0 * scale_factor_[1] * scale_factor_[2]);
    variables[i + 1].emplace_back(constraint_index,
                                  1.0 * scale_factor_[1] * scale_factor_[2]);
    variables[n + i].emplace_back(
        constraint_index, -delta_s_ * scale_factor_[0] * scale_factor_[2]);
    variables[2 * n + i].emplace_back(
        constraint_index,
        -delta_s_sq_ / 3.0 * scale_factor_[0] * scale_factor_[1]);
    variables[2 * n + i + 1].emplace_back(
        constraint_index,
        -delta_s_sq_ / 6.0 * scale_factor_[0] * scale_factor_[1]);

    lower_bounds->at(constraint_index) = 0.0;
    upper_bounds->at(constraint_index) = 0.0;
    ++constraint_index;
  }

  // constrain on x_init
  variables[0].emplace_back(constraint_index, 1.0);
  lower_bounds->at(constraint_index) = x_init_[0] * scale_factor_[0];
  upper_bounds->at(constraint_index) = x_init_[0] * scale_factor_[0];
  ++constraint_index;

  variables[n].emplace_back(constraint_index, 1.0);
  lower_bounds->at(constraint_index) = x_init_[1] * scale_factor_[1];
  upper_bounds->at(constraint_index) = x_init_[1] * scale_factor_[1];
  ++constraint_index;

  variables[2 * n].emplace_back(constraint_index, 1.0);
  lower_bounds->at(constraint_index) = x_init_[2] * scale_factor_[2];
  upper_bounds->at(constraint_index) = x_init_[2] * scale_factor_[2];
  ++constraint_index;

  CHECK_EQ(constraint_index, num_of_constraints);

  int ind_p = 0;
  for (int i = 0; i < num_of_variables; ++i) {
    A_indptr->push_back(ind_p);
    for (const auto& variable_nz : variables[i]) {
      // coefficient
      A_data->push_back(variable_nz.second);

      // constraint index
      A_indices->push_back(variable_nz.first);
      ++ind_p;
    }
  }
  // We indeed need this line because of
  // https://github.com/oxfordcontrol/osqp/blob/master/src/cs.c#L255
  A_indptr->push_back(ind_p);
}

最终根据不同地决策结果优化求解可得到下图中类似的曲线:

3 总结

        总体看下来,如果对path optimizer比较熟悉的读者,对于speed optimizer应该可以很快的看懂整个过程,因为两者有着比较高的相似度。同时读者也会对二次规划问题有了更深的认识,二次规划问题的“三部曲”(1)构造代价函数(2)构造约束(3)QP求解。

        自此,整个行车部分的planning模块讲解基本完结(还剩下STSC的轨迹优化问题),前面对Routing,Behavior Planning,Motion Planning等模块的内容都分别做了比较详细的阐述。后续会针对泊车部分、感知以及控制等其他模块开设博客,欢迎读者朋友们关注。

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

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

相关文章

【opencv】教程代码 —features2D(7)根据单应性矩阵估计相机坐标系下的物体位姿...

pose_from_homography.cpp从图像中找到棋盘角点并进行姿态估计 从图像中找到棋盘角点并显示 计算角点在世界坐标系中的位置 读取相机内参和畸变系数并校正图像中的角点 计算从3D点到2D点的单应性矩阵 通过奇异值分解(SVD)优化对旋转矩阵的估计 基于单应矩阵分解及其优化结果&am…

【数据结构】非线性结构---二叉树

1、树 1.1 树的相关概念 节点的度&#xff1a;一个节点含有的子树的个数称为该节点的度&#xff1b; 如上图&#xff1a;A的为6 叶节点或终端节点&#xff1a;度为0的节点称为叶节点&#xff1b; 如上图&#xff1a;B、C、H、I...等节点为叶节点 非终端节点或分支节点&#…

前端之CSS——网页的皮肤!!

目录 一、CSS简单介绍 二、css内容 2.1 css的编写方式 2.2 css选择器 2.3 样式属性 2.4 css包围盒 2.5 css中的display 2.6 css中的定位 2.7 css中的浮动与清除 2.7 弹性容器 2.8 字体图标 2.9 …

单片机简介(一)

51单片机 一台能够运行的计算机需要CPU做运算和控制&#xff0c;RAM做数据存储&#xff0c;ROM做程序存储&#xff0c;还有输入/输出设备&#xff08;串行口、并行输出口等&#xff09;&#xff0c;这些被分为若干块芯片&#xff0c;安装在主板&#xff08;印刷线路板&#xf…

探索组合总和问题(力扣39,40,216)

文章目录 题目前知LinkedList和ArryayList 组合总和I一、思路二、解题方法三、Code 组合总和II一、思路二、解题方法三、Code 组合总和III一、思路二、解题方法三、Code 总结 先看完上一期组合问题再看这一期更加容易理解喔&#x1f92f; 在算法和编程的世界中&#xff0c;组合…

文本直接生成2分钟视频,即将开源模型StreamingT2V

Picsart人工智能研究所、德克萨斯大学和SHI实验室的研究人员联合推出了StreamingT2V视频模型。通过文本就能直接生成2分钟、1分钟等不同时间&#xff0c;动作一致、连贯、没有卡顿的高质量视频。 虽然StreamingT2V在视频质量、多元化等还无法与Sora媲美&#xff0c;但在高速运…

6款Mac垃圾清理软件横评 Mac电脑清理软件哪个好 cleanmymac评测

鉴于苹果笔记本昂贵的硬盘价格&#xff0c;导致我们不得不定期清理自己的硬盘空间&#xff0c;释放给真正有用的各种程序等。 即便我们把程序安装到外置硬盘&#xff0c;但是程序运行时的缓存&#xff0c;仍然是在内置的硬盘中。 今天就让我们对比看看&#xff0c;目前市面上…

华为数通方向HCIP-DataCom H12-821题库(多选题:241-260)

第241题 [RTAospf100 [RTA-ospf-100]silent-intefaceGigabitEthernet 1/0/0上面是路由器RTA的部分配置,对于此部分的配置描述,正确的是: A、接口gigabitethemet 1/0/0的直连路由仍然可以发布出去 B、无法与该接口的直连邻居形成邻居关系 C、禁止接口gigabi tethemet 1/0/0发…

JavaEE初阶-线程2

文章目录 一、多线程安全问题1.1 线程安全问题的原因1.2 如何解决线程安全问题 二、加锁2.1 synchronized2.2 synchronized的几种使用方式2.3 synchronized的可重入性 三、死锁3.1 死锁的必要条件 一、多线程安全问题 代码示例如下&#xff1a; public class Demo20 {static …

直流电源电路(上)

直流电源电路&#xff08;上&#xff09; 综述&#xff1a;本篇文章讲述了直流电源电路的各种类型以及他们之间的优缺点对比。 一、总体关系框图 二、LDO 1&#xff09;LDO基础知识 2&#xff09;LDO电路框图 LDO电路由调整管、误差放大器、基准电压和采样电路组成。 3&…

docker容器之etcd

一、etcd介绍 1、etcd是什么 etcd是CoreOS团队于2013年6月发起的开源项目&#xff0c;它的目标是构建一个高可用的分布式键值(key-value)数据库。 2、etcd特点 简单的接口&#xff0c;通过标准的HTTP API进行调用&#xff0c;也可以使用官方提供的 etcdctl 操作存储的数据。…

【战略前沿】与中国达成生产协议后,飞行汽车即将起飞

【原文】Flying cars edge towards takeoff after Chinese production deal 【作者】Thomas Macaulay 斯洛伐克公司KleinVision签署了一项协议&#xff0c;将大规模生产AirCar。 一辆获得航空认证的飞行汽车向商业化又迈出了一大步。 空中汽车的创造者KleinVision今天宣布出售…

Anaconda/Python快速安装jieba 【win/mac】

一、直接上命令 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jieba 我实在PyCharm里面的终端输进去。 之后就很快速的看到成功的下图。 二、官网 官网下载的速度太慢了——这是官网地址https://pypi.org/project/jieba/#files 点进去之后点击下载&#xff0c…

黑马鸿蒙笔记 3

目录 11.ArkUI组件-Column和Row 12.ArkUI组件-循环控制 13.ArkUI组件-List 14.ArkUI组件-自定义组件 15.ArkUI组件-状态管理State装饰器 16.ArkUI组件-状态管理-任务统计案例 17.ArkUI组件-状态管理-PropLinkProvideConsume 11.ArkUI组件-Column和Row Colum和Row的交叉…

Docker容器与Serverless的融合:探索《2023腾讯云容器和函数计算技术实践精选集》中的云原生创新案例

Docker容器与Serverless的融合&#xff1a;探索《2023腾讯云容器和函数计算技术实践精选集》中的云原生创新案例 文章目录 Docker容器与Serverless的融合&#xff1a;探索《2023腾讯云容器和函数计算技术实践精选集》中的云原生创新案例一、引言二、《2023腾讯云容器和函数计算…

Tailscale:随时随地远程和使用服务器

文章目录 Tailscale是什么&#xff1f;Tailscale能做什么&#xff1f;1、传输文件2、远程开发3、代理 Tailscale怎么用&#xff1f;Windows下安装OpenSSH在线安装离线安装连接SSH服务器 Reference相关阅读 彩蛋&#xff1a;Pycharm远程连接服务器并运行代码 Tailscale是什么&am…

3d怎么两个模型连接圆润?---模大狮模型网

在3D建模中&#xff0c;如何实现两个3d模型的圆润连接是一个常见而又关键的问题。无论是为了美观的外观设计还是为了模型的功能性&#xff0c;圆润连接都能够增加模型的整体质感和流畅度。模大狮将介绍一些常见的方法和技巧&#xff0c;帮助您实现两个模型之间的圆润连接。 一、…

maven构建项目报错:Failure to find com.microsoft.sqlserver:sqljdbc4:jar:4.0 in

背景 今天在项目里面查询sqlserver的数据库的时候&#xff0c;本地maven中引入依赖&#xff1a; <dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>sqljdbc4</artifactId><version>4.0</version></dependenc…

若依框架学习——新建模块(图文)

文章目录 前言一、启动项目二、添加模块1、添加菜单2、创建表3、生成代码4、添加后端代码5、添加前端代码 前言 官网&#xff1a;添加链接描述 一、启动项目 项目地址&#xff1a;https://gitee.com/y_project/RuoYi-Vue 1、后端启动 使用idea工具打开项目&#xff0c;使用sq…

Red Hat配置本地yum源

Red Hat配置本地yum源 创建本地源文件夹 mkdir -p /mnt/cdrom挂载镜像文件至指定的目录 mount /dev/cdrom /mnt/cdrom备份本地源 cp -rf /etc/yum.repos.d /etc/yum.repos.d_$(date %Y%m%d_%H%M%S)删除默认原本地源 rm -rf /etc/yum.repos.d/*配置本地源&#xff0c;创建…