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

自动驾驶规划 - Apollo Lattice Planner算法【1】

文章目录

    • Lattice Planner简介
    • Lattice Planner 算法思路
      • 1. 离散化参考线的点
      • 2. 在参考线上计算匹配点
      • 3. 根据匹配点,计算Frenet坐标系的S-L值
      • 4. parse the decision and get the planning target
      • 5. 生成横纵向采样路径
      • 6. 轨迹cost值计算,进行碰撞检测
      • 7. 优先选择cost最小的轨迹且不碰撞的轨迹
    • 总结

Lattice Planner简介

LatticePlanner算法属于一种局部轨迹规划器,输出轨迹将直接输入到控制器,由控制器完成对局部轨迹的跟踪控制。因此,Lattice Planner输出的轨迹是一条光滑无碰撞满足车辆运动学约束和速度约束的平稳安全的局部轨迹。Lattice Planner的输入端主要由三部分组成,感知及障碍物信息、参考线信息及定位信息。局部规划模块的输出是带有速度信息的一系列轨迹点组成的轨迹,其保证了车辆控制器在车辆跟踪控制过程中的平稳性和安全性。挺复杂的越挖越多,看来得慢慢总结了(主要为了学思路,总结到自己的项目中)。


局部规划模块的输出是带有速度信息(速度信息哪里来的?)的一系列轨迹点组成的轨迹,其保证了车辆控制器在车辆跟踪控制过程中的平稳性和安全性。下面讲一下Apollo中的Lattice Planner的算法过程和代码解析。《刚学,写得可能不好,大佬们评论区指正😭😭》

Lattice Planner 算法思路

  1. 离散化参考线的点
  2. 在参考线上计算匹配点
  3. 根据匹配点,计算Frenet坐标系的S-L值
  4. 计算障碍物的s-t图
  5. 生成横纵向采样路径
  6. 计算cost值,进行碰撞检测
  7. 优先选择cost最小的轨迹且不碰撞的轨迹

在整体Apollo代码的结构,Lattice Planner的主要思路和实现是在Plan里面的PlanOnReferenceLine中,如下。

Status LatticePlanner::Plan(const TrajectoryPoint& planning_start_point,Frame* frame, ADCTrajectory* ptr_computed_trajectory) {...                        auto status =PlanOnReferenceLine(planning_start_point, frame, &reference_line_info);//主要是逻辑在这个函数里面...
}

PlanOnReferenceLine 主要是7步,按照上面的算法逻辑解析。

1. 离散化参考线的点

作为Lattice Planner的第一步,这部分的主要做的是将reference_line上的点,一个个搬到path_points中,其中增加了计算起点到当前点的累积距离ss的主要作用是为S-L坐标系使用。

  // 1. 离散化参考线的点(obtain a reference line and transform it to the PathPoint format)auto ptr_reference_line =std::make_shared<std::vector<PathPoint>>(ToDiscretizedReferenceLine(reference_line_info->reference_line().reference_points()));

详细函数逻辑:大部分的数据还是没变的,但是新增了s,主要用于S-L坐标系。

std::vector<PathPoint> ToDiscretizedReferenceLine(//离散化referenLineconst std::vector<ReferencePoint>& ref_points) {double s = 0.0;std::vector<PathPoint> path_points;for (const auto& ref_point : ref_points) {PathPoint path_point;path_point.set_x(ref_point.x());//笛卡尔坐标系下的点横坐标path_point.set_y(ref_point.y());path_point.set_theta(ref_point.heading());//航向角path_point.set_kappa(ref_point.kappa());//曲率path_point.set_dkappa(ref_point.dkappa());//曲率变化if (!path_points.empty()) {double dx = path_point.x() - path_points.back().x();double dy = path_point.y() - path_points.back().y();s += std::sqrt(dx * dx + dy * dy);//两点之间的距离}path_point.set_s(s);//Frenet坐标系会用到,ref_points的起点到当前path_point的距离path_points.push_back(std::move(path_point));//std::move的作用?????}return path_points;
}

2. 在参考线上计算匹配点

第二步是基于车当前所在的点(x,y),找到参考线上的最近匹配点,为什么要这样做? 因为参考线是车要循迹的线,车都是在不断向参考线修正的,直到此时和参考线完全重合就是最优的状态。

  1. 这一步的思路是,遍历参考线reference_line,找到距离车🚗当前点最近的点,没错就是穷举遍历(相信c++的计算速度😄)。
  2. 找到最近点后,不是直接把这个点当成匹配点,而是基于参考线上的该点往前后各找一个点,把这两个点连接成向量,方向向前。
  3. 然后把车当前的点(下图P)投影到这个向量上,这个投影点才是第二步要找的匹配点。
  PathPoint matched_point = PathMatcher::MatchToPath(*ptr_reference_line, planning_init_point.path_point().x(),planning_init_point.path_point().y());//planning_init_point.path_point()当前定位点

在这里插入图片描述

  • 根据距离找点
PathPoint PathMatcher::MatchToPath(const std::vector<PathPoint>& reference_line,const double x, const double y) {//x、y是当前定位点CHECK_GT(reference_line.size(), 0U);auto func_distance_square = [](const PathPoint& point, const double x,const double y) {double dx = point.x() - x;double dy = point.y() - y;return dx * dx + dy * dy;//两点距离平方};double distance_min = func_distance_square(reference_line.front(), x, y);std::size_t index_min = 0;for (std::size_t i = 1; i < reference_line.size(); ++i) {//遍历reference_line,找到距离最小的点的double distance_temp = func_distance_square(reference_line[i], x, y);//计算当前点到reference_line上i点的距离if (distance_temp < distance_min) {distance_min = distance_temp;//距离index_min = i;//点的索引}}//向后找一个点std::size_t index_start = (index_min == 0) ? index_min : index_min - 1;//向前找一个点std::size_t index_end = (index_min + 1 == reference_line.size()) ? index_min : index_min + 1;//目的主要是将当前的定位点投影到距离最短点的前后点的连线上,做预处理if (index_start == index_end) {return reference_line[index_start];}//投影操作return FindProjectionPoint(reference_line[index_start],reference_line[index_end], x, y);
}
  • 找到点之后算当前定位点在参考线上的投影
PathPoint PathMatcher::FindProjectionPoint(const PathPoint& p0,const PathPoint& p1, const double x,const double y) {//P0A向量double v0x = x - p0.x();double v0y = y - p0.y();//P0P1向量double v1x = p1.x() - p0.x();double v1y = p1.y() - p0.y();double v1_norm = std::sqrt(v1x * v1x + v1y * v1y);double dot = v0x * v1x + v0y * v1y;double delta_s = dot / v1_norm;//P0B长度return InterpolateUsingLinearApproximation(p0, p1, p0.s() + delta_s);//计算投影点的具体各项数据
}

手绘图:
请添加图片描述
简单理解一下这个图就能看懂代码了。😁😁

  • 计算投影点的具体各项数据

主要用线性插值(主要根据占比权重来算),公式做了合并同类项。

PathPoint InterpolateUsingLinearApproximation(const PathPoint &p0,const PathPoint &p1,const double s) {double s0 = p0.s();double s1 = p1.s();PathPoint path_point;double weight = (s - s0) / (s1 - s0);//p0.x()+weight*(p1.x()-p0.x())double x = (1 - weight) * p0.x() + weight * p1.x();double y = (1 - weight) * p0.y() + weight * p1.y();double theta = slerp(p0.theta(), p0.s(), p1.theta(), p1.s(), s);double kappa = (1 - weight) * p0.kappa() + weight * p1.kappa();double dkappa = (1 - weight) * p0.dkappa() + weight * p1.dkappa();double ddkappa = (1 - weight) * p0.ddkappa() + weight * p1.ddkappa();path_point.set_x(x);path_point.set_y(y);path_point.set_theta(theta);path_point.set_kappa(kappa);//曲率path_point.set_dkappa(dkappa);//曲率求导path_point.set_ddkappa(ddkappa);path_point.set_s(s);return path_point;
}

3. 根据匹配点,计算Frenet坐标系的S-L值

第3步的是计算车当前点P在Frenet坐标系下的坐标,这部分的原理可参考其他博客,主要就是套公式转换一下,为下面在S-T图的生成生成做准备。
在Frenet坐标系下的s就是第一步计算的s,L大小是上面手绘图的∣AB∣|AB|AB,方向和P0P1P_{0}P_{1}P0P1垂直,指向A。

  std::array<double, 3> init_s;std::array<double, 3> init_d;ComputeInitFrenetState(matched_point, planning_init_point, &init_s, &init_d);auto ptr_prediction_querier = std::make_shared<PredictionQuerier>(frame->obstacles(), ptr_reference_line);

补充说明一下,Frenet坐标系就是S-L坐标系,Frenet坐标系下的参数主要还是s,l,s就是累积距离,l就是后面提到的d,类似横向距离。

4. parse the decision and get the planning target

该部分总体代码:

  // 加入感知信息时,里面涉及多个函数,具体包括障碍物筛选,去掉与车辆未来轨迹不发生冲突的障碍物,设置动态障碍物。auto ptr_path_time_graph = std::make_shared<PathTimeGraph>(ptr_prediction_querier->GetObstacles(), *ptr_reference_line,reference_line_info, init_s[0]起始点,init_s[0] + FLAGS_speed_lon_decision_horizon=200, 0.0,FLAGS_trajectory_time_length=8s, init_d);double speed_limit =reference_line_info->reference_line().GetSpeedLimitFromS(init_s[0]);reference_line_info->SetLatticeCruiseSpeed(speed_limit);//限制巡航速度PlanningTarget planning_target = reference_line_info->planning_target();

这部分是计算障碍物的S-T图,这里的S是frenet下的s,t是时间。这部分接收感知那边传来的障碍物信息,计算障碍物在frenet坐标系下的最大值和最小值,代码如下:

SLBoundary PathTimeGraph::ComputeObstacleBoundary(const std::vector<common::math::Vec2d>& vertices,const std::vector<PathPoint>& discretized_ref_points) const {double start_s(std::numeric_limits<double>::max());double end_s(std::numeric_limits<double>::lowest());double start_l(std::numeric_limits<double>::max());double end_l(std::numeric_limits<double>::lowest());for (const auto& point : vertices) {//转frenet坐标auto sl_point = PathMatcher::GetPathFrenetCoordinate(discretized_ref_points,point.x(), point.y());//计算障碍物在s-l坐标系下的最大值最小值start_s = std::fmin(start_s, sl_point.first);end_s = std::fmax(end_s, sl_point.first);start_l = std::fmin(start_l, sl_point.second);end_l = std::fmax(end_l, sl_point.second);}SLBoundary sl_boundary;//下图障碍物的显示sl_boundary.set_start_s(start_s);sl_boundary.set_end_s(end_s);sl_boundary.set_start_l(start_l);sl_boundary.set_end_l(end_l);return sl_boundary;
}

在这里插入图片描述
像上图描述的,就是将障碍物投影到frenet坐标系下,和车的预瞄轨迹点是否有交集(apollo 的预瞄距离是200米),没有交集的就不需要考虑。

动态障碍物的思路大概一样,动态障碍物可以看作多个静态障碍物,如下图,主要都是投影到frenet坐标系下,为下一步在S-T图上表达障碍物,以及在S- T图上做撒点采样,生成轨迹做铺垫。
在这里插入图片描述
解析一下这个图,中间是🚗轨迹,左右分别是车道边界,t0,t1在车道线内则考虑,其他时刻不考虑,其实详细思路更加复杂😭。

到这里,我们就能够得到障碍物的距S-T图
在这里插入图片描述

5. 生成横纵向采样路径

这步是关键,前面的Frenet坐标系和障碍物的S-T图可以说都是为了后面生成轨迹做铺垫的。
请添加图片描述
对于上图,有些司机会按照右图中浅红色的轨迹,选择绕开蓝色的障碍车。另外有一些司机开车相对保守,会沿着右图中深红色较短的轨迹做一个减速,给蓝色障碍车让路。

既然对于同一个场景,人类司机会有多种处理方法,那么Lattice规划算法的第一步就是采样足够多的轨迹,提供尽可能多的选择。

如何进行横纵向采样呢?

  • 横向采样
    在这里插入图片描述

在这里插入图片描述
横向轨迹的采样需要涵盖多种横向运动状态。现在Apollo的代码中设计了三个末状态横向偏移量,-0.5,0.0和0.5,以及四个到达这些横向偏移量的纵向位移,分别为10,20,40,80。用两层循环遍历各种组合,再通过多项式拟合,即可获得一系列的横向轨迹。

  • 纵向采样

    纵向轨迹采样需要考虑巡航、跟车或超车、停车这三种状态。也就是说3种情况下都要生成轨迹,后面再进行评估选择cost小的。

    有了S-T图的概念,观察左图中的两条规划轨迹。红色的是一条跟车轨迹,绿色的是超车轨迹。这两条轨迹反映在S-T图中,就如右图所示。红色的跟车轨迹在蓝色阴影区域下方,绿色的超车轨迹在蓝色阴影区域上方

    如果有多个障碍物,就对这些障碍物分别采样超车和跟车所对应的末状态。
    在这里插入图片描述
    总结下来就是遍历所有和车道有关联的障碍物,对他们分别采样超车和跟车的末状态,然后用多项式拟合即可获得一系列纵向轨迹。多项式拟合如何生成多项式轨迹?可以看一下下面这篇blog。

无人车-运动规划-高阶多项式曲线方程求解

在这里插入图片描述
上图是将所有的轨迹整合,交给下一步来评估。

Lattice Planner会根据起点和终点的状态,在位置空间和时间上同时进行撒点。撒点的起始状态和终止状态各有6个参数,包括了3个横向参数,即横向位置、横向位置的导数和导数的导数;3个纵向参数,即纵向位置、纵向位置的一阶导数也就是速度、纵向位置的二阶导数(也就是加速度)。

起点的参数是车辆当时真实的状态,终点状态则是撒点枚举的各个情况。在确定了终点和起点状态以后,再通过五阶或者四阶的多项式连接起始状态和终止状态,从而得到规划的横向和纵向轨迹。

6. 轨迹cost值计算,进行碰撞检测

有了轨迹的cost以后,就是一个循环检测的过程。在这个过程中,每次会先挑选出cost最低的轨迹,对其进行物理限制检测和碰撞检测。如果挑出来的轨迹不能同时通过这两个检测,就将其筛除,考察下一条cost最低的轨迹

  TrajectoryEvaluator trajectory_evaluator(init_s, planning_target, lon_trajectory1d_bundle, lat_trajectory1d_bundle,ptr_path_time_graph, ptr_reference_line);// Get instance of collision checker and constraint checkerCollisionChecker collision_checker(frame->obstacles(), init_s[0], init_d[0],*ptr_reference_line, reference_line_info,ptr_path_time_graph);

7. 优先选择cost最小的轨迹且不碰撞的轨迹

获得采用轨迹之后,接着需要进行目标轨迹的曲率检查和碰撞检测,目的是为了使目标采样轨迹满足车辆的运动学控制要求和无碰撞要求,这样就形成了安全可靠的轨迹簇。这些轨迹簇都可以满足车辆的控制要求,但并不是最优的,因此需要从轨迹簇中选出一组最优的运行轨迹。这时就需要引入轨迹评价函数,用来对候选轨迹进行打分。

轨迹评价函数主要为了使得目标轨迹尽量靠近静态参考线轨迹运行,同时,速度尽量不发生大突变,满足舒适性要求,且尽量远离障碍物。因此最后轨迹评价函数可以通过如下伪代码描述:

traj_cost = k_lat * cost_lat + k_lon * cost_lon + k_obs * obs_cost;

上式中:
k_lat : 表示纵向误差代价权重
cost_lat: 表示纵向误差,综合考虑纵向速度误差,时间误差及加加速度的影响。
k_lon : 表示横向误差代价权重
cost_lon: 表示横向向误差,综合考虑了横向加速度误差及横向偏移误差的影响。
k_obs : 表示障碍物代价权重
obs_cost: 表示障碍物距离损失。
最后选择出代价值最好的一条轨迹输入到控制器,用于控制器的跟踪控制。

  while (trajectory_evaluator.has_more_trajectory_pairs()) {double trajectory_pair_cost =trajectory_evaluator.top_trajectory_pair_cost();auto trajectory_pair = trajectory_evaluator.next_top_trajectory_pair();// combine two 1d trajectories to one 2d trajectoryauto combined_trajectory = TrajectoryCombiner::Combine(*ptr_reference_line, *trajectory_pair.first, *trajectory_pair.second,planning_init_point.relative_time());// check longitudinal and lateral acceleration// considering trajectory curvaturesauto result = ConstraintChecker::ValidTrajectory(combined_trajectory);if (result != ConstraintChecker::Result::VALID) {++combined_constraint_failure_count;switch (result) {case ConstraintChecker::Result::LON_VELOCITY_OUT_OF_BOUND:lon_vel_failure_count += 1;break;case ConstraintChecker::Result::LON_ACCELERATION_OUT_OF_BOUND:lon_acc_failure_count += 1;break;case ConstraintChecker::Result::LON_JERK_OUT_OF_BOUND:lon_jerk_failure_count += 1;break;case ConstraintChecker::Result::CURVATURE_OUT_OF_BOUND:curvature_failure_count += 1;break;case ConstraintChecker::Result::LAT_ACCELERATION_OUT_OF_BOUND:lat_acc_failure_count += 1;break;case ConstraintChecker::Result::LAT_JERK_OUT_OF_BOUND:lat_jerk_failure_count += 1;break;case ConstraintChecker::Result::VALID:default:// Intentional emptybreak;}continue;}// check collision with other obstaclesif (collision_checker.InCollision(combined_trajectory)) {++collision_failure_count;continue;}// put combine trajectory into debug dataconst auto& combined_trajectory_points = combined_trajectory;num_lattice_traj += 1;reference_line_info->SetTrajectory(combined_trajectory);reference_line_info->SetCost(reference_line_info->PriorityCost() +trajectory_pair_cost);reference_line_info->SetDrivable(true);// Print the chosen end condition and start conditionADEBUG << "Starting Lon. State: s = " << init_s[0] << " ds = " << init_s[1]<< " dds = " << init_s[2];// castauto lattice_traj_ptr =std::dynamic_pointer_cast<LatticeTrajectory1d>(trajectory_pair.first);if (!lattice_traj_ptr) {ADEBUG << "Dynamically casting trajectory1d ptr. failed.";}}
}

总结

总体讲清楚了算法的过程,但是代码层面涉及太多,后面还得慢慢深入,特别是采样的过程的代码得再解析。

参考:
Baidu Apollo 6.2 Lattice Planner规划算法
Apollo星火计划学习笔记——第七讲自动驾驶规划技术原理1
Lattice算法之六轨迹评估

相关文章:

自动驾驶规划 - Apollo Lattice Planner算法【1】

文章目录Lattice Planner简介Lattice Planner 算法思路1. 离散化参考线的点2. 在参考线上计算匹配点3. 根据匹配点&#xff0c;计算Frenet坐标系的S-L值4. parse the decision and get the planning target5. 生成横纵向采样路径6. 轨迹cost值计算&#xff0c;进行碰撞检测7. 优…...

以太坊数据开发-Web3.py-安装连接以太坊数据

Web3.py是连接以太坊的python库&#xff0c;它的API从web3.js中派生而来。如果你用过web3.js&#xff0c;你会对它的API很熟悉。但惭愧的是&#xff0c;作为一个以太坊上Dapp的开发者&#xff0c;我几乎没有直接使用过web3.js&#xff0c;也没有看过它的API。 官网&#xff1a…...

【触摸屏功能测试】MQTT_STD本地调试说明-测试记录

1、MQTT简介 MQTT是一种基于发布/订阅模式的“轻量级”通讯协议。它是针对受限的、低带宽的、高延迟的、网络不可靠的环境下的网络通讯设备设计的。 发布是指客户端将消息传递给服务器&#xff0c;订阅是指客户端接收服务器推送的消息。每个消息有一个主题&#xff0c;包含若干…...

六十分之十三——黎明前

目录一、目标二、计划三、完成情况四、提升改进(最少3点)五、意外之喜(最少2点)六、总结一、目标 明确可落地&#xff0c;对于自身执行完成需要一定的努力才可以完成的 1.8本技术管理书籍阅读(使用番茄、快速阅读、最后输出思维导图)2.吴军系列硅谷来信1听书、香帅的北大金融…...

【Call for papers】CRYPTO-2023(CCF-A/网络与信息安全/2023年2月16日截稿)

Crypto 2023 will take place in Santa Barbara, USA on August 19-24, 2023. Crypto 2023 is organized by the International Association for Cryptologic Research (IACR). The proceedings will be published by Springer in the LNCS series. 文章目录1.会议信息2.时间节…...

线程的信号量和互斥量

文章目录线程的信号量初始化信号量&#xff1a;sem_init减少信号量&#xff1a;sem_wait增加信号量&#xff1a;sem_post删除信号量&#xff1a;sem_destroy代码示例线程的互斥量初始化互斥量&#xff1a;pthread_mutex_init锁住互斥量&#xff1a;pthread_mutex_lock解锁互斥量…...

关于Linux,开源社区与国产化的本质区别

因为生产力驱动而非理想主义驱动。 开源运动的蓬勃发展来自于GNU(GNU is not unix)&#xff0c;RichardMatthewStallman领导着一群黑客&#xff0c;带着对比尔盖茨的鄙视&#xff0c;制定了GPL协议&#xff0c;以后人人都能从伟大的前人身上学习到源代码的精髓&#xff0c;让软…...

Win11下Linux子系统迁移方法及报错解决

Win11 将Linux子系统从C盘迁移到其他盘Win11下Linux子系统迁移方法及报错解决1、下载LxRunOffline2、ERROR&#xff1a;directory is not empty 报错解决参考链接Win11下Linux子系统迁移方法及报错解决 C盘满了&#xff0c;Ubuntu子系统占了100多G怎么办&#xff1f;直接将子系…...

python维护的一些基础方法

1】通过命令行查看python安装库的基本信息 pip show numpy # 查看python中numpy库的安装版本信息 2】python 环境的开发与维护 python的开发与C\MATLAB等最大的不同就是&#xff0c;python中版本的更新不对历史版本负责&#xff0c;就是说你以历史版本开发的python程序&#…...

C语言 数组元素的指针

1.一个变量有地址&#xff0c;一个数组包含若干个元素&#xff0c;每个数组元素都在内存中占用存储单元&#xff0c;它们都有相应的地址。 2.指针变量既然可以指向变量&#xff0c;当然也可以指向数组元素&#xff08;把某一元素的地址放入一个指针变量中&#xff09;。 3.所谓…...

(C语言)指针进阶

问&#xff1a;1. ( )&#xff0c;[ ]&#xff0c;->&#xff0c;&#xff0c;--&#xff0c;. &#xff0c;&#xff0a;的操作符优先级是怎么样的&#xff1f;2. Solve the problems&#xff1a;只有一个常量字符串与一个字符指针&#xff0c;该怎么打印常量字符串所有内容…...

DS期末复习卷(三)

选择题 某数据结构的二元组形式表示为A(D&#xff0c;R)&#xff0c;D{01&#xff0c;02&#xff0c;03&#xff0c;04&#xff0c;05&#xff0c;06&#xff0c;07&#xff0c;08&#xff0c;09}&#xff0c;R{r}&#xff0c;r{<01&#xff0c;02>&#xff0c;<01&a…...

Java链表模拟实现+LinkedList介绍

文章目录一、模拟实现单链表成员属性成员方法0&#xff0c;构造方法1&#xff0c;addFirst——头插2&#xff0c;addLast——尾插3&#xff0c;addIndex——在任意位置插入3.1&#xff0c;checkIndex——判断index合法性3.2&#xff0c;findPrevIndex——找到index-1位置的结点…...

MySQL——单表、多表查询

一、单表查询 素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker ( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL, 工资 float(8,2) NOT NULL, 政治面貌 varcha…...

关于表的操作 数据库(3)

目录 前期准备工作&#xff1a; 一、单表查询&#xff1a; 二、多表查询&#xff1a; 前期准备工作&#xff1a; 修改数据库的配置文件&#xff0c;&#xff0c;使其可以显示库名&#xff0c;其中//d代表当前使用的数据库名 注&#xff1a;vim /etc/my.cnf.d/mysql-server.c…...

C++:红黑树

红黑树的概念 红黑树是一棵二叉搜索树&#xff0c;但是红黑树通过增加一个存储位表示结点的颜色RED或BLACK。通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出2倍&#xff0c;因而是接近平衡的。 红黑树的性质 ⭐…...

每天一道算法题の中缀表达式

中缀表达式&#xff08;、-、*、/&#xff09; &#xff1a;中缀表达式是指操作符位于操作数之间的数学表达式。例如&#xff0c;在中缀表达式"2 3"中&#xff0c;操作符""位于操作数"2"和"3"之间。现给定一个中缀表达式&#xff0c…...

Dar语法基础-泛型

泛型 如果查看基本数组类型 List 的 API 文档&#xff0c;您会发现该类型实际上是 List<E>。 <…> 表示法将 List 标记为泛型&#xff08;或参数化&#xff09;类型——具有正式类型参数的类型。 按照惯例&#xff0c;大多数类型变量的名称都是单字母的&#xff0…...

rt-thread------串口(一)配置

系列文章目录 rt-thread 之 fal移植 rt-thread 之 生成工程模板 文章目录系列文章目录前言一、串口的配置step1&#xff1a;通过串口名字找到串口句柄step2&#xff1a;配置串口参数step3&#xff1a;设置串口接收回调函数step4&#xff1a;打开串口设备前言 UART&#xff08…...

Android - 自动系统签名

一、系统签名 以下是两类应用开发场景&#xff1a; 普通应用开发&#xff1a;使用公司自定义 keystore 进行签名&#xff0c;如&#xff1a;微信、支付宝系统应用开发&#xff1a;使用 AOSP 系统签名或厂商自定义 keystore 进行签名&#xff0c;如&#xff1a;设置、录音 系…...

SSH 服务详解 (八)-- vscode 通过 SSH 远程连接 linux 服务器

vscode 通过 SSH 远程连接 linux 服务器 SSH服务详解(一)–Linux SSH 服务器与客户端的安装与启动 SSH服务详解(二)–使用私钥登录 SSH 服务器(免密登录) SSH 服务详解 (三)-- 使用 SSH 代理 SSH 服务详解 (四)-- 本地调用远程主机的命令 SSH 服务详解 (五)-- 远程文件拷贝…...

【PTA Advanced】1060 Are They Equal(C++)

目录 题目 Input Specification: Output Specification: Sample Input 1: Sample Output 1: Sample Input 2: Sample Output 2: 思路 C 知识点UP 代码 题目 If a machine can save only 3 significant digits, the float numbers 12300 and 12358.9 are considered …...

仿真与测试:通过Signal Builder模块生成输入信号

本文研究通过Signal Builder模块生成输入信号的方法。 文章目录1 生成输入信号2 仿真过程2.1 搭建被测模型2.2 搭建Signal Builder输入模块2.3 配置仿真log及仿真3 总结1 生成输入信号 在汽车的电控软件开发中&#xff0c;经常会在Simulink模型内部进行单元测试。单元测试的本…...

云计算培训靠谱吗?

怎么算靠谱的培训呢&#xff1f; 举个例子&#xff1a; 我想参加云计算培训找个工作&#xff0c;机构满足了我的要求&#xff0c;有工作了&#xff0c;但是不是做云计算相关的。 小强也参加了云计算培训&#xff0c;想学好云计算成为技术大牛&#xff0c;最后专业学得普普通…...

力扣SQL刷题10

目录标题618. 学生地理信息报告--完全不会的新题型1097. 游戏玩法分析 V - 重难点1127. 用户购买平台--难且不会618. 学生地理信息报告–完全不会的新题型 max()函数的功效&#xff1a;&#xff08;‘jack’, null, null&#xff09;中得出‘jack’&#xff0c;&#xff08;nul…...

31 岁生日快乐,Linux!

Linux 迎来了 31 岁生日&#xff0c;所以和我一起庆祝 Linux 的 31 岁生日吧&#xff0c;喝上一杯好香槟和一个美味的蛋糕&#xff01;虽然有些人不承认 8 月 25 日是 Linux 的生日&#xff0c;但我知道。1991 年 8 月 25 日&#xff0c;21 岁的芬兰学生 Linus Benedict Torval…...

分布式ID生成方案

文章目录前言一、分布式ID需要满足的条件二、分布式ID生成方式基于UUID数据库自增数据库集群数据库号段模式redis ID生成基于雪花算法&#xff08;Snowflake&#xff09;模式百度&#xff08;uid-generator&#xff09;美团&#xff08;Leaf&#xff09;滴滴&#xff08;Tinyid…...

合宙Air103|fbd数据库| fskv - 替代fdb库|LuatOS-SOC接口|官方demo|学习(16):类redis的fbd数据库及fskv库

基础资料 基于Air103开发板&#xff1a;&#x1f697; Air103 - LuatOS 文档 上手&#xff1a;开发上手 - LuatOS 文档 探讨重点 对官方社区库接口类redis的fbd数据库及fskv库的调用及示例进行复现及分析&#xff0c;了解两库的基本原理及操作方法。 软件及工具版本 Luat…...

【论文精读】Deep Residual Learning for Image Recognition

1 Degradation Problem&#x1f4a6; 深度卷积神经网络在图像分类方面取得了一系列突破。深度网络自然地将低/中/高级特征和分类器以端到端的多层方式集成在一起&#xff0c;特征的“层次”可以通过堆叠层数(深度)来丰富。最近的研究揭示了网络深度是至关重要的&#xff0c;在具…...

Lesson2:基础语法、输出输入

一、基础语法 1、行结构 一个Python程序可分为许多逻辑行&#xff0c;一般来说&#xff1a;一个语句就是一行代码&#xff0c;不会跨越多行。 """比如下面的Python程序&#xff0c;一共有3个逻辑行&#xff0c;每一行都通过print()输出一个结果。""…...

魔方网站建设/关键词优化

目录&#xff1a;一 Web应用的组成二 开发一个Web应用2.1 S端的简单开发与Http协议2.2 结合http协议改进S端2.3 返回Html2.4 jinja2模块三 Web框架的由来3.1 wsgiref模块3.2 简单Web框架实现3.3 简单web框架的使用3.4 三大web框架简介与wsgi协议一 Web应用的组成​ 我们接下来学…...

广州做网站的网络公司排名/大数据精准营销获客

https://blog.csdn.net/weixin_43687900/article/details/104122629...

html网站如何做seo/关键词优化排名的步骤

常用的表单验证指令 1. 必填项验证 某个表单输入是否已填写&#xff0c;只要在输入字段元素上添加HTML5标记required即可&#xff1a; <input type"text" required /> 2. 最小长度 验证表单输入的文本长度是否大于某个最小值&#xff0c;在输入字段上使用指令…...

个人网站有商业内容备案/微博推广有用吗

整编&#xff1a;Snow、UNA如今&#xff0c;在市场经济高度发达的当下&#xff0c;趋势与环境也更迭得越来越快&#xff0c;整个社会的消费观念、品味、需求也都处在不断变迁当中。而在新消费时代下&#xff0c;年轻消费群体重新定义市场&#xff0c;不管是传统消费人群心态与品…...

阿里云万网网站/餐饮品牌全案策划

需要利用Adobe illustrator&#xff08;AI&#xff09;可以快速方便的提取图片的轮廓线并形成矢量图形&#xff0c;同时AI支持导出为dxf和dwg格式&#xff0c;可以再导入CAD或Solidworks、Inventor等建模软件制作三维模型。软件&#xff1a;Adobe illustrator&#xff08;无版本…...

如何建立自己音乐网站/seo优化网站模板

目录一、MongoDB概念1、使用业务应用场景2、 使用场景选择3、简介4、体系结构二、基本命令使用1、数据库操作2、集合操作3、文档操作3.1.1.新增文档3.1.2.查询文档3.1.3.更新文档3.1.4.删除文档3.1.5.高级查询4、命令总结★一、MongoDB概念 1、使用业务应用场景 具体的应用场…...