Apollo 9.0 速度动态规划决策算法 – path time heuristic optimizer
文章目录
- 1. 动态规划
- 2. 采样
- 3. 代价函数
- 3.1 障碍物代价
- 3.2 距离终点代价
- 3.3 速度代价
- 3.4 加速度代价
- 3.5 jerk代价
- 4. 回溯
这一章将来讲解速度决策算法,也就是SPEED_HEURISTIC_OPTIMIZER task里面的内容。Apollo 9.0使用动态规划算法进行速度决策,从类名来看,PathTimeHeuristicOptimizer 是路径时间启发式优化器,顾名思义,这里的算法在非凸的ST空间进行了纵向超让的决策,同时也为后续速度规划算法提供了一个启发式的粗解。
1. 动态规划
动态规划,英文:Dynamic Programming,简称DP。
关于动态规划,网上其实有很多讲解,都很详细,这里不再赘述。其实如果你刷过动态规划的算法题,相信你对dp问题一定有很深的理解。如果没有,也没有关系。动态规划一句话概括就是:就是把大问题拆成小问题,记住小问题的答案,避免重复计算,最后拼出大问题的答案。
PathTimeHeuristicOptimizer算法,实际上是将ST空间进行离散,在这离散的图上寻找“最短”路径的问题,所以可以使用动态规划分而治之的思想进行求解。注意这里的“最短”,并不是路程上的最短,而是指包含障碍物代价,加速度代价等一系列代价最小的一条路径。
动态规划Task的入口在PathTimeHeuristicOptimizer类里面,下面是整个算法的求解步骤:
- 变道和非变道加载不同的dp参数
- 构建ST栅格图
- ST栅格图搜素
path_time_heuristic_optimizer.cc
bool PathTimeHeuristicOptimizer::SearchPathTimeGraph(SpeedData* speed_data) const {// 变道和非变道加载不同的dp参数const auto& dp_st_speed_optimizer_config = reference_line_info_->IsChangeLanePath()? config_.lane_change_speed_config(): config_.default_speed_config();// 构建ST栅格图GriddedPathTimeGraph st_graph(reference_line_info_->st_graph_data(), dp_st_speed_optimizer_config,reference_line_info_->path_decision()->obstacles().Items(), init_point_);// ST栅格图搜素if (!st_graph.Search(speed_data).ok()) {AERROR << "failed to search graph with dynamic programming.";return false;}return true;
}
2. 采样
接下来就是在ST的维度进行采样:近端稠密一些,远端稀疏一点,这样可以节省计算量。同时变道和非变道场景,S方向采样的稠密点间隔和范围都不一样,非变道场景更稠密,范围更广一些,这样可以更好应对cut-in和cut-out场景,让纵向速度计算更精细。

上图中,最左下角的点,T肯定是0,而S是速度规划的起点:

S e n d {\color{Green} S_{end} } Send:速度规划的终点,是路径规划的path长度;
T e n d T_{end} Tend:很难计算,和道路的限速有关,也和动态障碍物有关。这里需要打破一个思维定式,在路径规划里面,必须要把比如60m路径全部规划完。在速度规划里面,不强求在 T e n d T_{end} Tend里面完全规划完 S e n d {\color{Green} S_{end} } Send。比如说路径规划出了60m,但是速度规划可以只规划20m的距离,因为现实中动态障碍物的轨迹是千奇百怪的。所以,在这里设置 T e n d = 8 s T_{end}=8s Tend=8s,在这8s的时间内,不强求把 S e n d {\color{Green} S_{end} } Send全部规划完。
关于具体采样的步骤和内容可以见下面的代码,可以说加上注释已经非常容易理解了。唯一需要补充的一点是:cost_table_其实是一个dimension_t_行,dimension_s_列的二维表,也就是T是行维度,S是列维度,将上图所示的ST离散图顺时针旋转90°,就可以刚好对应上。但是后面在计算代价和回溯的时候行列S和T又有些混淆,不过没有关系,对照图是很好理解的。
Status GriddedPathTimeGraph::InitCostTable() {// Time dimension is homogeneous while Spatial dimension has two resolutions,// dense and sparse with dense resolution coming first in the spatial horizon// Sanity check for numerical stabilityif (unit_t_ < kDoubleEpsilon) {const std::string msg = "unit_t is smaller than the kDoubleEpsilon.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}// Sanity check on s dimension settingif (dense_dimension_s_ < 1) {const std::string msg = "dense_dimension_s is at least 1.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}dimension_t_ = static_cast<uint32_t>(std::ceil(total_length_t_ / static_cast<double>(unit_t_))) + 1;double sparse_length_s = total_length_s_ -static_cast<double>(dense_dimension_s_ - 1) * dense_unit_s_;sparse_dimension_s_ = sparse_length_s > std::numeric_limits<double>::epsilon()? static_cast<uint32_t>(std::ceil(sparse_length_s / sparse_unit_s_)): 0;dense_dimension_s_ = sparse_length_s > std::numeric_limits<double>::epsilon()? dense_dimension_s_: static_cast<uint32_t>(std::ceil(total_length_s_ / dense_unit_s_)) + 1;dimension_s_ = dense_dimension_s_ + sparse_dimension_s_;PrintCurves debug;// Sanity Checkif (dimension_t_ < 1 || dimension_s_ < 1) {const std::string msg = "Dp st cost table size incorrect.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}// 定义dp代价表,注意这个表:常规ST图顺时针旋转90°,一共t行,每行s列 , total_cost_默认无穷大cost_table_ = std::vector<std::vector<StGraphPoint>>(dimension_t_, std::vector<StGraphPoint>(dimension_s_, StGraphPoint()));double curr_t = 0.0;for (uint32_t i = 0; i < cost_table_.size(); ++i, curr_t += unit_t_) { // Tauto& cost_table_i = cost_table_[i];double curr_s = 0.0;// 稠密for (uint32_t j = 0; j < dense_dimension_s_; ++j, curr_s += dense_unit_s_) { // Scost_table_i[j].Init(i, j, STPoint(curr_s, curr_t));debug.AddPoint("dp_node_points", curr_t, curr_s);}// 稀疏curr_s = static_cast<double>(dense_dimension_s_ - 1) * dense_unit_s_ + sparse_unit_s_;for (uint32_t j = dense_dimension_s_; j < cost_table_i.size(); ++j, curr_s += sparse_unit_s_) {cost_table_i[j].Init(i, j, STPoint(curr_s, curr_t));debug.AddPoint("dp_node_points", curr_t, curr_s);}}// 获取第一行的sconst auto& cost_table_0 = cost_table_[0];spatial_distance_by_index_ = std::vector<double>(cost_table_0.size(), 0.0);for (uint32_t i = 0; i < cost_table_0.size(); ++i) {spatial_distance_by_index_[i] = cost_table_0[i].point().s();}return Status::OK();
}
3. 代价函数
接下来就是在离散表上,进行“最短”路径搜索,也就是使用动态规划算法计算速度规划起点到每个点的最小代价,然后回溯(反推)获取最优路径。
注意:速度动态规划可以进行剪枝操作,以减少计算量,依据车辆动力学max_acceleration_和max_deceleration_找到下一列可能到达的范围[next_lowest_row, next_highest_row],只需要计算当前点到下一列该范围内的点的代价。

一个点的total_cost由两部分构成:
- 点代价:
a. 障碍物代价:obstacle_cost
b. 距离终点代价:spatial_potential_cost
c. 前一个点的:total_cost - 边代价:
a. 速度代价:Speedcost
b. 加速度代价:AccelCost
c. 加加速度代价:JerkCost
3.1 障碍物代价
Apollo 9.0动态规划中障碍物代价计算做了简化,只需要计算该点到所有t时刻障碍物的代价,并进行累加。
如下图所示的 a 62 a_{62} a62点,该时刻有两个障碍物,通过公式计算障碍物代价即可:
- 不在范围内:0
- 在障碍物st边界框里:∞
- 其他: c o s t + = c o n f i g _ . o b s t a c l e _ w e i g h t ( ) ∗ c o n f i g _ . d e f a u l t _ o b s t a c l e _ c o s t ( ) ∗ s _ d i f f ∗ s _ d i f f cost += config\_.obstacle\_weight() * config\_.default\_obstacle\_cost() * s\_diff * s\_diff cost+=config_.obstacle_weight()∗config_.default_obstacle_cost()∗s_diff∗s_diff
这里需要注意的是,跟车和超车需要与前后车保持的安全距离大小不一样,超车情况下,需要离后车距离更大。

double DpStCost::GetObstacleCost(const StGraphPoint& st_graph_point) {const double s = st_graph_point.point().s();const double t = st_graph_point.point().t();double cost = 0.0;if (FLAGS_use_st_drivable_boundary) { // false // TODO(Jiancheng): move to configsstatic constexpr double boundary_resolution = 0.1;int index = static_cast<int>(t / boundary_resolution);const double lower_bound = st_drivable_boundary_.st_boundary(index).s_lower();const double upper_bound = st_drivable_boundary_.st_boundary(index).s_upper();if (s > upper_bound || s < lower_bound) {return kInf;}}// 遍历每个障碍物,计算t时刻障碍物st边界框的上界和下界,根据无人车的位置(t,s)与边界框是否重合,计算障碍物代价for (const auto* obstacle : obstacles_) {// Not applying obstacle approaching cost to virtual obstacle like created stop fencesif (obstacle->IsVirtual()) {continue;}// Stop obstacles are assumed to have a safety margin when mapping them out,// so repelling force in dp st is not needed as it is designed to have adc// stop right at the stop distance we design in prior mapping processif (obstacle->LongitudinalDecision().has_stop()) {continue;}auto boundary = obstacle->path_st_boundary();if (boundary.min_s() > FLAGS_speed_lon_decision_horizon) { // 纵向决策的最远距离 200continue;}if (t < boundary.min_t() || t > boundary.max_t()) {continue;}if (boundary.IsPointInBoundary(st_graph_point.point())) {return kInf;}// 情况4:需要减速避让或加速超过的障碍物。计算障碍物在t时刻的上界和下界位置,即上下界的累积距离sdouble s_upper = 0.0;double s_lower = 0.0;// 为了避免其他节点(t,s)再一次计算t时刻的障碍物上下界,利用缓存加速计算。GetBoundarySRange函数可以用来计算t时刻障碍物上界和下界累积距离s,并缓存int boundary_index = boundary_map_[boundary.id()];if (boundary_cost_[boundary_index][st_graph_point.index_t()].first < 0.0) { // 还没有计算过boundary.GetBoundarySRange(t, &s_upper, &s_lower);boundary_cost_[boundary_index][st_graph_point.index_t()] = std::make_pair(s_upper, s_lower);} else {s_upper = boundary_cost_[boundary_index][st_graph_point.index_t()].first; // 之前计算过,直接取值s_lower = boundary_cost_[boundary_index][st_graph_point.index_t()].second;}// t时刻, 无人车在障碍物后方if (s < s_lower) {const double follow_distance_s = config_.safe_distance(); // 0.2AINFO << "follow_distance_s : " << follow_distance_s;if (s + follow_distance_s < s_lower) { // 如果障碍物和无人车在t时刻距离大于安全距离,距离比较远,cost=0continue;} else { // 否则距离小于安全距离,计算cost。obstacle_weight:1.0,default_obstacle_cost:1000,auto s_diff = follow_distance_s - s_lower + s;cost += config_.obstacle_weight() * config_.default_obstacle_cost() * s_diff * s_diff;}// t时刻, 无人车在障碍物前方} else if (s > s_upper) {const double overtake_distance_s = StGapEstimator::EstimateSafeOvertakingGap(); // 20if (s > s_upper + overtake_distance_s) { // or calculated from velocitycontinue; // 如果障碍物和无人车在t时刻距离大于安全距离,距离比较远,cost=0} else { // 否则距离小于安全距离,计算cost。obstacle_weight:1.0,default_obstacle_cost:1000,auto s_diff = overtake_distance_s + s_upper - s;cost += config_.obstacle_weight() * config_.default_obstacle_cost() * s_diff * s_diff;}}}return cost * unit_t_; // unit_t_ = 1
}
3.2 距离终点代价
距离终点代价其实就是距离路径规划终点的一个惩罚,该点距离终点越近,代价越小;距离终点越远,代价越大。
目的很简单,就是希望速度规划能够就可能将路径规划的s全部路径都覆盖。

double DpStCost::GetSpatialPotentialCost(const StGraphPoint& point) {return (total_s_ - point.point().s()) * config_.spatial_potential_penalty(); // 距离终点惩罚 100 or 100000(变道)
}
3.3 速度代价
速度代价很简单,主要考虑两方面:
- 不要超速,尽量贴近限速;
- 尽量贴近巡航速度,也就是驾驶员设定的速度。
double DpStCost::GetSpeedCost(const STPoint& first, const STPoint& second,const double speed_limit,const double cruise_speed) const {double cost = 0.0;const double speed = (second.s() - first.s()) / unit_t_; //计算时间段[t-1,t]内的平均速度if (speed < 0) { // 倒车?速度代价无穷大return kInf;}const double max_adc_stop_speed = common::VehicleConfigHelper::Instance()->GetConfig().vehicle_param().max_abs_speed_when_stopped();AINFO << "max_adc_stop_speed : "<< max_adc_stop_speed; // 如果速度接近停车,并且在禁停区内 max_stop_speed = 0.2 if (speed < max_adc_stop_speed && InKeepClearRange(second.s())) {// first.s in range 在KeepClear区域低速惩罚 10 * * 1000cost += config_.keep_clear_low_speed_penalty() * unit_t_ * config_.default_speed_cost();}// 计算当前速度和限速的差值比,大于0,超速;小于0,未超速double det_speed = (speed - speed_limit) / speed_limit;if (det_speed > 0) {cost += config_.exceed_speed_penalty() * config_.default_speed_cost() * (det_speed * det_speed) * unit_t_;} else if (det_speed < 0) {cost += config_.low_speed_penalty() * config_.default_speed_cost() * -det_speed * unit_t_;}if (config_.enable_dp_reference_speed()) {double diff_speed = speed - cruise_speed;cost += config_.reference_speed_penalty() * config_.default_speed_cost() * fabs(diff_speed) * unit_t_; // 10 * 1000 * Δv * }return cost;
}
3.4 加速度代价
要计算边的加速度代价,首先需要计算边的加速度。
![[图片]](https://i-blog.csdnimg.cn/direct/1311cbd77cc2474290721279c4c710b3.png#pic_center)
以起点为例,计算起点到坐标点的加速度代价,直接用差分来代替速度、加速度、Jerk:
![[图片]](https://i-blog.csdnimg.cn/direct/6dbaaaf65bc24c94889c7b492adbbd0d.png#pic_center)
double DpStCost::GetAccelCostByThreePoints(const STPoint& first,const STPoint& second,const STPoint& third) {// 利用3个节点的累积距离s1,s2,s3来计算加速度 double accel = (first.s() + third.s() - 2 * second.s()) / (unit_t_ * unit_t_);return GetAccelCost(accel);
}double DpStCost::GetAccelCostByTwoPoints(const double pre_speed,const STPoint& pre_point,const STPoint& curr_point) {// 利用2个节点的累积距离s1,s2来计算加速度double current_speed = (curr_point.s() - pre_point.s()) / unit_t_;double accel = (current_speed - pre_speed) / unit_t_;return GetAccelCost(accel);
}
边的加速度代价:
( w c o s t _ d e c ∗ s ¨ ) 2 1 + e ( a − m a x _ d e c ) + ( w c o s t _ a c c ∗ s ¨ ) 2 1 + e − ( a − m a x _ a c c ) \frac{\left ( w_{cost\_dec} *\ddot{s} \right ) ^{2} }{1+e^{(a- max\_dec)}} +\frac{\left ( w_{cost\_acc} *\ddot{s} \right ) ^{2} }{1+e^{-(a- max\_acc)}} 1+e(a−max_dec)(wcost_dec∗s¨)2+1+e−(a−max_acc)(wcost_acc∗s¨)2
double DpStCost::GetAccelCost(const double accel) {double cost = 0.0;// 将给定的加速度 accel 标准化并加上一个偏移量 kShift 来计算得到。这样做可以确保不同的 accel 值映射到 accel_cost_ 中不同的索引位置。static constexpr double kEpsilon = 0.1; // 表示对加速度的精度要求static constexpr size_t kShift = 100;const size_t accel_key = static_cast<size_t>(accel / kEpsilon + 0.5 + kShift);DCHECK_LT(accel_key, accel_cost_.size());if (accel_key >= accel_cost_.size()) {return kInf;}if (accel_cost_.at(accel_key) < 0.0) {const double accel_sq = accel * accel;double max_acc = config_.max_acceleration(); // 3.0 m/s^2double max_dec = config_.max_deceleration(); // -4.0 m/s^2double accel_penalty = config_.accel_penalty(); // 1.0double decel_penalty = config_.decel_penalty(); // 1.0if (accel > 0.0) { // 计算加速度正情况下costcost = accel_penalty * accel_sq;} else { // 计算加速度负情况下costcost = decel_penalty * accel_sq;}// 总体costcost += accel_sq * decel_penalty * decel_penalty / (1 + std::exp(1.0 * (accel - max_dec))) +accel_sq * accel_penalty * accel_penalty / (1 + std::exp(-1.0 * (accel - max_acc)));accel_cost_.at(accel_key) = cost;} else {cost = accel_cost_.at(accel_key); // 该加速度之前计算过,直接索引}return cost * unit_t_;
}
3.5 jerk代价
加加速度代价的计算更简单:
![[图片]](https://i-blog.csdnimg.cn/direct/560eba9a2632484886613b72363b3748.png#pic_center)
double DpStCost::JerkCost(const double jerk) {double cost = 0.0;static constexpr double kEpsilon = 0.1;static constexpr size_t kShift = 200;const size_t jerk_key = static_cast<size_t>(jerk / kEpsilon + 0.5 + kShift); // 原理同acc的计算if (jerk_key >= jerk_cost_.size()) {return kInf;}if (jerk_cost_.at(jerk_key) < 0.0) {double jerk_sq = jerk * jerk;if (jerk > 0) {cost = config_.positive_jerk_coeff() * jerk_sq * unit_t_;} else {cost = config_.negative_jerk_coeff() * jerk_sq * unit_t_;}jerk_cost_.at(jerk_key) = cost;} else {cost = jerk_cost_.at(jerk_key);}// TODO(All): normalize to unit_t_return cost;
}
4. 回溯
回溯:也就是根据最小代价,从后往前倒推,得到最优的路径。这是动态规划算法中常用的一种手段。
因为 T e n d T_{end} Tend是人为给定的一个值,所以可能在 T e n d T_{end} Tend之前,速度规划就已经到达路径规划的终点。所以需要遍历最上边和最右边区域,找到cost最小的作为速度规划的终点。
![[图片]](https://i-blog.csdnimg.cn/direct/ef82db769e1a488d878153bce33110bd.png#pic_center)
由于我们不要求一定要规划完所有路径,所以最优的速度规划可以是紫色那种,也有可能是黄色那种,还有可能是橙色那种。
Status GriddedPathTimeGraph::RetrieveSpeedProfile(SpeedData* const speed_data) {// Step 1 : 计算规划终点double min_cost = std::numeric_limits<double>::infinity();const StGraphPoint* best_end_point = nullptr;PrintPoints debug("dp_node_edge");for (const auto& points_vec : cost_table_) {for (const auto& pt : points_vec) {debug.AddPoint(pt.point().t(), pt.point().s());}}// for debug plot// debug.PrintToLog();// 寻找最上一行(s=S)和最右一列(t=T)中最小的cost对应的节点,作为规划终点for (const StGraphPoint& cur_point : cost_table_.back()) { // 最右一列(t=T)if (!std::isinf(cur_point.total_cost()) &&cur_point.total_cost() < min_cost) {best_end_point = &cur_point;min_cost = cur_point.total_cost();}}for (const auto& row : cost_table_) { // 每一个t,也就是每一列const StGraphPoint& cur_point = row.back(); // 每一列的最上一行(s=S)if (!std::isinf(cur_point.total_cost()) &&cur_point.total_cost() < min_cost) {best_end_point = &cur_point;min_cost = cur_point.total_cost();}}if (best_end_point == nullptr) {const std::string msg = "Fail to find the best feasible trajectory.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}// Step 2 : 从规划终点开始回溯,找到最小cost的规划路径std::vector<SpeedPoint> speed_profile;const StGraphPoint* cur_point = best_end_point;PrintPoints debug_res("dp_result");while (cur_point != nullptr) {ADEBUG << "Time: " << cur_point->point().t();ADEBUG << "S: " << cur_point->point().s();ADEBUG << "V: " << cur_point->GetOptimalSpeed();SpeedPoint speed_point;debug_res.AddPoint(cur_point->point().t(), cur_point->point().s());speed_point.set_s(cur_point->point().s());speed_point.set_t(cur_point->point().t());speed_profile.push_back(speed_point);cur_point = cur_point->pre_point();}// for debug plot// debug_res.PrintToLog();std::reverse(speed_profile.begin(), speed_profile.end()); // 颠倒容器中元素的顺序static constexpr double kEpsilon = std::numeric_limits<double>::epsilon(); // 返回的是 double 类型能够表示的最小正数if (speed_profile.front().t() > kEpsilon ||speed_profile.front().s() > kEpsilon) {const std::string msg = "Fail to retrieve speed profile.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}AINFO << "front: " << speed_profile.front().t() << " " << speed_profile.front().s();// Step 3 : 计算速度 vfor (size_t i = 0; i + 1 < speed_profile.size(); ++i) {const double v = (speed_profile[i + 1].s() - speed_profile[i].s()) /(speed_profile[i + 1].t() - speed_profile[i].t() + 1e-3); // 斜率 speed_profile[i].set_v(v);AINFO << "v: " << v;}*speed_data = SpeedData(speed_profile);return Status::OK();
}
相关文章:
Apollo 9.0 速度动态规划决策算法 – path time heuristic optimizer
文章目录 1. 动态规划2. 采样3. 代价函数3.1 障碍物代价3.2 距离终点代价3.3 速度代价3.4 加速度代价3.5 jerk代价 4. 回溯 这一章将来讲解速度决策算法,也就是SPEED_HEURISTIC_OPTIMIZER task里面的内容。Apollo 9.0使用动态规划算法进行速度决策,从类名…...
Apache Iceberg 与 Apache Hudi:数据湖领域的双雄对决
在数据存储和处理不断发展的领域中,数据湖仓的概念已经崭露头角,成为了一种变革性的力量。数据湖仓结合了数据仓库和数据湖的最佳元素,提供了一个统一的平台,支持数据科学、商业智能、人工智能/机器学习以及临时报告等多种关键功能…...
【LeetCode Hot100 普通数组】最大子数组和、合并区间、旋转数组、除自身以外数组的乘积、缺失的第一个正整数
普通数组 1. 最大子数组和(Maximum Subarray)解题思路动态规划的优化解法(Kadane 算法)核心思想 代码解析 2. 合并区间(Merge Intervals)解题思路代码实现 3. 旋转数组(Rotate Array)…...
共享存储-一步一步部署ceph分布式文件系统
一、Ceph 简介 Ceph 是一个开源的分布式文件系统。因为它还支持块存储、对象存储,所以很自 然的被用做云计算框架 openstack 或 cloudstack 整个存储后端。当然也可以单独作 为存储,例如部署一套集群作为对象存储、SAN 存储、NAS 存储等。 二、ceph 支…...
19.Python实战:实现对博客文章的点赞系统
Flask博客点赞系统 一个基于Flask的简单博客系统,具有文章展示和点赞功能。系统使用MySQL存储数据,支持文章展示、点赞/取消点赞等功能。 功能特点 文章列表展示文章详情查看(模态框展示)点赞/取消点赞功能(每个IP只…...
【stm32】定时器输出PWM波形(hal库)
一. PWM基本原理 PWM是一种通过调节信号的占空比(Duty Cycle)来控制输出平均电压的技术。占空比是指高电平时间与整个周期时间的比值。例如: - 占空比为50%时,输出平均电压为电源电压的一半。 - 占空比为100%时,输出始…...
当Ollama遇上划词翻译:我的Windows本地AI服务搭建日记
🚀 实现Windows本地大模型翻译服务 - 基于OllamaFlask的划词翻译实践 🛠️ 步骤概要1️⃣ python 环境准备2️⃣ Ollama 安装3️⃣ 一个 Flask 服务4️⃣ Windows 服务化封装5️⃣ 测试本地接口6️⃣ 配置划词翻译自定义翻译源7️⃣ 效果展示8️⃣ debug…...
Linux上Elasticsearch 集群部署指南
Es 集群部署 Es 集群部署 Es 集群部署 准备好三台服务器。示例使用:110.0.5.141/142/143 1、es用户和用户组创建,使用root账号 groupadd esuseradd -g es es2、将es安装包和ik分词器上传到:/home/es/目录下(任意目录都行&#…...
字节Trae使用感想(后端)
前言 昨天分享了字节哥的Trae从0到1创作模式构建一个vue前端项目,今天又来试试她的后端项目能力。不是我舔,不得不说确实不错。可惜现在曾经没有好好学习,进不了字节。既然进不了字节,那我就用字节哥的产品吧。 后面有惊喜…...
国产编辑器EverEdit - 二进制模式下观察Window/Linux/MacOs换行符差异
1 换行符格式 1.1 应用场景 稍微了解计算机历史的人都知道, 计算机3大操作系统: Windows、Linux/Unix、MacOS,这3大系统对文本换行的定义各不相同,且互不相让,导致在文件的兼容性方面存在一些问题,比如它们…...
文心一言4月起全面免费,6月底开源新模型:AI竞争进入新阶段?
名人说:莫听穿林打叶声,何妨吟啸且徐行。—— 苏轼 Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、文心一言免费化的背后:AI成本与应用的双重驱动1️⃣成本下降,推动文心一言普及2…...
解锁机器学习算法 | 线性回归:机器学习的基石
在机器学习的众多算法中,线性回归宛如一块基石,看似质朴无华,却稳稳支撑起诸多复杂模型的架构。它是我们初涉机器学习领域时便会邂逅的算法之一,其原理与应用广泛渗透于各个领域。无论是预测房价走势、剖析股票市场波动࿰…...
如何使用Three.js制作3D月球与星空效果
目录 1. 基本设置2. 创建星空效果3. 创建月球模型4. 添加中文3D文字5. 光照与相机配置6. 动画与控制7. 响应式布局8. 结语 在本文中,我们将一起学习如何利用Three.js实现一个3D月球与星空的效果,并添加一些有趣的元素,比如中文3D文字和互动功…...
SQL语句语法
SQL数据库的结构为 库database 表table 段segment 行row 列column 或field SQL 语句主要分为以下几类: 数据定义语言(DDL):用于定义数据库对象,如数据库、表、视图、索引等。数据操作语言(DML)&…...
github上文件过大无法推送问题
GitHub 对文件大小有限制,超过 100 MB 的文件无法直接推送到仓库中。 解决思路: 使用 Git Large File Storage (Git LFS) 来管理大文件不上传对应的大文件 使用Git LFS: 1. 安装 Git LFS 首先,你需要安装 Git LFS。可以按照以…...
微信小程序的请求函数封装(ts版本,uniapp开发)
主要封装函数代码: interface HttpOptions {url: string;method?: string;headers?: { [key: string]: string };data?: any; }class Http {private timeout: number;private baseUrl: string;public constructor() { this.timeout 60 * 1000;this.baseUrl ht…...
Visual Studio Code支持WSL,直接修改linux/ubuntu中的文件
步骤1 开始通过 WSL 使用 VS Code | Microsoft Learn 点击远程开发扩展包。 步骤2 Remote Development - Visual Studio Marketplace 点击install, 允许打开Visual Studio Code。 步骤3 共有4项,一齐安装。 步骤4 在WSL Linux(Ubuntu)中…...
openAI最新o1模型 推理能力上表现出色 准确性方面提升 API如何接入?
OpenAI o1模型在回答问题前会进行深入思考,并生成一条内部推理链,使其在尝试解决问题时可以识别并纠正错误,将复杂的步骤分解为更简单的部分,并在当前方法无效时尝试不同的途径。据悉,o1不仅数学水平与美国奥林匹克竞赛…...
GC 基础入门
什么是GC(Garbage Collection)? 内存管理方式通常分为两种: 手动内存管理(Manual Memory Management)自动内存管理(Garbage Collection, GC) 手动内存管理 手动内存管理是指开发…...
Go语言协程Goroutine高级用法(一)
什么协程 在Go语言中,协程就是一种轻量的线程,是并发编程的单元,由Go来管理,所以在GO层面的协程会更加的轻量、高效、开销更小,并且更容易实现并发编程。 轻量级线程 Go语言中协程(线程)与传…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
