移动机器人激光SLAM导航(四):GMapping SLAM 篇

参考引用

  • GMapping ROS-Wiki
  • 从零开始搭二维激光SLAM
  • GMapping 漫谈
  • 小白学移动机器人
  • 机器人工匠阿杰
  • wpr_simulation

移动机器人激光SLAM导航(文章链接汇总)

1. GMapping

1.1 FastSLAM 问题分解

概率论相关公式

  • 1. 条件概率公式 p ( x , y ) = p ( x ∣ y ) p ( y ) = p ( y ∣ x ) p ( x ) p(x,y)=p(x|y)p(y)=p(y|x)p(x) p(x,y)=p(xy)p(y)=p(yx)p(x)
  • 2. 贝叶斯公式 p ( x ∣ y ) = p ( y ∣ x ) p ( x ) p ( y ) = η p ( y ∣ x ) p ( x ) , η 为归一化常数 p(x|y)=\frac{p(y|x)p(x)}{p(y)}=\eta p(y|x)p(x), \eta为归一化常数 p(xy)=p(y)p(yx)p(x)=ηp(yx)p(x),η为归一化常数
  • 3. 条件贝叶斯公式 p ( x ∣ y , z ) = p ( y ∣ x , z ) p ( x ∣ z ) p ( y ∣ z ) = η p ( y ∣ x , z ) p ( x ∣ z ) p(x|y,z)=\frac{p(y|x,z)p(x|z)}{p(y|z)}=\eta p(y|x,z)p(x|z) p(xy,z)=p(yz)p(yx,z)p(xz)=ηp(yx,z)p(xz)
  • 4. 条件联合概率公式 p ( x , y ∣ z ) = p ( x ∣ y , z ) p ( y ∣ z ) p(x,y|z)=p(x|y,z)p(y|z) p(x,yz)=p(xy,z)p(yz)
  • FastSLAM 算法采用 RBPF(Rao-Blackwellized Particle Filters) 方法,将 SLAM 分解成两个问题:机器人定位问题 & 已知机器人定位进行地图构建问题。在定位问题中,每一个粒子只需要表示一个可能的姿态,但 SLAM 中的姿态和地图都是变量,所以一个粒子需要同时保存所有历史时刻的机器人位姿和整个地图
    • p ( x 1 : t ∣ z 1 : t , u 1 : t − 1 ) p(x_{1:t}\mid z_{1:t},u_{1:t-1}) p(x1:tz1:t,u1:t1) :利用上一时刻里程计数据和当前时刻的传感器观测来估计机器人的轨迹/位姿(定位)
    • p ( m ∣ x 1 : t , z 1 : t ) p(m\mid x_{1:t},z_{1:t}) p(mx1:t,z1:t) :利用当前时刻的机器人的轨迹/位姿和传感器观测来估计环境地图(建图)
    • 通过上一个时刻的地图和运动模型预测当前的位姿,然后计算权重,即在当前位姿下得到当前观测的可能性,再进行重采样,进而更新粒子地图,如此往复

p ( x 1 : t , m ∣ z 1 : t , u 1 : t − 1 ) = p ( m ∣ x 1 : t , z 1 : t ) ⋅ p ( x 1 : t ∣ z 1 : t , u 1 : t − 1 ) . \begin{matrix}p(x_{1:t},m\mid z_{1:t},u_{1:t-1})=\\p(m\mid x_{1:t},z_{1:t})\cdot p(x_{1:t}\mid z_{1:t},u_{1:t-1}).\end{matrix} p(x1:t,mz1:t,u1:t1)=p(mx1:t,z1:t)p(x1:tz1:t,u1:t1).

1.2 GMapping 改善方案

1.2.1 改善提议分布,降低粒子数量

问题:建图对机器人位姿要求较高,对任意一个粒子,仅仅依靠运动模型采样(里程计)的结果构建地图,误差会非常大,通过运动模型得到的提议分布(proposal)太过于分散,与真实分布相差过大,则需要较多的粒子才能更好的表示机器人位姿的估计,这样将会造成内存使用与计算量急剧上升

  • 方案 1:采用极大似然估计

    • 里程计的概率模型比较平滑,范围较大,如果对整个范围采样将需要很多的粒子,如果能找到一个位姿优值,在其周围进行小范围采样,就可以降低粒子数量
    • 因此直接采用极大似然估计的方式,根据粒子的位姿预测分布与地图的匹配程度,通过扫描匹配找到粒子的最优位姿参数,就用该位姿参数直接当做新粒子的位姿
  • 方案 2:激光雷达的观测模型

    • 粒子滤波采用里程计运动模型作为提议分布,导致大部分粒子都偏离真实位置,而与运动模型相反,激光雷达的观测模型则可以给出一个相对集中的分布
    • 通过激光雷达最近一帧的观测(scan),把 proposal 分布限制在一个狭小的有效区域,然后再对 proposal 分布进行采样,这样可以用更少的粒子来覆盖机器人位姿的概率分布,从下图可以看到激光雷达观测模型方差较小
      在这里插入图片描述
  • 总结如下

    • 1、首先按照里程计运动模型给出预测,图 1 中所有粒子通过里程计运动模型预测出新的位置如图 2 所示
    • 2、以图 2 粒子的预测位置为初始值,对每一个粒子进行一次扫描匹配,通过扫描匹配得到该粒子的最优位姿如图 3,使得当前观测与该粒子所携带的地图最为贴合
    • 3、得到每个粒子的最优位姿后,再在每个粒子的附近撒播 k 个粒子,用高斯函数来模拟提议分布如图 4 所示,根据这 k 个粒子的里程计和观测模型来计算高斯分布均值和方差
    • 4、新的粒子从高斯分布中采样得到,对于每个粒子,还需要赋予一个权值,以供后续的重采样步骤使用
      在这里插入图片描述
1.2.2 限制重采样次数,缓解粒子耗散

问题:GMapping 采用粒子滤波算法对移动机器人轨迹进行估计,必然少不了粒子重采样过程,但随着重采样次数的增多,会出现所有粒子都从一个粒子复制而来,粒子的多样性会消失

  • 解决方案:自适应重采样
    • 重采样的目的是抛弃那些明显远离真实值的粒子,增强那些离真实值近的粒子
      • 如果所有的粒子都在真实值附近且分布均匀,那么就没有理由执行重采样
      • 在回环发生之前,即使有些粒子已经远离了真实值,但现有的观测不足以区分正确的粒子和错误的粒子,也没有理由执行重采样
      • 只有在回环发生之后,新的观测彻底拉开正确粒子与错误粒子权重差距,此时重采样才能有效
    • 根据所有粒子自身权重的离散程度(也就是权重方差)来决定是否进行粒子重采样的操作
      • Neff 越大,粒子的权重差距越小,当 Neff 小于某个阈值,说明粒子的权重差距较大,即粒子的分布与真实分布差距很大,在粒子层面表现为某些粒子离真实值很近,而很多粒子离真实值较远。这正是回环发生时经常出现的情况,此时就应该进行重采样,否则不进行采样

N e f f = 1 ∑ i = 1 N ( w ~ ( i ) ) 2 N_{\mathrm{eff}}=\frac1{\sum_{i=1}^N\left(\tilde{w}^{(i)}\right)^2} Neff=i=1N(w~(i))21

1.3 GMapping 解析

1.3.1 简介
  • GMapping 是一个基于 2D 激光雷达使用 RBPF 算法完成二维栅格地图构建的 SLAM 算法
    • 通过里程计数据获取粒子群的先验位姿,再通过雷达数据与地图的匹配程度对所有粒子进行打分,通过分数高的粒子群来近似机器人的真实位姿
    • 作为 ROS 节点称为 slam_gmapping
  • 硬件要求
    • 需要一个提供里程计数据并配备一个水平安装的、固定的 2D 激光雷达的移动机器人,slam_gmapping 节点将尝试将每次传入的扫描(scan)转化为 odom (里程计) tf 坐标系
  • 优缺点
    • 可实时构建室内环境地图,在小场景中计算量少且地图精度较高,对激光雷达扫描频率要求较低
    • 不适合大场景建图

    对于 200 x 200 米的范围,如果栅格分辨率是 5cm,每个栅格占用一个字节内存,那么每个粒子携带的地图都要 16M 的内存,如果是 100 粒子就是 1.6G 内存

1.3.2 话题节点
  • 订阅的话题(Topic)
    • /tf (tf/tfMessage):订阅激光雷达坐标系、基坐标系和里程计坐标系之间的变换
    • /scan (sensor_msgs/LaserScan):订阅 2D 激光雷达扫描数据
  • 发布的话题(Topic)
    • map_metadata (nav_msgs/MapMetaData):发布 Meta(元)地图数据
    • map (nav_msgs/OccupancyGrid):发布占据栅格地图数据
    • ~entropy (std_msgs/Float64):发布机器人定位的置信度(越大则定位错误的可能性越大)
  • Service
    • dynamic_map (nav_msgs/GetMap):获取地图数据
1.3.3 TF 变换

有关 TF 基础请查看往期博客:ROS学习5:ROS常用组件

  • 必要的 TF 变换

    • <scan frame> → base_link:激光雷达坐标系 与 基坐标系 之间的变换,一般由 robot_state_publisher 或 static_transform_publisher 发布
    • base_link → odom:基坐标系 与 里程计坐标系 之间的变换,一般由里程计节点发布
  • 发布的 TF 变换

    • map → odom:地图坐标系 与 里程计坐标系 之间的变换,估计机器人在地图 map 坐标系中的位姿

1.4 建图测试

本小节使用 移动机器人激光SLAM导航(二):运动控制与传感器篇 中安装的测试环境 wpr_simulation(GMapping 已在此环境中通过脚本安装)

  • 沿用 移动机器人激光SLAM导航(三):Hector SLAM 篇 中 2.4 小节创建的功能包 slam_pkg,在 slam_pkg 中的 launch 文件夹中新建 gmapping.launch 文件
    <launch><include file="$(find wpr_simulation)/launch/wpb_stage_robocup.launch" /><node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" /><node pkg="rviz" type="rviz" name="rviz" args="-d $(find slam_pkg)/rviz/slam.rviz" /><node pkg="wpr_simulation" type="keyboard_vel_ctrl" name="keyboard_vel_ctrl" />
    </launch>
    
  • 编译并启动 gmapping.launch 建图
    $ cd ~/wpr_ws
    $ catkin_make
    $ source devel/setup.bash
    $ roslaunch slam_pkg gmapping.launch
    

在这里插入图片描述

  • 机器人运动控制节点 keyboard_vel_ctrl
    #include <ros/ros.h>
    #include <geometry_msgs/Twist.h>
    #include <stdio.h>
    #include <termios.h>static float linear_vel = 0.1;
    static float angular_vel = 0.1;
    static int k_vel = 3;int GetCh() {static struct termios oldt, newt;tcgetattr(STDIN_FILENO, &oldt);newt = oldt;newt.c_lflag &= ~(ICANON);tcsetattr(STDIN_FILENO, TCSANOW, &newt);int c = getchar();tcsetattr(STDIN_FILENO, TCSANOW, &oldt);return c;
    }int main(int argc, char** argv) {ros::init(argc, argv, "keyboard_vel_cmd");printf("键盘控制 WPR 机器人: \n");printf("w - 向前加速 \n");printf("s - 向后加速 \n");printf("a - 向左加速 \n");printf("d - 向右加速 \n");printf("q - 左旋加速 \n");printf("e - 右旋加速 \n");printf("空格 - 刹车 \n");printf("x - 退出 \n");printf("------------- \n");ros::NodeHandle n;ros::Publisher cmd_vel_pub = n.advertise<geometry_msgs::Twist>("/cmd_vel", 10);geometry_msgs::Twist base_cmd;base_cmd.linear.x = 0;base_cmd.linear.y = 0;base_cmd.angular.z = 0;while(n.ok()) {int cKey = GetCh();if (cKey == 'w') {base_cmd.linear.x += linear_vel;if (base_cmd.linear.x > linear_vel*k_vel)base_cmd.linear.x = linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 's') {base_cmd.linear.x += -linear_vel;if (base_cmd.linear.x < -linear_vel*k_vel)base_cmd.linear.x = -linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'a') {base_cmd.linear.y += linear_vel;if (base_cmd.linear.y > linear_vel*k_vel)base_cmd.linear.y = linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'd') {base_cmd.linear.y += -linear_vel;if (base_cmd.linear.y < -linear_vel*k_vel)base_cmd.linear.y = -linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);}  else if (cKey == 'q') {base_cmd.angular.z += angular_vel;if (base_cmd.angular.z > angular_vel*k_vel)base_cmd.angular.z = angular_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'e') {base_cmd.angular.z += -angular_vel;if (base_cmd.angular.z < -angular_vel*k_vel)base_cmd.angular.z = -angular_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == ' ') {base_cmd.linear.x = 0;base_cmd.linear.y = 0;base_cmd.angular.z = 0;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'x') {base_cmd.linear.x = 0;base_cmd.linear.y = 0;base_cmd.angular.z = 0;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);printf("退出! \n");return 0;} else {printf(" - 未定义指令 \n");}}return 0;
    }
    

1.5 建图参数设置 Parameters

在这里插入图片描述

在这里插入图片描述

<launch><include file="$(find wpr_simulation)/launch/wpb_stage_robocup.launch" /><node pkg="gmapping" type="slam_gmapping" name="slam_gmapping"><param name="maxUrange" value="3.0" /><param name="map_update_interval" value="0.5" /><param name="linearUpdate" value="0.1" /></node><node pkg="rviz" type="rviz" name="rviz" args="-d $(find slam_pkg)/rviz/slam.rviz" /><node pkg="wpr_simulation" type="keyboard_vel_ctrl" name="keyboard_vel_ctrl" />
</launch>

2. 地图的保存与加载

使用 map_server 软件包实现占据栅格地图的保存与加载

2.1 保存地图

  • GMapping 建图

    $ cd ~/wpr_ws
    $ source devel/setup.bash
    $ roslaunch slam_pkg gmapping.launch
    
  • 保存地图

    $ rosrun map_server map_saver -f mymap
    

    通过上述保存地图指令会在 /home 目录生成 mymap.pgm(地图图片)和 mymap.yaml(地图信息) 两个文件

  • mymap.yaml

    image: mymap.pgm  # 到包含占用数据的图像文件的路径;可以是绝对的,也可以是相对于 YAML 文件的位置
    resolution: 0.050000   # 地图分辨率,单位:米/像素
    origin: [-100.000000, -100.000000, 0.000000]  # 地图中左下方像素的二维姿态为 (x, y, 偏航),偏航为逆时针旋转(偏航 = 0表示无转动)
    negate: 0    # 白/黑的空闲/被占用 语义是否应该颠倒 (阈值的解释不受影响)
    occupied_thresh: 0.65  # 占据概率大于该阈值的像素被认为是完全占据的
    free_thresh: 0.196     # 占有率小于这个阈值的像素被认为是完全自由的
    

2.2 加载地图

  • 加载地图
    $ roscore
    
    $ cd ~
    $ rosrun map_server map_server mymap.yaml
    
    $ rosrun rviz rviz
    

在这里插入图片描述

3. 2D 激光雷达运动畸变去除

  • 详解2D激光雷达运动畸变去除

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

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

相关文章

将链表反转

反转链表在解决需要从尾节点开始遍历到头节点的地方很实用&#xff0c;是一种常用的解题技巧。在反转时&#xff0c;我们可以考虑从前向后反转和从后向前反转两种方式。 法一&#xff1a;递归 每次将链表的头节点的下一个节点作为新的头节点&#xff0c;然后对剩余部分调用递归…

深度学习入门笔记(五)前馈网络与反向传播

接着上一节,本节讲解模型自我学习的数学计算过程究竟是怎么样的。 5.1 前馈网络 一个最简单的前馈神经网络如图所示,对于每一个隐藏层,输入对应前一层每一个节点权重乘以节点输出值,输出则是经过激活函数(例如sigmoid函数)计算后的值。 在这样的网络中,输入的数据 x 经…

小白水平理解面试经典题目_数组类Leetcode 412. Fizz Buzz【数学解法】

412 FizzBuzz 小白渣翻译&#xff1a; 给定一个整数 n &#xff0c;返回一个字符串数组 answer &#xff08;从 1 开始索引&#xff09;&#xff0c;其中&#xff1a; answer[i] “FizzBuzz” 如果 i 能被 3 和 5 整除。answer[i] “Fizz” 如果 i 能被 3 整除。answer[i]…

8. 实现VLAN间的通信

文章目录 一. 初识VLAN通信1.1. VLAN的概述1.2. Dot1q 终结子接口(单臂路由 )1.3. VLANIF接口 二. 实验专题2.1. 实验1&#xff1a; Dotlq 终结子接口2.1.1. 实验目的2.1.2. 实验拓扑图2.1.3. 实验步骤&#xff08;1&#xff09;配置PC机的网络&#xff08;2&#xff09;交换机…

八、访存顺序(Memory Ordering)

前言 这部分的内容比较抽象&#xff0c;很多内容我无法理解&#xff0c;都是直接翻译过来的。虽然难&#xff0c;但是不可不看&#xff0c;如果遇到无法理解的都直接跳过&#xff0c;那后面都无法学习下去了。觉得无法理解是因为目前的知识还很欠缺&#xff0c;到后面具备了这…

在PostgreSQL中不开归档?恭喜你!锅你背定了

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

【操作宝典】IntelliJ IDEA新建maven项目详细教程

目录 &#x1f33c;1. 配置maven环境 &#x1f33c;2. 创建maven项目 &#x1f33c;3. 创建maven项目完整示例 a. 导入spring boot环境 b. 修改maven配置 c. 下载jar包 d. 创建Java类 &#x1f33c;1. 配置maven环境 【安装指南】maven下载、安装与配置详细教程-CSDN博客…

反物质(anti matter)和湮灭反应(Annihilation)浅读

反物质 反物质是正常物质的反状态。当正反物质相遇时&#xff0c;双方就会相互湮灭抵消&#xff0c;发生爆炸并产生巨大能量。 概念 正电子、负质子都是反粒子&#xff0c;它们跟通常所说的电子、质子相比较&#xff0c;电量相等但电性相反。科学家设想在宇宙中可能存在完全由…

【GitHub项目推荐--一个 C++ 实现快速存储的库】【转载】

一个提供可嵌入、持久键值存储以实现快速存储的库。 github地址 https://github.com/facebook/rocksdb 国内镜像 http://www.gitpp.com/ag/rocksdb RocksDB 是一个开源的嵌入式键值存储库&#xff0c;由 Facebook 开发&#xff0c;用于处理大量的数据&#xff0c;特别适合于…

基于SSM的个性化旅游攻略定制系统设计与实现(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的个性化旅游攻略定制系统设计与实现&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xf…

故障诊断 | 一文解决,RF随机森林的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,RF随机森林的故障诊断(Matlab) 模型描述 随机森林(Random Forest)是一种集成学习(Ensemble Learning)方法,常用于解决分类和回归问题。它由多个决策树组成,每个决策树都独立地对数据进行训练,并且最终的预测结果是由所有决策…

为什么SSL会握手失败?SSL握手失败原因及解决方案

随着网络安全技术的发展&#xff0c;SSL证书作为网站数据安全的第一道防线&#xff0c;被越来越多的企业选择。SSL证书使用的是SSL协议&#xff0c;而SSL握手是SSL协议当中最重要的一部分。当部署SSL证书时&#xff0c;如果服务器和客户端之间无法建立安全连接&#xff0c;就会…

C++学习Day01之using声明以及using编译指令

目录 一、程序1.1 using声明1.2 using声明与就近原则1.3 using编译指令与就近原则1.4 多个using编译指令 二、分析与总结 一、程序 1.1 using声明 #include<iostream> using namespace std;namespace KingGlory {int sunwukongId 1; } void test01() {//1、using声明u…

由数据插入超长引起的问题——了解GaussDB和openGauss的字符集

前言 故事是这样开始的。我们的小DEMO项目的数据库版本从openGauss 2.1.0升级到了5.0.0版本。升级后进行功能验证的时候&#xff0c;测试同学发现个BUG&#xff0c;原来通过gs_restore导出来的数据再导入时报超长&#xff0c;插入失败了&#xff0c;如下图所示&#xff0c;nva…

2024年第十五届电子商务、管理与经济国际会议(ICEME 2024)即将召开!

2024年第十五届电子商务、管理与经济国际会议(ICEME 2024) 将于2024年7月19-21日在北京召开。本次会议将由北京工业大学主办&#xff0c;中国澳门圣若瑟大学提供学术支持。ICEME 2024旨在为来自世界各地的电子商务、管理与经济的研究人员提供一个展示最新研究成果的高质量交流平…

市场复盘总结 20240202

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 昨日主题投资 连板进级率 6/30 20% 二进三…

java多线程详解

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。简单理解为&#xff1a;引用软件中相互独立&#xff0c;可以同时允许的功能 进程是程序的基本执行实体 并发&#xff1a;在同一时刻&#xff0c;有多个指令在单个CPU上交替…

C++类和对象(3)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 概念 2.2 特性 3.析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任…

Linux服务详解

如有错误或有补充&#xff0c;以及任何改进的意见&#xff0c;请在评论区留下您的高见&#xff0c;同时文中给出大部分命令的示例&#xff0c;即是您暂时无法在Linux中查看&#xff0c;您也可以知道各种操作的功能以及输出 如果觉得本文写的不错&#xff0c;不妨点个赞&#x…