【ROS-Navigation】—— Astar路径规划算法解析

news2024/12/26 23:43:34

文章目录

  • 前言
  • 1. 导航的相关启动和配置文件
    • 1.1 demo01_gazebo.launch
    • 1.2 nav06_path.launch
    • 1.3 nav04_amcl.launch
    • 1.4 nav05_path.launch
    • 1.5 move_base_params.yaml
    • 1.6 global_planner_params.yaml
  • 2. Astar路径规划算法解析
    • 2.1 astar.h
    • 2.2 astar.cpp
  • 参考文献

前言

    最近在学习ROS的navigation部分,写些东西作为笔记,方便理解与日后查看。本文从Astar算法入手,对navigation源码进行解析。
PS:ros navigation源码版本https://gitcode.net/mirrors/ros-planning/navigation/-/tree/noetic-devel

    对于Astar算法,想必大家都非常熟悉了。具体算法原理就不在本文详细介绍了,不太熟悉的话,可以参考下面这篇博客:
自动驾驶路径规划——A*(Astar)算法

1. 导航的相关启动和配置文件

1.1 demo01_gazebo.launch

<launch>
    <!-- 将 Urdf 文件的内容加载到参数服务器 -->
    <param name="robot_description" command="$(find xacro)/xacro $(find urdf02_gazebo)/urdf/xacro/car.urdf.xacro" />
    <!-- 启动 gazebo -->
    <include file="$(find gazebo_ros)/launch/empty_world.launch" >
        <arg name="world_name" value="$(find urdf02_gazebo)/worlds/test.world"/>
    </include>
    <!-- 在 gazebo 中显示机器人模型 -->
    <node pkg="gazebo_ros" type="spawn_model" name="model" args="-urdf -model mycar -param robot_description  -x -3.182779  -y 0.866966 -Y -1.57"  />
    <!-- <node pkg="gazebo_ros" type="spawn_model" name="model" args="-urdf -model mycar -param robot_description "  /> -->
</launch>

首先该launch文件启动,将机器人模型的xacro文件加载到参数服务器;再启动gazebo,加载出预设的地图以及加载机器人的初始位置.

1.2 nav06_path.launch

<launch>
    <!-- 设置地图的配置文件 -->
    <arg name="map" default="test01.yaml" />
    <!-- 运行地图服务器,并且加载设置的地图-->
    <node name="map_server" pkg="map_server" type="map_server" args="$(find nav_demo)/map/$(arg map)"/>

    <!-- 启动AMCL节点 -->
    <include file="$(find nav_demo)/launch/nav04_amcl.launch" />

    <!-- 启动move_base节点 -->
    <include file="$(find nav_demo)/launch/nav05_path.launch" />

    <!-- 运行rviz -->
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find nav_demo)/config/test02.rviz" />
    <!-- 添加关节状态发布节点 -->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
    <!-- 添加机器人状态发布节点 -->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
</launch>

启动这个launch文件后,将会运行地图服务器,加载设置的地图;启动AMCL节点,实现机器人的定位功能;启动move_base节点,加载move_base相关的参数;运行rviz,显示机器人在rviz中的图像.

1.3 nav04_amcl.launch

这部分就不细讲了,具体可以参考【ROS】—— 机器人导航(仿真)—导航实现(十八)中的amcl部分.

1.4 nav05_path.launch

<launch>
    <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen" clear_params="true">
        <rosparam file="$(find nav_demo)/param/costmap_common_params.yaml" command="load" ns="global_costmap" />
        <rosparam file="$(find nav_demo)/param/costmap_common_params.yaml" command="load" ns="local_costmap" />
        <rosparam file="$(find nav_demo)/param/local_costmap_params.yaml" command="load" />
        <rosparam file="$(find nav_demo)/param/global_costmap_params.yaml" command="load" />
        <!-- <rosparam file="$(find nav_demo)/param/base_local_planner_params.yaml" command="load" /> -->
        <rosparam file="$(find nav_demo)/param/move_base_params.yaml" command="load" />
        <rosparam file="$(find nav_demo)/param/global_planner_params.yaml" command="load" />
        <rosparam file="$(find nav_demo)/param/dwa_local_planner_params.yaml" command="load" />

    </node>
</launch>

这部分是move_base的核心,主要目的是加载代价图的相关参数,包括全局代价地图和局部代价地图的参数;加载move_base的参数以及全局规划器和局部规划器(这里选用的是dwa_local_planner)的相关参数.部分参数文件在【ROS】—— 机器人导航(仿真)—导航实现(十八)中已有讲述,这里就不重复了.本文主要对move_base_params.yaml global_planner_params.yaml进行介绍.

1.5 move_base_params.yaml

参数配置的说明可以参考注释

shutdown_costmaps: false  #当move_base在不活动状态时,是否关掉costmap.

controller_frequency: 15.0  #向底盘控制移动话题cmd_vel发送命令的频率.
controller_patience: 3.0 #在空间清理操作执行前,控制器花多长时间等有效控制下发
 
planner_frequency: 5.0  #全局规划操作的执行频率.如果设置为0.0,则全局规划器仅
                        #在接收到新的目标点或者局部规划器报告路径堵塞时才会重新执行规划操作.
planner_patience: 5.0  #在空间清理操作执行前,留给规划器多长时间来找出一条有效规划.

oscillation_timeout: 10.0  #执行修复机制前,允许振荡的时长.
oscillation_distance: 0.02  #来回运动在多大距离以上不会被认为是振荡.

#全局路径规划器
# base_global_planner: "global_planner/GlobalPlanner" #指定用于move_base的全局规划器插件名称.
# base_global_planner: "navfn/NavfnROS" #指定用于move_base的局部规划器插件名称.
base_global_planner: "global_planner/GlobalPlanner"
# base_global_planner: "carrot_planner/CarrotPlanner"

#局部路径规划器的调用在文件【teb_local_planner.launch】、【dwa_local_planner.launch】对应文件内自动调用,该部分可以忽略
# base_local_planner: "teb_local_planner/TebLocalPlannerROS" #指定用于move_base的局部规划器插件名称.
base_local_planner: "dwa_local_planner/DWAPlannerROS" #指定用于move_base的全局规划器插件名称.


max_planning_retries: 1  #最大规划路径的重复次数,-1表示无限次

recovery_behavior_enabled: true  #是否启动恢复机制
clearing_rotation_allowed: true  #是否启动旋转的恢复,必须是recovery_behavior_enabled在使能的基础上才能生效


recovery_behaviors:  # 自恢复行为
  - name: 'conservative_reset'  
    type: 'clear_costmap_recovery/ClearCostmapRecovery'  
  #- name: 'aggressive_reset'
  #  type: 'clear_costmap_recovery/ClearCostmapRecovery'
  #- name: 'super_reset'
  #  type: 'clear_costmap_recovery/ClearCostmapRecovery'
  - name: 'clearing_rotation'
    type: 'rotate_recovery/RotateRecovery'  
  # - name: 'move_slow_and_clear'
  #   type: 'move_slow_and_clear/MoveSlowAndClear'

#保守清除,用户指定区域之外的障碍物将从机器人地图中清除
conservative_reset:  
  reset_distance: 1.0  
  #layer_names: [static_layer, obstacle_layer, inflation_layer]
  layer_names: [obstacle_layer]  
#保守清除后,如果周围障碍物允许,机器人将进行原地旋转以清理空间

#保守清除失败,积极清除,清除指定区域之外的所有障碍物,然后进行旋转
aggressive_reset:  
  reset_distance: 3.0  
  #layer_names: [static_layer, obstacle_layer, inflation_layer]
  layer_names: [obstacle_layer]
#积极清除也失败后,放弃规划路径

#可能是更进一步的清除,wiki未找到相关资料
super_reset:  
  reset_distance: 5.0  
  #layer_names: [static_layer, obstacle_layer, inflation_layer]
  layer_names: [obstacle_layer]

#另一种恢复行为,需要注意该行为只能与具有动态设置速度限制功能的局部路径规划器适配,例如dwa
move_slow_and_clear:  
  clearing_distance: 0.5  #与小车距离0.5内的障碍物会被清除
  limited_trans_speed: 0.1  
  limited_rot_speed: 0.4  
  limited_distance: 0.3  
 base_global_planner: "navfn/NavfnROS"
 base_global_planner: "global_planner/GlobalPlanner"
 base_global_planner: "carrot_planner/CarrotPlanner"

此部分指定用于move_base的局部规划器插件名称.这里选择global_planner/GlobalPlanner,当然也可以选择navfn/NavfnROS,只不过后者版本较老,而carrot_planner/CarrotPlanner适合目标点距离障碍物比较近的规划场景,对于大部分导航场景来说,可能并非那么实用.

recovery_behavior_enabled: true  
clearing_rotation_allowed: true 

此部分来决定是否启动恢复机制与旋转机制(可以减少小车规划路径失败导致的卡死现象)

1.6 global_planner_params.yaml

GlobalPlanner:
  allow_unknown: false  #默认true,是否允许路径穿过未知区域
  default_tolerance: 0.2  #默认0.0,目标容差
  visualize_potential: false #默认false,是否显示从PointCloud2计算得到的势区域
  use_dijkstra: false #默认true,true表示使用dijkstra's否则使用A*
  use_quadratic: true #默认true,true表示使用二次函数近似函数
  use_grid_path: false #默认false,true表示使路径沿栅格边界生成,否则使用梯度下降算法
  old_navfn_behavior:  true #默认false,是否复制navfn规划器的结果
  lethal_cost: 253 #默认253,致命代价值
  neutral_cost: 50 #默认50,中等代价值
  cost_factor: 3.0 #默认3.0,代价因子
  publish_potential: true #默认true,是否发布costmap的势函数
  orientation_mode: 0 #默认0,设置点的方向
  orientation_window_size: 1 #默认1,根据orientation_mode指定的位置积分确定窗口方向

在修改这部分参数的时候遇到了一些问题:
若要启动Astar算法,需将use_dijkstra:置为false,此时运行遇到以下问题

[ERROR] [1674480358.446397317, 514.551000000]: NO PATH!
[ERROR] [1674480358.446429697, 514.551000000]: Failed to get a plan from potential when a legal potential was found. This shouldn't happen.

参考Navigation中全局路径规划效果对比和Some errors when using GlobalPlanner,得出以下可能的原因:
在ROS navigation包中A*算法是基于梯度势场实现的,而并非我们所熟知的栅格地图,这种方式可能是非最优的,但其规划出的路径平滑度更高.但这种方法在"precise start(精确启动?)"机制上可能存在着一些BUG,所以经常会启动失败.

解决方法:

  1. use_grid_path置为true.即将其转化为基于栅格地图的Astar算法.实现效果如下图所示(规划出的路线不光滑,折线明显).
    在这里插入图片描述
  2. old_navfn_behavior置为true.使用旧版本的规划器.实现效果如下图所示

也有人通过减小inflation radius 并增大 cost scaling factor,从而使得全局代价地图在障碍物周围膨胀得并不多,从而不至于规划失败.这种方法可以参考,但我没有细调过参数所以没有实现.

2. Astar路径规划算法解析

附上Astar的源码

2.1 astar.h

#ifndef _ASTAR_H
#define _ASTAR_H

#include <global_planner/planner_core.h>
#include <global_planner/expander.h>
#include <vector>
#include <algorithm>

namespace global_planner {
class Index {
    public:
        Index(int a, float b) {
            i = a;
            cost = b;
        }
        int i;
        float cost;
};

struct greater1 {
        bool operator()(const Index& a, const Index& b) const {
            return a.cost > b.cost;
        }
};

class AStarExpansion : public Expander {
    public:
        AStarExpansion(PotentialCalculator* p_calc, int nx, int ny);
        bool calculatePotentials(unsigned char* costs, double start_x, double start_y, double end_x, double end_y, int cycles,
                                float* potential);
    private:
        void add(unsigned char* costs, float* potential, float prev_potential, int next_i, int end_x, int end_y);
        std::vector<Index> queue_;
};

} //end namespace global_planner
#endif

2.2 astar.cpp

#include<global_planner/astar.h>
#include<costmap_2d/cost_values.h>

namespace global_planner {

AStarExpansion::AStarExpansion(PotentialCalculator* p_calc, int xs, int ys) :
        Expander(p_calc, xs, ys) {
}

/*	function:计算规划代价的函数
	potential: 代价数组, 
	costs: 地图指针, 
	cycles:循环次数; 
*/
bool AStarExpansion::calculatePotentials(unsigned char* costs, double start_x, double start_y, double end_x, double end_y,  int cycles, float* potential) {
	//queue_为启发式搜索到的向量队列:<i , cost>,每次规划前清空队列里所有的元素
    queue_.clear();
	//起点对应的序号
    int start_i = toIndex(start_x, start_y);
	//1 将起点放入队列
    queue_.push_back(Index(start_i, 0));
	//2 将所有点的potential都设为一个极大值1e10
	//potential就是估计值g, f=g+h
	//std::fill函数的作用是:将一个区间的元素都赋予指定的值,即在[first, last)范围内填充指定值
    std::fill(potential, potential + ns_, POT_HIGH);
	//3 起点的potential设为0;
    potential[start_i] = 0;

	//终点对应的序号
    int goal_i = toIndex(end_x, end_y); 
    int cycle = 0;

	//4 进入循环,继续循环的判断条件为只要队列大小大于0且循环次数小于cycles 
	//代码中cycles = 2 *nx * ny, 即为所有格子数的2倍
	///目的:得到最小cost的索引,并删除它,如果索引指向goal(目的地)则退出算法,返回true
    while (queue_.size() > 0 && cycle < cycles) {
		//最小代价点
        Index top = queue_[0];
		///将首元素放到最后,其他元素按照Cost值从小到大排列
		//pop_heap() 是将堆顶元素与最后一个元素交换位置,之后用pop_back将最后一个元素删除
		//greater1()是按小顶堆
        std::pop_heap(queue_.begin(), queue_.end(), greater1());
		//删除最后一个元素,即删除最小代价的点
        queue_.pop_back();

        int i = top.i;
		//如果到了目标点,就结束了
        if (i == goal_i)
            return true;

		//6. 对前后左右四个点执行add函数,将代价最小点i周围点加入搜索队里并更新代价值
        add(costs, potential, potential[i], i + 1, end_x, end_y);
        add(costs, potential, potential[i], i - 1, end_x, end_y);
        add(costs, potential, potential[i], i + nx_, end_x, end_y);
        add(costs, potential, potential[i], i - nx_, end_x, end_y);

        cycle++;
    }

    return false;
}

/*add函数:添加点并更新代价函数;
如果是已经添加的的点则忽略,根据costmap的值如果是障碍物的点也忽略。
potential[next_i]是起点到当前点的cost即g(n), 
distance * neutral_cost_是当前点到目的点的cost即h(n)。
f(n) = g(n) + h(n)
*/
void AStarExpansion::add(unsigned char* costs, float* potential, float prev_potential, int next_i, int end_x, int end_y) {
	//next_i 点不在网格内 忽略
    if (next_i < 0 || next_i >= ns_)
        return;

	//未搜索的点cost为POT_HIGH,如小于该值,则为已搜索点,跳过;
    if (potential[next_i] < POT_HIGH)
        return;

	//障碍物点 忽略
    if(costs[next_i]>=lethal_cost_ && !(unknown_ && costs[next_i]==costmap_2d::NO_INFORMATION))
        return;

	
	/*
	  potential 存储所有点的g(n),即从初始点到节点n的实际代价
	  costs[next_i] + neutral_cost_
	  potential[next_i]   是起点到当前点的cost即g(n)
	  neutral_cost_ 设定的一个默认值,为50
	  prev_potentia   当前点的f

	  calculatePotential()计算根据use_quadratic的值有下面两个选择
	        若为TRUE, 则使用二次曲线计算
			若为False, 则采用简单方法计算, return prev_potential + cost
						即:costs[next_i] + neutral_cost_+ prev_potential
					 地图代价+单格距离代价(初始化为50)+之前路径代价 为G
	*/
    potential[next_i] = p_calc_->calculatePotential(potential, costs[next_i] + neutral_cost_, next_i, prev_potential);
    int x = next_i % nx_, y = next_i / nx_;
	//启发式函数:即h(n) 从节点n到目标点最佳路径的估计代价,这里选用了曼哈顿距离
    float distance = abs(end_x - x) + abs(end_y - y);

	//两个cost后,加起来即为f(n),代价 f = g +h;,将其存入队列中
	// f = potential[next_i] + distance * neutral_cost_ 为总
    queue_.push_back(Index(next_i, potential[next_i] + distance * neutral_cost_));

	//对加入的再进行堆排序, 把最小代价点放到front队头queue_[0]
    std::push_heap(queue_.begin(), queue_.end(), greater1());
}

} 

首先来看一下AStarExpansion::calculatePotentials这个函数,这个函数的传参为地图指针costs,起点和终点的位置信息,循环次数cycles以及代价数组potential.

程序开始,先对启发式搜索得到的向量队列进行初始化

queue_.clear();

再将起始点的位置信息转化为序列号

int start_i = toIndex(start_x, start_y);

toIndex()具体定义如下:

        inline int toIndex(int x, int y) {
            return x + nx_ * y;
        }//nx_为栅格地图中x方向的像素数目
  1. 将起点放入队列之中

index()定义如下

// 第一项为序列号值,第二项为代价值
class Index {
    public:
        Index(int a, float b) {
            i = a;
            cost = b;
        }
        int i;
        float cost;
};
  1. 将所有点的potential都设为一个极大值 1 e 10 1e10 1e10
    potential就是实际代价值 g , f = g + h g, f=g+h g,f=g+h,在planner_core.h中,POT_HIGH定义如下
#define POT_HIGH 1.0e10

std::fill函数的作用是:将一个区间的元素都赋予指定的值,即在[first, last)范围内填充指定值

  1. 起点的potential设为0;
potential[start_i] = 0;

再将终点的位置信息转化为序列号

int goal_i = toIndex(end_x, end_y); 
  1. 进入循环,继续循环的判断条件为:队列大小大于0且循环次数小于cycles
    代码中cycles = 2 *nx * ny, 即为所有格子数的2倍
    目的:得到最小cost的索引,并删除它,如果索引指向goal(目的地)则退出算法,返回true

top为最小代价点

Index top = queue_[0];

将首元素放到最后,其他元素按照Cost值从小到大排列
pop_heap() 是将堆顶元素与最后一个元素交换位置,之后用pop_back将最后一个元素删除,greater1()是按小顶堆的方式

std::pop_heap(queue_.begin(), queue_.end(), greater1());

删除最后一个元素,即删除最小代价的点

queue_.pop_back();

当找到目标点时,规划结束

int i = top.i;
if (i == goal_i)
    return true;

最后对前后左右四个点执行add函数,将代价最小点i周围点加入搜索队里并更新代价值(和grid算法不同,这里的Astar是四邻域的)

        add(costs, potential, potential[i], i + 1, end_x, end_y);
        add(costs, potential, potential[i], i - 1, end_x, end_y);
        add(costs, potential, potential[i], i + nx_, end_x, end_y);
        add(costs, potential, potential[i], i - nx_, end_x, end_y);

再来看看AStarExpansion::add函数
add函数的作用:添加点并更新代价

首先判断新的点是否在地图内;是否被访问过;是否是障碍物.是的话,跳过这个点.

    if (next_i < 0 || next_i >= ns_)
        return;

    if (potential[next_i] < POT_HIGH)
        return;

    if(costs[next_i]>=lethal_cost_ && !(unknown_ && costs[next_i]==costmap_2d::NO_INFORMATION))
        return;

计算start_i到 邻点(i+1,i-1,i+nx,i-nx) 的最小代价g(n),

potential[next_i] = p_calc_->calculatePotential(potential, costs[next_i] + neutral_cost_, next_i, prev_potential);

potential 存储所有点的g(n),即从初始点到节点n的实际代 costs[next_i] + neutral_cost_
potential[next_i] 是起点到当前点的cost即g(n)
neutral_cost_ 设定的一个默认值,为50
prev_potentia 当前点的f

        virtual float calculatePotential(float* potential, unsigned char cost, int n, float prev_potential=-1){
            if(prev_potential < 0){
                // get min of neighbors
                float min_h = std::min( potential[n - 1], potential[n + 1] ),
                      min_v = std::min( potential[n - nx_], potential[n + nx_]);
                prev_potential = std::min(min_h, min_v);
            }

            return prev_potential + cost;
        }

启发式函数:即h(n) 从节点n到目标点最佳路径的估计代价,这里选用了曼哈顿距离

float distance = abs(end_x - x) + abs(end_y - y);

两个cost后,加起来即为f(n),代价 f = g +h;,将其存入队列中

    queue_.push_back(Index(next_i, potential[next_i] + distance * neutral_cost_));

对加入的再进行堆排序, 把最小代价点放到front队头queue_[0]

std::push_heap(queue_.begin(), queue_.end(), greater1());

参考文献

[1] ros navigation 解析之A*路径规划
[2] Navigation中全局路径规划效果对比
[3] ROS - move_base全局路径规划之A*程序分析
[4] 移动机器人gazebo仿真(5)—规划算法A*

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《职场求生攻略》学习笔记 Day8

系列文章目录 这是本周期内系列打卡文章的所有文章的目录 《Go 并发数据结构和算法实践》学习笔记 Day 1《Go 并发数据结构和算法实践》学习笔记 Day 2《说透芯片》学习笔记 Day 3《深入浅出计算机组成原理》学习笔记 Day 4《编程高手必学的内存知识》学习笔记 Day 5NUMA内存知…

JavaEE-多线程进阶

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录常见的锁策略乐观锁 vs 悲观锁轻量级锁 vs 重量级锁自旋锁 vs 挂起等待锁互斥锁 vs 读写锁公平锁 vs 非公平锁可重入锁 vs 不可…

恶意代码分析实战 8 恶意代码行为

8.1 Lab 11-01 代码分析 首先使用strings进行分析。 Gina是在 msgina.dll中的。 很多有关资源的函数。 关于注册表的函数。 使用ResourceHacker查看。 发现是一个PE文件。 保存为dll文件。 动态分析 启动Promon。 进入注册表查看。 这个恶意代码向磁盘释放了什么&…

分享140个ASP源码,总有一款适合您

ASP源码 分享140个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 140个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1vxAMMEI7WYS8SAnfbJKdGQ?pwdsas8 提取码&#x…

【ARIXV2209】Multi-Scale Attention Network for Single Image Super-Resolution

【ARIXV2209】Multi-Scale Attention Network for Single Image Super-Resolution 代码&#xff1a;https://github.com/icandle/MAN 这是来自南开大学的工作&#xff0c;将多尺度机制与大核注意机制结合&#xff0c;用于图像超分辨率。 2022年初&#xff0c;大核卷积火了&a…

GBD学习

GBD gbd官网 GDB, the GNU Project debugger 是一个debug工具 支持多种语言&#xff1a; Ada、Assembly、C、C、D、Fortran、Go、 Objective-C、OpenCL、Modula-2、Pascal、Rust 编译文件 首先使用gcc -g .c文件 -o 可执行文件名 进行编译&#xff0c;再使用gdb 可执行文件名…

恶意代码分析实战 10 数据加密

10.1 Lab13-01 比较恶意代码中的字符串&#xff08;字符串命令的输出&#xff09;与动态分析提供的有用信息&#xff0c;基于这些比较&#xff0c;哪些元素可能被加密&#xff1f; 使用WireShark进行动态分析。 有一串字符看起来像是加密的。 使用Strings分析一下。 发现疑似…

KUKA机器人初次通电配置

安全配置 机器人KSS系统里&#xff0c;会提示选择机器人信息的对话框&#xff0c;选择“机器人”按钮&#xff0c;如下&#xff1a; 承接上一步骤&#xff0c;通过示教器确认所有消息&#xff0c;单击消息提示区域&#xff0c;此时一定会弹出如图 所标示的报警消息&#xff0…

【唐诗学习】一、古诗概述

一、古诗概述 为什么要学习古诗词&#xff1f; 古诗词可以陶冶情操&#xff0c;传承文化诗词&#xff0c;其实就是古代的流行歌曲&#xff0c;它们记录着一个个时代的变迁&#xff0c;是历史的旋律。还有一点很重要&#xff1a;同样是记录历史&#xff0c;史书是国家视角&…

零基础学JavaWeb开发(二十二)之 springmvc入门到精通(2)

3、SpringMVC PostMan工具使用 PostMan简介 Postman 是一款功能超级强大的用于发送 HTTP 请求的 Chrome插件 。做web页面开发和测试的人员会使用到该工具其主要特点 特点&#xff1a; 创建 测试&#xff1a;创建和发送任何的HTTP请求&#xff0c;使用PostMan发送 Get、Post、…

Java IO流之字符流详解

字符流概述 字符流的底层其实就是字节流 字符流 字节流 字符集 字符流特点 输入流&#xff1a;一次读一个字节&#xff0c;遇到中文时&#xff0c;一次读多个字节输出流&#xff1a;底层会把数据按照指定的编码方式进行编码&#xff0c;变成字节再写到文件中 使用场景 用于…

学习Go的全部网站集合

给Golang 入门新手整理了一份全部所需网站集合&#xff0c;主要分为三类。 社区类&#xff1a;这类网站是问答、文档、搜索、资源类网站汇总 镜像和安装包类&#xff1a;有些安装包可能无法下载&#xff0c;此处列出一些可下载Go包和镜像的网站。 开发工具&#xff1a;目前流…

代码随想录--栈与队列章节总结

代码随想录–栈与队列章节总结 1.LeetCode232 用栈实现队列 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾i…

2.4.2 浮点型

1.浮点型基本数据类型介绍 浮点类型用于表示有小数部分的数值。在JAVA种有俩种浮点类型&#xff0c;分别是float和double. 类型字节长度位数取值范围float4字节32约 3.40282347E38Fdouble8字节64约 1.79769313486231570E308 double表示这种类型的数值精度是float类型的俩倍&a…

PyCharm中运行LeetCode中代码

Leetcode中题目只需要写函数体里面内容即可。不需要关注输入&#xff0c;输出。 这里拿LeetCode中第一题&#xff0c;两数之和 “给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数…

随机梯度下降算法 入门介绍(最通俗易懂)

文章目录1.什么是梯度2.什么是梯度下降算法3.什么是随机梯度下降算法1.什么是梯度 首先给出高数课程中梯度的定义&#xff1a; 如果对上面的定义没有理解也没有关系&#xff0c;用通俗的语言来说&#xff0c;梯度实际上就是一个向量&#xff0c;向量中的各个元素表示多元函数在…

Java IO流之字节流详解

一、OutputStream输出流 FileOutputStream概述&#xff1a; 操作本地文件的字节输出流&#xff0c;可以把程序中的数据写到本地文件中 书写步骤&#xff1a; ① 创建字节输出流对象 细节1&#xff1a;参数一是字符串表示的路径或者File对象都是可以的 细节2&#xff1a;如果文…

《安富莱嵌入式周报》第300期:几百种炫酷灯阵玩法, USB Web网页固件升级,波士顿动力整活,并联二极管问题,VisualStudio升级,STM32C0

往期周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 祝大家春节快乐&#xff01; 视频版&#xff1a; https://www.bilibili.com/video/BV1UY4y1d7C7 《安富莱嵌入式周…

(考研湖科大教书匠计算机网络)第二章物理层-第三、四节:传输方式和编码与调制

文章目录一&#xff1a;传输方式&#xff08;1&#xff09;串行传输和并行传输&#xff08;2&#xff09;同步传输和异步传输&#xff08;3&#xff09;单工、半双工和全双工二&#xff1a;编码与调制&#xff08;1&#xff09;通信相关基础知识①&#xff1a;消息、数据、信号…

linux实战笔记整理(1.24)

后台挂起程序: 如果报错&#xff1a;nohup: ignoring input and appending output to nohup.out&#xff0c;则在指令最后加一个&让程序自己运行&#xff1a;nohup command增加rm 提醒&#xff1a;&#xff08;重要的命令问三遍&#xff09;&#xff1a; 操作&#xff1a; …