关于程序员:机器人操作系统-RO2-和-RTThread-通信

19次阅读

共计 11146 个字符,预计需要花费 28 分钟才能阅读完成。

本文由 RT-Thread 官方论坛用户 @wuhanstudio 公布:https://club.rt-thread.org/as…,未经受权禁止转发

MicroROS on RT-Thread

最近刚好总结了一下 ROS1 和 ROS2 在 RT-Thread 上的利用:
https://doc.wuhanstudio.cc/mi…

  • MicroROS on RT-Thread
  • 前言

    • ROS 简介
    • RT-Thread 与 ROS
    • rosserial 和 micro_ros 的区别
  • RT-Thread 管制 Kobuki 机器人

    • RT-Thread 应用 ESP8266 联网
    • ROS1 (rosserial)
    • ROS2 (micro_ros)
  • 总结
  • References

前言

ROS 简介

最后 2007 年左右,斯坦福机器人实验室的两个博士生,Eric Berger 和 Keenan Wyrobek 发现身边的同学们对机器人开发有一种望而生畏的感觉。因为机器人自身是一个跨专业的学科,做软件的同学们不太理解机械构造,不相熟机器人的设计拆卸流程;做算法的同学们又不太理解嵌入式,不太分明底层传感器驱动的工作原理,于是单干起来会碰到很多阻碍。为了解决这个问题,他们设计了最后的 Robot Operating System (ROS),极大地提高了团队单干效率。

因为 ROS 简略好用上手快,从 2007 年 ROS 公布第一版并举办第一届寰球 ROS Conf,到 2012 年底第五届会议,应用 ROS 的实验室曾经遍布寰球了。2013 年 ROS 就由 Open Source Robotics Foundation (OSRF) 接手保护了。另一方面,2005 年斯坦福拿下 DARPA 无人驾驶挑战赛冠军一举成名,随后 ROS 也诞生在斯坦福,很快在无人驾驶畛域 ROS 也失去了广泛应用,并且 2018 DARPA 无人驾驶挑战赛专门设置了 ROS 赛道,参赛队伍须要在 ROS Gazebo 模仿环境下测试本人的无人车并失去评分。

尽管 Robot Operating System (ROS) 取名为机器人操作系统,但其实它并不是一个操作系统,而是在 Linux 之上开发的一系列软件包。为了阐明 ROS 开发的简略,高效和稳固,这里简略介绍一下 ROS 很重要的 4 个设计:

  • Message (音讯):有的时候可能会苦恼传感器的信息应该以什么样的数据结构发送进来,于是 ROS 定义好了各种常见传感器的数据格式,有了模板只须要填充内容就能够了,这样节俭了自定义数据结构的工夫。另一方面,也能够本人写一个 .msg 文件定义新的数据结构,ROS 就能够主动生成对应的头文件。这和 Google 的 protobuf 十分类似,定义数据结构,就能够主动生成源码。总而言之,ROS 的一个音讯 (message) 也就是想要发送进来的一个数据包
  • Topic (话题):定义好了数据结构,如何把数据稳固的发送进来,稳固接管也是个问题。一对一的发送可能比较简单,然而如果多个算法须要同一个传感器输出,同一个算法又须要不同传感器输出,很快数据同步就变得辣手了。于是 ROS 采纳了公布 (publish) – 订阅 (subscribe) 的模式,比方用 ROS 事后定义好的数据结构发送图像数据,公布进来的图像信息就能够在 /camera 这个话题下找到,所有订阅了这个话题的节点都会收到音讯。相当于咱们把音讯发给报社,所有订了报纸的人就会收到音讯,这样咱们只管发消息就是了,剩下的 ROS 会保障所有节点都收到同步的音讯。

  • Node (节点):像后面说的那样,音讯公布进来后,ROS 会保障所有订阅了这个话题的节点都会收到同步的音讯。于是 一个节点的作用通常就是收一些音讯,做一些解决,可能再公布一些新的音讯 。比方人脸检测的节点,可能会订阅图像相干的话题,辨认完再通知其余节点本人辨认的后果。通常, 一个节点只实现一件事件 ,例如人脸识别的节点就只做人脸识别,不会同时又做摔倒检测。这样一个节点只解决一个比拟小的工作,再单干实现更大的指标。顺便一提, 一个节点不等于一台电脑,因为一台电脑如果性能比拟强,齐全能够部署好几个节点,这样也充分利用资源。
  • Service (服务):一方面节点能够通过订阅音讯取得须要的信息,另一方面节点之间也能够通信。例如指标追踪的节点,可能先须要调用指标检测节点的服务,晓得指标在哪再去追踪。所以服务就是字面上的意思,每个节点能够提供一些服务供其余节点调用。

于是有了 ROS 之后,做嵌入式的同学只须要把传感器信息 (message) 公布进来,做算法的同学订阅本人须要的话题 (topic),作为算法输出,再公布对应的输入。这样每个同学只用分心保护本人的节点,确保本人节点的输入输出正确,整个零碎就能失常运作了。


除了软件设计,后面提到的 ROS Gazebo 仿真环境也极大地放慢了迭代过程,在真正的机械臂加工拆卸之前,先在模仿环境确保能够实现预期的静止,也防止了不必要的试错过程。

当然,Gazebo 也能够和理论的机器人连贯进行联结调试 [1]:


RT-Thread 与 ROS

那么问题来了,后面提到 ROS 是在 Linux 上运行的一套软件框架,Linux 自身并不是实时零碎,机器人一些对实时性要求比拟高的工作并不太适宜用 Linux,而 RT-Thread 尽管是实时操作系统(RTOS),但毕竟 MCU 的资源无限,并不能间接运行残缺的 ROS。

于是,通常的做法是利用 Linux 丰盛的软件包实现一些顶层算法,而 RT-Thread 则负责实时控制相干的工作,它们之间的通信就是前面会介绍到的 rosserial 和 micro_ros。

rosserial 和 micro_ros 的区别

它们独特的指标都是为了把 MCU 接入 ROS,使得 MCU 能和 ROS 通信,并且在 MCU 上调用 ROS 的 API,次要区别就在于 rosserial 是针对 ROS1,而 micro_ros 是针对 ROS2 [2]。

第一代的 ROS 倒退很多年后,当然也暴露出很多设计不合理的中央,比方有多个 ROS 主机的时候,须要设置 ROS_MASTER_URI 和 ROS_HOST_NAME 环境变量,帮忙 ROS Master 找到不同的主机的 IP。一旦主机变多,IP 地址又不太固定的时候就会变得很麻烦,如果能让 ROS 主动发现主机就会不便很多。为了解决第一代 ROS 的历史遗留问题,并且增加新性能,OSRF 发现这会造成新的 ROS 破坏性地不兼容以前的版本,于是他们罗唆就间接发表进入第二代 ROS,从新再开发一套新的 ROS,这也就是 ROS2。

新一代的 ROS2 应用 Data Distribution Service (DDS) 通信,能够主动发现主机,这样分布式的零碎设计就更加不便了,然而 DDS 并不算是一个轻量级的框架,如果要让 MCU 接入 DDS 必然须要足够的硬件资源。然而 ROS2 次要针对的是 64 位的中高端平台,MCU 并不是 ROS2 的次要反对对象,以至于第一代的 rosserial 到了第二代也根本放弃保护了。尽管如此,还是有不少开发者心愿能让 MCU 接入 ROS2,间接在 MCU 上调用 ROS2 的 API,于是最终还是有了 Micro ROS。尽管 Micro ROS 借助 Micro XRCE-DDS 实现了轻量级的 DDS,然而 至多还是须要一个 32 位的中端 MCU,不像第一代的 rosserial 那样能够运行在 8 位的低端 MCU 上。

RT-Thread 管制 Kobuki 机器人

后面介绍了 ROS1/ROS2,也介绍了 rosserial (ROS1) 和 micro_ros (ROS2) 的区别,当初 RT-Thread 曾经有了 rosserial 和 micro_ros 软件包别离能和 ROS1/ROS2 通信,同时也有 Kobuki 机器人底盘软件包 [5] 和激光雷达 rplidar 软件包 [6],该当是能够跳过树莓派,间接用 RT-Thread 做一个 SLAM 机器人。顺便一提,rosserial 反对串口和 TCP 通信,micro_ros 则反对串口和 UDP 通信;rosserial 只反对 C++,而 micro_ros 反对 C/C++。

<img src=”https://www.tegakari.net/wp-content/uploads/2016/10/kobuki_img-640×381.jpg” width=50%>

上面就以 RT-Thread 配合 ROS 管制 Kobuki 机器人为例,别离介绍如何在 RT-Thread 上和 ROS1 (rosserial),ROS2 (micro_ros) 通信,并且上面的例子都是以无线 (Wifi) 为例,这样就能够不须要树莓派,运行 RT-Thread 的 MCU 间接接入 ROS 了。顺便一提,上面的操作只有确保有 3 个串口,用什么开发板都能够。,当然,如果不须要 RTT 控制台就只须要 2 个了。

RT-Thread 应用 ESP8266 联网

无论是 rosserial (ROS1),还是 micro_ros (ROS2),联网局部都是一样的,这里就以 ESP8266 的 AT 固件联网为例。当然,用以太网也是能够的。

在开始 RT-Thread 开发之前,须要确保用来联网的 ESP8266 是应用的 AT 固件,固件能够在这里下载 (ai-thinker_esp8266_at_firmware_dout_v1.5.4.1-a_20171130.rar):

https://docs.ai-thinker.com/e…

把 ESP8266 的串口连贯到电脑当前,拉低 GPIO0 复位进入固件下载模式,这样 ESP8266 就能够解决 AT 指令了:

$ esptool.exe --port com5 erase_flash
$ esptool.exe --port com5 write_flash --flash_mode dout 0 Ai-Thinker_ESP8266_DOUT_8Mbit_v1.5.4.1-a_20171130.bin

把 ESP8266 和本人的开发板串口 (UART2) 连贯上之后,依据本人的开发板型号,在 RT-Thread Studio 里新建我的项目。后面提到咱们须要 3 个串口,所以在 driver/board.h 里须要关上三个串口,一个用作控制台,一个连贯 ESP8266,最初一个和 Kobuki 机器人通信:

# MSH Console
#define BSP_USING_UART1
#define BSP_UART1_TX_PIN       "PA9"
#define BSP_UART1_RX_PIN       "PA10"

# ESP8266
#define BSP_USING_UART2
#define BSP_UART2_TX_PIN       "PA2"
#define BSP_UART2_RX_PIN       "PA3"

# Kobuki Robot
#define BSP_USING_UART3
#define BSP_UART3_TX_PIN       "PB10"
#define BSP_UART3_RX_PIN       "PB11"

接下来在 RT-Thread Studio 外面增加 AT_Device 软件包:

并且双击软件包批改配置选项,抉择本人的 wifi 模块,配置 WIFI SSID 和明码,抉择 UART2 作为通信接口:

保留配置编译,如果一切正常的话,RT-Thread 上电后会主动连贯 WIFI,并且能够 ping 通内部主机:

 \ | /
- RT -     Thread Operating System
 / | \     4.0.2 build Jun 14 2021
 2006 - 2019 Copyright by rt-thread team
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/at.clnt] AT client(V1.3.0) on device uart2 initialize success.
msh >[I/at.dev.esp] esp0 device wifi is connected.
[I/at.dev.esp] esp0 device network initialize successfully.
[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!
[W/at.dev.esp] please check and update esp0 device firmware to support the "AT+CIPDNS_CUR?" cmd.
[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!
[W/at.dev.esp] please check and update esp0 device firmware to support the "AT+CIPDNS_CUR?" cmd.
[E/at.skt] AT socket (0) receive timeout (2000)!

msh >ping rt-thread.org
32 bytes from 118.31.15.152 icmp_seq=0 time=242 ms
32 bytes from 118.31.15.152 icmp_seq=1 time=245 ms
32 bytes from 118.31.15.152 icmp_seq=2 time=241 ms
32 bytes from 118.31.15.152 icmp_seq=3 time=245 ms

msh >

当然,这一部分联网的操作也能够参照 RT-Thread 官网的文档:

https://www.rt-thread.org/doc…

最初咱们把 Kobuki 机器人的串口和 RT-Thread 开发板的串口 (UART3) 接上,在 RTT Studio 软件包里增加 Kobuki:

并双击软件包配置 Kobuki 通信接口为 uart3 就能够筹备对接 ROS 了。

ROS1 (rosserial)

第一代 ROS 反对 串口 和 TCP 通信,这里以 TCP 为例,在 RT-Thread Studio 先增加软件包:

双击软件包后配置应用 TCP 连贯并且增加 Kobuki 机器人的例程:

残缺的代码在 Github 下面都有,所以这里次要解释 rosserial 的外围初始化代码。在 MCU 上的初始化和在 PC 上初始化简直是截然不同的,就是首先 setConnection() 定义 ROS Master 的 IP 地址和端口,接下来 initNode() 初始化节点,最初订阅 /cmd_vel 用来接管电脑传过来的管制信息,并且设置 Kobuki 的速度。

static ros::Subscriber<geometry_msgs::Twist> sub("cmd_vel", messageCb);

static void rosserial_kobuki_thread_entry(void *parameter)
{
    // Please make sure you have network connection first
    // Set ip address and port
    nh.getHardware()->setConnection("192.168.199.100", 11411);

    nh.initNode();
    nh.subscribe(sub);
    while (1)
    {nh.spinOnce();
        rt_thread_mdelay(500);
    }
}

一旦收到 PC 传来的管制信息,在回调函数里更新线性静止的速度,和旋转角速度:

static void messageCb(const geometry_msgs::Twist& twist_msg)
{
    linear_x  = twist_msg.linear.x;
    angular_z = twist_msg.angular.z;
}

当然,Kobuki 线程里会不停地发送管制命令,因为Kobuki 底盘的特点是,一旦一段时间没有收到命令,机器人就会进行静止,所以咱们须要不停地发送管制命令。

static void kobuki_entry(void *parameter)
{kobuki_init(&robot);
    while(1)
    {rt_thread_mdelay(100);
        kobuki_set_speed(linear_x, angular_z);
    }
    kobuki_close(&robot);
}

当然,既然咱们是近程管制,当然不可能用串口连到 RTT 控制台输出命令启动机器人,所以须要在 main.cpp 外面主动启动工作。

#include <rtthread.h>
#include <ros.h>

int main(void)
{extern int rosserial_kobuki_control(int argc, char **argv);
    rt_thread_mdelay(10000);
    rosserial_kobuki_control(0, 0);
}

须要留神的是,这里主函数是 main.cpp,因为 rosserial 只反对 C++。掐指一算,rosserial 相干的代码还不到 10 行,所以这也体现了 ROS 简略好用的特点。最初还有一点, 之前 rosserial 有一个 1Hz 的 bug,导致系统响应十分迟缓,这里要感激 zhengyangliu 的 PR,当初曾经修复了,整体 rosserial 应用 串口 和 TCP 都十分稳固(然而,ESP8266 自身不是很稳固)。

RT-Thread 相干的代码就完结了,当然咱们还须要启动 ROS 主节点,这里我就用 docker 镜像了,把须要的端口映射进去:

$ docker run -p 11411:11411 -it helloysd/rosserial:noetic /bin/bash

docker 容器的 bash 外面装置相干软件包并启动 ROS Serial:

$ apt update
$ apt install curl
$ curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
$ apt update
$ apt install ros-noetic-teleop-twist-keyboard ros-noetic-rosserial-python
$ rosrun rosserial_python serial_node.py tcp

如果一切顺利的话会看到 RTT 节点连贯上来:

root@29bd821af356:/# rosrun rosserial_python serial_node.py tcp
[INFO] [1624089042.689792]: ROS Serial Python Node
[INFO] [1624089042.693095]: Fork_server is: False
[INFO] [1624089042.693884]: Waiting for socket connections on port 11411
[INFO] [1624089042.694750]: Waiting for socket connection
[INFO] [1624089055.564072]: Established a socket connection from 172.17.0.1 on port 55784
[INFO] [1624089055.565926]: calling startSerialClient
[INFO] [1624089057.674819]: Requesting topics...
[INFO] [1624089229.750517]: Note: subscribe buffer size is 512 bytes
[INFO] [1624089229.751418]: Setup subscriber on cmd_vel [geometry_msgs/Twist]

终于能够应用 ROS 的 teleop 软件包用键盘近程管制 Kobuki 机器人了:

$ rosrun teleop_twist_keyboard teleop_twist_keyboard.py

ROS2 (micro_ros)

后面提到,第二代 ROS 主机之间的通信是建设在 DDS/RTPS 之上的,不过这些不须要本人实现,能够间接在 RT-Thread Studio 外面增加 micro_ros 软件包:

简直同样的流程,双击软件包之后抉择通信形式为 UDP,依据本人的开发板抉择不同的架构,因为 micro_ros 相干的库是事后编译好为 libmicroros.a 的,并且当初反对的有 Cortex M0,Cortex M3,Cortex M4,Cortex M7,如果是其余架构的话,就须要在 extras/library_generation 下增加相干的编译文件,从新编译对应架构的库文件。另一方面,默认编译库文件用的是 gcc 5.4.1,和 RTT Studio 的编译器版本统一也是嵌入式最罕用的版本,如果是更新版本的 gcc 或者 Keil 等其余编译器,也须要从新生成 libmicroros.a。

当然,抉择了 Kobuki 的例程之后,咱们也须要指定 Kobuki 的串口 UART3。

同样的,例程默认是把相干的工作导出为命令,咱们在机器人上电后不可能每次都连上串口在 RTT 控制台上面启动工作,所以须要在 main.c 外面主动启动相干的工作:

#include <rtthread.h>

int main(void)
{extern int microros_kobuki_control(int argc, char **argv);
    rt_thread_mdelay(10000);
    microros_kobuki_control(0, (void*)0);
}

这里介绍一下 micro_ros 的启动流程,先设置 micro_ros client 的 IP 地址,UPD 端口号,在启动注册相干的节点,最初订阅本人须要的话题 (topic),启动 executor 设置相干的回调函数就能够了。

set_microros_udp_transports("192.168.199.100", 9999);

allocator = rcl_get_default_allocator();

// create init_options
if (rclc_support_init(&support, 0, NULL, &allocator) != RCL_RET_OK)
{rt_kprintf("[micro_ros] failed to initialize\n");
    return;
};

// create node
if (rclc_node_init_default(&node, "micro_ros_rtt_sub_twist", "", &support) != RCL_RET_OK)
{rt_kprintf("[micro_ros] failed to create node\n");
    return;
}

// create subscriber
rclc_subscription_init_default(
  &subscriber,
  &node,
  ROSIDL_GET_MSG_TYPE_SUPPORT(geometry_msgs, msg, Twist),
  "cmd_vel");

// create executor
rclc_executor_init(&executor, &support.context, 1, &allocator);
rclc_executor_add_subscription(&executor, &subscriber, &msg, &kobuki_callback, ON_NEW_DATA);

每当收到新的音讯时候,就会在回调函数里更新机器人的管制信息。

//twist message cb
static void kobuki_callback(const void *msgin) {const geometry_msgs__msg__Twist * msg = (const geometry_msgs__msg__Twist *)msgin;
    linear_x = msg->linear.x;
    angular_z = msg->angular.z;
}

同样的,Kobuki 机器人须要不停地发送管制命令,一旦一段时间没有收到管制命令,Kobuki 就会主动停下来,这也是一种爱护机制。

static void kobuki_entry(void *parameter)
{kobuki_init(&robot);
    while(1)
    {rt_thread_mdelay(100);
        kobuki_set_speed(linear_x, angular_z);
    }
    kobuki_close(&robot);
}

static void microros_kobuki_entry(void *parameter)
{while(1)
    {rt_thread_mdelay(100);
        rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
    }
}

RT-Thread 开发实现之后,最初当然还是须要在电脑上启动 micro_ros client,这里我仍旧应用 Docker:

$ docker run -it -p 9999:9999/udp --privileged microros/micro-ros-agent:foxy udp4 -p 9999

启动 client 之后咱们就能够让 RTT 连贯上了:

 \ | /
- RT -     Thread Operating System
 / | \     4.0.4 build Jun  9 2021
 2006 - 2021 Copyright by rt-thread team
msh >
msh >microros_pub_int32
[micro_ros] node created
[micro_ros] publisher created
[micro_ros] timer created
[micro_ros] executor created
[micro_ros] New thread mr_pubint32

如果一切正常,咱们就能够在 client 的输入里看到客户端的连贯信息:

[1623057529.937043] info     | TermiosAgentLinux.cpp | init                     | running...             | fd: 4
[1623057529.937150] info     | Root.cpp           | set_verbose_level        | logger setup           | verbose_level: 4
[1623057541.764331] info     | Root.cpp           | create_client            | create                 | client_key: 0x6F7B427A, session_id: 0x81
[1623057541.764507] info     | SessionManager.hpp | establish_session        | session established    | client_key: 0x1870348922, address: 0

最初当然也还是启动 teleop 用键盘来管制 Kobuki 机器人:

$ rosrun teleop_twist_keyboard teleop_twist_keyboard.py

总结

总的来看,不得不说 ROS1 的 rosserial 初始化过程还是更加简略一些。

另一方面,如果心愿能在 RT-Thread 上间接运行 ROS Master 节点,还要期待 RT-Thread Smart。

References

[1] Gazebo 模拟器: https://www.youtube.com/watch…
[2] MicroROS 和 rosseiral 区别: https://micro.ros.org/docs/co…
[3] rosserial 软件包: https://github.com/wuhanstudi…
[4] micro_ros 软件包: https://github.com/wuhanstudi…
[5] kobuki 软件包: https://github.com/wuhanstudi…
[6] rplidar 软件包: https://github.com/wuhanstudi…

正文完
 0