Bläddra i källkod

feat(chapt16): 完成第十六章

鱼香ROS 2 år sedan
förälder
incheckning
65fde14c87

+ 12 - 6
docs/_sidebar.md

@@ -166,6 +166,7 @@
     - 进阶篇
       -  [1.Gazebo仿真插件之超声波](humble/chapt9/advanced/1.Gazebo仿真插件之超声波.md) 
       <!-- - 2.Gazebo仿真插件之深度相机 -->
+  
 - (四)Nav2导航篇
   - 第 10 章 SLAM建图
     - 章节导读
@@ -202,6 +203,7 @@
     - 进阶篇-优化配置
       - 暂定
       <!-- - 使用Carto纯定位替换AMCL -->
+  
 - (五)ROS2硬件控制篇
   - 第 13 章 嵌入式开发之从点灯开始
     - [章节导读](humble/chapt13/章节导读.md)
@@ -245,6 +247,7 @@
       - [3.使用pwm控制舵机角度](humble/chapt15/3.控制舵机学会使用执行器.md)
       - [4.实现循环扫描测量](humble/chapt15/4.舵机+超声波循环扫描.md)
       - [5.ROS雷达消息合成与发布](humble/chapt15/5.可视化点云-雷达消息合成.md)
+  
 - (六)FishBot移动机器人开发篇
   - 第 16 章 移动机器人控制系统搭建
     - [章节导读](humble/chapt16/章节导读.md)
@@ -262,12 +265,13 @@
     - [12.实时速度计算-运动学正解](humble/chapt16/12.实时速度计算-运动学正解.md)
     - [13.目标速度控制-运动学逆解](humble/chapt16/13.目标速度控制-运动学逆解.md)
     - [14.里程计计算-速度积分](humble/chapt16/14.里程计计算-速度积分.md)
-    <!-- - 15.可视化里程计-发布Odometry
-    - 16.完善数据-上位机时间同步
-    - 17.优化服务-源码编译Agent
-    - 18.控制系统总结-论一个合格的底盘 -->
+    - [15.采用MicroROS发布里程计.md](humble/chapt16/15.采用MicroROS发布里程计.md)
+    - [16.项目总结与扩展](humble/chapt16/16.项目总结与扩展.md)
+    - [17.优化服务-源码编译Agent](humble/chapt16/17.拓展-源码编译Agent.md)
     
-  <!--  - 第 17 章 FishBot建图与导航实现
+  
+  第 17 章 FishBot建图与导航实现
+  
    - 章节导读
     - FishBot建图实现
       - 1.可视化雷达点云-学会驱动雷达
@@ -288,7 +292,8 @@
     - 3.纯追踪控制器介绍
     - 4.纯追踪控制器代码实现
     - 5.自定义代价地图导航
-    - 6.超声波数据发布 -->
+    - 6.超声波数据发布 
+  
 - (七)Moveit2机械臂篇
   <!-- - 第 19 章 Moveit2仿真
     - 章节导读
@@ -324,6 +329,7 @@
       - 使用Moveit2控制真实机械臂
     - 进阶篇-实战演练
       - 使用真实机械臂完成抓取 -->
+  
 - [ROS 2常用代码模板](humble/codebook/README.md)
   - rclcpp
     -  [节点](humble/codebook/rclcpp/nodes.md) 

+ 391 - 0
docs/humble/chapt16/15.采用MicroROS发布里程计.md

@@ -0,0 +1,391 @@
+# 15.采用MicroROS发布里程计
+
+获得了里程计数据后,下一步就是将里程计通过MicroROS话题发布到ROS 2 系统中。
+
+## 一、了解接口
+
+在 ROS 2 已有的消息接口中:
+
+```
+nav_msgs/msg/Odometry
+```
+
+用于表示里程计数据,该接口内容如下:
+
+```
+ 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
+        builtin_interfaces/Time stamp
+                int32 sec
+                uint32 nanosec
+        string frame_id
+
+# 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
+        Pose pose
+                Point position
+                        float64 x
+                        float64 y
+                        float64 z
+                Quaternion orientation
+                        float64 x 0
+                        float64 y 0
+                        float64 z 0
+                        float64 w 1
+        float64[36] covariance
+
+# Estimated linear and angular velocity relative to child_frame_id.
+geometry_msgs/TwistWithCovariance twist
+        Twist twist
+                Vector3  linear
+                        float64 x
+                        float64 y
+                        float64 z
+                Vector3  angular
+                        float64 x
+                        float64 y
+                        float64 z
+        float64[36] covariance
+```
+
+注意看,除了表示位置的 pose 和表示速度的 twist ,还有 child_frame_id 这一参数,它表示里程计子坐标系名称,根据ROS 导航堆栈定义,一般用 base_link 或者 base_footprint 。
+
+接着我们来编写代码。
+
+## 二、编写代码
+
+如何发布话题在前面的章节中我们已经学习过了,现在我们来编写代码,因为是直接在原来的代码基础上修改的,所以下面展示的代码前如果是+表示新增行,-表示删除行,没有符号表示没有修改。
+
+### Kinematics.h
+
+首先是/lib/Kinematics/Kinematics.h,增加四元数定义和欧拉角转四元数函数,这是因为ROS中姿态的表示使用的是四元数。
+
+```cpp
++typedef struct
++{
++    float w;
++    float x;
++    float y;
++    float z;
++} quaternion_t;
++
+ /**
+  * @brief 里程计相关信息,根据轮子速度信息和运动模型推算而来
+  *
+@@ -41,6 +49,7 @@ typedef struct
+     float x;                 // 坐标x
+     float y;                 // 坐标y
+     float yaw;               // yaw
++    quaternion_t quaternion; // 姿态四元数
+     float linear_speed;      // 线速度
+     float angular_speed;     // 角速度
+ } odom_t;
+@@ -56,6 +65,7 @@ private:
+ public:
+     Kinematics(/* args */) = default;
+     ~Kinematics() = default;
++    static void Euler2Quaternion(float roll, float pitch, float yaw, quaternion_t &q);
+ 
+     static void TransAngleInPI(float angle,float& out_angle);
+```
+
+### Kinematics.cpp
+
+接着是:lib/Kinematics/Kinematics.cpp,增加 Euler2Quaternion 函数实现,在 odom 函数中增加对 Euler2Quaternion 函数的调用。
+
+```cpp
+ #include "Kinematics.h"
+ 
++// 用于将欧拉角转换为四元数。
++void Kinematics::Euler2Quaternion(float roll, float pitch, float yaw, quaternion_t &q)
++{
++    // 传入机器人的欧拉角 roll、pitch 和 yaw。
++    // 计算欧拉角的 sin 和 cos 值,分别保存在 cr、sr、cy、sy、cp、sp 六个变量中    
++    // https://blog.csdn.net/xiaoma_bk/article/details/79082629
++    double cr = cos(roll * 0.5);
++    double sr = sin(roll * 0.5);
++    double cy = cos(yaw * 0.5);
++    double sy = sin(yaw * 0.5);
++    double cp = cos(pitch * 0.5);
++    double sp = sin(pitch * 0.5);
++    // 计算出四元数的四个分量 q.w、q.x、q.y、q.z
++    q.w = cy * cp * cr + sy * sp * sr;
++    q.x = cy * cp * sr - sy * sp * cr;
++    q.y = sy * cp * sr + cy * sp * cr;
++    q.z = sy * cp * cr - cy * sp * sr;
++}
+
+
+ odom_t &Kinematics::odom()
+ {
++    // 调用 Euler2Quaternion 函数,将机器人的欧拉角 yaw 转换为四元数 quaternion。
++    Kinematics::Euler2Quaternion(0, 0, odom_.yaw, odom_.quaternion);
+     return odom_;
+ }
+```
+
+### main.cpp
+
+接着修改了 /src/main.cpp ,主要添加了一个发布者,接着对时间进行同步,方便发布里程计话题时使用当前的时间。
+
+然后对数据的各项进行设置,最后添加了里程计数据的发布,间隔 50ms 进行发布。
+
+```cpp
+ #include <Esp32McpwmMotor.h>         // 包含使用 ESP32 的 MCPWM 硬件模块控制 DC 电机的 ESP32 MCPWM 电机库
+ #include <PidController.h>           // 包含 PID 控制器库,用于实现 PID 控制
+ #include <Kinematics.h>              // 运动学相关实现
++#include <nav_msgs/msg/odometry.h>
++#include <micro_ros_utilities/string_utilities.h>
++rcl_publisher_t odom_publisher;   // 用于发布机器人的里程计信息(Odom)
++nav_msgs__msg__Odometry odom_msg; // 机器人的里程计信息
+ 
+ Esp32PcntEncoder encoders[2];      // 创建一个长度为 2 的 ESP32 PCNT 编码器数组
+ rclc_executor_t executor;          // 创建一个 RCLC 执行程序对象,用于处理订阅和发布
+ 
+void microros_task(void *param)
+ {
++  // 使用 micro_ros_string_utilities_set 函数设置到 odom_msg.header.frame_id 中
++  odom_msg.header.frame_id = micro_ros_string_utilities_set(odom_msg.header.frame_id, "odom");
++  odom_msg.child_frame_id = micro_ros_string_utilities_set(odom_msg.child_frame_id, "base_link");
+   // 等待 2 秒,以便网络连接得到建立。
+   delay(2000);
+
++  rclc_publisher_init_best_effort(
++      &odom_publisher,
++      &node,
++      ROSIDL_GET_MSG_TYPE_SUPPORT(nav_msgs, msg, Odometry),
++      "odom");
+   // 设置 micro-ROS 执行器,并将订阅添加到其中。
+   rclc_executor_init(&executor, &support.context, 1, &allocator);
+   rclc_executor_add_subscription(&executor, &subscriber, &sub_msg, &twist_callback, ON_NEW_DATA);
+
+      // 循环运行 micro-ROS 执行器以处理传入的消息。
+      while (true)
+      {
++       if (!rmw_uros_epoch_synchronized())
++       {
++         rmw_uros_sync_session(1000);
++         // 如果时间同步成功,则将当前时间设置为MicroROS代理的时间,并输出调试信息。
++         delay(10);
++       }
+        delay(100);
+        rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
+      }
+  }
+ 
+ unsigned long previousMillis = 0; // 上一次打印的时间
+-unsigned long interval = 1000;    // 间隔时间,单位为毫秒
++unsigned long interval = 50;      // 间隔时间,单位为毫秒
+ 
+ void loop()
+ {
+     previousMillis = currentMillis; // 记录上一次打印的时间
+     float linear_speed, angle_speed;
+     kinematics.kinematic_forward(kinematics.motor_speed(0), kinematics.motor_speed(1), linear_speed, angle_speed);
+-    Serial.printf("[%ld] linear:%f angle:%f\n", currentMillis, linear_speed, angle_speed);                       // 打印当前时间
+-    Serial.printf("[%ld] x:%f y:%f yaml:%f\n", currentMillis,kinematics.odom().x, kinematics.odom().y, kinematics.odom().yaw); // 打印当前时间
++    // Serial.printf("[%ld] linear:%f angle:%f\n", currentMillis, linear_speed, angle_speed);                       // 打印当前时间
++    // Serial.printf("[%ld] x:%f y:%f yaml:%f\n", currentMillis,kinematics.odom().x, kinematics.odom().y, kinematics.odom().yaw); // 打印当前时间
++    // 用于获取当前的时间戳,并将其存储在消息的头部中
++    int64_t stamp = rmw_uros_epoch_millis();
++    // 获取机器人的位置和速度信息,并将其存储在一个ROS消息(odom_msg)中
++    odom_t odom = kinematics.odom();
++    odom_msg.header.stamp.sec = stamp * 1e-3;
++    odom_msg.header.stamp.nanosec = stamp - odom_msg.header.stamp.sec * 1000;
++    odom_msg.pose.pose.position.x = odom.x;
++    odom_msg.pose.pose.position.y = odom.y;
++    odom_msg.pose.pose.orientation.w = odom.quaternion.w;
++    odom_msg.pose.pose.orientation.x = odom.quaternion.x;
++    odom_msg.pose.pose.orientation.y = odom.quaternion.y;
++    odom_msg.pose.pose.orientation.z = odom.quaternion.z;
++
++    odom_msg.twist.twist.angular.z = odom.angular_speed;
++    odom_msg.twist.twist.linear.x = odom.linear_speed;
++
++    rcl_publish(&odom_publisher, &odom_msg, NULL);
+   }
+ 
+
+```
+
+这三个文件修改好,接着就可以下载代码进行测试了。
+
+## 三、下载测试
+
+下载代码,运行agent,点击RST按键。
+
+```shell
+sudo docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:$ROS_DISTRO udp4 --port 8888 -v6
+```
+
+![image-20230306023859873](15.%E9%87%87%E7%94%A8MicroROS%E5%8F%91%E5%B8%83%E9%87%8C%E7%A8%8B%E8%AE%A1/imgs/image-20230306023859873.png)
+
+看到连接建立表示通信成功,接着用`ros2 topic list`
+
+```shell
+ros2 topic list
+---
+/cmd_vel
+/odom
+/parameter_events
+/rosout
+```
+
+接着我们可以查看里程计数据或其发布频率。
+
+```
+ros2 topic echo /odom --once # 查看数据
+---
+header:
+  stamp:
+    sec: 2093
+    nanosec: 40
+  frame_id: odom
+child_frame_id: base_link
+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
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 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
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+---
+```
+
+查看数据频率
+
+```
+ros2 topic hz /odom
+---
+average rate: 19.376
+        min: 0.047s max: 0.063s std dev: 0.00326s window: 21
+average rate: 19.558
+        min: 0.039s max: 0.063s std dev: 0.00338s window: 41
+average rate: 19.527
+        min: 0.039s max: 0.063s std dev: 0.00307s window: 61
+average rate: 19.533
+        min: 0.039s max: 0.063s std dev: 0.00301s window: 81
+```
+
+查看数据带宽
+
+```
+ros2 topic bw /odom
+---
+Subscribed to [/odom]
+14.78 KB/s from 20 messages
+	Message size mean: 0.72 KB min: 0.72 KB max: 0.72 KB
+14.26 KB/s from 39 messages
+	Message size mean: 0.72 KB min: 0.72 KB max: 0.72 KB
+14.34 KB/s from 59 messages
+	Message size mean: 0.72 KB min: 0.72 KB max: 0.72 KB
+14.19 KB/s from 78 messages
+	Message size mean: 0.72 KB min: 0.72 KB max: 0.72 KB
+14.25 KB/s from 98 messages
+	Message size mean: 0.72 KB min: 0.72 KB max: 0.72 KB
+14.18 KB/s from 100 messages
+	Message size mean: 0.72 KB min: 0.72 KB max: 0.72 KB
+14.25 KB/s from 100 messages
+	Message size mean: 0.72 KB min: 0.72 KB max: 0.72 KB
+```
+
+## 四、总结
+
+有了控制话题和里程计话题,底盘部分就完成的差不多了,但是对于一个合格的底盘来说,其实还有很多待完善的地方,下一节我们就来说说可以怎么完善,以及完善的后的代码。

BIN
docs/humble/chapt16/15.采用MicroROS发布里程计/imgs/image-20230306023859873.png


BIN
docs/humble/chapt16/15.采用MicroROS发布里程计/imgs/image-20230306024034226.png


+ 62 - 0
docs/humble/chapt16/16.项目总结与扩展.md

@@ -0,0 +1,62 @@
+# 16.项目总结与扩展
+
+上一节我们完成后,就可以通过话题获取到机器人的里程计,也可以通过话题控制机器人移动了。对于一个移动底盘来说,这两个话题就是最关键的两个,有了这两个就可以控制底盘完成移动。但如果想让底盘更好用,可以增加OLED模块显示数据,比如电池的电压。同时我们的板子还支持IMU和超声波模块,其实都可以通过话题发布出来。
+
+说了那么多,你可能不知道怎么做,没关系,小鱼写好了所有的代码,有了前面的基础,加上代码中的详细注释,看懂他们对你来说并不难。完成代码可以直接通过git进行克隆到本地。
+
+```shell
+git clone http://github.fishros.org/https://github.com/fishros/fishbot_motion_control_microros.git
+```
+
+如果你的网络不错,也可以在线查看代码:https://github.dev/fishros/fishbot_motion_control_microros
+
+该代码的结构如下:
+
+```
+.
+├── extra_packages
+│   └── fishbot_interfaces
+│       ├── CMakeLists.txt   # CMake构建配置文件
+│       ├── msg
+│       │   └── MyCustomMessage.msg   # ROS消息定义
+│       ├── package.xml   # ROS包描述文件
+│       └── srv
+│           └── FishBotConfig.srv   # ROS服务定义
+├── include
+│   ├── fishbot_config.h   # 头文件
+│   ├── fishbot.h   # 头文件
+│   ├── fishlog.h   # 头文件
+│   └── README   # 说明文档
+├── Installer   # 安装器相关
+├── lib
+│   ├── Displays
+│   │   ├── fishbot_display.cpp   # 显示相关源码
+│   │   └── fishbot_display.h   # 显示相关头文件
+│   ├── FishbotUtils
+│   │   ├── fishbot_utils.cpp   # 实用工具源码
+│   │   └── fishbot_utils.h   # 实用工具头文件
+│   ├── Kinematics
+│   │   ├── Kinematics.cpp   # 运动学计算源码
+│   │   └── Kinematics.h   # 运动学计算头文件
+│   ├── MicroRosRwm
+│   │   ├── micro_ros_transport_serial.cpp   # MicroROS串口传输源码
+│   │   ├── micro_ros_transport_serial.h   # MicroROS串口传输头文件
+│   │   ├── micro_ros_transport_wifi_udp.cpp   # MicroROS WiFi/UDP传输源码
+│   │   └── micro_ros_transport_wifi_udp.h   # MicroROS WiFi/UDP传输头文件
+│   ├── PidController
+│   │   ├── PidController.cpp   # PID控制器源码
+│   │   └── PidController.h   # PID控制器头文件
+│   └── README   # 说明文档
+├── LICENSE   # 许可证文件
+├── partition.csv   # 分区配置文件
+├── platformio.ini   # PlatformIO配置文件
+├── README.md   # 项目主README文件
+├── RELEASES.md   # 发布说明
+├── src
+    ├── fishbot_config.cpp   # 配置相关源码
+    ├── fishbot.cpp   # 主要功能源码
+    └── main.cpp   # 主程序入口源码
+```
+
+下一章我们开始学习如何进行导航和建图。
+

+ 54 - 0
docs/humble/chapt16/17.拓展-源码编译Agent.md

@@ -0,0 +1,54 @@
+# 17.拓展-源码编译Agent
+
+本文介绍了如何拓展MicroROS的Agent,将其作为一个功能包进行源码编译,并提供了详细的步骤如下:
+
+## 一、下载microros-agent
+
+首先,我们需要下载MicroROS的Agent源码,并准备相应的依赖。以下是下载和准备的步骤:
+
+1. 安装必要的依赖项:
+   
+   ```
+   sudo apt-get install -y build-essential
+   ```
+
+2. 创建工作空间并进入源码目录:
+   
+   ```
+   mkdir -p microros_ws/src
+   cd microros_ws/src
+   ```
+
+3. 下载MicroROS Agent和相关消息包的源码:
+   
+   ```
+   git clone http://github.fishros.org/https://github.com/micro-ROS/micro-ROS-Agent.git -b humble
+   git clone http://github.fishros.org/https://github.com/micro-ROS/micro_ros_msgs.git -b humble
+   ```
+
+## 二、编译运行
+
+在成功下载源码并准备好依赖后,我们可以进行编译并运行MicroROS Agent。以下是编译和运行的步骤:
+
+1. 返回工作空间目录并执行编译:
+   
+   ```
+   cd microros_ws
+   colcon build
+   ```
+
+2. 运行MicroROS Agent,注意可能存在串口权限问题。可以参考链接(https://fishros.org.cn/forum/topic/1150)来设置权限。运行命令如下(假设串口为`/dev/ttyUSB0`,波特率为921600):
+
+   ```shell
+   ros2 run micro_ros_agent micro_ros_agent serial -b 921600 --dev /dev/ttyUSB0 -v
+   ```
+
+   或者使用UDP方法:
+
+   ```shell
+   ros2 run micro_ros_agent micro_ros_agent serial udp4 --port 8888 -v6
+   ```
+
+## 三、总结
+
+通过上述步骤,我们成功地拓展了MicroROS的Agent功能包,实现了源码的编译和运行。通过MicroROS Agent,我们能够在资源受限的嵌入式系统中实现强大的ROS 2通信能力。