Browse Source

[feat]:完成前九章迁移

鱼香ROS 3 năm trước cách đây
mục cha
commit
5543b32ab1
94 tập tin đã thay đổi với 3196 bổ sung18 xóa
  1. 19 18
      docs/_sidebar.md
  2. 283 0
      docs/humble/chapt8/get_started/1.URDF统一机器人建模语言.md
  3. BIN
      docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220112134251483.png
  4. BIN
      docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220112162931639.png
  5. BIN
      docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220113094606907.png
  6. BIN
      docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220113104129248.png
  7. 285 0
      docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型.md
  8. BIN
      docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113104129248.png
  9. BIN
      docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113105040037.png
  10. BIN
      docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113111238677.png
  11. BIN
      docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113111805296.png
  12. 300 0
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型.md
  13. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117001030066.png
  14. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117004252071.png
  15. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012033644.png
  16. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012057367.png
  17. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012331955.png
  18. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012528123.png
  19. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012745344.png
  20. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012946727.png
  21. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117193835801.png
  22. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220125114933059.png
  23. BIN
      docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220409003039566.png
  24. 367 0
      docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节.md
  25. BIN
      docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/image-20220117012331955.png
  26. BIN
      docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/image-20220125120447619.png
  27. BIN
      docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/image-20220210212327950.png
  28. BIN
      docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/rotate_urdf.gif
  29. 229 0
      docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波.md
  30. BIN
      docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429113619974.png
  31. BIN
      docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429143335942.png
  32. BIN
      docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429145543467.png
  33. BIN
      docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429145701982.png
  34. 74 0
      docs/humble/chapt9/get_started/1.机器人仿真介绍.md
  35. 129 0
      docs/humble/chapt9/get_started/2.给两轮差速机器人添加物理参数.md
  36. BIN
      docs/humble/chapt9/get_started/2.给两轮差速机器人添加物理参数/imgs/453a07bf69814351a7c673deddf78087.png
  37. BIN
      docs/humble/chapt9/get_started/2.给两轮差速机器人添加物理参数/imgs/9b7f85f6d4314130ae6abdce6beeae8f.png
  38. 297 0
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型.md
  39. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405224729354.png
  40. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405233406029.png
  41. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405233825788.png
  42. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405233947338.png
  43. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234118003.png
  44. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234507958.png
  45. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234644118.png
  46. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234948237.png
  47. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405235058954.png
  48. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405235212262.png
  49. BIN
      docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220406000341792.png
  50. 493 0
      docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速.md
  51. BIN
      docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/boot.gif
  52. BIN
      docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408125719602.png
  53. BIN
      docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408131355255.png
  54. BIN
      docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408131855683.png
  55. BIN
      docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408141925261.png
  56. BIN
      docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408142436034.png
  57. 302 0
      docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU.md
  58. BIN
      docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220409150448805.png
  59. BIN
      docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220409150658019.png
  60. BIN
      docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220409150754602.png
  61. BIN
      docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220411102037226.png
  62. 213 0
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达.md
  63. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/467829a0-048a-4913-923c-0426107fde95.jpeg
  64. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/60c8fc45-d5a3-4d30-863a-328794b018af.jpeg
  65. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411104637837.png
  66. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411110353617.png
  67. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411110936454.png
  68. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411111006587.png
  69. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411111153820.png
  70. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411111459079.png
  71. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411115245023.png
  72. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411120215730.png
  73. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411120602685.png
  74. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411120707286.png
  75. BIN
      docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411125925399.png
  76. 173 0
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建.md
  77. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415190947423.png
  78. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415191443344.png
  79. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415200653000.png
  80. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415201033741.png
  81. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203442641.png
  82. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203452096.png
  83. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203602181.png
  84. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203658743.png
  85. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203729210.png
  86. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415204027598.png
  87. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415205459283.png
  88. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415205713054.png
  89. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415205821811.png
  90. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212035364.png
  91. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212321161.png
  92. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212856652.png
  93. BIN
      docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212957807.png
  94. 32 0
      docs/humble/chapt9/章节导读.md

+ 19 - 18
docs/_sidebar.md

@@ -117,7 +117,7 @@
       - 4.兼容仿真工具UnityForROS2
       - 5.ROS2内存管理工具
   
-- (二)机器人学篇(更新中)
+- (二)机器人学篇
   - 第 6 章 运动学基础
     - 章节导读
     - 基础篇-数学基础
@@ -160,12 +160,12 @@
       - 1.常见机器人构型
       - 2.常见建模软件及工具
     - 入门篇-机器人建模
-      - 1.URDF建模介绍
-      - 2.RVIZ2可视化URDF模型
-      - 3.创建一个两轮差速模型
-      - 4.两轮差速可视化及关节控制
-      <!-- - 创建一个机械臂模型 -->
-      <!-- - 机械臂可视化及关节控制 -->
+      -  [1.URDF统一机器人建模语言](humble/chapt8/get_started/1.URDF统一机器人建模语言.md) 
+      -  [2.RVIZ2可视化URDF模型](humble/chapt8/get_started/2.RVIZ2可视化URDF模型.md) 
+      -  [3.创建一个两轮差速模型](humble/chapt8/get_started/3.创建一个两轮差速模型.md) 
+      -  [4.通过JointStates控制RVIZ2关节](humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节.md) 
+      - <!-- - 创建一个机械臂模型 -->
+        <!-- - 机械臂可视化及关节控制 -->
     - 进阶篇-其他建模方式
       - Xacro介绍
       - 使用Xacro简化机器人模型
@@ -176,20 +176,21 @@
   - 第 9 章 机器人仿真
     - 章节导读
     - 基础篇-概念
-      - 刚体及其动力学参数
+      - 待定
+      <!-- - 1.刚体及其动力学参数 -->
     - 入门篇-机器人仿真
-      - 1.仿真软件Gazebo介绍与安装
-      - 2.给两轮差速机器人添加物理参数
-      - 3.在Gazebo加载机器人模型
-      - 4.Gazebo仿真插件之两轮差速
-      - 5.Gazebo仿真插件之IMU
-      - 6.Gazebo仿真插件之激光雷达
-      - 7.Gazebo仿真环境搭建
+      -  [1.机器人仿真介绍](humble/chapt9/get_started/1.机器人仿真介绍.md) 
+      -  [2.给两轮差速机器人添加物理参数](humble/chapt9/get_started/2.给两轮差速机器人添加物理参数.md) 
+      -  [3.在Gazebo加载机器人模型](humble/chapt9/get_started/3.在Gazebo加载机器人模型.md) 
+      -  [4.Gazebo仿真插件之两轮差速](humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速.md) 
+      -  [5.Gazebo仿真插件之IMU](humble/chapt9/get_started/5.Gazebo仿真插件之IMU.md) 
+      -  [6.Gazebo仿真插件之激光雷达](humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达.md) 
+      -  [7.Gazebo仿真环境搭建](humble/chapt9/get_started/7.Gazebo仿真环境搭建.md) 
     - 进阶篇
-      - 1.Gazebo仿真插件之超声波
-      - 2.Gazebo仿真插件之深度相机
+      -  [1.Gazebo仿真插件之超声波](humble/chapt9/advanced/1.Gazebo仿真插件之超声波.md) 
+      <!-- - 2.Gazebo仿真插件之深度相机 -->
 
-- (四)Nav2导航篇
+- (四)Nav2导航篇(更新中)
   - 第 10 章 SLAM建图
     - 章节导读
     - 基础篇-图像基础

+ 283 - 0
docs/humble/chapt8/get_started/1.URDF统一机器人建模语言.md

@@ -0,0 +1,283 @@
+# 1. URDF统一机器人建模语言
+
+大家好,我是小鱼,本节课我们来介绍机器人的URDF建模。
+
+URDF(Unified Robot Description Format)统一机器人描述格式,URDF使用XML格式描述机器人文件。
+
+> XML是 **被设计用来传输和存储数据的可扩展标记语言**,注意语言本身是没有含义的,只是规定了其数据格式
+>
+> 比如说下面这段信息:
+>
+> ```
+> <note>
+> <to>小鱼</to>
+> <from>鱼粉</from>
+> <heading>100万</heading>
+> <body>收到来自鱼粉的100万打赏!</body>
+> </note>
+> ```
+>
+> 其中的`<node></node>`等标签都是自定义的,只要符合xml格式即可
+>
+> XML格式在线校验工具:[在线代码格式化 (oschina.net)](https://tool.oschina.net/codeformat/xml)
+>
+> XML格式注释:<!-- 注释内容 -->
+>
+> robot标签的属性`name`: `<robot name="fishbot"></robot>`
+>
+> robot标签的子标签`link`: `<robot name="fishbot"><link name="base_link"></link>link></robot>`
+
+
+
+使用XML定义的一个最简单的URDF模型可以像下面这样
+
+```xml
+<?xml version="1.0"?>
+<robot name="fishbot">
+  <link name="base_link">
+    <visual>
+      <geometry>
+        <cylinder length="0.18" radius="0.06"/>
+      </geometry>
+    </visual>
+  </link>
+</robot>
+```
+
+
+
+接着我们从下面四个方面介绍URDF:
+
+1. URDF的组成介绍
+2. URDF-Link介绍
+3. URDF-Joint介绍
+4. 创建一个简单的URDF并在RVIZ2中可视化
+
+
+
+## 1.URDF的组成介绍
+
+一般情况下,URDF由`一个声明信息`和`两种关键组件`共同组成
+
+### 1.1 声明信息
+
+声明信息包含两部分,第一部分是xml的声明信息,放在第一行
+
+```
+<?xml version="1.0"?>
+```
+
+第二部分是机器人的声明,通过robot标签就可以声明一个机器人模型
+
+```
+<robot name="fishbot">
+ 	
+</robot>
+```
+
+## 1.2 两种关键组件
+
+观察下图机器人的结构
+
+![image-20220113094606907](1.URDF建模介绍/imgs/image-20220113094606907.png)
+
+可以简化为如下五个部件组成:
+
+- 躯体
+
+- 左右轮子
+
+- 支撑轮
+
+- 雷达激光
+
+- IMU模块
+
+这五个部件之间的固定方式为:
+
+```mermaid
+graph 
+A[左轮] -->C[躯体]
+B[右轮] -->C[躯体]
+D[IMU] -->C[躯体]
+E[雷达] -->C[躯体]
+F[支撑轮子] -->C[躯体]
+```
+
+我们把左轮,右轮、支撑轮子,IMU和雷达部件称为机器人的Link
+
+**而Link和Link之间的连接部分称之为Joint关节**
+
+接着我们给每个link和joint取个名字。
+
+```mermaid
+graph 
+A[左轮:left_wheel_link] --left_wheel_joint-->C[躯体]
+B[右轮:right_wheel_link] --right_wheel_joint-->C[躯体]
+D[IMU:imu_link] --imu_joint-->C[躯体]
+E[雷达:laser_link] --laser_joint-->C[躯体]
+F[支撑轮子:caster_link] --caster_joint-->C[躯体:base_link]
+```
+
+所以我们就可以使用6个link和5个joint来描述这个机器人,接着我们分别对link和joint进行详细的介绍。
+
+![image-20220112134251483](1.URDF建模介绍/imgs/image-20220112134251483.png)
+
+
+
+## 2.Link介绍
+
+上面我们介绍完了link,那一个link该怎么写呢?
+
+我们来看一个base_link的写法,通过link标签即可声明一个link,属性name指定部件名字
+
+```xml
+  <link name="base_link">
+
+  </link>
+```
+
+通过两行代码就可以定义好base_link,但现在的base_link是空的,我们还要声明我们的base_link长什么样,通过`visual`子标签就可以声明出来机器人的visual形状。
+
+```xml
+  <!-- base link -->
+  <link name="base_link">
+  	<visual>
+      <origin xyz="0 0 0.0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.12" radius="0.10"/>
+      </geometry>
+    </visual>
+  </link>
+```
+
+### 2.1 link标签定义
+
+link的子标签列表
+
+- visual 显示形状
+  - `<geometry> `(几何形状)
+    - `<box>` 长方体
+      - 标签属性: `size`-长宽高
+      - 举例:`<box size="1 1 1" />`
+    - `<cylinder>` 圆柱体
+      - 标签属性:`radius` -半径 `length`-高度
+      - 举例:`<cylinder radius="1" length="0.5"/>`
+    - `sphere` 球体
+      - 属性:`radius` -半径
+      - 举例:`<sphere radius="0.015"/>`
+    - `mesh` 第三方导出的模型文件
+      - 属性:filename
+      - 举例:` <mesh filename="package://robot_description/meshes/base_link.DAE"/>`
+  - origin (可选:默认在物体几何中心)
+    - 属性 `xyz`默认为零矢量 `rpy`弧度表示的翻滚、俯仰、偏航
+    - 举例:`<origin xyz="0 0 0" rpy="0 0 0" />`
+  - material 材质
+    - 属性 `name` 名字
+      - color 
+        - 属性 `rgba` a代表透明度
+        - 举例:`<material name="white"><color rgba="1.0 1.0 1.0 0.5" /> </material>`
+- collision  碰撞属性,仿真章节中讲解
+- inertial 惯性参数 质量等,仿真章节中讲解
+
+
+
+## 3.Joint介绍
+
+joint为机器人关节,机器人关节用于连接两个机器人部件,主要写明父子关系
+
+- 父子之间的连接类型,包括是否固定的,可以旋转的等
+
+- 父部件名字
+- 子部件名字
+- 父子之间相对位置
+- 父子之间的旋转轴,绕哪个轴转
+
+![image-20220112162931639](1.URDF建模介绍/imgs/image-20220112162931639.png)
+
+比如我们再建立一个雷达部件`laser_link`,然后将`laser_link`固定到`base_link`
+
+```xml
+<?xml version="1.0"?>
+<robot name="fishbot">
+    
+  <!-- base link -->
+  <link name="base_link">
+  	<visual>
+      <origin xyz="0 0 0.0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.12" radius="0.10"/>
+      </geometry>
+    </visual>
+  </link>
+    
+  <!-- laser link -->
+  <link name="laser_link">
+  	<visual>
+      <origin xyz="0 0 0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.02" radius="0.02"/>
+      </geometry>
+      <material name="black">
+      	<color rgba="0.0 0.0 0.0 0.5" /> 
+      </material>
+    </visual>
+  </link>
+    
+  <!-- laser joint -->
+    <joint name="laser_joint" type="fixed">
+        <parent link="base_link" />
+        <child link="laser_link" />
+        <origin xyz="0 0 0.075" />
+    </joint>
+
+</robot>
+```
+
+### 3.1 joint标签详解
+
+#### joint属性
+
+- name 关节的名称
+- type 关节的类型
+  - **revolute: 旋转关节,绕单轴旋转,角度有上下限,比如舵机0-180**
+  - **continuous: 旋转关节,可以绕单轴无限旋转,比如自行车的前后轮**
+  - **fixed: 固定关节,不允许运动的特殊关节**
+  - prismatic: 滑动关节,沿某一轴线移动的关节,有位置极限
+  - planer: 平面关节,允许在xyz,rxryrz六个方向运动
+  - floating: 浮动关节,允许进行平移、旋转运动
+
+#### joint的子标签
+
+- `parent` 父link名称
+  - ` <parent link="base_link" />`
+- `child`子link名称
+  - `<child link="laser_link" />`
+- `origin` 父子之间的关系xyz rpy
+  - ` <origin xyz="0 0 0.014" />`
+- `axis` 围绕旋转的关节轴
+  - `<axis xyz="0 0 1" />`
+
+
+
+下一节我们尝试将我们的机器人模型在RVIZ2中显示出来
+
+![image-20220113104129248](1.URDF建模介绍/imgs/image-20220113104129248.png)
+
+
+
+参考文档
+
+- [urdf/XML/link - ROS Wiki](http://wiki.ros.org/urdf/XML/link)
+
+- http://docs.ros.org/en/foxy/Tutorials/URDF/URDF-Main.html
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220112134251483.png


BIN
docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220112162931639.png


BIN
docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220113094606907.png


BIN
docs/humble/chapt8/get_started/1.URDF统一机器人建模语言/imgs/image-20220113104129248.png


+ 285 - 0
docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型.md

@@ -0,0 +1,285 @@
+# 8.2 RVIZ2可视化移动机器人模型
+
+大家好,我是小鱼,上一节讲完joint和link,我们来把我们上面定义的简单的URDF(包含身体和雷达)用RVIZ2显示出来,直观的感受下,我们的机器人模型。
+
+URDF可视化的步骤如下:
+
+1. 建立机器人描述功能包
+2. 建立`urdf`文件夹编写urdf文件
+3. 建立`launch`文件夹,编写launch文件
+4. 修改`setup.py`配置,编译测试
+
+
+
+## 1.建立功能包
+
+轻车熟路,先创建一个`fishbot_ws`工作空间,然后建立功能包,包的类型选`ament_python`
+
+```shell
+ros2 pkg create fishbot_description --build-type ament_python
+```
+
+
+
+## 2.建立URDF文件
+
+建立URDF文件夹,创建urdf文件
+
+```
+cd fishbot_description && mkdir urdf 
+touch fishbot_base.urdf
+```
+
+完成后src下的目录结构:
+
+```
+├── fishbot_description
+│   ├── __init__.py
+├── package.xml
+├── setup.cfg
+├── setup.py
+└── urdf
+    └── fishbot_base.urdf
+```
+
+编辑`fishbot_base.urdf`
+
+```xml
+<?xml version="1.0"?>
+<robot name="fishbot">
+    
+  <!-- base link -->
+  <link name="base_link">
+  	<visual>
+      <origin xyz="0 0 0.0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.12" radius="0.10"/>
+      </geometry>
+    </visual>
+  </link>
+    
+  <!-- laser link -->
+  <link name="laser_link">
+  	<visual>
+      <origin xyz="0 0 0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.02" radius="0.02"/>
+      </geometry>
+      <material name="black">
+      	<color rgba="0.0 0.0 0.0 0.8" /> 
+      </material>
+    </visual>
+  </link>
+    
+  <!-- laser joint -->
+    <joint name="laser_joint" type="fixed">
+        <parent link="base_link" />
+        <child link="laser_link" />
+        <origin xyz="0 0 0.075" />
+    </joint>
+
+</robot>
+```
+
+
+
+## 3.建立launch文件
+
+在目录`src/fishbot_description`下创建launch文件夹并在其下新建`display_rviz2.launch.py`文件。
+
+```
+mkdir launch
+touch display_rviz2.launch.py
+```
+
+完成后的目录结构:
+
+```
+├── fishbot_description
+│   ├── __init__.py
+├── launch
+│   └── display_rviz2.launch.py
+├── package.xml
+├── setup.cfg
+├── setup.py
+└── urdf
+    └── fishbot_base.urdf
+```
+
+
+```python
+import os
+from launch import LaunchDescription
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+from launch_ros.substitutions import FindPackageShare
+
+
+def generate_launch_description():
+    package_name = 'fishbot_description'
+    urdf_name = "fishbot_base.urdf"
+
+    ld = LaunchDescription()
+    pkg_share = FindPackageShare(package=package_name).find(package_name) 
+    urdf_model_path = os.path.join(pkg_share, f'urdf/{urdf_name}')
+
+    robot_state_publisher_node = Node(
+        package='robot_state_publisher',
+        executable='robot_state_publisher',
+        arguments=[urdf_model_path]
+        )
+
+    joint_state_publisher_node = Node(
+        package='joint_state_publisher_gui',
+        executable='joint_state_publisher_gui',
+        name='joint_state_publisher_gui',
+        arguments=[urdf_model_path]
+        )
+
+    rviz2_node = Node(
+        package='rviz2',
+        executable='rviz2',
+        name='rviz2',
+        output='screen',
+        )
+
+    ld.add_action(robot_state_publisher_node)
+    ld.add_action(joint_state_publisher_node)
+    ld.add_action(rviz2_node)
+
+    return ld
+```
+
+想要可视化模型需要三个节点参与
+
+- `joint_state_publisher_gui` 负责发布机器人关节数据信息,通过`joint_states`话题发布
+- `robot_state_publisher_node`负责发布机器人模型信息`robot_description`,并将`joint_states`数据转换tf信息发布
+- `rviz2_node`负责显示机器人的信息
+
+```mermaid
+graph
+A[joint_state_publisher]--joint_states-->B
+B[robot_state_publisher]--robot_description-->C
+C[rviz2]
+
+```
+
+这里我们用到了`joint_state_publisher_gui`和`robot_state_publisher`两个包,如果你的系统没有安装这两个包,可以手动安装:
+
+```
+sudo apt install ros-$ROS_DISTRO-joint-state-publisher-gui ros-$ROS_DISTRO-robot-state-publisher
+```
+
+> `joint_state_publisher_gui`,还有一个兄弟叫做`joint_state_publisher`两者区别在于`joint_state_publisher_gui`运行起来会跳出一个界面,通过界面可以操作URDF中能动的关节
+
+## 4.修改setup.py
+
+导入头文件
+
+```python
+from glob import glob
+import os
+```
+
+加入目录安装
+
+```python
+('share/ament_index/resource_index/packages',
+['resource/' + package_name]),
+('share/' + package_name, ['package.xml']),
+```
+
+完整
+
+```python
+from setuptools import setup
+from glob import glob
+import os
+
+package_name = 'fishbot_description'
+
+setup(
+    name=package_name,
+    version='0.0.0',
+    packages=[package_name],
+    data_files=[
+        ('share/ament_index/resource_index/packages',
+            ['resource/' + package_name]),
+        ('share/' + package_name, ['package.xml']),
+        (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
+        (os.path.join('share', package_name, 'urdf'), glob('urdf/**')),
+    ],
+    install_requires=['setuptools'],
+    zip_safe=True,
+    maintainer='root',
+    maintainer_email='root@todo.todo',
+    description='TODO: Package description',
+    license='TODO: License declaration',
+    tests_require=['pytest'],
+    entry_points={
+        'console_scripts': [
+        ],
+    },
+)
+```
+
+
+
+## 5.编译测试
+
+编译
+
+```
+colcon build
+```
+
+运行测试
+
+```
+source install/setup.bash
+ros2 launch fishbot_description display_rviz2.launch.py
+```
+
+添加robotmodel模块,分别选择link名称如下,即可看到机器人的模型显示
+
+![image-20220113104129248](2.RVIZ2可视化URDF模型/imgs/image-20220113104129248.png)
+
+此时看看节点关系图
+
+![image-20220113105040037](2.RVIZ2可视化URDF模型/imgs/image-20220113105040037.png)
+
+
+
+这里大家可以参考图理一理launch文件中启动的三个节点的关系。
+
+然后打开TF模块,看一下机器人的坐标系关系
+
+![image-20220113111238677](2.RVIZ2可视化URDF模型/imgs/image-20220113111238677.png)
+
+
+
+
+
+## 6.本节练习
+
+练习1:尝试将修改机器人身体颜色为蓝色,透明度为50%`(0.1 0.1 1.0 0.5)`
+
+练习2:尝试在URDF中添加imu_link并使用imu_joint将其固定在车体的中心上方2cm,imu采用的几何形状为box。长宽高均为2cm
+
+
+
+结果展示:
+
+![image-20220113111805296](2.RVIZ2可视化URDF模型/imgs/image-20220113111805296.png)
+
+
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113104129248.png


BIN
docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113105040037.png


BIN
docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113111238677.png


BIN
docs/humble/chapt8/get_started/2.RVIZ2可视化URDF模型/imgs/image-20220113111805296.png


+ 300 - 0
docs/humble/chapt8/get_started/3.创建一个两轮差速模型.md

@@ -0,0 +1,300 @@
+# 8.3 动手创建一个移动机器人
+
+大家好,我是小鱼,本节课我们来一起动手创建一个两轮差速的移动机器人`fishbot`,在上一节的时候我们已经给我们的机器人安装上了雷达,本节课我们接着上节课的来继续完善我们的机器人模型。
+
+
+
+机器人除了雷达之外,还需要`IMU`加速度传感器以及可以驱动的轮子,在第七章中我们曾介绍过机器人学部分,曾对两差速模型进行过介绍,所以我们还需要再创建两个差速驱动轮和一个支撑轮。
+
+
+
+所以本节小鱼带你一起给机器人添加如下部件和关节:
+
+1. IMU传感器部件与关节
+2. 左轮子部件与关节
+3. 右轮子部件与关节
+4. 支撑轮子部件与关节
+
+![image-20220117001030066](3.创建一个两轮差速模型/imgs/image-20220117001030066.png)
+
+
+
+## 1.添加IMU传感器(上节作业)
+
+IMU传感器和透明度与颜色修改是上节课,作业,小鱼先带你一起完成一下
+
+> 练习1:尝试将修改机器人身体颜色为蓝色,透明度为50%`(0.1 0.1 1.0 0.5)`
+>
+> 练习2:尝试在URDF中添加imu_link并使用imu_joint将其固定在车体的中心上方2cm,imu采用的几何形状为box,长宽高各是2cm
+
+### 1.1 修改颜色
+
+透明度修改只需要在`base_link`中添加`material`
+
+```xml
+<link name="base_link">
+    <visual>
+    	<origin xyz="0 0 0.0" rpy="0 0 0"/>
+    <geometry>
+    	<cylinder length="0.12" radius="0.10"/>
+    </geometry>
+    <material name="blue">
+    	<color rgba="0.1 0.1 1.0 0.5" /> 
+    </material>
+    </visual>
+</link>
+```
+
+### 1.2 添加imu
+
+```xml
+  <link name="imu_link">
+  	<visual>
+      <origin xyz="0 0 0.0" rpy="0 0 0"/>
+      <geometry>
+		    <box size="0.02 0.02 0.02"/>
+      </geometry>
+    </visual>
+  </link>
+
+  <!-- imu joint -->
+  <joint name="imu_joint" type="fixed">
+      <parent link="base_link" />
+      <child link="imu_link" />
+      <origin xyz="0 0 0.02" />
+  </joint>
+```
+
+## 2.添加右轮
+
+### 2.1 添加关节
+
+关节名称为`right_wheel_link`,小鱼在做ros2小车的时候采用的轮子如下图:
+
+![image-20220117193835801](3.创建一个两轮差速模型/imgs/image-20220117193835801.png)
+
+轮子的宽为4cm,直径为6.4cm,几何形状是个圆柱体,所以`geometry`配置如下:
+
+```xml
+<geometry>
+	<cylinder length="0.04" radius="0.032"/>
+</geometry>
+```
+
+需要注意的是,圆柱默认的朝向是向上的
+
+![image-20220117004252071](3.创建一个两轮差速模型/imgs/image-20220117004252071.png)
+
+我们可通过`origin`的`rpy`改变轮子的旋转角度,让其绕`x轴`旋转`pi/2`,所以`origin`的配置为
+
+```xml
+<origin xyz="0 0 0" rpy="1.57079 0 0"/>
+```
+
+颜色换黑色,可以得到下面的配置:
+
+```xml
+  <link name="right_wheel_link">
+      <visual>
+        <origin xyz="0 0 0" rpy="1.57079 0 0"/>
+        <geometry>
+          <cylinder length="0.04" radius="0.032"/>
+        </geometry>
+          <material name="black">
+            <color rgba="0.0 0.0 0.0 0.5" /> 
+          </material>
+      </visual>
+  </link>
+```
+
+### 2.2 添加joint
+
+我们把左轮子的中心固定在机器人左后方
+
+需要注意的是`origin`和`axis`值的设置
+
+**先看origin**
+
+因为base_link的高度是0.12,我们
+
+- z表示child相对parent的z轴上的关系,想将轮子固定在机器人的下表面,所以`origin`的z向下偏移0.12/2=0.06m(向下符号为负)
+
+- y表示child相对parent的y轴上的关系,base_link的半径是0.10,所以我们让轮子的y轴向负方向偏移0.10m(向左符号为负)
+- x表示child相对parent的x轴上的关系,向后偏移则是x轴向后进行偏移,我们用个差不多的值0.02m(向后符号为负)
+
+![image-20220125114933059](3.创建一个两轮差速模型/imgs/image-20220125114933059.png)
+
+**再看axis**
+
+轮子是会转动的,那应该按照哪个轴转动呢?从上图可以看出是绕着y轴的逆时针方向,所以axis的设置为:
+
+```xml
+<axis xyz="0 1 0" />
+```
+
+```xml
+  <joint name="right_wheel_joint" type="continuous">
+      <parent link="base_link" />
+      <child link="right_wheel_link" />
+      <origin xyz="-0.02 -0.10 -0.06" />
+      <axis xyz="0 1 0" />
+  </joint>
+```
+
+
+
+### 3.添加左轮
+
+左轮就是右轮的映射,不再赘述
+
+```xml
+  <link name="left_wheel_link">
+      <visual>
+        <origin xyz="0 0 0" rpy="1.57079 0 0"/>
+        <geometry>
+          <cylinder length="0.04" radius="0.032"/>
+        </geometry>
+          <material name="black">
+            <color rgba="0.0 0.0 0.0 0.5" /> 
+          </material>
+      </visual>
+  </link>
+    
+  <joint name="left_wheel_joint" type="continuous">
+      <parent link="base_link" />
+      <child link="left_wheel_link" />
+      <origin xyz="-0.02 0.10 -0.06" />
+      <axis xyz="0 1 0" />
+  </joint>
+```
+
+
+
+### 4.添加支撑轮
+
+支撑轮子固定在机器人的前方,用个球体,半径用`0.016m`,小球的直径为0.032m与左右轮子半径相同,然后向下偏移`0.016+0.06=0.076m`,向下值为负,同时把支撑论向前移动一些,选个`0.06m`
+
+最终结果如下:
+
+```xml
+<link name="caster_link">
+    <visual>
+      <origin xyz="0 0 0" rpy="0 0 0"/>
+      <geometry>
+          <sphere radius="0.016"/>
+      </geometry>
+        <material name="black">
+          <color rgba="0.0 0.0 0.0 0.5" /> 
+        </material>
+    </visual>
+  </link>
+    
+  <joint name="caster_joint" type="fixed">
+      <parent link="base_link" />
+      <child link="caster_link" />
+      <origin xyz="0.06 0.0 -0.076" />
+  </joint>
+```
+
+> 最终URDF文件:https://raw.githubusercontent.com/fishros/fishbot/master/src/fishbot_description/urdf/fishbot_base.urdf
+
+
+
+
+
+
+
+## 5.测试运行
+
+### 5.1 编译测试
+
+```shell
+colcon build
+source install/setup.bash
+ros2 launch fishbot_description display_rviz2.launch.py
+```
+
+
+
+### 5.2 最终结果
+
+rviz的配置
+
+![image-20220117012057367](3.创建一个两轮差速模型/imgs/image-20220117012057367.png)
+
+最终结果
+
+![image-20220117012033644](3.创建一个两轮差速模型/imgs/image-20220117012033644.png)
+
+jointstate多出两个滑动条
+
+![image-20220117012528123](3.创建一个两轮差速模型/imgs/image-20220117012528123.png)
+
+
+
+节点关系
+
+![image-20220117012331955](3.创建一个两轮差速模型/imgs/image-20220117012331955.png)
+
+打印`joint_states`话题
+
+```
+ros2 topic echo /joint_states
+```
+
+![image-20220117012745344](3.创建一个两轮差速模型/imgs/image-20220117012745344.png)
+
+
+
+### 5.3 通过joint_state_gui改变关节tf中关节角度
+
+在JointStatePublisher中,拖动滑动条,观察
+
+1. `rviz2`中`tf`的变换
+2. `joint_states`中的值的变换
+
+
+
+![image-20220117012946727](3.创建一个两轮差速模型/imgs/image-20220117012946727.png)
+
+
+
+可以看到随着进度条拖动,话题中的值和rviz2中机器人关节在同步的旋转,joint_states话题也可以手动发送,下一节课小鱼带你一起通过手动发送`joint_states`来控制机器人轮子转动
+
+### 5.4 论如何让车轮着地
+
+虽然显示出了机器人模型,但有一个问题不知道你发现没有,那就是在RVIZ中的机器人轮子是在地面之下的。
+
+原因在于我们fixed-frame选择的是base_link,base_link的位置本来就在left_wheel_link和right_wheel_link只上,那该怎么办呢?
+
+其实很简单,我们增加一个虚拟link和关节,这个关节与base_link相连,位置位于base_link向下刚好到车轮下表面的位置。
+
+来,让我们**给base_link添加一个父link-`base_footprint`**,新增的URDF代码如下:
+
+```
+  <!-- Robot Footprint -->
+  <link name="base_footprint"/>
+
+  <joint name="base_joint" type="fixed">
+    <parent link="base_footprint"/>
+    <child link="base_link"/>
+    <origin xyz="0.0 0.0 0.076" rpy="0 0 0"/>
+  </joint>
+```
+
+因为是虚拟关节,我们不用对这个link的形状进行描述,joint的origin设置为`xyz="0.0 0.0 0.076"`表示关节base_footprint向上0.076就是base_link(觉得不好理解可以看下图)。
+
+保存编译再次运行测试,此时车轮就在地面只上啦~
+
+![image-20220409003039566](3.创建一个两轮差速模型/imgs/image-20220409003039566.png)
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划
+

BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117001030066.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117004252071.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012033644.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012057367.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012331955.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012528123.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012745344.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117012946727.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220117193835801.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220125114933059.png


BIN
docs/humble/chapt8/get_started/3.创建一个两轮差速模型/imgs/image-20220409003039566.png


+ 367 - 0
docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节.md

@@ -0,0 +1,367 @@
+# 8.4 控制移动机器人轮子运动
+
+我是小鱼,本节我们来看看如何手动的发送`joint_states`来控制机器人轮子连续转动
+
+![](4.通过JointStates控制RVIZ2关节/imgs/rotate_urdf.gif)
+
+
+
+要实现上图效果,我们需要自己编写节点,取代`joint_state_publisher`发送关节位姿给`robot_state_pubsher`,robot_state_publisher发送tf控制机器人的关节转动。
+
+![替换joint_state_publisher](4.通过JointStates控制RVIZ2关节/imgs/image-20220117012331955.png)
+
+1.新建节点
+
+2.创建发布者
+
+3.编写发布逻辑
+
+4.编译测试
+
+## 1.新建节点
+
+方便起见,我们就在`fishbot_describle`包中新建节点(参考李四节点代码)
+
+```shell
+cd fishbot_ws
+touch fishbot_description/fishbot_description/rotate_wheel.py
+```
+
+```python
+#!/usr/bin/env python3
+import rclpy
+from rclpy.node import Node
+
+
+class RotateWheelNode(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info(f"node {name} init..")
+
+def main(args=None):
+    """
+    ros2运行该节点的入口函数
+    1. 导入库文件
+    2. 初始化客户端库
+    3. 新建节点
+    4. spin循环节点
+    5. 关闭客户端库
+    """
+    rclpy.init(args=args) # 初始化rclpy
+    node = RotateWheelNode("rotate_fishbot_wheel")  # 新建一个节点
+    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
+    rclpy.shutdown() # 关闭rclpy
+```
+
+配置下setup.py
+
+```
+    entry_points={
+        'console_scripts': [
+            "rotate_wheel= fishbot_description.rotate_wheel:main"
+        ],
+    },
+```
+
+编译运行
+
+```
+colcon build
+source install/setup.bash
+ros2 run fishbot_description rotate_wheel
+```
+
+![image-20220125120447619](4.通过JointStates控制RVIZ2关节/imgs/image-20220125120447619.png)
+
+## 2.创建发布者
+
+创建发布者之前,要知道`robot_state_pubsher`所订阅的话题类型是什么?
+
+回忆前面章节中学习的内容,我们可以采用如下指令查看
+
+```
+ros2 topic info /joint_states
+```
+
+```
+Type: sensor_msgs/msg/JointState
+Publisher count: 1
+Subscription count: 1
+```
+
+接着
+
+```
+ros2 interfaces show sensor_msgs/msg/JointState
+```
+
+```
+# This is a message that holds data to describe the state of a set of torque controlled joints.
+#
+# The state of each joint (revolute or prismatic) is defined by:
+#  * the position of the joint (rad or m),
+#  * the velocity of the joint (rad/s or m/s) and
+#  * the effort that is applied in the joint (Nm or N).
+#
+# Each joint is uniquely identified by its name
+# The header specifies the time at which the joint states were recorded. All the joint states
+# in one message have to be recorded at the same time.
+#
+# This message consists of a multiple arrays, one for each part of the joint state.
+# The goal is to make each of the fields optional. When e.g. your joints have no
+# effort associated with them, you can leave the effort array empty.
+#
+# All arrays in this message should have the same size, or be empty.
+# This is the only way to uniquely associate the joint name with the correct
+# states.
+
+std_msgs/Header header
+
+string[] name
+float64[] position
+float64[] velocity
+float64[] effort
+```
+
+知道了话题类型,我们就可以来创建发布者了参考代码[4.2.1 话题通信实现(Python)](https://fishros.com/d2lros2foxy/#/chapt4/4.2话题通信实现(Python))
+
+```python
+#!/usr/bin/env python3
+import rclpy
+from rclpy.node import Node
+# 1.导入消息类型JointState
+from sensor_msgs.msg import JointState
+
+class RotateWheelNode(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info(f"node {name} init..")
+        # 2.创建并初始化发布者成员属性pub_joint_states_
+        self.pub_joint_states_ = self.create_publisher(JointState,"joint_states", 10) 
+```
+
+
+
+## 3.编写发布逻辑
+
+### 3.1 多线程定频发布Rate
+
+创建好发布者,我们想让话题按照某个固定的速度进行发布,可以采用ROS2中的定时神器Rate,不清楚Rate的小伙伴可以看看小鱼的这篇文章:[ROS中的定频神器你会用吗](https://mp.weixin.qq.com/s/bFbTIh6rGou1k0Ach-hlqA)
+
+为了能够一直循环使用rate,我们单独开一个线程用于发布joint_states话题数据,在ROS2程序中单独开线程进行话题发布的方法为:
+
+```python
+import threading
+from rclpy.node import Node
+
+class RotateWheelNode(Node):
+    def __init__(self):
+        # 创建一个Rate和线程
+        self.pub_rate = self.create_rate(5) #5Hz
+        # 创建线程
+        self.thread_ = threading.Thread(target=self._thread_pub)
+        self.thread_.start()
+
+    def _thread_pub(self):
+        while rclpy.ok():
+        	#做一些操作,使用rate保证循环频率
+            self.pub_rate.sleep()
+```
+
+### 3.2 构造发布数据
+
+接着我们来构造发布的数据:
+
+joint_states有一个头和四个数组需要赋值(可通过ros2 interface指令查询)
+
+```
+std_msgs/Header header #时间戳信息 和 frame_id
+string[] name
+float64[] position
+float64[] velocity
+float64[] effort
+```
+
+对应的含义为:
+
+```
+# 这是一个持有数据的信息,用于描述一组扭矩控制的关节的状态。
+#
+# 每个关节(渐进式或棱柱式)的状态由以下因素定义。
+# #关节的位置(rad或m)。
+# #关节的速度(弧度/秒或米/秒)和
+# #在关节上施加的力(Nm或N)。
+#
+# 每个关节都由其名称来唯一标识
+# 头部规定了记录关节状态的时间。所有的联合状态
+# 必须是在同一时间记录的。
+#
+# 这个消息由多个数组组成,每个部分的联合状态都有一个数组。
+# 目标是让每个字段都是可选的。例如,当你的关节没有
+# 扭矩与它们相关,你可以让扭矩数组为空。
+#
+# 这个信息中的所有数组都应该有相同的大小,或者为空。
+# 这是唯一能将关节名称与正确的
+# 状态。
+string[] name #关节名称数组
+float64[] position #关节位置数组
+float64[] velocity #关节速度数组
+float64[] effort #扭矩数据
+```
+
+
+
+#### 3.2.1 name
+
+name是关节的名称,要与urdf中的定义的关节名称相同,根据我们的URDF定义有
+
+```python
+self.joint_states.name = ['left_wheel_joint','right_wheel_joint']
+```
+
+#### 3.2.2 position
+
+表示关节转动的角度值,因为关节类型为`continuous`,所以其值无上下限制,初始值赋值为0.0
+
+```python
+# 关节的位置
+self.joint_states.position = [0.0,0.0]
+```
+
+我们采用速度控制机器人轮子转动,所以机器人的位置更新则可以通过下面式子计算得出
+
+> 某一段时间内轮子转动的角度 = (当前时刻-上一时刻)*两个时刻之间的轮子转速
+
+```python
+delta_time =  time.time()-last_update_time
+# 更新位置
+self.joint_states.position[0]  += delta_time*self.joint_states.velocity[0]
+self.joint_states.position[1]  += delta_time*self.joint_states.velocity[1]
+```
+
+#### 3.2.3 velocity
+
+因为我们采用速度进行控制,所以对外提供一个速度更改接口。
+
+```python
+def update_speed(self,speeds):
+	self.joint_speeds = speeds
+```
+
+### 3.3 完成后代码
+
+```python
+#!/usr/bin/env python3
+import rclpy
+from rclpy.node import Node
+# 1.导入消息类型JointState
+from sensor_msgs.msg import JointState
+
+import threading
+import time
+
+class RotateWheelNode(Node):
+    def __init__(self,name):
+        super().__init__(name)
+        self.get_logger().info(f"node {name} init..")
+        # 创建并初始化发布者成员属性pub_joint_states_
+        self.joint_states_publisher_ = self.create_publisher(JointState,"joint_states", 10) 
+        # 初始化数据
+        self._init_joint_states()
+        self.pub_rate = self.create_rate(30)
+        self.thread_ = threading.Thread(target=self._thread_pub)
+        self.thread_.start()
+
+    
+    def _init_joint_states(self):
+        # 初始左右轮子的速度
+        self.joint_speeds = [0.0,0.0]
+        self.joint_states = JointState()
+        self.joint_states.header.stamp = self.get_clock().now().to_msg()
+        self.joint_states.header.frame_id = ""
+        # 关节名称
+        self.joint_states.name = ['left_wheel_joint','right_wheel_joint']
+        # 关节的位置
+        self.joint_states.position = [0.0,0.0]
+        # 关节速度
+        self.joint_states.velocity = self.joint_speeds
+        # 力 
+        self.joint_states.effort = []
+
+    def update_speed(self,speeds):
+        self.joint_speeds = speeds
+
+    def _thread_pub(self):
+        last_update_time = time.time()
+        while rclpy.ok():
+            delta_time =  time.time()-last_update_time
+            last_update_time = time.time()
+            # 更新位置
+            self.joint_states.position[0]  += delta_time*self.joint_states.velocity[0]
+            self.joint_states.position[1]  += delta_time*self.joint_states.velocity[1]
+            # 更新速度
+            self.joint_states.velocity = self.joint_speeds
+            # 更新 header
+            self.joint_states.header.stamp = self.get_clock().now().to_msg()
+            # 发布关节数据
+            self.joint_states_publisher_.publish(self.joint_states)
+            self.pub_rate.sleep()
+
+def main(args=None):
+    rclpy.init(args=args) # 初始化rclpy
+    node = RotateWheelNode("rotate_fishbot_wheel")  # 新建一个节点
+    node.update_speed([15.0,-15.0])
+    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
+    rclpy.shutdown() # 关闭rclpy
+```
+
+
+
+## 4.编译测试
+
+编译程序
+
+```
+colcon build --packages-select fishbot_description
+```
+
+此时运行关节数据发布节点
+
+```
+ros2 run fishbot_description rotate_wheel
+```
+
+测试之前还需要修改下`display_rviz2.launch.py`文件,注释其`joint_state_publisher`节点
+
+```
+# ld.add_action(joint_state_publisher_node)
+ld.add_action(robot_state_publisher_node)
+ld.add_action(rviz2_node)
+```
+
+先运行rviz和robot_state_publisher
+
+```
+source install/setup.bash
+ros2 launch fishbot_description display_rviz2.launch.py
+```
+
+观察此时rviz界面,可以看到轮子疯狂转动
+
+![image-20220210212327950](4.通过JointStates控制RVIZ2关节/imgs/image-20220210212327950.png)
+
+## 5.课后练习
+
+尝试将左右轮速度参数化,然后尝试采用rqt动态参数配置工具,实时控制轮子的转速。
+
+
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- c 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/image-20220117012331955.png


BIN
docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/image-20220125120447619.png


BIN
docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/image-20220210212327950.png


BIN
docs/humble/chapt8/get_started/4.通过JointStates控制RVIZ2关节/imgs/rotate_urdf.gif


+ 229 - 0
docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波.md

@@ -0,0 +1,229 @@
+# 1.Gazebo仿真插件之超声波
+
+本节是拓展章节,主要讲解一下如何给FishBot添加一个超声波传感器。
+
+在实际的机器人开发过程中,我们可能会利用超声波传感器实现实时避障的功能,毕竟超声波的价格相较于激光雷达要便宜很多(便宜的几块钱)。
+
+所以本节我们来说一下如何使用ROS2+Gazebo来仿真超声波传感器。
+
+
+
+## 1.超声波传感器介绍
+
+百科来一段:
+
+超声波传感器是将超声波信号转换成其它能量信号(通常是电信号)的传感器。超声波是[振动频率](https://baike.baidu.com/item/振动频率/8068137)高于20kHz的机械波。它具有频率高、波长短、绕射现象小,特别是方向性好、能够成为[射线](https://baike.baidu.com/item/射线/327964)而定向传播等特点。超声波对液体、固体的穿透本领很大,尤其是在阳光不透明的固体中。超声波碰到杂质或分界面会产生显著反射形成反射回波,碰到活动物体能产生[多普勒效应](https://baike.baidu.com/item/多普勒效应/115710)。超声波传感器广泛应用在工业、国防、生物医学等方面。
+
+接着看看长什么样子:
+
+![img](1.Gazebo仿真插件之超声波/imgs/image-20220429113619974.png)
+
+便宜的就长这样子,一共两个头,一个头用于发送波,一个头接收波。这个还稍微高级一点,带一个光敏电阻,可以为超声波数据做一些补偿。
+
+超声波传感器原理是什么呢?
+
+```
+距离=(发送时间-接收时间)*速度/2
+```
+
+看了超声波的原理,你有没有发现和前面的激光雷达传感器是一样的,是的,所以超声波传感器插件和激光雷达传感器插件在Gazebo插件中是同一个:
+
+```
+libgazebo_ros_ray_sensor.so
+```
+
+## 2.超声波插件配置
+
+直接上配置,接着再解释
+
+### 2.1 添加超声波关节
+
+超声波总要装在机器人身上某个位置,所以我们先添加一个关节和Joint,为了省事,link我们就只写个名字,你如果有需要可以按照前面的章节那样添加一下。
+
+```xml
+<link name="ultrasonic_sensor_link" />
+
+  <joint name="ultrasonic_sensor_joint" type="fixed">
+    <parent link="base_link"/>
+    <child link="ultrasonic_sensor_link"/>
+    <origin xyz="0.07 0.0 0.076" rpy="0 0 0"/>
+  </joint>
+```
+
+### 2.2 添加Gazebo插件
+
+添加完了关节,我们就可以配置gazebo的插件了,gazebo插件配置如下
+
+```xml
+  <gazebo reference="ultrasonic_sensor_link">
+    <sensor type="ray" name="ultrasonic_sensor">
+      <pose>0 0 0 0 0 0</pose>
+      <!-- 是否可视化,gazebo里能不能看到 -->
+      <visualize>true</visualize>
+      <!-- 扫描速率,也就是数据更新速率 -->
+      <update_rate>5</update_rate>
+      <ray>
+        <scan>
+          <!-- 水平扫描的点数 -->
+          <horizontal>
+            <samples>5</samples>
+            <resolution>1</resolution>
+            <min_angle>-0.12</min_angle>
+            <max_angle>0.12</max_angle>
+          </horizontal>
+          <!-- 垂直方向扫描的点数 -->
+          <vertical>
+            <samples>5</samples>
+            <resolution>1</resolution>
+            <min_angle>-0.01</min_angle>
+            <max_angle>0.01</max_angle>
+          </vertical>
+        </scan>
+        <!-- 超声波检测的范围和数据分辨率单位m -->
+        <range>
+          <min>0.02</min>
+          <max>4</max>
+          <resolution>0.01</resolution>
+        </range>
+        <!-- 数据噪声采用高斯噪声 -->
+        <noise>
+          <type>gaussian</type>
+          <mean>0.0</mean>
+          <stddev>0.01</stddev>
+        </noise>
+      </ray>
+      <plugin name="ultrasonic_sensor_controller" filename="libgazebo_ros_ray_sensor.so">
+        <ros>
+          <!-- 重映射输出的话题名称 -->
+          <remapping>~/out:=ultrasonic_sensor_1</remapping>
+        </ros>
+        <!-- 输出消息的类型,注意与雷达区分,这里是sensor_msgs/Range -->
+        <output_type>sensor_msgs/Range</output_type>
+        <!-- 射线类型,这里要写ultrasound,注意和雷达区分 -->
+        <radiation_type>ultrasound</radiation_type>
+        <!-- frame名称,填写link名称即可 -->
+        <frame_name>ultrasonic_sensor_link</frame_name>
+      </plugin>
+    </sensor>
+  </gazebo>
+```
+
+## 3.编译运行测试
+
+### 3.1Gazebo可视化
+
+添加完成后就可以编译测试下代码
+
+```shell
+colcon build --packages-select fishbot_description
+source install/setup.bash
+```
+
+```shell
+ros2 launch fishbot_description gazebo.launch.py
+```
+
+没有物体的前面可以放个东西,因为本节是后面补充的,所有小鱼这里有个墙。
+
+![image-20220429143335942.png](1.Gazebo仿真插件之超声波/imgs/image-20220429143335942.png)
+
+### 3.2话题数据
+
+打开终端,输入下面指令
+
+```
+ros2 topic list 
+ros2 topic info /ultrasonic_sensor_1
+ros2 topic echo /ultrasonic_sensor_1
+```
+
+不出意外可以看到下面的数据
+
+```
+header:
+  stamp:
+    sec: 4458
+    nanosec: 1000000
+  frame_id: ultrasonic_sensor_link
+radiation_type: 0
+field_of_view: 0.23999999463558197
+min_range: 0.019999999552965164
+max_range: 4.0
+range: 2.6798219680786133
+```
+
+这里的range就是fishbot到墙之间的距离:2.67982
+
+我们来讲一讲超声波传感器的数据类型`sensor_msgs/msg/Range`
+
+```
+# ros2 topic info /ultrasonic_sensor_1
+Type: sensor_msgs/msg/Range
+Publisher count: 1
+Subscription count: 0
+```
+
+你可以使用`ros2 interface show  sensor_msgs/msg/Range`看到详细的解释,我们翻译一下
+
+```
+# Single range reading from an active ranger that emits energy and reports
+# one range reading that is valid along an arc at the distance measured.
+# This message is  not appropriate for laser scanners. See the LaserScan
+# message if you are working with a laser scanner.
+#
+# This message also can represent a fixed-distance (binary) ranger.  This
+# sensor will have min_range===max_range===distance of detection.
+# These sensors follow REP 117 and will output -Inf if the object is detected
+# and +Inf if the object is outside of the detection range.
+
+std_msgs/Header header # timestamp in the header is the time the ranger
+                             # returned the distance reading
+
+# Radiation type enums
+# If you want a value added to this list, send an email to the ros-users list
+uint8 ULTRASOUND=0
+uint8 INFRARED=1
+
+uint8 radiation_type    # 传感器射线类型
+                        # (sound, IR, etc) [enum]
+
+float32 field_of_view   # 距离数据对应的弧[rad]的大小,测量物体的范围介于		 
+    					# -field_of_view/2 到 field_of_view/2 之间。
+                        # 0 角度对应于传感器的 x 轴。
+
+float32 min_range       # 最小范围值 [m]
+float32 max_range       # 最大范围值 [m]
+                        #  固定距离需要 min_range==max_range
+
+float32 range           # 范围数据 [m]
+                        # (Note: values < range_min or > range_max should be discarded)
+                        # Fixed distance rangers only output -Inf or +Inf.
+                        # -Inf represents a detection within fixed distance.
+                        # (Detection too close to the sensor to quantify)
+                        # +Inf represents no detection within the fixed distance.
+                        # (Object out of range)
+```
+
+结论,主要关注range就可以了。
+
+### 3.3 在RVIZ2中可视化超声波数据
+
+Add ->By topic->Range
+
+![image-20220429145543467.png](1.Gazebo仿真插件之超声波/imgs/image-20220429145543467.png)
+
+![image-20220429145701982.png](1.Gazebo仿真插件之超声波/imgs/image-20220429145701982.png)
+
+## 4.总结
+
+本节主要介绍了如何对超声波传感器进行仿真,在导航的过程中我们通常把超声波放到一个代价地图中进行停障等行为。所以学习其仿真还是很有用的。
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429113619974.png


BIN
docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429143335942.png


BIN
docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429145543467.png


BIN
docs/humble/chapt9/advanced/1.Gazebo仿真插件之超声波/imgs/image-20220429145701982.png


+ 74 - 0
docs/humble/chapt9/get_started/1.机器人仿真介绍.md

@@ -0,0 +1,74 @@
+# 9.1 机器人仿真介绍
+
+大家好,我是小鱼,本节课我们主要对机器人仿真进行介绍,同时对ROS2支持的常用仿真平台进行介绍。
+
+## 1.为什么需要机器人仿真
+
+所谓机器人仿真其实就是通过软件来模仿硬件的特性,用于验证机器人算法、架构等。
+
+肯定有同学会问为什么要做机器人仿真,有这个用软件来模拟硬件的空挡,直接搞真实的机器人不行吗?
+
+答案肯定是可以的,对于个人极客来说,直接动手比仿真要来的快和直接的多。单对于公司和非常复杂的项目来说,仿真就变得很重要了。
+
+原因在于:
+
+1. 仿真可以解决真机资源不足,真实的机器人一般价格都很贵,搭建起来也很耗费资源,小鱼是从一家创业公司一路走来的,不同部门抢机器人的现象时常发生。
+
+2. 仿真可以保证环境的一致和稳定,举个例子,之前小鱼在部署导航系统时发现在A机器人上没问题,但在B机器人上老是丢位置,明明算法是一致的,后来发现是B机器人的IMU模块出现了松动。
+
+3. 仿真场景可以更加灵活,在测试机器人算法时可以通过仿真软件快速更改仿真环境,验证算法(甚至还可以让机器人原地起飞)
+
+
+## 2.仿有哪些缺点
+
+上面说完仿真的各种好,那仿真有哪些缺陷呢?
+
+> 之前听一位做机械臂动力学的朋友说,他们做研发时候从来没做过仿真,原因在于仿真环境中的机器人和真实环境机器人差别过大。
+
+所以机器人仿真的主要缺陷就是仿不全,现实世界中的环境非常复杂,光线、材质、电磁干扰等等,仿真平台无法做到100%的仿真。
+
+
+## 3.常用仿真平台
+
+相较于ROS,ROS2支持更多更专业的仿真平台,常用的有:
+
+### 3.1 Gazebo
+
+> 官网链接:https://www.cyberbotics.com/
+
+Gazebo是ROS中常用的机器人仿真平台,也是OSRF(开源机器人基金会)的作品之一,关于Gazebo的介绍,小鱼已经在第六章进行介绍了,忘记的同学可以翻看下。
+
+### 3.2 WeBots
+
+> 官网链接:https://www.cyberbotics.com/
+
+Webots由Cyberbotics公司开发,是一个用于模拟机器人的开源和多平台桌面应用程序。它提供了一个完整的开发环境来对机器人进行建模、编程和仿真。Webots内核基于开源动力学引擎ODE和OpenGL,可以在Windows,Linux和macOS上运行,并且支持多种编程语言(C/C++,Python,Java,MATLAB)。
+
+### 3.3 Ignition
+
+> 官网链接:https://gazebosim.org/
+> 官方文档:https://ignitionrobotics.org/docs
+
+Ignition是继承于Gazebo的下一代仿真平台,Ignition Robotics基于开发库和云服务等丰富全面的工具箱,提供了一种全新的仿真方式,进一步简化仿真。高度逼真的传感器可在接近真实的环境中快速迭代更新机器人物理设计。在安全上可测试控制策略,并在持续的集成化侧重中利用仿真的诸多优势。
+
+### 3.4 Unity
+
+> 官网链接:hhttps://unity.com/
+> UnityForROS2:https://github.com/RobotecAI/ros2-for-unity
+
+Unity Robotics软件包带有许多现成的接口,能让你轻松与ROS或ROS 2的交换信息。你也能用URDF Importer直接从URDF文件中导入机器人配置,在Unity高质量的渲染管线与高精度的物理模拟加持下开始训练机器人。Unity的Asset Store还售有大量现成的环境和道具,可用于补充机器人的训练环境、完善训练任务。只需几键,你搭建的模拟就可以构建并部署到Windows 10、Mac OS或Linux等任意主流操作系统。你甚至可以使用C#、Bolt可视化编程及Asset Store上的众多脚本和实用程序来根据自己的需求进一步定制模拟环境。
+
+
+## 4.我们用哪个仿真平台?
+
+鉴于从ROS和Gazebo中得到的大量经验,我们依然选择Gazebo作为本次进行仿真的主要平台。但在本章节的后面,小鱼会带你一起探索下其他的仿真平台。
+
+让我们保持好奇心,下一节在上一章节机器人建模的基础上,为我们的机器人模型注入仿真需要的物理属性参数。
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**

+ 129 - 0
docs/humble/chapt9/get_started/2.给两轮差速机器人添加物理参数.md

@@ -0,0 +1,129 @@
+# 2.为机器人URDF模型注入物理属性
+
+上节我们知道,机器人仿真就是用软件来模拟硬件的特性,那么我们必须要告诉仿真平台机器人各个关节的物理属性,比如:
+
+- 有多重,
+- 有多大的惯性
+- 重心在哪
+- 碰撞边界在哪
+- 关节的上下界限
+- 其他的一些必要信息等等
+
+所以这节课小鱼就带你将物理信息写入到urdf中,让机器人在gazebo中显示出来。
+
+## 1.需要哪些物理信息?
+
+一般来说有`碰撞`和`内参`两个就够了,但是因为小鱼之前的偷懒,还要加一个摩擦力配置。
+
+碰撞描述是物体的用于碰撞检测的包围形状。内参用于描述物体的质量,惯性矩阵。link的摩擦力。
+
+### 1.1 碰撞检测
+
+在机器人仿真中,我们要对物体之前是否接触,是否发生碰撞做检测,常用的检测方法比如包围盒,判断两个物体的包围盒是否相交来快速判断物体是否发生碰撞。
+
+在URDF中,我们可以可以在link标签下添加collison子标签来对物体的形状进行描述。
+
+collision可以包含的子标签如下:
+
+- origin,表示碰撞体的中心位姿
+- geometry,用于表示用于碰撞检测的几何形状
+- `material`,可选的,描述碰撞几何体的材料(这个设置可以在gazebo仿真时通过view选项看到碰撞包围体的形状)
+
+一个完整的collision标签实例如下:
+
+```xml
+    <collision>
+      <origin xyz="0 0 0.0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.12" radius="0.10"/>
+      </geometry>
+      <material name="blue">
+      	<color rgba="0.1 0.1 1.0 0.5" /> 
+      </material>
+    </collision>
+```
+
+### 1.2 旋转惯量
+
+旋转惯量矩阵是用于描述物体的惯性的,在做动力学仿真的时候,这些参数尤为重要。
+
+在URDF中我们可以通过在link下添加inertial子标签,为link添加惯性参数的描述。
+
+intertial标签包含的子标签如下:
+
+- mass,描述link的质量
+- inertia,描述link的旋转惯量(该标签有六个属性值ixx\ixy\ixz\iyy\iyz\izz)
+
+一个完整的inertial标签示例如下:
+
+```
+   <inertial>
+      <mass value="0.2"/>
+      <inertia ixx="0.0122666" ixy="0" ixz="0" iyy="0.0122666" iyz="0" izz="0.02"/>
+    </inertial>
+```
+
+关于intertial的属性设置,不是随意设置的,常见的几何体我们可以通过公式进行计算。计算方法可以看小鱼的这篇文章-[URDF仿真惯性参数不知道怎么配?快收藏,常见几何物体URDF分享](https://mp.weixin.qq.com/s/3L8Lilesy2W_WY5qup0gmA)。
+
+比如我们上一章节的fishbot的轮子和车体,都是实心圆柱,可以采用下面的公式进行计算:
+
+> 注意:这个矩阵是一个对称矩阵,所以只需要通过其上三角即可描述完整描述这个矩阵,所以在URDF中只需要填写六个数字即可。
+> 实心圆柱体的惯性矩阵:半径为r,高度为h,质量为m 的实心圆柱体
+> 形状:![实心圆柱体](2.给两轮差速机器人添加物理参数/imgs/453a07bf69814351a7c673deddf78087.png)
+> 矩阵:![矩阵](2.给两轮差速机器人添加物理参数/imgs/9b7f85f6d4314130ae6abdce6beeae8f.png)
+
+
+### 1.3 摩擦力和刚性系数
+
+在Fishbot的URDF中,前面的支撑轮主要起支撑作用,因为我们将其使用fixed标签固定到了base_link上,所以它无法转动。
+
+哪该怎么办呢?小鱼教你一个取巧的办法,我们可以要把这个轮子的摩擦力设置为0,让它直接在地上滑动即可,
+
+如何设置呢?6行代码放到URDF中:
+
+```
+  <gazebo reference="caster_link">
+    <mu1 value="0.0"/>
+    <mu2 value="0.0"/>
+    <kp value="1000000.0" />
+    <kd value="10.0" />
+  </gazebo>
+```
+
+其中mu1,mu2代表摩擦力,kp,kd代表刚性系数。
+
+## 2.为FishBot添加物理惯性
+
+利用上面的方法公式,为我们的fishbot哥哥link添加好物理属性,完成后的base_link如下:
+
+```xml
+  <!-- base link -->
+  <link name="base_link">
+  	<visual>
+      <origin xyz="0 0 0.0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.12" radius="0.10"/>
+      </geometry>
+      <material name="blue">
+      	<color rgba="0.1 0.1 1.0 0.5" /> 
+      </material>
+    </visual>
+    <collision>
+      <origin xyz="0 0 0.0" rpy="0 0 0"/>
+      <geometry>
+		<cylinder length="0.12" radius="0.10"/>
+      </geometry>
+      <material name="blue">
+      	<color rgba="0.1 0.1 1.0 0.5" /> 
+      </material>
+    </collision>
+    <inertial>
+      <mass value="0.2"/>
+      <inertia ixx="0.0122666" ixy="0" ixz="0" iyy="0.0122666" iyz="0" izz="0.02"/>
+    </inertial>
+  </link>
+```
+
+完全添加好的机器人URDF模型小鱼已经放到了这里:[fishbot_gazebo.urdf](https://github.com/fishros/fishbot/blob/navgation2/src/fishbot_description/urdf/fishbot_gazebo.urdf)
+
+可以将小鱼配置好的模型下载到`src/c/urdf`文件夹下,等下我们要用gazebo将该模型显示出来。

BIN
docs/humble/chapt9/get_started/2.给两轮差速机器人添加物理参数/imgs/453a07bf69814351a7c673deddf78087.png


BIN
docs/humble/chapt9/get_started/2.给两轮差速机器人添加物理参数/imgs/9b7f85f6d4314130ae6abdce6beeae8f.png


+ 297 - 0
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型.md

@@ -0,0 +1,297 @@
+# 3.使用gazebo加载URDF
+
+## 1.Gazebo-ROS2插件介绍
+
+在第六章中小鱼曾介绍过,gazebo是独立于ROS/ROS2之外的仿真软件,我们可以独立使用Gazebo。如果我们想要通过ROS2和Gazebo进行交互,需要通过gazebo_ros插件来进行。
+
+接下来小鱼先带你通过命令行的形式来启动gazebo-ros2插件以及使用插件提供的服务来将fishbot的urdf模型在gazebo中显示出来。
+
+
+### 1.1 安装Gazebo插件
+
+
+```shell
+sudo apt install ros-foxy-gazebo-ros
+```
+
+### 1.2 启动Gazebo并启动插件
+
+安装完成后,我们就可以通过下面的命令行来启动gazebo并加载ros2插件。
+
+```shell
+gazebo --verbose -s libgazebo_ros_factory.so
+```
+
+看到下面的日志和Gazebo界面代表启动成功
+
+```
+Gazebo multi-robot simulator, version 11.9.0
+Copyright (C) 2012 Open Source Robotics Foundation.
+Released under the Apache 2 License.
+http://gazebosim.org
+
+[Msg] Waiting for master.
+Gazebo multi-robot simulator, version 11.9.0
+Copyright (C) 2012 Open Source Robotics Foundation.
+Released under the Apache 2 License.
+http://gazebosim.org
+
+[Msg] Waiting for master.
+[Msg] Connected to gazebo master @ http://127.0.0.1:11345
+[Msg] Publicized address: 192.168.2.103
+[Msg] Loading world file [/usr/share/gazebo-11/worlds/empty.world]
+[INFO] [1649151283.208884022] [gazebo_ros_node]: ROS was initialized without arguments.
+[Msg] Connected to gazebo master @ http://127.0.0.1:11345
+[Msg] Publicized address: 192.168.2.103
+```
+
+![image-20220405224729354](3.在Gazebo加载机器人模型/imgs/image-20220405224729354.png)
+
+
+
+## 2.插件节点及其服务介绍
+
+使用3.1中的指令启动Gazebo并加载gazebo_ros插件,我们使用下面的指令来看插件的节点,以及改节点为我们提供的服务有哪些?
+
+节点列表
+
+```shell
+ros2 node list
+```
+
+正确返回
+
+```
+/gazebo
+```
+
+然后我们看看这个节点对外提供的服务有哪些?
+
+```
+ros2 service list
+```
+
+```
+/delete_entity
+/get_model_list
+/spawn_entity
+/gazebo/describe_parameters
+/gazebo/get_parameter_types
+/gazebo/get_parameters
+/gazebo/list_parameters
+/gazebo/set_parameters
+/gazebo/set_parameters_atomically
+```
+
+除去和参数相关的几个服务,我们可以看到另外三个特殊服务:
+
+- /spawn_entity,用于加载模型到gazebo中
+- /get_model_list,用于获取模型列表
+- /delete_entity,用于删除gazbeo中已经加载的模型
+
+我们想要让gazebo显示出我们配置好的fishbot使用/spawn_entity来加载即可。
+
+接着我们可以来请求服务来加载模型,小鱼先带你看一下服务的接口类型。
+
+```shell
+ros2 service type /spawn_entity
+```
+
+返回
+
+```
+gazebo_msgs/srv/SpawnEntity
+```
+
+指令
+
+```
+ros2 interface show gazebo_msgs/srv/SpawnEntity
+```
+
+返回
+
+```
+string name                       # Name of the entity to be spawned (optional).
+string xml                        # Entity XML description as a string, either URDF or SDF.
+string robot_namespace            # Spawn robot and all ROS interfaces under this namespace
+geometry_msgs/Pose initial_pose   # Initial entity pose.
+string reference_frame            # initial_pose is defined relative to the frame of this entity.
+                                  # If left empty or "world" or "map", then gazebo world frame is
+                                  # used.
+                                  # If non-existent entity is specified, an error is returned
+                                  # and the entity is not spawned.
+---
+bool success                      # Return true if spawned successfully.
+string status_message             # Comments if available.
+```
+
+可以看到服务的请求内容包括:
+
+- string name  ,需要加载的实体的名称 (可选的)。
+- string xml      ,实体的XML描述字符串, URDF或者SDF。
+- string robot_namespace  ,产生的机器人和所有的ROS接口的命名空间,多机器人仿真的时候很有用。
+- geometry_msgs/Pose initial_pose   ,机器人的初始化位置
+- string reference_frame    ,初始姿态是相对于该实体的frame定义的。如果保持"empty"或"world"或“map”,则使用 gazebo的world作为frame。如果指定了不存在的实体,则会返回错误
+
+## 3.调用服务加载fishbot
+
+看到这里你是不是迫不及待敲起来命令行来加载我们的机器人到gazebo了,别着急,小鱼再推荐一个可视化服务请求工具,其实在第六章中小鱼介绍过,在rqt工具集里有一个叫服务请求工具。
+
+命令行输入rqt,在插件选项中选择Services->Service Caller,然后再下拉框选择/spawn_entity服务,即可看到下面的界面。
+
+![image-20220405233406029](3.在Gazebo加载机器人模型/imgs/image-20220405233406029.png)
+
+
+
+接着我们把我们的FishBot的URDF模型复制粘贴,放到xml中(注意要把原来的''删掉哦!),然后拿起我们的小电话,和小鱼一起Call。
+
+![image-20220405233825788](3.在Gazebo加载机器人模型/imgs/image-20220405233825788.png)
+
+接着就可以看到工厂返回说成功把机器人制作出来送入gazebo了。
+
+此时再看我们的Gazebo,一个小小的,白白的机器人出现了。
+
+![image-20220405233947338](3.在Gazebo加载机器人模型/imgs/image-20220405233947338.png)
+
+按住Shift加鼠标左键,拖动一下,来好好的欣赏欣赏我们的机器人。
+
+![image-20220405234118003](3.在Gazebo加载机器人模型/imgs/image-20220405234118003.png)
+
+### 3.4 在不同位置加载多个机器人
+
+欣赏完毕后,小鱼再带你生产一个fishbot(为了后面需要多机器人仿真的小伙伴)。
+
+修改rqt中的参数,增加一个命名空间,然后修改一个位置,让第二个机器人和第一个相距1m的地方生产,然后点击Call。
+
+![image-20220405234507958](3.在Gazebo加载机器人模型/imgs/image-20220405234507958.png)
+
+返回成功,此时拖送Gazebo观察一下,发现多出了一个机器人,距离刚好是在X轴(红色)1米(一个小格子一米)处。
+
+![image-20220405234644118](3.在Gazebo加载机器人模型/imgs/image-20220405234644118.png)
+
+
+
+### 3.5 查询和删除机器人
+
+利用rqt工具,我们再对另外两个服务接口进行请求。
+
+![image-20220405234948237](3.在Gazebo加载机器人模型/imgs/image-20220405234948237.png)
+
+查到了三个模型,一个大地,一个fishbot,一个fishbot_0。
+
+我们接着尝试把fishbot_0删掉,选择删除实体,输入fishbot_0的名字,拿起小电话通知工厂回收我们的0号fishbot。
+
+![image-20220405235058954](3.在Gazebo加载机器人模型/imgs/image-20220405235058954.png)
+
+调用成功,观察gazebo发现机器人没了
+
+![image-20220405235212262](3.在Gazebo加载机器人模型/imgs/image-20220405235212262.png)
+
+
+
+## 4. 将启动gazebo和生产fishbot写成launch文件
+
+打开fishbot工作空间,在`src/fishbot_description/launch`中添加一个`gazebo.launch.py`文件,我们开始编写launch文件来在gazebo中加载机器人模型。
+
+启动gazebo,我们可以将命令行写成一个launch节点
+
+```
+ExecuteProcess(
+   cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_factory.so'],
+   output='screen')
+```
+
+上面我们加载机器人是直接将XML格式的URDF复制过去进行加载的,这样很不方便,我们可以使用gazebo_ros为我们提供好的一个叫做`spawn_entity.py`节点,该节点支持从文件地址直接生产机器人到Gazebo。
+
+> 其实该节点的原理也很简单,从URDF中读取机器人模型,然后再调用服务,和我们手动操作一个样子,小鱼只道没差别。
+
+该节点需要两个参数,一个机器人的模型名字和urdf的文件地址,这个简单,前面我们曾经使用package_share来拼接过urdf路径。
+
+```python
+spawn_entity_cmd = Node(
+	package='gazebo_ros', 
+	executable='spawn_entity.py',
+	arguments=['-entity', robot_name_in_model,  '-file', urdf_model_path ], output='screen')
+```
+
+最终写好的launch文件如下:
+
+```python
+import os
+from launch import LaunchDescription
+from launch.actions import ExecuteProcess
+from launch_ros.actions import Node
+from launch_ros.substitutions import FindPackageShare
+
+
+def generate_launch_description():
+    robot_name_in_model = 'fishbot'
+    package_name = 'fishbot_description'
+    urdf_name = "fishbot_gazebo.urdf"
+
+    ld = LaunchDescription()
+    pkg_share = FindPackageShare(package=package_name).find(package_name) 
+    urdf_model_path = os.path.join(pkg_share, f'urdf/{urdf_name}')
+
+    # Start Gazebo server
+    start_gazebo_cmd = ExecuteProcess(
+        cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_factory.so'],
+        output='screen')
+
+    # Launch the robot
+    spawn_entity_cmd = Node(
+        package='gazebo_ros', 
+        executable='spawn_entity.py',
+        arguments=['-entity', robot_name_in_model,  '-file', urdf_model_path ], output='screen')
+
+    ld.add_action(start_gazebo_cmd)
+    ld.add_action(spawn_entity_cmd)
+
+
+    return ld
+```
+
+编译运行
+
+```
+colcon build --packages-select fishbot_description
+source install/setup.bash
+ros2 launch fishbot_description gazebo.launch.py
+```
+
+ 完美显示
+
+![image-20220406000341792](3.在Gazebo加载机器人模型/imgs/image-20220406000341792.png)
+
+## 5.总结
+
+这节课我们为Fishbot注入了仿真必须的物理属性,但是机器人还是不会动,下一节课我们就利用Gazebo的其他插件,让我们的机器人动起来。
+
+最后再留一个课后作业:
+
+1. 尝试将fishbot的物理属性去掉,再加载机器人看看会发生什么?
+
+2. 尝试将fishbot的碰撞改成很小,再看看会发生什么?
+
+3. gazebo还支持link的材料修改,在URDF中添加下面的代码,给支撑轮一个不一样的材质吧,你也可以将reference改成其他link,装点一下你的机器人。
+
+   ```xml
+     <gazebo reference="caster_link">
+       <material>Gazebo/Black</material>
+     </gazebo>
+   ```
+
+
+欢迎将你的实验结果在我们的[fishros社区分享](https://fishros.org.cn/forum/)~
+
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405224729354.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405233406029.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405233825788.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405233947338.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234118003.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234507958.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234644118.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405234948237.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405235058954.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220405235212262.png


BIN
docs/humble/chapt9/get_started/3.在Gazebo加载机器人模型/imgs/image-20220406000341792.png


+ 493 - 0
docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速.md

@@ -0,0 +1,493 @@
+# 4.Gazebo仿真插件之两轮差速
+
+小鱼又来了,完成了上节课的Gazebo加载FishBot,但是机器人还是不会动,你一定很不开心吧,本节课小鱼就带你一起通过配置两轮差速控制插件,让我们的机器人动起来~
+
+最终效果:
+
+![boot](../../../foxy/chapt9/9.3为FishBot配置两轮差速控制插件/imgs/boot.gif)
+
+
+
+## 1.Gazebo插件介绍
+
+之前小鱼说过Gazebo是一个独立于ROS的软件,对外提供了丰富的API可以使用,gazebo的插件按照用途大致可以分为两种:
+
+1. **用于控制的插件**,通过插件可以控制机器人关节运动,可以进行位置、速度、力的控制,比如我们这节课的两轮差速控制器。
+2. **用于数据采集的插件**,比如IMU传感器用于采集机器人的惯性,激光雷达用于采集机器人周围的点云信息。
+
+当然上面两类插件功能也可以写到一个插件里,两轮差速插件就是一个二合一加强版。
+
+## 2.两轮差速插件介绍
+
+两轮差速插件用于控制机器人轮子关节的位置变化,同时该插件还会获取轮子的位置以及速度的信息的反馈,根据反馈的位置信息结合运动学模型即可计算出当前机器人的位姿(里程计)。
+
+> 该插件的名称为:gazebo_ros_diff_drive
+>
+> 源码地址:https://github.com/ros-simulation/gazebo_ros_pkgs/blob/foxy/gazebo_plugins/src/gazebo_ros_diff_drive.cpp
+
+**两轮差速控制器和Gazebo的关系**
+
+两轮差速控制器可以将轮子的目标转速发送给Gazebo,并从Gazebo获取到实际的速度和位置。
+
+> 注意:发送给Gazebo是目标速度,反馈回来的是实际速度。目标!=实际,比如轮子卡住了,无论你发什么目标速度,实际速度都是0。
+
+要想快速了解一个系统的功能,最直接的就是看系统的对外的输入和输出是什么?什么都不要说,看下图:
+
+```mermaid
+graph LR;
+D[配置参数-轮距直径等]-->B
+A[控制指令-cmdvel]-->B[gazebo_ros_diff_drive]
+B-->C[里程计-odom]
+B-->E[里程计tf-可选]
+B-->F[轮子tf-可选]
+```
+
+上图就是对gazebo_ros_diff_drive的输入和输出信息的总结,可以很直观的看到该插件主要**输入控制指令**,主要**输出里程计信息**。接着小鱼带你分别认识一下输入和输出两个部分。
+
+### 2.2 输入参数
+
+#### 2.2.1 配置参数
+
+不知道你是否还记得在第七章中,小鱼对两轮差速底盘的运动学正的介绍。如果要完成底盘的正逆解和里程计的推算就必须要知道轮子的直径和间距。
+
+同时该插件还提供了一些可以控制输出的选项,因为是仿真,所以还要告诉插件轮子对应的joint名称等信息,这样就有了下面这个参数表格:
+
+| 配置项                 | 含义                                    |
+| ---------------------- | --------------------------------------- |
+| ros                    | ros相关配置,包含命名空间和话题重映射等 |
+| update_rate            | 数据更新速率                            |
+| left_joint             | 左轮关节名称                            |
+| right_joint            | 右轮关节名称                            |
+| wheel_separation       | 左右轮子的间距                          |
+| wheel_diameter         | 轮子的直径                              |
+| max_wheel_torque       | 轮子最大的力矩                          |
+| max_wheel_acceleration | 轮子最大的加速度                        |
+| publish_odom           | 是否发布里程计                          |
+| publish_odom_tf        | 是否发布里程计的tf开关                  |
+| publish_wheel_tf       | 是否发布轮子的tf数据开关                |
+| odometry_frame         | 里程计的framed ID,最终体现在话题和TF上 |
+| robot_base_frame       | 机器人的基础frame的ID                   |
+
+#### 2.2.2 控制指令
+
+两轮差速控制器默认通过订阅话题`cmd_vel`来获取目标线速度和角速度。该话题的类型为:`geometry_msgs/msg/Twist`
+
+我们通过ros2的CLI来看一下这个消息包含的内容有哪些?
+
+```
+ros2 interface show geometry_msgs/msg/Twist
+```
+
+```
+# This expresses velocity in free space broken into its linear and angular parts.
+
+Vector3  linear
+Vector3  angular
+```
+
+可以看到包含线速度和角速度,我们用proto在看一下包含的基本数据类型有哪些?
+
+```
+ros2 interface proto geometry_msgs/msg/Twist
+```
+
+```
+"linear:
+  x: 0.0
+  y: 0.0
+  z: 0.0
+angular:
+  x: 0.0
+  y: 0.0
+  z: 0.0
+"
+```
+
+线速度和角速度都包含在x、y、z,代表坐标系的三个方向上的对应速度。
+
+两轮差速控制器收到这个话题数据后将其中的角速度和线速度转换上两个轮子的转动速度发送给Gazebo。
+
+### 2.3 输出参数
+
+#### 2.3.1 里程计
+
+里程计信息默认的输出话题为`odom`,其消息类型为:`nav_msgs/msg/Odometry`
+
+同样的使用CLI看一下其消息的组成结构:
+
+```
+ros2 interface show nav_msgs/msg/Odometry
+```
+
+```
+# This represents an estimate of a position and velocity in free space.
+# The pose in this message should be specified in the coordinate frame given by header.frame_id
+# The twist in this message should be specified in the coordinate frame given by the child_frame_id
+
+# Includes the frame id of the pose parent.
+std_msgs/Header header
+
+# Frame id the pose points to. The twist is in this coordinate frame.
+string child_frame_id
+
+# Estimated pose that is typically relative to a fixed world frame.
+geometry_msgs/PoseWithCovariance pose
+
+# Estimated linear and angular velocity relative to child_frame_id.
+geometry_msgs/TwistWithCovariance twist
+
+```
+
+```
+ros2 interface proto nav_msgs/msg/Odometry
+```
+
+```
+"header:
+  stamp:
+    sec: 0
+    nanosec: 0
+  frame_id: ''
+child_frame_id: ''
+pose:
+  pose:
+    position:
+      x: 0.0
+      y: 0.0
+      z: 0.0
+    orientation:
+      x: 0.0
+      y: 0.0
+      z: 0.0
+      w: 1.0
+  covariance:
+  - 0.0
+  - 0.0
+    ...
+twist:
+  twist:
+    linear:
+      x: 0.0
+      y: 0.0
+      z: 0.0
+    angular:
+      x: 0.0
+      y: 0.0
+      z: 0.0
+  covariance:
+  - 0.0
+  - 0.0
+	...
+"
+```
+
+可以看到其数据主要包含三个部分:
+
+- header,表示该消息发布的时间
+- pose,表示当前机器人位置和朝向
+- twist,表示当前机器人的线速度和角速度
+
+> 数据中还包含一个covariance,其代表协方差矩阵,后面小鱼写篇文章来介绍下,这里只需了解其含义即可。
+
+#### 2.3.2 里程计TF信息
+
+设为true,订阅tf话题里你就可以看到像下面的msg,建议后面配置好后,手动修改下,对比区别
+
+```
+- header:
+    stamp:
+      sec: 6157
+      nanosec: 907000000
+    frame_id: odom
+  child_frame_id: base_footprint
+  transform:
+    translation:
+      x: 0.0005557960241049835
+      y: -0.0007350446303238693
+      z: 0.01599968753145574
+    rotation:
+      x: 4.691143395208505e-07
+      y: 7.115496626557812e-06
+      z: -0.018531475772549166
+      w: 0.9998282774331005
+```
+
+
+
+#### 2.3.3 左右轮子TF信息
+
+设为true,订阅tf话题里你就可以看到像下面的msg,建议后面配置好后,手动修改下,对比区别
+
+```
+- header:
+    stamp:
+      sec: 6157
+      nanosec: 941000000
+    frame_id: base_link
+  child_frame_id: left_wheel_link
+  transform:
+    translation:
+      x: -0.02
+      y: 0.1
+      z: -0.06
+    rotation:
+      x: 0.0
+      y: 0.049519025127821005
+      z: 0.0
+      w: 0.9987731805321918
+- header:
+    stamp:
+      sec: 6157
+      nanosec: 941000000
+    frame_id: base_link
+  child_frame_id: right_wheel_link
+  transform:
+    translation:
+      x: -0.02
+      y: -0.1
+      z: -0.06
+    rotation:
+      x: 0.0
+      y: -0.0663387077034509
+      z: 0.0
+      w: 0.9977971616817898
+```
+
+## 3.在URDF中配置两轮差速模型
+
+上面该介绍的我们都给介绍了,接着我们直接来配置。
+
+因为是给Gazebo的插件,所以在`URDF`中,我们需要使用`<gazebo>`进行配置,因为是要给`gazebo`配置插件,所有要在`gazebo`标签下添加`plugin`子插件。
+
+话不多说,上代码
+
+```
+  <gazebo>
+    <plugin name='diff_drive' filename='libgazebo_ros_diff_drive.so'>
+          <ros>
+            <namespace>/</namespace>
+            <remapping>cmd_vel:=cmd_vel</remapping>
+            <remapping>odom:=odom</remapping>
+          </ros>
+          <update_rate>30</update_rate>
+          <!-- wheels -->
+          <left_joint>left_wheel_joint</left_joint>
+          <right_joint>right_wheel_joint</right_joint>
+          <!-- kinematics -->
+          <wheel_separation>0.2</wheel_separation>
+          <wheel_diameter>0.065</wheel_diameter>
+          <!-- limits -->
+          <max_wheel_torque>20</max_wheel_torque>
+          <max_wheel_acceleration>1.0</max_wheel_acceleration>
+          <!-- output -->
+          <publish_odom>true</publish_odom>
+          <publish_odom_tf>true</publish_odom_tf>
+          <publish_wheel_tf>true</publish_wheel_tf>
+          <odometry_frame>odom</odometry_frame>
+          <robot_base_frame>base_footprint</robot_base_frame>
+      </plugin>
+```
+
+将这段代码加到我们的URDF中,然后对着上面小鱼介绍的配置项,一一看下,接着我们就可以来测试运行了。
+
+完整的URDF可以参考:
+
+## 4.两轮差速插件测试
+
+修改完了URDF模型我们将代码编译一下,让更新后的URDF文件安装到install目录,接着就可以运行9.2中的launch文件,将模型加载到Gazebo中。
+
+### 4.1 编译-启动
+
+```
+colcon build
+```
+
+```
+ros2 launch fishbot_description gazebo.launch.py
+```
+
+![image-20220408125719602](4.Gazebo仿真插件之两轮差速/imgs/image-20220408125719602.png)
+
+
+接着你可以使用CLI工具看一下系统有哪些节点在运行
+
+```
+ros2 node list
+ros2 topic list
+```
+
+```
+ros2 node list
+---------------
+/diff_drive
+/gazebo
+
+ros2 topic list
+---------------
+/clock
+/cmd_vel
+/odom
+/parameter_events
+/performance_metrics
+/rosout
+/tf
+```
+
+相信此时你已经看到了我们插件订阅的的/cmd_vel和发布的/odom了。
+
+
+### 4.3 使用键盘控制fishbot
+
+你还记得第二章中小鱼带你玩的小乌龟吗?当时我们用键盘来控制小乌龟运动,现在我们可以用键盘来控制fishbot动起来了。
+
+你需要一个键盘控制工具,可以用下面的指令安装
+
+```
+sudo apt install ros-foxy-teleop-twist-keyboard
+```
+
+这个功能包下有一个节点,这个节点会监听键盘的按键事件,然后发布cmd_vel话题,该话题被gazebo的两轮差速插件所订阅。所以我们就可以通过这个节点来控制fishbot。
+
+```
+ros2 run teleop_twist_keyboard teleop_twist_keyboard
+```
+
+> 如果你想让这个节点不是发布cmd_vel话题,而是别的,可以采用ROS2的话题重映射功能。
+> eg: `ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args --remap cmd_vel:=cmd_vel1`
+
+接着尝试使用来控制机器人运动
+
+```
+   U    I    O
+   J    K    L
+   M    <    >
+```
+
+点一下I,你就能看到fishbot在Gazebo中飞速的移动。接着打开终端,打印一下odom话题和tf话题,移动机器人观察数据变化。
+
+此时你应该玩一会,体验一下各种花式走法。
+
+### 4.4 使用rqt显示速度数据
+
+接着我们尝试使用rqt将数据在rqt中可视化出来,打开终端输入rqt。
+
+```
+rqt
+```
+
+选择Plugin->Visualization->Plot
+
+![image-20220408131355255](4.Gazebo仿真插件之两轮差速/imgs/image-20220408131355255.png)
+
+在上方Topic输入`/cmd_vel/linear/x`,再输入`/cmd_vel/angular/z`,然后用键盘控制机器人移动。
+
+![image-20220408131855683](4.Gazebo仿真插件之两轮差速/imgs/image-20220408131855683.png)
+
+cmd_vel中的速度代表目标速度,接着我们显示一下当前速度(在odom.twist中)
+
+![image-20220408141925261](4.Gazebo仿真插件之两轮差速/imgs/image-20220408141925261.png)
+
+
+
+### 4.5 在RVIZ2中显示Fishbot及其轨迹
+
+打开rviz2
+
+```
+rviz2
+```
+
+1. 修改FixedFrame为odom
+2. 添加插件,Add->Odometry->OK
+3. 选择话题,Odometry->Topic->选/odom
+4. 去除协方差显示,Odometry->Covariance>取消勾选
+5. 键盘控制节点,点个U,原地转圈圈
+
+最终结果:
+
+![image-20220408142436034](4.Gazebo仿真插件之两轮差速/imgs/image-20220408142436034.png)
+
+### 4.6 在RVIZ2中显示机器人模型
+
+虽然机器人的轨迹已经在RVIZ中显示出来了,但是并没有机器人的模型,也看不到轮子的转动,小鱼来带你一起解决这个问题。
+
+前面小鱼介绍过,要发布机器人模型我们所使用的节点是`robot_state_publisher`,所以我们在`gazebo.launch.py`中加入这个节点,同时再加上rviz2的启动节点,最终的`gazebo.launch.py`内容如下:
+
+```python
+import os
+from launch import LaunchDescription
+from launch.actions import ExecuteProcess
+from launch_ros.actions import Node
+from launch_ros.substitutions import FindPackageShare
+
+
+def generate_launch_description():
+    robot_name_in_model = 'fishbot'
+    package_name = 'fishbot_description'
+    urdf_name = "fishbot_gazebo.urdf"
+
+    ld = LaunchDescription()
+    pkg_share = FindPackageShare(package=package_name).find(package_name) 
+    urdf_model_path = os.path.join(pkg_share, f'urdf/{urdf_name}')
+
+    # Start Gazebo server
+    start_gazebo_cmd = ExecuteProcess(
+        cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_factory.so'],
+        output='screen')
+
+    # Launch the robot
+    spawn_entity_cmd = Node(
+        package='gazebo_ros', 
+        executable='spawn_entity.py',
+        arguments=['-entity', robot_name_in_model,  '-file', urdf_model_path ], output='screen')
+	
+    # Start Robot State publisher
+    start_robot_state_publisher_cmd = Node(
+        package='robot_state_publisher',
+        executable='robot_state_publisher',
+        arguments=[urdf_model_path]
+    )
+
+    # Launch RViz
+    start_rviz_cmd = Node(
+        package='rviz2',
+        executable='rviz2',
+        name='rviz2',
+        output='screen',
+        # arguments=['-d', default_rviz_config_path]
+        )
+
+    ld.add_action(start_gazebo_cmd)
+    ld.add_action(spawn_entity_cmd)
+    ld.add_action(start_robot_state_publisher_cmd)
+    ld.add_action(start_rviz_cmd)
+
+
+    return ld
+```
+
+保存编译启动
+
+```shell
+colcon build
+```
+
+```shell
+ros2 launch fishbot_description gazebo.launch.py
+```
+
+然后继续启动键盘控制,Enjoy It!
+
+![boot](../../../foxy/chapt9/9.3为FishBot配置两轮差速控制插件/imgs/boot.gif)
+
+
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/boot.gif


BIN
docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408125719602.png


BIN
docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408131355255.png


BIN
docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408131855683.png


BIN
docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408141925261.png


BIN
docs/humble/chapt9/get_started/4.Gazebo仿真插件之两轮差速/imgs/image-20220408142436034.png


+ 302 - 0
docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU.md

@@ -0,0 +1,302 @@
+# 5.Gazebo仿真插件之IMU
+
+大家好,我是小鱼,上节课通过配置两轮差速控制器我们已经成功的让fishbot在gazebo中动了起来,本节课我们通过给fishbot的URDF配置IMU传感器插件,让IMU模块工作起来。
+
+## 1.惯性测量单元IMU介绍
+
+惯性测量单元是测量物体三轴姿态角(或角速率)以及加速度的装置。一般的,一个IMU包含了三个单轴的加速度计和三个单轴的陀螺,加速度计检测物体在载体坐标系统独立三轴的加速度信号,而陀螺检测载体相对于导航坐标系的角速度信号,**测量物体在三维空间中的角速度和加速度**,并以此解算出**物体的姿态**。在导航中有着很重要的应用价值。
+
+上面这段话是小鱼从百科中摘抄出来的,你需要知道的一个关键点是IMU可以测量以下三组数据:
+
+- 三维角度
+- 三维加速度
+- 三维角速度
+
+### 1.1 IMU长啥样?
+
+便宜的长这样:
+
+![image-20220409150754602](5.Gazebo仿真插件之IMU/imgs/image-20220409150448805.png)
+
+
+贵的长这样:
+
+![image-20220409150754602](5.Gazebo仿真插件之IMU/imgs/image-20220409150658019.png)
+
+
+不要钱的长什么样?
+
+![image-20220409150754602](5.Gazebo仿真插件之IMU/imgs/image-20220409150754602.png)
+
+仿真的不要钱哈哈,接着我们来配置一下仿真的IMU。
+
+
+
+## 2.Gazebo-IMU插件介绍
+
+仿真的IMU也是对应一个后缀为`.so`的动态链接库,使用下面的指令可以查看所有的动态链接库:
+
+```
+ls /opt/ros/foxy/lib/libgazebo_ros*
+```
+
+```
+/opt/ros/foxy/lib/libgazebo_ros2_control.so
+/opt/ros/foxy/lib/libgazebo_ros_ackermann_drive.so
+/opt/ros/foxy/lib/libgazebo_ros_bumper.so
+/opt/ros/foxy/lib/libgazebo_ros_camera.so
+/opt/ros/foxy/lib/libgazebo_ros_diff_drive.so
+/opt/ros/foxy/lib/libgazebo_ros_elevator.so
+/opt/ros/foxy/lib/libgazebo_ros_factory.so
+/opt/ros/foxy/lib/libgazebo_ros_force.so
+/opt/ros/foxy/lib/libgazebo_ros_force_system.so
+/opt/ros/foxy/lib/libgazebo_ros_ft_sensor.so
+/opt/ros/foxy/lib/libgazebo_ros_gps_sensor.so
+/opt/ros/foxy/lib/libgazebo_ros_hand_of_god.so
+/opt/ros/foxy/lib/libgazebo_ros_harness.so
+/opt/ros/foxy/lib/libgazebo_ros_imu_sensor.so
+/opt/ros/foxy/lib/libgazebo_ros_init.so
+/opt/ros/foxy/lib/libgazebo_ros_joint_pose_trajectory.so
+/opt/ros/foxy/lib/libgazebo_ros_joint_state_publisher.so
+/opt/ros/foxy/lib/libgazebo_ros_node.so
+/opt/ros/foxy/lib/libgazebo_ros_p3d.so
+/opt/ros/foxy/lib/libgazebo_ros_planar_move.so
+/opt/ros/foxy/lib/libgazebo_ros_projector.so
+/opt/ros/foxy/lib/libgazebo_ros_properties.so
+/opt/ros/foxy/lib/libgazebo_ros_ray_sensor.so
+/opt/ros/foxy/lib/libgazebo_ros_state.so
+/opt/ros/foxy/lib/libgazebo_ros_template.so
+/opt/ros/foxy/lib/libgazebo_ros_tricycle_drive.so
+/opt/ros/foxy/lib/libgazebo_ros_utils.so
+/opt/ros/foxy/lib/libgazebo_ros_vacuum_gripper.so
+/opt/ros/foxy/lib/libgazebo_ros_video.so
+/opt/ros/foxy/lib/libgazebo_ros_wheel_slip.so
+```
+
+IMU对应的消息类型为`sensor_msgs/msg/Imu`
+
+```
+ros2 interface show sensor_msgs/msg/Imu
+```
+
+```
+# This is a message to hold data from an IMU (Inertial Measurement Unit)
+#
+# Accelerations should be in m/s^2 (not in g's), and rotational velocity should be in rad/sec
+#
+# If the covariance of the measurement is known, it should be filled in (if all you know is the
+# variance of each measurement, e.g. from the datasheet, just put those along the diagonal)
+# A covariance matrix of all zeros will be interpreted as "covariance unknown", and to use the
+# data a covariance will have to be assumed or gotten from some other source
+#
+# If you have no estimate for one of the data elements (e.g. your IMU doesn't produce an
+# orientation estimate), please set element 0 of the associated covariance matrix to -1
+# If you are interpreting this message, please check for a value of -1 in the first element of each
+# covariance matrix, and disregard the associated estimate.
+
+std_msgs/Header header
+
+geometry_msgs/Quaternion orientation
+float64[9] orientation_covariance # Row major about x, y, z axes
+
+geometry_msgs/Vector3 angular_velocity
+float64[9] angular_velocity_covariance # Row major about x, y, z axes
+
+geometry_msgs/Vector3 linear_acceleration
+float64[9] linear_acceleration_covariance # Row major x, y z
+```
+
+可以看到除了每个数据对应的三个协方差之外,每一个还都对应一个`3*3`的协方差矩阵。
+
+## 3.给FIshbot配置IMU传感器
+
+有了上节课的经验,我们可以很轻松的添加IMU传感器,但是还有一个需要注意的地方,为了更真实的模拟IMU传感器,我们需要给我们的仿真IMU传感器加点料。
+
+加什么?加点高斯噪声,高斯噪声只需要指定平均值和标准差两个参数即可,不过因为IMU传感器的特殊性,我们还需要给模型添加两个偏差参数,分别是 `平均值偏差`和`标准差偏差`。
+
+有关Gazebo仿真和噪声模型更深入的介绍可以参考小鱼发的两篇推文:
+
+- [Gazebo仿真进阶教程之传感器高斯噪声(一)](https://mp.weixin.qq.com/s/0-OEATkyfMf6wEyrP5csGw)
+- [Gazebo仿真进阶教程之传感器高斯噪声(二)](https://mp.weixin.qq.com/s/5k1SEGdASjUMbwWdSpf1PQ)
+
+下面是IMU传感器的URDF配置代码,大家结合文章对应可以理解一下,IMU对应的插件库`libgazebo_ros_imu_sensor.so`
+
+```
+    <gazebo reference="imu_link">
+      <sensor name="imu_sensor" type="imu">
+      <plugin filename="libgazebo_ros_imu_sensor.so" name="imu_plugin">
+          <ros>
+            <namespace>/</namespace>
+            <remapping>~/out:=imu</remapping>
+          </ros>
+          <initial_orientation_as_reference>false</initial_orientation_as_reference>
+        </plugin>
+        <always_on>true</always_on>
+        <update_rate>100</update_rate>
+        <visualize>true</visualize>
+        <imu>
+          <angular_velocity>
+            <x>
+              <noise type="gaussian">
+                <mean>0.0</mean>
+                <stddev>2e-4</stddev>
+                <bias_mean>0.0000075</bias_mean>
+                <bias_stddev>0.0000008</bias_stddev>
+              </noise>
+            </x>
+            <y>
+              <noise type="gaussian">
+                <mean>0.0</mean>
+                <stddev>2e-4</stddev>
+                <bias_mean>0.0000075</bias_mean>
+                <bias_stddev>0.0000008</bias_stddev>
+              </noise>
+            </y>
+            <z>
+              <noise type="gaussian">
+                <mean>0.0</mean>
+                <stddev>2e-4</stddev>
+                <bias_mean>0.0000075</bias_mean>
+                <bias_stddev>0.0000008</bias_stddev>
+              </noise>
+            </z>
+          </angular_velocity>
+          <linear_acceleration>
+            <x>
+              <noise type="gaussian">
+                <mean>0.0</mean>
+                <stddev>1.7e-2</stddev>
+                <bias_mean>0.1</bias_mean>
+                <bias_stddev>0.001</bias_stddev>
+              </noise>
+            </x>
+            <y>
+              <noise type="gaussian">
+                <mean>0.0</mean>
+                <stddev>1.7e-2</stddev>
+                <bias_mean>0.1</bias_mean>
+                <bias_stddev>0.001</bias_stddev>
+              </noise>
+            </y>
+            <z>
+              <noise type="gaussian">
+                <mean>0.0</mean>
+                <stddev>1.7e-2</stddev>
+                <bias_mean>0.1</bias_mean>
+                <bias_stddev>0.001</bias_stddev>
+              </noise>
+            </z>
+          </linear_acceleration>
+        </imu>
+      </sensor>
+    </gazebo>
+```
+
+将上面的代码加到`fishbot_gazebo.urdf`中,接着我们就可以进行测试了。
+
+## 4.编译测试
+
+编译
+
+```shell
+colcon build
+```
+
+运行
+
+```
+ros2 launch fishbot_description gazebo.launch.py
+```
+
+CLI看话题
+
+```
+ros2 topic list
+ros2 topic info /imu
+```
+
+```
+ros2 topic echo /imu
+```
+
+输出:
+
+```
+header:
+  stamp:
+    sec: 150
+    nanosec: 599000000
+  frame_id: base_footprint
+orientation:
+  x: 3.434713830866392e-07
+  y: 7.119913105768616e-06
+  z: -0.00028312437320413914
+  w: 0.9999999598948884
+orientation_covariance:
+- 0.0
+- 0.0
+- 0.0
+- 0.0
+- 0.0
+- 0.0
+- 0.0
+- 0.0
+- 0.0
+angular_velocity:
+  x: -0.00013597855247901325
+  y: 0.0006306135617081868
+  z: -0.00015794894627685146
+angular_velocity_covariance:
+- 4.0e-08
+- 0.0
+- 0.0
+- 0.0
+- 4.0e-08
+- 0.0
+- 0.0
+- 0.0
+- 4.0e-08
+linear_acceleration:
+  x: 0.08679200038530369
+  y: 0.07753419258567491
+  z: 9.687910969061628
+linear_acceleration_covariance:
+- 0.00028900000000000003
+- 0.0
+- 0.0
+- 0.0
+- 0.00028900000000000003
+- 0.0
+- 0.0
+- 0.0
+- 0.00028900000000000003
+```
+
+用rqt可视化:
+
+![image-20220411102037226](5.Gazebo仿真插件之IMU/imgs/image-20220411102037226.png)
+
+
+
+## 5.总结
+
+本节我们对IMU传感器进行介绍,并通过gazbeo的imu插件完成了fishbot的IMU数据的输出。
+
+最后还有小练习等着你:
+
+1. 再次启动遥控节点,控制fishbot,观察IMU传感器的数据变化
+
+
+
+
+
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划为FishBot添加IMU传感器

BIN
docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220409150448805.png


BIN
docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220409150658019.png


BIN
docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220409150754602.png


BIN
docs/humble/chapt9/get_started/5.Gazebo仿真插件之IMU/imgs/image-20220411102037226.png


+ 213 - 0
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达.md

@@ -0,0 +1,213 @@
+# 6.为FishBot添加添加激光雷达传感器
+
+本节我们来认识一个新的传感器,该传感器在自动驾驶、室内导航等应用非常多,比如扫地机器人上就是用的它作为感知环境的重要工具,该传感器是激光雷达。
+
+## 1.激光雷达介绍
+
+激光雷达(Light Detection And Ranging),缩写`LiDAR`,翻译一下叫——激光探测与测距。
+
+### 1.1 激光雷达原理介绍
+
+激光雷达的原理也很简单,就像蝙蝠的定位方法一样,蝙蝠定位大家都知道吧,像下面这样子的回声定位。
+
+![image-20220411104637837](6.Gazebo仿真插件之激光雷达/imgs/image-20220411104637837.png)
+
+普通的单线激光雷达一般有一个发射器,一个接收器,发射器发出激光射线到前方的目标上,物品会将激光反射回来,然后激光雷达的接受器可以检测到反射的激光。
+
+![激光三角测距原理图](6.Gazebo仿真插件之激光雷达/imgs/60c8fc45-d5a3-4d30-863a-328794b018af.jpeg)
+
+通过计算发送和反馈之间的时间间隔,乘上激光的速度,就可以计算出激光飞行的距离,该计算方法成为TOF(飞行时间法Time of flight,也称时差法)。
+
+除了TOF之外还有其他方法进行测距,比如三角法,这里就不拓展了放一篇文章,大家自行阅读。
+
+- [激光三角测距原理详述](https://www.slamtec.com/cn/News/Detail/190)
+
+目前市面上的激光雷达,几乎都是采用三角测距,比如思岚的:
+
+![激光雷达选购](6.Gazebo仿真插件之激光雷达/imgs/467829a0-048a-4913-923c-0426107fde95.jpeg)
+
+**需要注意的是虽然只有一个发射器和一个接受器,激光雷达通过电机可以进行旋转,这样就可以达到对周围环境360度测距的目的。**
+
+### 1.2 激光雷达大赏
+
+五位数的长这样
+
+![image-20220411110353617](6.Gazebo仿真插件之激光雷达/imgs/image-20220411110353617.png)
+
+四位数的长这样
+
+![image-20220411110936454](6.Gazebo仿真插件之激光雷达/imgs/image-20220411110936454.png)
+
+三位数的长这样
+
+![image-20220411111006587](6.Gazebo仿真插件之激光雷达/imgs/image-20220411111006587.png)
+
+两位数的长这样
+
+![image-20220411111153820](6.Gazebo仿真插件之激光雷达/imgs/image-20220411111153820.png)
+
+不要钱的长这样
+
+仿真的,不要钱
+
+![image-20220411111459079](6.Gazebo仿真插件之激光雷达/imgs/image-20220411111459079.png)
+
+## 2.Gazebo激光雷达插件
+
+因为激光雷达是属于射线类传感器,该类传感在在Gazebo插件中都被封装成了一个动态库`libgazebo_ros_ray_sensor.so`。
+
+接着我们来看看LiDAR的话题消息接口`sensor_msgs/msg/LaserScan`。
+
+```
+ros2 interface show  sensor_msgs/msg/LaserScan
+```
+
+```
+# Single scan from a planar laser range-finder
+#
+# If you have another ranging device with different behavior (e.g. a sonar
+# array), please find or create a different message, since applications
+# will make fairly laser-specific assumptions about this data
+
+std_msgs/Header header # timestamp in the header is the acquisition time of
+                             # the first ray in the scan.
+                             #
+                             # in frame frame_id, angles are measured around
+                             # the positive Z axis (counterclockwise, if Z is up)
+                             # with zero angle being forward along the x axis
+
+float32 angle_min            # start angle of the scan [rad]
+float32 angle_max            # end angle of the scan [rad]
+float32 angle_increment      # angular distance between measurements [rad]
+
+float32 time_increment       # time between measurements [seconds] - if your scanner
+                             # is moving, this will be used in interpolating position
+                             # of 3d points
+float32 scan_time            # time between scans [seconds]
+
+float32 range_min            # minimum range value [m]
+float32 range_max            # maximum range value [m]
+
+float32[] ranges             # range data [m]
+                             # (Note: values < range_min or > range_max should be discarded)
+float32[] intensities        # intensity data [device-specific units].  If your
+                             # device does not provide intensities, please leave
+                             # the array empty.
+```
+
+雷达的数据结构有些复杂,但通过注释和名字相信你可以看的七七八八,看不懂也没关系,一般情况下我们不会直接的对雷达的数据做操作。小鱼这里也就先skip。
+
+## 3.为FishBot添加雷达插件
+
+有了前面的经验,我们需要在URDF添加以下内容即可
+
+```xml
+  <gazebo reference="laser_link">
+      <sensor name="laser_sensor" type="ray">
+      <always_on>true</always_on>
+      <visualize>true</visualize>
+      <update_rate>5</update_rate>
+      <pose>0 0 0.075 0 0 0</pose>
+      <ray>
+          <scan>
+            <horizontal>
+              <samples>360</samples>
+              <resolution>1.000000</resolution>
+              <min_angle>0.000000</min_angle>
+              <max_angle>6.280000</max_angle>
+            </horizontal>
+          </scan>
+          <range>
+            <min>0.120000</min>
+            <max>3.5</max>
+            <resolution>0.015000</resolution>
+          </range>
+          <noise>
+            <type>gaussian</type>
+            <mean>0.0</mean>
+            <stddev>0.01</stddev>
+          </noise>
+      </ray>
+
+      <plugin name="laserscan" filename="libgazebo_ros_ray_sensor.so">
+        <ros>
+          <remapping>~/out:=scan</remapping>
+        </ros>
+        <output_type>sensor_msgs/LaserScan</output_type>
+        <frame_name>laser_link</frame_name>
+      </plugin>
+      </sensor>
+    </gazebo>
+```
+
+可以看到:
+
+1. 雷达也可以设置更新频率`update_rate`,这里设置为5
+2. 雷达可以设置分辨率,设置为1,采样数量360个,最终生成的点云数量就是360
+3. 雷达也有噪声,模型为`gaussian`
+4. 雷达有扫描范围`range`,这里配置成0.12-3.5,0.015分辨率
+5. 雷达的`pose`就是雷达的joint中位置的设置值
+
+## 4.编译测试
+
+
+编译
+
+```shell
+colcon build
+```
+
+运行
+
+```
+ros2 launch fishbot_description gazebo.launch.py
+```
+
+![image-20220411115245023](6.Gazebo仿真插件之激光雷达/imgs/image-20220411115245023.png)
+
+CLI看话题
+
+```
+ros2 topic list
+ros2 topic info /scan
+```
+
+```
+ros2 topic echo /scan
+```
+
+接着我们尝试使用rviz2进行可视化激光雷达数据
+
+添加和修改RVIZ2的如下:(通过LaserScan插件可以看到激光数据)
+
+![image-20220411120215730](6.Gazebo仿真插件之激光雷达/imgs/image-20220411120215730.png)
+
+相信你改完之后依然是看不到任何激光雷达的数据的,反看topic的echo出来的数据,不是0就是inf(无限大),再看看gazebo你会发现,激光雷达并没有达到任何一个物体上。
+
+![image-20220411115245023](6.Gazebo仿真插件之激光雷达/imgs/image-20220411115245023.png)
+
+所以我们可以手动的给激光雷达周围添加一下东西,点击Gazebo工具栏的正方体,圆球或者圆柱,随意放置几个到我们激光雷达的最大扫描半径内。
+
+![image-20220411120602685](6.Gazebo仿真插件之激光雷达/imgs/image-20220411120602685.png)
+
+![image-20220411120707286](6.Gazebo仿真插件之激光雷达/imgs/image-20220411120707286.png)
+
+接着我们再看一下RVIZ2,这里小鱼把size改大了10倍0.01->0.1。
+
+![image-20220411125925399](6.Gazebo仿真插件之激光雷达/imgs/image-20220411125925399.png)
+
+## 5.总结
+
+到这里我们就把fishbot的各个传感器都仿真出来了,第九章的内容也暂且告一段落,迎接我们的是第十章,也就是fishbot的导航仿真,nav2了。
+
+如果你想先本教程一步学习Nav2,可以到[nav2中文网](http://dev.nav2.fishros.com/)哦~
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/467829a0-048a-4913-923c-0426107fde95.jpeg


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/60c8fc45-d5a3-4d30-863a-328794b018af.jpeg


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411104637837.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411110353617.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411110936454.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411111006587.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411111153820.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411111459079.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411115245023.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411120215730.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411120602685.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411120707286.png


BIN
docs/humble/chapt9/get_started/6.Gazebo仿真插件之激光雷达/imgs/image-20220411125925399.png


+ 173 - 0
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建.md

@@ -0,0 +1,173 @@
+# 7.Gazebo仿真环境搭建
+
+本节我们要在Gazebo中建立一个测试的环境,其实也很简单,利用Gazebo的画墙工具即可完成。
+
+![image-20220415212856652](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415212856652.png)
+
+## 1. Gazebo的world介绍
+
+world即世界,gazebo的world文件就是用于描述世界模型的,也就是环境模型。
+
+Gazebo已经为我们准备了很多常用的物体模型,除了基础的圆球,圆柱,立方体外的,其实还有飞机、汽车、房子等你现实中无法拥有的。
+
+但是一开始安装Gazebo的时候并不会帮你下载好这些模型,需要我们手动下载,万幸的是小鱼已经帮你封装成了一行代码下载指令,打开终端,复制粘贴下面这句
+
+```shell
+cd ~/.gazebo && wget https://gitee.com/ohhuo/scripts/raw/master/gazebo_model.py && python3 gazebo_model.py
+```
+
+然后等待脚本运行完成,当然也不用等它完成,因为一共有281个模型,是逐一下载并解压到`~/.gazebo/models/`目录的。
+
+此时再次打开终端,输入`gazebo`,把选项卡切换到Insert
+
+![image-20220415190947423](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415190947423.png)
+
+在Insert选项卡下可以看到一个目录,以及目录下的模型名称,随着下载脚本的不断下载,这里的模型会越来越多。
+
+随手拖几个,搭建一个漂亮的环境出来~
+
+每个成功的男人都有一辆车,小鱼也不例外
+
+![image-20220415191443344](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415191443344.png)
+
+上面是Gazebo为我们准备好的开源模型,我们也可以通过Gazebo的工具来自己画一个环境。
+
+## 2. 通过建墙工具建立world
+
+Gazebo左上角->Edit->Building Editor
+
+接着可以看到这样一个编辑界面
+
+![image-20220415200653000](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415200653000.png)
+
+### 2.1 随手画墙
+
+点击左边的Wall,你就可以在上方的白色区域进行建墙了。
+
+![image-20220415201033741](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415201033741.png)
+
+建完后还可以用选Add Color或者Add Texture,然后点击下方墙,给墙添加颜色或者纹理。
+
+### 2.2 从已有地图画墙
+
+首先你要有一个地图,小鱼为你准备了两个,两个图片都是800*600像素的。
+
+![image-20220415203442641](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415203442641.png)
+
+![image-20220415203452096](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415203452096.png)
+
+打开Gazebo->Gazebo左上角->Edit->Building Editor->左下方选Import
+
+![image-20220415203602181](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415203602181.png)
+
+将上面两个图片存到本地,在这个界面选图片,记着选Next
+
+![image-20220415203658743](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415203658743.png)
+
+左边选尺寸对应关系
+
+![image-20220415203729210](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415203729210.png)
+
+我们选择默认的,100像素/米。点击OK(需要手动将100改变一下才能点击OK哦),之后就可以用图片画墙了。
+
+**注意:导入完图片不会直接出来墙,图片只是提供了墙的大概位置,需要你手动用墙再将边描一遍。**
+
+![image-20220415204027598](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415204027598.png)
+
+建完后点击File->Exit,在退出的弹框中选Exit。
+
+接着在Gazebo界面中就可以看到墙了,我们再手动添加几个物体,就可以用于下面的导航使用了。
+
+![image-20220415205459283](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415205459283.png)
+
+添加完,接着点击File->SaveWorld,将文件保存到我们的fishbot_descrption的world下。
+
+> 没有world目录的小伙伴可以先手动创建下
+
+![image-20220415205713054](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415205713054.png)
+
+点击右上角Sace,在文件夹就可以看到这个world文件
+
+![image-20220415205821811](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415205821811.png)
+
+## 3.启动时加载world
+
+### 3.1 命令行加载World
+
+加载world其实也很简单,可以先启动Gazebo,再手动的加载文件,也可以在Gazebo启动时加载:
+
+比如在前面加载ROS2插件基础上再加载fishbot.world。
+
+```
+gazebo --verbose -s  libgazebo_ros_factory.so  你的world文件目录/fishbot.world
+```
+
+### 3.2 在launch中加载World
+
+修改launch文件,将上面的命令行写到`gazebo.launch.py`中即可。
+
+```python
+    gazebo_world_path = os.path.join(pkg_share, 'world/fishbot.world')
+
+    # Start Gazebo server
+    start_gazebo_cmd = ExecuteProcess(
+        cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_factory.so', gazebo_world_path],
+        output='screen')
+```
+
+最后记得修改setup.py文件,让编译后将world文件拷贝到install目录下
+
+添加一行
+
+```
+        (os.path.join('share', package_name, 'world'), glob('world/**')),
+```
+
+添加完后
+
+```
+    data_files=[
+        ('share/ament_index/resource_index/packages',
+            ['resource/' + package_name]),
+        ('share/' + package_name, ['package.xml']),
+        (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
+        (os.path.join('share', package_name, 'urdf'), glob('urdf/**')),
+        (os.path.join('share', package_name, 'world'), glob('world/**')),
+    ],
+```
+
+### 3.3 编译测试
+
+```
+colcon build
+source install/setup.bash
+ros2 launch fishbot_description gazebo.launch.py 
+```
+
+![image-20220415212035364](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415212035364.png)
+
+打开RVIZ2看看雷达
+
+![image-20220415212321161](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415212321161.png)
+
+## 4.总结
+
+本节我们实现了在Gazebo中简单的搭建了一个环境,下节我们就开始对SLAM建图进行介绍。
+
+课后作业:
+
+- 将雷达的Decay Time修改成1000,然后遥控Fishbot在环境中走一圈,然后观察雷达留下的点云形状。
+
+正确答案如下(小鱼的环境):
+
+![image-20220415212957807](../../../foxy/chapt10/10.2Gazebo仿真环境准备/imgs/image-20220415212957807.png)
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划

BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415190947423.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415191443344.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415200653000.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415201033741.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203442641.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203452096.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203602181.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203658743.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415203729210.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415204027598.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415205459283.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415205713054.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415205821811.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212035364.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212321161.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212856652.png


BIN
docs/humble/chapt9/get_started/7.Gazebo仿真环境搭建/imgs/image-20220415212957807.png


+ 32 - 0
docs/humble/chapt9/章节导读.md

@@ -0,0 +1,32 @@
+# 第九章 机器人仿真
+
+搞定了第八章的机器人建模,本章节尝试采用之前介绍过的gazebo让机器人在仿真环境中动起来。
+
+通过本章节的学习,你将有以下收获~
+
+- [x] 机器人仿真介绍
+- [x] 赋予URDF以物理属性
+- [x] Gazebo仿真插件介绍
+- [x] 在URDF中配置仿真插件
+- [x] 控制Gazebo机器人运动
+- [x] 了解里程计、角速度与线速度
+- [x] 在Gazebo中使用激光雷达传感器
+- [ ] 学会配置机械臂URDF
+- [ ] 实现Gazebo机械臂运动
+- [ ] 拓展:下一代仿真工具Ignition
+- [ ] 拓展:比URDF更灵活的SDF
+
+有不明白的地方欢迎加小鱼微信和小鱼一起沟通,快点开始你的升级打怪之路吧~
+
+
+
+--------------
+
+技术交流&&问题求助:
+
+- **微信公众号及交流群:鱼香ROS**
+- **小鱼微信:AiIotRobot**
+- **QQ交流群:139707339**
+
+- 版权保护:已加入“维权骑士”(rightknights.com)的版权保护计划
+