【局部路径规划算法】—— DWA动态窗口法(c++实现))

2024-04-04 09:44

本文主要是介绍【局部路径规划算法】—— DWA动态窗口法(c++实现)),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考资料:
(1)机器人局部避障的动态窗口法(dynamic window approach)
(2)机器人局部避障的动态窗口法
(3)局部规划算法:DWA算法原理
(4)SLAM学习:DWA算法原理和编程实现(PYTHON)
(5)【路径规划】局部路径规划算法——DWA算法(动态窗口法)|(含python实现 | c++实现)

1 DWA动态窗口法

动态窗口法(Dynamic Window Approach):常用的局部避障规划方法
在这里插入图片描述
(1) 轨迹预测:建立智能汽车关于速度和角速度的运动学模型
x = x + v Δ t cos ⁡ ( θ t ) y = y + v Δ t sin ⁡ ( θ t ) θ t = θ t + w Δ t \begin{array}{l} x=x+v \Delta t \cos \left(\theta_{t}\right) \\ y=y+v \Delta t \sin \left(\theta_{t}\right) \\ \theta_{t}=\theta_{t}+w \Delta t \end{array} x=x+vΔtcos(θt)y=y+vΔtsin(θt)θt=θt+wΔt

  • 上式表明在一定时间窗口内,车辆的位置和航向角可由当前车辆速度和当前角速度决定,那么不同的速度和角速度组合将产生不同的车辆位置和航向角,也就产生不同的车辆运动轨迹。
  • 在速度和加速度所构成的二维空间,由于不受任何约束,存在着无穷多种组合,速度和加速度采样的目的就是将采样区域缩小在一定范围,并适当设置速度分辨率,以生成若干组数量有限的速度和加速度组合。缩小采样区域通过设置各类约束条件实现。

(2) 速度采样:充分考虑智能汽车的速度、加速度等物理限制约束条件,在速度和角速度空间内采样获取一对速度指令,通过设定一个时间区间获得一簇轨迹曲线
在这里插入图片描述

(3) 轨迹评价:构造一种轨迹性能评价指标选取最优轨迹

  • 在每一个蓝色虚线框内,车辆都会根据当前的速度组合生成若干条备选运动轨迹,这些轨迹构成的轨迹曲线簇就称为一个时间窗
  • 车辆在这个窗口内按照特定规则选择一条最优轨迹运动。当运动到1号窗口轨迹的末端后又重新生成一个新的轨迹曲线簇(即2号窗口),如此反复循环动态执行,故命名为动态窗口法
  1. 航向角评价函数。我们设在当前采样速度组合下的时间窗口轨迹末端的航向角为heading,该朝向与目标点的角度差值越小,则方位角评价函数值越高,表明此速度组合对应的运动轨迹越好。
  2. 障碍物距离评价函数。设dist为速度组合(v,w)对应的轨迹曲线与障碍车的最小距离, 数值越大,则障碍物距离评价函数值越高,表明此速度组合对应的运动轨迹越好。
  3. 速度评价函数。我们希望车辆在正常行驶过程中与目标速度越接近越好,故速度越高,可以认为评价函数值越高。

在这里插入图片描述

最后,利用归一化思想对上述三种轨迹评价函数进行相加构成综合评价函数指标,以此作为标准筛选时间窗内的最优轨迹

在这里插入图片描述DWA算法demo

BEGIN DWA(robotPose,robotGoal,robotModel)  laserscan = readScanner()  allowable_v = generateWindow(robotV, robotModel)  allowable_w  = generateWindow(robotW, robotModel)  // 根据能否及时刹车剔除不安全的速度for each v in allowable_v  for each w in allowable_w  dist = find_dist(v,w,laserscan,robotModel)  breakDist = calculateBreakingDistance(v)//刹车距离  if (dist > breakDist)  //如果能够及时刹车,该对速度可接收heading = hDiff(robotPose,goalPose, v,w)   //clearance与原论文稍不一样  clearance = (dist-breakDist)/(dmax - breakDist)   cost = costFunction(heading,clearance, abs(desired_v - v))  if (cost > optimal)  best_v = v  best_w = w  optimal = cost  set robot trajectory to best_v, best_w  
END  

2 DWA算法流程

在这里插入图片描述

3 应用场景

应用模型: 适用于两轮差分和全向移动模型、不能用在阿克曼模型。
优点
(1)计算复杂度低:考虑到速度和加速度的限制,只有安全的轨迹会被考虑,且每次采样的时间较短,因此轨迹空间较小
(2)可以实现避障:可以实时避障,但是避障效果一般
(3)适用于两轮差分和全向移动模型
缺点
(1)前瞻性不足: 只模拟并评价了下一步,如在机器人前段遇见“C”字形障碍时,不能很好的避障
(2)非全局最优路径: 每次都选择下一步的最佳路径,而非全局最优路径
(3)动态避障效果差
(4)不能用在阿克曼模型

4 DWA可视化

参考b站Ally大佬的代码
dwa.h

#include <stdio.h>
#include <eigen3/Eigen/Dense>
#include <iostream>
#include <memory>
#include <vector>
#include <unordered_map>
#include "matplotlibcpp.h"
#include <stdlib.h>
#include <algorithm>#define PI 3.14159
class DWA {
private:// 位姿typedef struct{double x = 0;double y = 0;} Pos;// 权重struct Weight{double heading = 1;double dist = 3;double speed = 3;} weight_;// 速度采样typedef struct{double linear_vel_min = 0;double linear_vel_max = 0;double angular_vel_min = 0;double angular_vel_max = 0;} SpeedRange;// 车辆状态typedef struct{double pos_x = 0;double pos_y = -1.75;double heading = 0;double linear_vel = 2;double angular_vel = 0;} VehicleState;// 窗口信息typedef struct{double linear_vel = 0;double angular_vel = 0;std::vector<VehicleState> trajectory;double heading_value;double dist_value;double vel_value;double value = 0;} WindowInfo;// 障碍物typedef struct{double center_x = 0;double center_y = 0;double radius = 0;} Obstacle;// 场景基本参数double road_width_ = 3.5; // 道路标准宽度double road_len_ = 60;    // 道路长度double veh_width_ = 1.6;  // 汽车宽度double veh_len_ = 4.5;    // 车长Pos goal_pos_;            // 目标点位置// 时间参数double dt_ = 0.1;        // 车辆单步运动时间double window_time_ = 3; // 窗口时间// 自车运动学模型参数double v_max_ = 15;                 // 最高速度double omega_max_ = 200 * PI / 180; // 最高角速度double acc_max_ = 3;                // 最高加速度double alpha_max_ = 50 * PI / 180;  // 最高角加速度double v_res_ = 0.1;                // 速度分辨率double omega_res_ = 2 * PI / 180;   // 角速度分辨率VehicleState vehicle_state_;std::vector<WindowInfo> window_info_;std::vector<Obstacle> obstacles_;std::vector<double> path_x;std::vector<double> path_y;
public:DWA() {}~DWA() {}bool run();// 设置障碍物环境void setObstacles();// 根据当前状态和约束计算当前的速度参数允许范围SpeedRange GetSpeedRange();// 获取窗口信息void GetWindowInfo(DWA::SpeedRange speed_range);// 更新自车状态void UpdateState(int max_value_idx);// 找到最大评价函数对应的索引int FindMaxValue();// 获取本时间窗内的轨迹void GetTrajectory(std::vector<VehicleState> *trajectory, double linear_vel, double angular_vel);// 可视化void Plot(bool to_goal_dist_flag, int max_value_idx);// 获取自车几个点位void GetEgoPoint(std::vector<double> *x_list, std::vector<double> *y_list);
};

dwa.cpp

#include "../include/dwa.h"bool DWA::run() {goal_pos_.x = 57;goal_pos_.y = 2.5;// 设置障碍物环境DWA::setObstacles();bool to_goal_dist_flag = false;for (size_t i = 0; i < 1000; i++){// 根据当前状态和约束计算当前的速度参数允许范围DWA::SpeedRange speed_range = GetSpeedRange();// 根据速度范围和分辨率,生成若干条运动轨迹DWA::GetWindowInfo(speed_range);// 找到最大评价函数对应的索引int max_value_idx = DWA::FindMaxValue();// 更新自车状态DWA::UpdateState(max_value_idx);// 判断是否到达终点,并画图std::cout << "iter: " << i << "; pos_x: " << vehicle_state_.pos_x << "; pos_y: " << vehicle_state_.pos_y<< "; heading: " << vehicle_state_.heading << "; linear_vel: " << vehicle_state_.linear_vel<< "; angular_vel: " << vehicle_state_.angular_vel << std::endl;double to_goal_dist = std::sqrt(std::pow(vehicle_state_.pos_x - goal_pos_.x, 2) +std::pow(vehicle_state_.pos_y - goal_pos_.y, 2));if (to_goal_dist < 1){std::cout << "finish !" << std::endl;to_goal_dist_flag = true;DWA::Plot(to_goal_dist_flag, max_value_idx);break;}DWA::Plot(to_goal_dist_flag, max_value_idx);}return true;
}void DWA::setObstacles()
{DWA::Obstacle obstacle_tmp;// 1obstacle_tmp.center_x = 13.0;obstacle_tmp.center_y = -1.75;obstacle_tmp.radius = 2.0;obstacles_.push_back(obstacle_tmp);// 2obstacle_tmp.center_x = 27.0;obstacle_tmp.center_y = -1.75;obstacle_tmp.radius = 2.0;obstacles_.push_back(obstacle_tmp);// 3obstacle_tmp.center_x = 40.0;obstacle_tmp.center_y = 1.75;obstacle_tmp.radius = 2.0;obstacles_.push_back(obstacle_tmp);// 4obstacle_tmp.center_x = 50.0;obstacle_tmp.center_y = -1.75;obstacle_tmp.radius = 2.0;obstacles_.push_back(obstacle_tmp);// 将道路便捷模拟为若干个小的障碍物,下边界for (size_t i = 0; i < 120; i++){obstacle_tmp.center_x = i * 0.5;obstacle_tmp.center_y = 3.5;obstacle_tmp.radius = 0.5;obstacles_.push_back(obstacle_tmp);}// 将道路便捷模拟为若干个小的障碍物,上边界for (size_t i = 0; i < 120; i++){obstacle_tmp.center_x = i * 0.5;obstacle_tmp.center_y = -3.5;obstacle_tmp.radius = 0.5;obstacles_.push_back(obstacle_tmp);}
}DWA::SpeedRange DWA::GetSpeedRange()
{DWA::SpeedRange speed_range;speed_range.linear_vel_min = std::max(vehicle_state_.linear_vel - acc_max_ * dt_, 0.0);speed_range.linear_vel_max = std::min(vehicle_state_.linear_vel + acc_max_ * dt_, v_max_);speed_range.angular_vel_min = std::max(vehicle_state_.angular_vel - alpha_max_ * dt_, -omega_max_);speed_range.angular_vel_max = std::min(vehicle_state_.angular_vel + alpha_max_ * dt_, omega_max_);return speed_range;
}void DWA::GetWindowInfo(DWA::SpeedRange speed_range)
{window_info_.clear(); //清空DWA::WindowInfo window_info_tmp;std::vector<VehicleState> trajectory;double linear_vel = speed_range.linear_vel_min;while (true){double angular_vel = speed_range.angular_vel_min;while (true){// 初始化轨迹trajectory.clear();GetTrajectory(&trajectory, linear_vel, angular_vel);// 赋值window_info_tmp.linear_vel = linear_vel;window_info_tmp.angular_vel = angular_vel;window_info_tmp.trajectory = trajectory;window_info_.push_back(window_info_tmp);// 判断是否退出循环angular_vel = angular_vel + omega_res_;if (angular_vel >= speed_range.angular_vel_max){break;}}// 判断是否退出循环linear_vel = linear_vel + v_res_;if (linear_vel >= speed_range.linear_vel_max){break;}}
}void DWA::GetTrajectory(std::vector<DWA::VehicleState> *trajectory, double linear_vel, double angular_vel)
{DWA::VehicleState vehicle_state = vehicle_state_;trajectory->push_back(vehicle_state);// 循环获得窗口时间内的轨迹double time = 0;while (time <= window_time_){time = time + dt_; // 时间更新Eigen::Matrix<double, 5, 5> A;A << 1, 0, 0, 0, 0,0, 1, 0, 0, 0,0, 0, 1, 0, 0,0, 0, 0, 0, 0,0, 0, 0, 0, 0;Eigen::Matrix<double, 5, 2> B;B << dt_ * std::cos(vehicle_state.heading), 0,dt_ * std::sin(vehicle_state.heading), 0,0, dt_,1, 0,0, 1;Eigen::Matrix<double, 5, 1> x;x << vehicle_state.pos_x,vehicle_state.pos_y,vehicle_state.heading,vehicle_state.linear_vel,vehicle_state.angular_vel;Eigen::Matrix<double, 2, 1> u;u << linear_vel,angular_vel;// 状态更新Eigen::Matrix<double, 5, 1> state_new;state_new = A * x + B * u;// 更新state指针vehicle_state.pos_x = state_new(0);vehicle_state.pos_y = state_new(1);vehicle_state.heading = state_new(2);vehicle_state.linear_vel = state_new(3);vehicle_state.angular_vel = state_new(4);trajectory->push_back(vehicle_state);}
}int DWA::FindMaxValue()
{double heading_value_sum = 0;double dist_value_sum = 0;double vel_value_sum = 0;for (std::vector<DWA::WindowInfo>::iterator iter = window_info_.begin(); iter != window_info_.end();){DWA::VehicleState end_state = iter->trajectory[iter->trajectory.size() - 1];// 计算航向角评价函数double theta = end_state.heading * 180 / PI;double goal_theta = std::atan2(goal_pos_.y - end_state.pos_y, goal_pos_.x - end_state.pos_x) * 180 / PI;double target_theta = std::abs(goal_theta - theta);double heading_value = 180 - target_theta;// 计算轨迹终点距离最近障碍物距离的评价函数double dist_value = 1e5;for (size_t i = 0; i < obstacles_.size(); i++){double dist = std::sqrt(std::pow(end_state.pos_x - obstacles_[i].center_x, 2) +std::pow(end_state.pos_y - obstacles_[i].center_y, 2)) -obstacles_[i].radius;dist_value = std::min(dist_value, dist);}if (dist_value < 0){iter = window_info_.erase(iter);continue;}iter->dist_value = dist_value;// 计算速度的评价函数double vel_value = end_state.linear_vel;double stop_dist = std::pow(end_state.linear_vel, 2) / (2 * acc_max_);if (dist_value < stop_dist){iter = window_info_.erase(iter);continue;}iter->vel_value = vel_value;// 计算累计值,用于归一化处理heading_value_sum = heading_value_sum + heading_value;dist_value_sum = dist_value_sum + dist_value;vel_value_sum = vel_value_sum + vel_value;iter++;}// 归一化处理int idx = 0;double max_value = 0;for (size_t i = 0; i < window_info_.size(); i++){double heading_value_tmp = window_info_[i].heading_value / std::max(heading_value_sum, 0.01);double dist_value_tmp = window_info_[i].dist_value / std::max(dist_value_sum, 0.01);double vel_value_tmp = window_info_[i].vel_value / std::max(vel_value_sum, 0.01);window_info_[i].value = heading_value_tmp * weight_.heading +dist_value_tmp * weight_.dist + vel_value_tmp * weight_.speed;window_info_[i].value > max_value ? idx = i : idx = idx;}return idx;
}void DWA::UpdateState(int max_value_idx)
{std::vector<DWA::WindowInfo>::iterator iter = window_info_.begin() + max_value_idx;vehicle_state_ = *(iter->trajectory.begin() + 1);path_x.push_back(vehicle_state_.pos_x);path_y.push_back(vehicle_state_.pos_y);
}void DWA::Plot(bool to_goal_dist_flag, int max_value_idx)
{matplotlibcpp::clf();matplotlibcpp::title("DWA");matplotlibcpp::xlabel("X-axis");matplotlibcpp::ylabel("Y-axis");// 画灰色路面std::vector<double> x_list = {-5, -5, road_len_, road_len_};std::vector<double> y_list = {-road_width_ - 0.5, road_width_ + 0.5,road_width_ + 0.5, -road_width_ - 0.5};std::map<std::string, std::string> keywords = {{"color", "0.55"}};matplotlibcpp::fill(x_list, y_list, keywords);// 画车道线x_list = {-5, road_len_};y_list = {0, 0};keywords = {{"linestyle", "dashed"}, {"color", "w"}};matplotlibcpp::plot(x_list, y_list, keywords);x_list = {-5, road_len_};y_list = {road_width_, road_width_};matplotlibcpp::plot(x_list, y_list, "w");x_list = {-5, road_len_};y_list = {-road_width_, -road_width_};matplotlibcpp::plot(x_list, y_list, "w");// 画交通车辆for (size_t i = 0; i < 4; i++){x_list = {obstacles_[i].center_x, obstacles_[i].center_x,obstacles_[i].center_x - veh_len_, obstacles_[i].center_x - veh_len_};y_list = {obstacles_[i].center_y - veh_width_ / 2, obstacles_[i].center_y + veh_width_ / 2,obstacles_[i].center_y + veh_width_ / 2, obstacles_[i].center_y - veh_width_ / 2};keywords = {{"color", "b"}};matplotlibcpp::fill(x_list, y_list, keywords);}// 画自车GetEgoPoint(&x_list, &y_list);keywords = {{"color", "r"}};matplotlibcpp::fill(x_list, y_list, keywords);// 画窗口轨迹簇for (size_t i = 0; i < window_info_.size(); i++){x_list.clear();y_list.clear();for (size_t j = 0; j < window_info_[i].trajectory.size(); j++){x_list.push_back(window_info_[i].trajectory[j].pos_x);y_list.push_back(window_info_[i].trajectory[j].pos_y);}matplotlibcpp::plot(x_list, y_list, "g");}// 画本窗口内的最优轨迹x_list.clear();y_list.clear();for (size_t i = 0; i < window_info_[max_value_idx].trajectory.size(); i++){x_list.push_back(window_info_[max_value_idx].trajectory[i].pos_x);y_list.push_back(window_info_[max_value_idx].trajectory[i].pos_y);}matplotlibcpp::plot(x_list, y_list, "r");// 画历史轨迹matplotlibcpp::plot(path_x, path_y, "y");// 限制横纵范围matplotlibcpp::xlim(-5.0, road_len_);matplotlibcpp::ylim(-4.0, 4.0);// 设置横纵比例matplotlibcpp::set_aspect(1);// 若还未到终点,则每一次执行结束后停顿0.1sif (to_goal_dist_flag == false){matplotlibcpp::show(false); // 阻塞标志位置为falsematplotlibcpp::pause(0.01);}else{matplotlibcpp::show(true);}// 保存图片
matplotlibcpp::save("./dwa.png");}void DWA::GetEgoPoint(std::vector<double> *x_list, std::vector<double> *y_list)
{x_list->clear();y_list->clear();// 车头中心点double front_posx = vehicle_state_.pos_x;double front_posy = vehicle_state_.pos_y;double heading = vehicle_state_.heading;// 根据车头中心点位置和航向角计算车尾中心点位置double rear_posx = front_posx - veh_len_ * std::cos(heading);double rear_posy = front_posy - veh_len_ * std::sin(heading);// 根据前后中心点、航向角计算目障碍车四个轮廓点位置(顺时针编号)x_list->push_back(rear_posx - veh_width_ / 2 * std::sin(heading));y_list->push_back(rear_posy + veh_width_ / 2 * std::cos(heading));x_list->push_back(front_posx - veh_width_ / 2 * std::sin(heading));y_list->push_back(front_posy + veh_width_ / 2 * std::cos(heading));x_list->push_back(front_posx + veh_width_ / 2 * std::sin(heading));y_list->push_back(front_posy - veh_width_ / 2 * std::cos(heading));x_list->push_back(rear_posx + veh_width_ / 2 * std::sin(heading));y_list->push_back(rear_posy - veh_width_ / 2 * std::cos(heading));
}

main.cpp

#include <iostream>
#include <fstream>
#include <string>
#include "../include/dwa.h"int main()
{std::shared_ptr<DWA> dwa = std::make_shared<DWA>();dwa->run();return 0;
}

CmakeList.txt

cmake_minimum_required(VERSION 3.0.2)
project(DWA)set(CMAKE_CXX_STANDARD 11)file(GLOB_RECURSE PYTHON2.7_LIB "/usr/lib/python2.7/config-x86_64-linux-gnu/*.so")
set(PYTHON2.7_INLCUDE_DIRS "/usr/include/python2.7")find_package(catkin REQUIRED COMPONENTSroscppstd_msgs
)catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES DWA
#  CATKIN_DEPENDS roscpp std_msgs
#  DEPENDS system_lib
)
# 找python包
find_package(Python3 COMPONENTS Development NumPy)include_directories(include${PYTHON2.7_INLCUDE_DIRS}
)add_executable(dwa src/dwa.cppsrc/main.cpp)target_link_libraries(dwa${catkin_LIBRARIES}${PYTHON2.7_LIB}
)

可视化结果:
在这里插入图片描述


DWA算法不正之处望指教

这篇关于【局部路径规划算法】—— DWA动态窗口法(c++实现))的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/875455

相关文章

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::