当前位置: 首页 > news >正文

【Apollo学习笔记】——规划模块TASK之PATH_ASSESSMENT_DECIDER

文章目录

  • 前言
  • PATH_ASSESSMENT_DECIDER功能简介
  • PATH_ASSESSMENT_DECIDER相关信息
  • PATH_ASSESSMENT_DECIDER总体流程
    • 1. 去除无效路径
    • 2. 分析并加入重要信息给speed决策
      • SetPathInfo
        • SetPathPointType
    • 3. 排序选择最优的路径
    • 4. 更新必要的信息

前言

在Apollo星火计划学习笔记——Apollo路径规划算法原理与实践与【Apollo学习笔记】——Planning模块讲到……Stage::Process的PlanOnReferenceLine函数会依次调用task_list中的TASK,本文将会继续以LaneFollow为例依次介绍其中的TASK部分究竟做了哪些工作。由于个人能力所限,文章可能有纰漏的地方,还请批评斧正。

modules/planning/conf/scenario/lane_follow_config.pb.txt配置文件中,我们可以看到LaneFollow所需要执行的所有task。

stage_config: {stage_type: LANE_FOLLOW_DEFAULT_STAGEenabled: truetask_type: LANE_CHANGE_DECIDERtask_type: PATH_REUSE_DECIDERtask_type: PATH_LANE_BORROW_DECIDERtask_type: PATH_BOUNDS_DECIDERtask_type: PIECEWISE_JERK_PATH_OPTIMIZERtask_type: PATH_ASSESSMENT_DECIDERtask_type: PATH_DECIDERtask_type: RULE_BASED_STOP_DECIDERtask_type: SPEED_BOUNDS_PRIORI_DECIDERtask_type: SPEED_HEURISTIC_OPTIMIZERtask_type: SPEED_DECIDERtask_type: SPEED_BOUNDS_FINAL_DECIDERtask_type: PIECEWISE_JERK_SPEED_OPTIMIZER# task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZERtask_type: RSS_DECIDER

本文将继续介绍LaneFollow的第6个TASK——PATH_ASSESSMENT_DECIDER

PATH_ASSESSMENT_DECIDER功能简介

路径评价,选出最优路径

在这里插入图片描述
PATH_BOUNDS_DECIDER可以知道会产生以下几种类型的路径边界:

  • fallback
  • fallback+lanechange
  • fallback+pullover
  • fallback+regular

依据不同的边界会产生不同的路径,接着便需要筛选出一条最优的路径。依据以下规则,进行评价:

  • 路径是否和障碍物碰撞
  • 路径长度
  • 路径是否会停在对向车道
  • 路径离自车远近
  • 哪个路径更早回自车道

PATH_ASSESSMENT_DECIDER相关信息

  • 输入:Status PathAssessmentDecider::Process(Frame* const frame, ReferenceLineInfo* const reference_line_info)
    输入Frame,reference_line_info。
  • 输出:路径排序之后,选择第一个路径。结果保存在reference_line_info中

PATH_ASSESSMENT_DECIDER总体流程

在这里插入图片描述

在这里插入图片描述
首先来看看PathAssessmentDecider::Process流程部分:

Process部分主要完成路径重复使用判断、去除无效路径、分析路径并加入重要信息提供给速度决策部分、排序选择最优的路径以及最后的更新必要的信息。

1. 去除无效路径

  // 1. Remove invalid path.// 1. 删掉无效路径.std::vector<PathData> valid_path_data;for (const auto& curr_path_data : candidate_path_data) {// RecordDebugInfo(curr_path_data, curr_path_data.path_label(),//                 reference_line_info);if (curr_path_data.path_label().find("fallback") != std::string::npos) {// fallback的无效路径是偏离参考线以及道路的路径if (IsValidFallbackPath(*reference_line_info, curr_path_data)) {valid_path_data.push_back(curr_path_data);}} else {// regular的无效路径是偏离参考线、道路,碰撞,停在相邻的逆向车道的路径。if (IsValidRegularPath(*reference_line_info, curr_path_data)) {valid_path_data.push_back(curr_path_data);}}}const auto& end_time1 = std::chrono::system_clock::now();std::chrono::duration<double> diff = end_time1 - end_time0;ADEBUG << "Time for path validity checking: " << diff.count() * 1000<< " msec.";

其中fallback的无效路径是偏离参考线以及道路的路径。regular的无效路径是偏离参考线、道路,碰撞,停在相邻的逆向车道的路径。

2. 分析并加入重要信息给speed决策

  // 2. Analyze and add important info for speed decider to use// 2. 分析并加入重要信息给speed决策size_t cnt = 0;const Obstacle* blocking_obstacle_on_selflane = nullptr;for (size_t i = 0; i != valid_path_data.size(); ++i) {auto& curr_path_data = valid_path_data[i];if (curr_path_data.path_label().find("fallback") != std::string::npos) {// remove empty path_data.if (!curr_path_data.Empty()) {if (cnt != i) {valid_path_data[cnt] = curr_path_data;}++cnt;}continue;}// 添加相关信息SetPathInfo(*reference_line_info, &curr_path_data);// Trim all the lane-borrowing paths so that it ends with an in-lane// position.// 修剪所有路径(只要不是pull-over),使其能够以in-lane结尾if (curr_path_data.path_label().find("pullover") == std::string::npos) {TrimTailingOutLanePoints(&curr_path_data);}// find blocking_obstacle_on_selflane, to be used for lane selection later// 找到self_lane上的阻塞障碍物, 为下一步选择车道做准备if (curr_path_data.path_label().find("self") != std::string::npos) {const auto blocking_obstacle_id = curr_path_data.blocking_obstacle_id();blocking_obstacle_on_selflane =reference_line_info->path_decision()->Find(blocking_obstacle_id);}// remove empty path_data.if (!curr_path_data.Empty()) {if (cnt != i) {valid_path_data[cnt] = curr_path_data;}++cnt;}// RecordDebugInfo(curr_path_data, curr_path_data.path_label(),//                 reference_line_info);ADEBUG << "For " << curr_path_data.path_label() << ", "<< "path length = " << curr_path_data.frenet_frame_path().size();}valid_path_data.resize(cnt);// If there is no valid path_data, exit.// 如果没有有效路径,退出if (valid_path_data.empty()) {const std::string msg = "Neither regular nor fallback path is valid.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}ADEBUG << "There are " << valid_path_data.size() << " valid path data.";const auto& end_time2 = std::chrono::system_clock::now();diff = end_time2 - end_time1;ADEBUG << "Time for path info labeling: " << diff.count() * 1000 << " msec.";

SetPathInfo

void PathAssessmentDecider::SetPathInfo(const ReferenceLineInfo& reference_line_info, PathData* const path_data) {// Go through every path_point, and label its://  - in-lane/out-of-lane info (side-pass or lane-change)//  - distance to the closest obstacle.std::vector<PathPointDecision> path_decision;// 0. Initialize the path info.InitPathPointDecision(*path_data, &path_decision);// 1. Label caution types, differently for side-pass or lane-change.if (reference_line_info.IsChangeLanePath()) {// If lane-change, then label the lane-changing part to// be out-on-forward lane.SetPathPointType(reference_line_info, *path_data, true, &path_decision);} else {// Otherwise, only do the label for borrow-lane generated paths.// 仅仅对借道进行标记if (path_data->path_label().find("fallback") == std::string::npos &&path_data->path_label().find("self") == std::string::npos) {SetPathPointType(reference_line_info, *path_data, false, &path_decision);}}// SetObstacleDistance(reference_line_info, *path_data, &path_decision);path_data->SetPathPointDecisionGuide(std::move(path_decision));
}

这一部分中函数SetPathInfo完成以下功能:初始化path info;根据是lane-change还是side-pass,设置路径点的类型;添加相关决策引导信息等信息。

SetPathPointType

在设置路径点的类型时涉及到SetPathPointType这一个函数。
流程如下图所示:

在这里插入图片描述

void PathAssessmentDecider::SetPathPointType(const ReferenceLineInfo& reference_line_info, const PathData& path_data,const bool is_lane_change_path,std::vector<PathPointDecision>* const path_point_decision) {// Sanity checks.CHECK_NOTNULL(path_point_decision);// Go through every path_point, and add in-lane/out-of-lane info.const auto& discrete_path = path_data.discretized_path();const auto& vehicle_config =common::VehicleConfigHelper::Instance()->GetConfig();const double ego_length = vehicle_config.vehicle_param().length();const double ego_width = vehicle_config.vehicle_param().width();const double ego_back_to_center =vehicle_config.vehicle_param().back_edge_to_center();// 车辆几何中心点与车辆后轴的偏移距离const double ego_center_shift_distance =ego_length / 2.0 - ego_back_to_center;bool is_prev_point_out_lane = false;for (size_t i = 0; i < discrete_path.size(); ++i) {// 以车辆后轴中心获取boundingboxconst auto& rear_center_path_point = discrete_path[i];const double ego_theta = rear_center_path_point.theta();Box2d ego_box({rear_center_path_point.x(), rear_center_path_point.y()},ego_theta, ego_length, ego_width);Vec2d shift_vec{ego_center_shift_distance * std::cos(ego_theta),ego_center_shift_distance * std::sin(ego_theta)};// 将boundingbox从车辆后轴中心变换到几何中心(apollo在这里采用的是AABB的boundingbox,其中有些细节等之后再细看)ego_box.Shift(shift_vec);// 得到SL坐标系下的boundarySLBoundary ego_sl_boundary;if (!reference_line_info.reference_line().GetSLBoundary(ego_box,&ego_sl_boundary)) {ADEBUG << "Unable to get SL-boundary of ego-vehicle.";continue;}double lane_left_width = 0.0;double lane_right_width = 0.0;double middle_s =(ego_sl_boundary.start_s() + ego_sl_boundary.end_s()) / 2.0;if (reference_line_info.reference_line().GetLaneWidth(middle_s, &lane_left_width, &lane_right_width)) {// Rough sl boundary estimate using single point lane widthdouble back_to_inlane_extra_buffer = 0.2;double in_and_out_lane_hysteresis_buffer =is_prev_point_out_lane ? back_to_inlane_extra_buffer : 0.0;// Check for lane-change and lane-borrow differently:if (is_lane_change_path) {// For lane-change path, only transitioning part is labeled as// out-of-lane.if (ego_sl_boundary.start_l() > lane_left_width ||ego_sl_boundary.end_l() < -lane_right_width) {// This means that ADC hasn't started lane-change yet.// 再次重申,变道时是以要变道的目标车道作为参考线std::get<1>((*path_point_decision)[i]) =PathData::PathPointType::IN_LANE;} else if (ego_sl_boundary.start_l() >-lane_right_width + back_to_inlane_extra_buffer &&ego_sl_boundary.end_l() <lane_left_width - back_to_inlane_extra_buffer) {// This means that ADC has safely completed lane-change with margin.std::get<1>((*path_point_decision)[i]) =PathData::PathPointType::IN_LANE;} else {// ADC is right across two lanes.std::get<1>((*path_point_decision)[i]) =PathData::PathPointType::OUT_ON_FORWARD_LANE;}} else {// For lane-borrow path, as long as ADC is not on the lane of// reference-line, it is out on other lanes. It might even be// on reverse lane!if (ego_sl_boundary.end_l() >lane_left_width + in_and_out_lane_hysteresis_buffer ||ego_sl_boundary.start_l() <-lane_right_width - in_and_out_lane_hysteresis_buffer) {if (path_data.path_label().find("reverse") != std::string::npos) {std::get<1>((*path_point_decision)[i]) =PathData::PathPointType::OUT_ON_REVERSE_LANE;} else if (path_data.path_label().find("forward") !=std::string::npos) {std::get<1>((*path_point_decision)[i]) =PathData::PathPointType::OUT_ON_FORWARD_LANE;} else {std::get<1>((*path_point_decision)[i]) =PathData::PathPointType::UNKNOWN;}if (!is_prev_point_out_lane) {if (ego_sl_boundary.end_l() >lane_left_width + back_to_inlane_extra_buffer ||ego_sl_boundary.start_l() <-lane_right_width - back_to_inlane_extra_buffer) {is_prev_point_out_lane = true;}}} else {// The path point is within the reference_line's lane.std::get<1>((*path_point_decision)[i]) =PathData::PathPointType::IN_LANE;if (is_prev_point_out_lane) {is_prev_point_out_lane = false;}}}} else {AERROR << "reference line not ready when setting path point guide";return;}}
}

PS:关于ego_box.Shift(shift_vec);这一步是如何实现的,可以关注这篇博客:Apollo EM中path_assesment_task相关细节的讨论

3. 排序选择最优的路径

  ... ...// 3. Pick the optimal path.// 3. 选择最优路径,两两比较路径。排序是根据 ComparePathData 函数的返回值进行的。std::sort(valid_path_data.begin(), valid_path_data.end(),std::bind(ComparePathData, std::placeholders::_1,std::placeholders::_2, blocking_obstacle_on_selflane));ADEBUG << "Using '" << valid_path_data.front().path_label()<< "' path out of " << valid_path_data.size() << " path(s)";if (valid_path_data.front().path_label().find("fallback") !=std::string::npos) {FLAGS_static_obstacle_nudge_l_buffer = 0.8;}*(reference_line_info->mutable_path_data()) = valid_path_data.front();reference_line_info->SetBlockingObstacle(valid_path_data.front().blocking_obstacle_id());const auto& end_time3 = std::chrono::system_clock::now();diff = end_time3 - end_time2;ADEBUG << "Time for optimal path selection: " << diff.count() * 1000<< " msec.";... ...

主要排序规则在ComparePathData函数中。

bool ComparePathData(const PathData& lhs, const PathData& rhs,const Obstacle* blocking_obstacle) {ADEBUG << "Comparing " << lhs.path_label() << " and " << rhs.path_label();// Empty path_data is never the larger one.// 空的路径永远排在后面if (lhs.Empty()) {ADEBUG << "LHS is empty.";return false;}if (rhs.Empty()) {ADEBUG << "RHS is empty.";return true;}// Regular path goes before fallback path.regular > fallback// 如果lhs是regular路径而rhs是fallback路径,那么lhs会被认为更好,返回true。bool lhs_is_regular = lhs.path_label().find("regular") != std::string::npos;bool rhs_is_regular = rhs.path_label().find("regular") != std::string::npos;if (lhs_is_regular != rhs_is_regular) {return lhs_is_regular;}// Select longer path.// If roughly same length, then select self-lane path.bool lhs_on_selflane = lhs.path_label().find("self") != std::string::npos;bool rhs_on_selflane = rhs.path_label().find("self") != std::string::npos;static constexpr double kSelfPathLengthComparisonTolerance = 15.0;static constexpr double kNeighborPathLengthComparisonTolerance = 25.0;double lhs_path_length = lhs.frenet_frame_path().back().s();double rhs_path_length = rhs.frenet_frame_path().back().s();// 至少其中有一条是self_laneif (lhs_on_selflane || rhs_on_selflane) {// 如果两条路径的长度相差超过了kSelfPathLengthComparisonTolerance(在这里是15.0),那么较长的路径将被认为更好。if (std::fabs(lhs_path_length - rhs_path_length) >kSelfPathLengthComparisonTolerance) {return lhs_path_length > rhs_path_length;} else {// 如果两条路径的长度相差在这个容差范围内,并且其中一条路径在"self"车道上,那么"self"车道上的路径将被认为更好。return lhs_on_selflane;}} else {// 没有一条是self_laneif (std::fabs(lhs_path_length - rhs_path_length) >kNeighborPathLengthComparisonTolerance) {return lhs_path_length > rhs_path_length;}}// If roughly same length, and must borrow neighbor lane,// then prefer to borrow forward lane rather than reverse lane.int lhs_on_reverse =ContainsOutOnReverseLane(lhs.path_point_decision_guide());int rhs_on_reverse =ContainsOutOnReverseLane(rhs.path_point_decision_guide());// TODO(jiacheng): make this a flag.// 如果需要借用逆向车道的次数差超过了6次,那么次数较少的路径将被认为更好(相当于选择逆向距离短的)。if (std::abs(lhs_on_reverse - rhs_on_reverse) > 6) {return lhs_on_reverse < rhs_on_reverse;}// For two lane-borrow directions, based on ADC's position,// select the more convenient one.if ((lhs.path_label().find("left") != std::string::npos &&rhs.path_label().find("right") != std::string::npos) ||(lhs.path_label().find("right") != std::string::npos &&rhs.path_label().find("left") != std::string::npos)) {if (blocking_obstacle) {// select left/right path based on blocking_obstacle's position// 有障碍物,选择合适的方向,左或右借道const double obstacle_l =(blocking_obstacle->PerceptionSLBoundary().start_l() +blocking_obstacle->PerceptionSLBoundary().end_l()) /2;ADEBUG << "obstacle[" << blocking_obstacle->Id() << "] l[" << obstacle_l<< "]";// 如果阻挡障碍物的横向位置大于0(在障碍物的右侧),那么含有"right"的路径将被认为更好;否则,含有"left"的路径将被认为更好。return (obstacle_l > 0.0? (lhs.path_label().find("right") != std::string::npos): (lhs.path_label().find("left") != std::string::npos));} else {// select left/right path based on ADC's position// 无障碍物,根据adc的位置选择借道方向double adc_l = lhs.frenet_frame_path().front().l();if (adc_l < -1.0) {return lhs.path_label().find("right") != std::string::npos;} else if (adc_l > 1.0) {return lhs.path_label().find("left") != std::string::npos;}}}// If same length, both neighbor lane are forward,// then select the one that returns to in-lane earlier.// 路径长度相同,相邻车道都是前向的,选择较早返回自车道的路径static constexpr double kBackToSelfLaneComparisonTolerance = 20.0;int lhs_back_idx = GetBackToInLaneIndex(lhs.path_point_decision_guide());int rhs_back_idx = GetBackToInLaneIndex(rhs.path_point_decision_guide());double lhs_back_s = lhs.frenet_frame_path()[lhs_back_idx].s();double rhs_back_s = rhs.frenet_frame_path()[rhs_back_idx].s();if (std::fabs(lhs_back_s - rhs_back_s) > kBackToSelfLaneComparisonTolerance) {return lhs_back_idx < rhs_back_idx;}// If same length, both forward, back to inlane at same time,// select the left one to side-pass.// 如果路径长度相同,前向借道,返回自车道时间相同,选择从左侧借道的路径bool lhs_on_leftlane = lhs.path_label().find("left") != std::string::npos;bool rhs_on_leftlane = rhs.path_label().find("left") != std::string::npos;if (lhs_on_leftlane != rhs_on_leftlane) {ADEBUG << "Select " << (lhs_on_leftlane ? "left" : "right") << " lane over "<< (!lhs_on_leftlane ? "left" : "right") << " lane.";return lhs_on_leftlane;}// Otherwise, they are the same path, lhs is not < rhs.// 最后如果两条路径相同,则 lhs is not < rhlreturn false;
}

路径排序规则如下:(道路评估的优劣通过排序获得)

1.空的路径永远排在后面
2.regular > fallback
3.如果self-lane有一个存在,选择那个。如果都存在,选择较长的.如果长度接近,选择self-lane如果self-lane都不存在,选择较长的路径
4.如果路径长度接近,且都要借道:

  • (1) 都要借逆向车道,选择距离短的
  • (2) 针对具有两个借道方向的情况:
    • 有障碍物,选择合适的方向,左或右借道
    • 无障碍物,根据adc的位置选择借道方向
  • (3) 路径长度相同,相邻车道都是前向的,选择较早返回自车道的路径
  • (4) 如果路径长度相同,前向借道,返回自车道时间相同,选择从左侧借道的路径

5.最后如果两条路径相同,则 lhs is not < rhl

排序之后:选择最优路径,即第一个路径

4. 更新必要的信息

  // 4. Update necessary info for lane-borrow decider's future uses.// Update front static obstacle's info.auto* mutable_path_decider_status = injector_->planning_context()->mutable_planning_status()->mutable_path_decider();if (reference_line_info->GetBlockingObstacle() != nullptr) {int front_static_obstacle_cycle_counter =mutable_path_decider_status->front_static_obstacle_cycle_counter();mutable_path_decider_status->set_front_static_obstacle_cycle_counter(std::max(front_static_obstacle_cycle_counter, 0));mutable_path_decider_status->set_front_static_obstacle_cycle_counter(std::min(front_static_obstacle_cycle_counter + 1, 10));mutable_path_decider_status->set_front_static_obstacle_id(reference_line_info->GetBlockingObstacle()->Id());} else {int front_static_obstacle_cycle_counter =mutable_path_decider_status->front_static_obstacle_cycle_counter();mutable_path_decider_status->set_front_static_obstacle_cycle_counter(std::min(front_static_obstacle_cycle_counter, 0));mutable_path_decider_status->set_front_static_obstacle_cycle_counter(std::max(front_static_obstacle_cycle_counter - 1, -10));}// Update self-lane usage info.if (reference_line_info->path_data().path_label().find("self") !=std::string::npos) {// && std::get<1>(reference_line_info->path_data()//                 .path_point_decision_guide()//                 .front()) == PathData::PathPointType::IN_LANE)int able_to_use_self_lane_counter =mutable_path_decider_status->able_to_use_self_lane_counter();if (able_to_use_self_lane_counter < 0) {able_to_use_self_lane_counter = 0;}mutable_path_decider_status->set_able_to_use_self_lane_counter(std::min(able_to_use_self_lane_counter + 1, 10));} else {mutable_path_decider_status->set_able_to_use_self_lane_counter(0);}// Update side-pass direction.if (mutable_path_decider_status->is_in_path_lane_borrow_scenario()) {bool left_borrow = false;bool right_borrow = false;const auto& path_decider_status =injector_->planning_context()->planning_status().path_decider();for (const auto& lane_borrow_direction :path_decider_status.decided_side_pass_direction()) {if (lane_borrow_direction == PathDeciderStatus::LEFT_BORROW &&reference_line_info->path_data().path_label().find("left") !=std::string::npos) {left_borrow = true;}if (lane_borrow_direction == PathDeciderStatus::RIGHT_BORROW &&reference_line_info->path_data().path_label().find("right") !=std::string::npos) {right_borrow = true;}}mutable_path_decider_status->clear_decided_side_pass_direction();if (right_borrow) {mutable_path_decider_status->add_decided_side_pass_direction(PathDeciderStatus::RIGHT_BORROW);}if (left_borrow) {mutable_path_decider_status->add_decided_side_pass_direction(PathDeciderStatus::LEFT_BORROW);}}const auto& end_time4 = std::chrono::system_clock::now();diff = end_time4 - end_time3;ADEBUG << "Time for FSM state updating: " << diff.count() * 1000 << " msec.";// Plot the path in simulator for debug purpose.RecordDebugInfo(reference_line_info->path_data(), "Planning PathData",reference_line_info);return Status::OK();

更新必要信息:

1.更新adc前方静态障碍物的信息
2.更新自车道使用信息
3.更新旁车道的方向根据:PathDeciderStatusRIGHT_BORROWLEFT_BORROW判断是从左侧借道,还是从右侧借道

相关文章:

【Apollo学习笔记】——规划模块TASK之PATH_ASSESSMENT_DECIDER

文章目录 前言PATH_ASSESSMENT_DECIDER功能简介PATH_ASSESSMENT_DECIDER相关信息PATH_ASSESSMENT_DECIDER总体流程1. 去除无效路径2. 分析并加入重要信息给speed决策SetPathInfoSetPathPointType 3. 排序选择最优的路径4. 更新必要的信息 前言 在Apollo星火计划学习笔记——Ap…...

09 mysql fetchSize 所影响的服务器和客户端的交互

前言 这是一个 之前使用 spark 的时候 记一次 spark 读取大数据表 OOM OutOfMemoryError: GC overhead limit exceeded 因为一个 OOM 的问题, 当时使用了 fetchSize 的参数 应用服务 hang 住, 导致服务 503 Service Unavailable 在这个问题的地方, 出现了一个查询 32w 的数据…...

DevEco Studio 配置

首先,打开deveco studio 进入首页 …我知道你们想说什么,我也想说 汉化配置 没办法,老样子,先汉化吧,毕竟母语看起来舒服 首先,点击软件左下角的configure,在配置菜单里选择plugins 进入到插件页面, 输入chinese,找到汉化插件,(有一说一写到这我心里真是很不舒服) 然后点击o…...

Nginx自动探活后端服务状态自动转发,nginx_upstream_check_module的使用

一、三种方案 nginx对后端节点健康检查的方式主要有3种 1. gx_http_proxy_module 模块和ngx_http_upstream_module模块(自带) 官网地址:http://nginx.org/cn/docs/http/ng … proxy_next_upstream 严格来说,nginx自带是没有针对负载均衡后端节点的健康检查的,但是可以通…...

CSS 一个好玩的卡片“开卡效果”

文章目录 一、用到的一些CSS技术二、实现效果三、代码 一、用到的一些CSS技术 渐变 conic-gradientbox-shadowclip-path变换、过渡 transform、transition动画 animation keyframes伪类、伪元素 :hover、::before、::after …绝对布局。。。 clip-path 生成网站 https://techb…...

lintcode 667 · 最长的回文序列【中等 递归到动态规划】

题目 https://www.lintcode.com/problem/667/ 给一字符串 s, 找出在 s 中的最长回文子序列的长度. 你可以假设 s 的最大长度为 1000.样例 样例1输入&#xff1a; "bbbab" 输出&#xff1a; 4 解释&#xff1a; 一个可能的最长回文序列为 "bbbb" 样例2输入…...

oracle sql语言模糊查询

在Where子句中&#xff0c;可以对datetime、char、varchar字段类型的列用Like子句配合通配符选取那些“很像...”的数据记录&#xff0c;以下是可使用的通配符&#xff1a; % 零或者多个字符 _ 单一任何字符&#xff08;下划线&#xff09; / 特殊字符 [] 在某一范…...

【Ubuntu】解决ubuntu虚拟机和物理机之间复制粘贴问题(无需桌面工具)

解决Ubuntu虚拟机和物理机之间复制粘贴问题 第一步 先删除原来的vmware tools&#xff08;如果有的话&#xff09; sudo apt-get autoremove open-vm-tools第二步 安装软件包&#xff0c;一般都是用的desktop版本&#xff08;如果是server换一下&#xff09; sudo apt-get …...

解决ubuntu文件系统变成只读的方法

所欲文件变成只读&#xff0c;这种情况一般是程序执行发生错误&#xff0c;磁盘的一种保护措施 使用fsck修复 方法一&#xff1a; # 切换root sudo su # 修复磁盘错误 fsck -t ext4 -v /dev/sdb6 方法二&#xff1a; fsck.ext4 -y /dev/sdb6 重新用读写挂载 上面两种方法&…...

高数刷题笔记

常见等价无穷小 注意讨论 第二个等价无穷小 夹逼定理&#xff01;&#xff01;&#xff01; 递归数列可以尝试用关系式法 通常用到夹逼定理的时候都会用到放缩构造出一大一小两个函数&#xff0c;将原函数夹在中间&#xff0c;然后使得两端函数极限相同则可推出原函数的极限&am…...

c++入门一

参考&#xff1a;https://www.learncpp.com/cpp-tutorial/ When you finish, you will not only know how to program in C, you will know how NOT to program in C, which is arguably as important. Tired or unhappy programmers make mistakes, and debugging code tends…...

2023年项目进度管理平台排行榜

项目进度管理是项目管理学科中的一门重要课程&#xff0c;通过合理的项目计划&#xff0c;有效控制项目进度&#xff0c;保障项目能够按时交付。 不过&#xff0c;项目进度管理并不是一件简单的工作&#xff0c;不仅需要面对项目过程中各种突发情况&#xff0c;还需要做好团队协…...

【设计模式】面向对象设计八大原则

&#xff08;1&#xff09;依赖倒置原则&#xff08;DIP&#xff09; 高层模块&#xff08;稳定&#xff09;不应该依赖于低层模块&#xff08;变化&#xff09;&#xff0c;二者都应该依赖于抽象&#xff08;稳定&#xff09;。抽象&#xff08;稳定&#xff09;不应该依赖于…...

python数分实战探索霍尔特法之销售预测python代码实现以及预测图绘制

探索霍尔特法:时间序列预测中的线性趋势捕捉 时间序列分析是统计学和数据分析中的一个核心领域。无论是预测股票市场的走势,还是预测未来的销售量,一个精确和可靠的预测模型都是至关重要的。在众多的时间序列预测方法中,霍尔特法(Holts method)脱颖而出,特别是当我们面…...

java线程状态

图形说明: Thread.State源码注释: public enum State {/*** 新生状态&#xff1a;线程对象创建&#xff0c;但是还未start()*/NEW,/*** 线程处于可运行状态&#xff0c;但是这个可运行状态并不代表线程一定在虚拟机中执行。* 需要等待从操作系统获取到资源(比如处理器时间片…...

编译问题:error: ‘printf’ was not declared in this scope

这个错误提示意味着编译器在当前作用域内无法找到 printf 函数的声明。这通常是因为没有包含 <stdio.h> 头文件导致的。 解决方法是在程序中添加 #include <stdio.h> 这一行代码。这个头文件中包含了 printf 函数的声明&#xff0c;告诉编译器如何处理该函数。...

改变C++中私有变量成员的值

1、没有引用的情况&#xff1a; #include <iostream> #include <queue> using namespace std; class Person { public:queue<int>que; public:queue<int> getQueue(){return que;}void push(int a){que.push(a);}void pop(){que.pop();} };int main()…...

线程唯一的单例

经典设计模式的单例模式是指进程唯一的对象实例&#xff0c;实现code如下&#xff1a; package cun.zheng.weng.design.sinstnce;import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExec…...

明厨亮灶监控实施方案 opencv

明厨亮灶监控实施方案通过pythonopencv网络模型图像识别算法&#xff0c;一旦发现现场人员没有正确佩戴厨师帽或厨师服&#xff0c;及时发现明火离岗、不戴口罩、厨房抽烟、老鼠出没以及陌生人进入后厨等问题生成告警信息并进行提示。OpenCV是一个基于Apache2.0许可&#xff08…...

14 mysql bit/json/enum/set 的数据存储

前言 这里主要是 由于之前的一个 datetime 存储的时间 导致的问题的衍生出来的探究 探究的主要内容为 int 类类型的存储, 浮点类类型的存储, char 类类型的存储, blob 类类型的存储, enum/json/set/bit 类类型的存储 本文主要 的相关内容是 bit/json/enum/set 类类型的相关…...

04_19linux自己撸内存池实战,仿造slab分配器

前言 自己撸一个内存池 其实就相当于linux里面带的 slab分配器 可以翻翻之前的章 看看slab 和伙伴分配器的不同 在学习c语言时&#xff0c;我们常常会使用到malloc()去申请一块内存空间&#xff0c;用于存放我们的数据。刚开始我们只要知道申请内存时使用用malloc去申请一块就…...

【HDFS】XXXRpcServer和ClientNamenodeProtocolServerSideTranslatorPB小记

初始化RouterRpcServer时候会new ClientNamenodeProtocolServerSideTranslatorPB,并把当前RouterRpcServer对象(this)传入构造函数: ClientNamenodeProtocolServerSideTranslatorPBclientProtocolServerTranslator =new ClientNamenodeProtocolServerSideTranslatorPB(this…...

二分,Dijkstra,340. 通信线路

在郊区有 N 座通信基站&#xff0c;P 条 双向 电缆&#xff0c;第 i 条电缆连接基站 Ai 和 Bi。 特别地&#xff0c;1 号基站是通信公司的总站&#xff0c;N 号基站位于一座农场中。 现在&#xff0c;农场主希望对通信线路进行升级&#xff0c;其中升级第 i 条电缆需要花费 L…...

Stable Diffusion---Ai绘画-下载-入门-进阶(笔记整理)

前言 注&#xff1a;本文偏向于整理&#xff0c;都是跟着大佬们学的。 推荐两个b站up主&#xff0c;学完他们俩的东西基本就玩转SD为底的ai绘画&#xff1a; 秋葉aaaki&#xff0c;Nenly同学 1.首先SD主流的就是秋叶佬的Webui了&#xff0c;直接压缩包下载即可&#xff0c;下…...

Java 乘等赋值运算

下面这个题目是在一公司发过来的&#xff0c;如果你对 Java 的赋值运算比较了解的话&#xff0c;会很快知道答案的。 这个运算符在 Java 里面叫做乘等或者乘和赋值操作符&#xff0c;它把左操作数和右操作数相乘赋值给左操作数。 例如下面的&#xff1a;density * invertedRat…...

【性能优化】聊聊性能优化那些事

针对于互联网应用来说&#xff0c;性能优化其实就是一直需要做的事情&#xff0c;因为系统响应慢&#xff0c;是非常影响用户的体验&#xff0c;可能回造成用户流失。所以对于性能非常重要。最近正好接到一个性能优化的需求&#xff0c;需要对所负责的系统进行性能提升。目前接…...

k8s 查看加入主节点命令 k8s重新查看加入节点命令 k8s输入删除,重新查看加入命令 kuberadm查看加入节点命令

1. 使用kuberadm 安装成功后&#xff0c;clear清除了屏幕数据&#xff0c;加入命令无法查看&#xff0c;使用如下&#xff0c;重新查看node如何加入主节点命令&#xff1a; kubeadm token create --print-join-command --ttl 0 2.画圈的全部是&#xff0c;都复制&#xff0c;在…...

Scalene:Python CPU+GPU+内存分析器,具有人工智能驱动的优化建议

一、前言 Python 是一种广泛使用的编程语言&#xff0c;通常与其他语言编写的库一起使用。在这种情况下&#xff0c;如何提高性能和内存使用率可能会变得很复杂。但是&#xff0c;现在有一个解决方案&#xff0c;可以轻松地解决这些问题 - 分析器。 分析器旨在找出哪些代码段…...

C语言练习8(巩固提升)

C语言练习8 编程题 前言 奋斗是曲折的&#xff0c;“为有牺牲多壮志&#xff0c;敢教日月换新天”&#xff0c;要奋斗就会有牺牲&#xff0c;我们要始终发扬大无畏精神和无私奉献精神。奋斗者是精神最为富足的人&#xff0c;也是最懂得幸福、最享受幸福的人。正如马克思所讲&am…...

Java匿名内部类

文章目录 前言一、使用匿名内部类需要注意什么&#xff1f;二、使用步骤匿名内部类的结构匿名内部类的实用场景1. 事件监听器2. 过滤器3. 线程4. 实现接口5.单元测试&#xff1a;6.GUI编程7.回调函数 前言 Java中的匿名内部类是一种可以在声明时直接创建对象的内部类。这种内部…...