引言 這篇文檔主要介紹 RT-Thread 如何使用串口或者無(wú)線和 ROS 連接,會(huì)包含這么些內(nèi)容: 第一部分:ROS 環(huán)境搭建 第二部分:RT-Thread rosserial 軟件包 第二部分:RT-Thread 添加 USART2 和 PWM 第三部分:RT-Thread 使用 ESP8266 AT 固件聯(lián)網(wǎng) 這里先介紹一下什么是 ROS?為什么要和 ROS 連接? 機(jī)器人操作系統(tǒng) ROS (Robots Operating System) 最早是斯坦福大學(xué)的一個(gè)軟件框架,現(xiàn)在不管是工業(yè)機(jī)器人,還是娛樂(lè)用的機(jī)器人都運(yùn)行著 ROS。 file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps9.png 一個(gè)機(jī)器人通常有很多個(gè)部件、傳感器,為了保證機(jī)器人不會(huì)因?yàn)槟骋粋(gè)傳感器故障,導(dǎo)致整個(gè)系統(tǒng)癱瘓,所以采用了分布式的節(jié)點(diǎn),利用不同節(jié)點(diǎn)之間的通訊收集傳感器數(shù)據(jù)和控制指令,這篇文檔后面會(huì)使用到的通訊協(xié)議就是 rosserial。和 ROS 連接的好處在于,一方面由 ROS 管理各個(gè)機(jī)器人節(jié)點(diǎn)更穩(wěn)定,另一方面 ROS 現(xiàn)在已經(jīng)有了非常多成熟的軟件包,使用 ROS 就可以非常方便的為自己的機(jī)器人添加攝像頭圖像識(shí)別、激光雷達(dá)建圖導(dǎo)航等高級(jí)功能。不過(guò)這篇文檔只會(huì)涉及 RT-Thread 和 ROS 建立基本的連接,實(shí)現(xiàn)小車的運(yùn)動(dòng)控制,之后可能會(huì)有后續(xù)文檔介紹如何連接激光雷達(dá)建圖,并進(jìn)行全局路徑規(guī)劃。 這篇文章假定大家都已經(jīng)會(huì)用 RT-Thread 的 env 工具下載軟件包,生成項(xiàng)目上傳固件到 stm32 上,并且熟悉 Ubuntu 的基本使用。 這里的開發(fā)環(huán)境搭建其實(shí)是需要搭建 2 份,一份是小車上的 ARM 開發(fā)板 (樹莓派,NanoPi 什么的),另一個(gè)則是自己的電腦,因?yàn)槲覀兿M央娔X作為 ROS 從節(jié)點(diǎn),連接到小車上的 ROS 主節(jié)點(diǎn),不過(guò)開發(fā)板和電腦的 ROS 安裝是一模一樣的。 既然要和 ROS 連接,那么首先就得要有一個(gè)正常運(yùn)行的 ROS。安裝 ROS 其實(shí)非常簡(jiǎn)單,這里推薦使用 Ubuntu 18 (開發(fā)板推薦系統(tǒng)用 Armbian),因?yàn)楣俜綄?duì) Ubuntu 的支持優(yōu)先級(jí)是最高的,安裝教程也可以參照 官網(wǎng):http://wiki.ros.org/melodic/Installation/Ubuntu只需要輸入下面的 4 行命令,就在 Ubuntu 上裝好了 ROS。 1sudo sh -c 'echo "deb https://mirror.tuna.tsinghua.edu.cn/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' 2sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 3sudo apt update 4sudo apt install ros-melodic-ros-base 上面我使用了清華大學(xué)的鏡像源,這樣從國(guó)內(nèi)下載 ROS 會(huì)快很多,而且我只安裝了 ROS 的基本軟件包,沒(méi)有安裝圖形化軟件包 gviz,gazebo 什么的,因?yàn)楹竺嬉矝](méi)有用到。 1.2 ROS 環(huán)境初始化ROS 安裝好之后還需要進(jìn)行初始化,不過(guò)也是只有短短幾行命令: 1sudo rosdep init 2rosdep update 3 4echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc 5source ~/.bashrc 1.3 啟動(dòng) ROS 啟動(dòng) ROS 的話我們需要確保它是常駐后臺(tái)運(yùn)行的,所以我們可以使用 tmux: 1roscore 在 tmux 里啟動(dòng)了 ROS 主節(jié)點(diǎn)后,我們就可以 Ctrl + B D 退出了,而 ROS 主節(jié)點(diǎn)依舊在后臺(tái)運(yùn)行。 1.4 參考文獻(xiàn)· Armbian:https://www.armbian.com/ · ROS Melodic 安裝:http://wiki.ros.org/melodic/Installation/Ubuntu 2 RT-Thread 串口連接 ROS這一部分會(huì)介紹如何使用串口將運(yùn)行著 RT-Thread 的 STM32 開發(fā)板和運(yùn)行著 ROS 的 ARM 開發(fā)板連接,看起來(lái)差不多就是這樣。 file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps10.png 這里說(shuō)明一下不同開發(fā)板的分工,STM32 運(yùn)行著 RT-Thread 負(fù)責(zé)控制電機(jī),接收傳感器信息;ARM 運(yùn)行著 ROS 負(fù)責(zé)進(jìn)行全局控制,例如給小車發(fā)出前進(jìn)的指令。 2.1 RT-Thread 配置首先我們需要打開 usart2,因?yàn)?usart1 被 msh 使用了,保留作為調(diào)試還是挺方便的。file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps11.png 在 CubeMX 里我打開了 USART2,另外還打開了 4 路 PWM,因?yàn)槲液竺媸褂昧?2 個(gè)電機(jī),每個(gè)電機(jī)需要 2 路 PWM 分別控制前進(jìn)和后退。接下來(lái)還需要在 menuconfig 里面打開對(duì)應(yīng)的選項(xiàng),考慮到有的開發(fā)板默認(rèn)的 bsp 可能沒(méi)有這些選項(xiàng),可以修改 board/Kconfig 添加下面的內(nèi)容。串口的配置: 1menuconfig BSP_USING_UART 2 bool "Enable UART" 3 default y 4 select RT_USING_SERIAL 5 if BSP_USING_UART 6 config BSP_USING_UART1 7 bool "Enable UART1" 8 default y 9 10 config BSP_UART1_RX_USING_DMA 11 bool "Enable UART1 RX DMA" 12 depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA 13 default n 14 15 config BSP_USING_UART2 16 bool "Enable UART2" 17 default y 18 19 config BSP_UART2_RX_USING_DMA 20 bool "Enable UART2 RX DMA" 21 depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA 22 default n 23 endif PWM 的配置: 1menuconfig BSP_USING_PWM 2 bool "Enable pwm" 3 default n 4 select RT_USING_PWM 5 if BSP_USING_PWM 6 menuconfig BSP_USING_PWM3 7 bool "Enable timer3 output pwm" 8 default n 9 if BSP_USING_PWM3 10 config BSP_USING_PWM3_CH1 11 bool "Enable PWM3 channel1" 12 default n 13 config BSP_USING_PWM3_CH2 14 bool "Enable PWM3 channel2" 15 default n 16 config BSP_USING_PWM3_CH3 17 bool "Enable PWM3 channel3" 18 default n 19 config BSP_USING_PWM3_CH4 20 bool "Enable PWM3 channel4" 21 default n 22 endif 23 endif 這樣我們?cè)?/font> env 下就可以看到有對(duì)應(yīng)的配置了, file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps12.png file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps13.png 除此之外,我們還需要選擇 rosserial 軟件包: file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps14.png 可以看到上面默認(rèn)的串口就是 USART2,這樣我們就可以生成對(duì)應(yīng)的工程了: 1pkgs --update 2scons --target=mdk5 -s 如果我們打開 Keil 項(xiàng)目,首先需要把 main.c 修改為 main.cpp,因?yàn)?rosserial 很多數(shù)據(jù)格式的定義都是用 C++ 寫的,所以如果要使用 rosserial 庫(kù),我們先得把后綴改為 cpp,這樣 Keil 就會(huì)用 C++ 編譯器編譯。 file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps15.png 下面是 main.cpp 的內(nèi)容,其實(shí)就是初始化了電機(jī),然后發(fā)布了 2 個(gè)話題 (topic),一個(gè)是 /vel_x 告訴 ROS 當(dāng)前小車的速度,一個(gè)是 /turn_bias 告訴 ROS 當(dāng)前小車的旋轉(zhuǎn)速度。同時(shí)又訂閱了一個(gè)話題 /cmd_vel,用來(lái)接收從 ROS 發(fā)出的控制指令。代碼不是特別長(zhǎng),我也添加了一些注釋,所以這里就不一行行分析了。 1#include 2#include 3#include 4 5#include 6#include 7#include 8#include "motors.h" 9 10ros::NodeHandle nh; 11MotorControl mtr(1, 2, 3, 4); //Motor 12 13bool msgRecieved = false; 14float velX = 0, turnBias = 0; 15char stat_log[200]; 16 17// 接收到命令時(shí)的回調(diào)函數(shù) 18void velCB( const geometry_msgs::Twist& twist_msg) 19{ 20 velX = twist_msg.linear.x; 21 turnBias = twist_msg.angular.z; 22 msgRecieved = true; 23} 24 25//Subscriber 26ros::Subscriber 27 28//Publisher 29std_msgs::Float64 velX_tmp; 30std_msgs::Float64 turnBias_tmp; 31ros:ublisher xv("vel_x", &velX_tmp); 32ros:ublisher xt("turn_bias", &turnBias_tmp); 33 34static void rosserial_thread_entry(void *parameter) 35{ 36 //Init motors, specif>y the respective motor pins 37 mtr.initMotors(); 38 39 //Init node> 40 nh.initNode(); 41 42 // 訂閱了一個(gè)話題 /cmd_vel 接收控制指令 43 nh.subscribe(sub); 44 45 // 發(fā)布了一個(gè)話題 /vel_x 告訴 ROS 小車速度 46 nh.advertise(xv); 47 48 // 發(fā)布了一個(gè)話題 /turn_bias 告訴 ROS 小車的旋轉(zhuǎn)角速度 49 nh.advertise(xt); 50 51 mtr.stopMotors(); 52 53 while (1) 54 { 55 // 如果接收到了控制指令 56 if (msgRecieved) 57 { 58 velX *= mtr.maxSpd; 59 mtr.moveBot(velX, turnBias); 60 msgRecieved = false; 61 } 62 63 velX_tmp.data = velX; 64 turnBias_tmp.data = turnBias/mtr.turnFactor; 65 66 // 更新話題內(nèi)容 67 xv.publish( &velX_tmp ); 68 xt.publish( &turnBias_tmp ); 69 70 nh.spinOnce(); 71 } 72} 73 74int main(void) 75{ 76 // 啟動(dòng)一個(gè)線程用來(lái)和 ROS 通信 77 rt_thread_t thread = rt_thread_create("rosserial", rosserial_thread_entry, RT_NULL, 2048, 8, 10); 78 if(thread != RT_NULL) 79 { 80 rt_thread_startup(thread); 81 rt_kprintf("[rosserial] New thread rosserial\n"); 82 } 83 else 84 { 85 rt_kprintf("[rosserial] Failed to create thread rosserial\n"); 86 } 87 return RT_EOK; 88} 另外還有對(duì)應(yīng)的電機(jī)控制的代碼,不過(guò)這個(gè)大家的小車不同,驅(qū)動(dòng)應(yīng)當(dāng)也不一樣,我這里由于小車電機(jī)上沒(méi)有編碼器,所以全部是開環(huán)控制的。 motors.h 1#include 2 3class MotorControl { 4 public: 5 //Var 6 rt_uint32_t maxSpd; 7 float moveFactor; 8 float turnFactor; 9 10 MotorControl(int fl_for, int fl_back, 11 int fr_for, int fr_back); 12 void initMotors(); 13 void rotateBot(int dir, float spd); 14 void moveBot(float spd, float bias); 15 void stopMotors(); 16 private: 17 struct rt_device_pwm *pwm_dev; 18 //The pins 19 int fl_for; 20 int fl_back; 21 int fr_for; 22 int fr_back; 23 int bl_for; 24 int bl_back; 25 int br_for; 26 int br_back; 27}; motors.c 1#include 2#include 3#include "motors.h" 4 5#define PWM_DEV_NAME "pwm3" 6 7MotorControl::MotorControl(int fl_for, int fl_back, 8 int fr_for, int fr_back) 9{ 10 this->maxSpd = 500000; 11 this->moveFactor = 1.0; 12 this->turnFactor = 3.0; 13 14 this->fl_for = fl_for; 15 this->fl_back = fl_back; 16 17 this->fr_for = fr_for; 18 this->fr_back = fr_back; 19} 20 21void MotorControl::initMotors() { 22 /* 查找設(shè)備 */ 23 this->pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); 24 if (pwm_dev == RT_NULL) 25 { 26 rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME); 27 } 28 rt_kprintf("pwm found %s device!\n", PWM_DEV_NAME); 29 rt_pwm_set(pwm_dev, fl_for, maxSpd, 0); 30 rt_pwm_enable(pwm_dev, fl_for); 31 32 rt_pwm_set(pwm_dev, fl_back, maxSpd, 0); 33 rt_pwm_enable(pwm_dev, fl_back); 34 35 rt_pwm_set(pwm_dev, fr_for, maxSpd, 0); 36 rt_pwm_enable(pwm_dev, fr_for); 37 38 rt_pwm_set(pwm_dev, fr_back, maxSpd, 0); 39 rt_pwm_enable(pwm_dev, fr_back); 40} 41 42// 小車運(yùn)動(dòng) 43void MotorControl::moveBot(float spd, float bias) { 44 float sL = spd * maxSpd; 45 float sR = spd * maxSpd; 46 int dir = (spd > 0) ? 1 : 0; 47 48 if(bias != 0) 49 { 50 rotateBot((bias > 0) ? 1 : 0, bias); 51 return; 52 } 53 54 if( sL < -moveFactor * maxSpd) 55 { 56 sL = -moveFactor * maxSpd; 57 } 58 if( sL > moveFactor * maxSpd) 59 { 60 sL = moveFactor * maxSpd; 61 } 62 63 if( sR < -moveFactor * maxSpd) 64 { 65 sR = -moveFactor * maxSpd; 66 } 67 if( sR > moveFactor * maxSpd) 68 { 69 sR = moveFactor * maxSpd; 70 } 71 72 if (sL < 0) 73 { 74 sL *= -1; 75 } 76 77 if (sR < 0) 78 { 79 sR *= -1; 80 } 81 82 rt_kprintf("Speed Left: %ld\n", (rt_int32_t)sL); 83 rt_kprintf("Speed Right: %ld\n", (rt_int32_t)sR); 84 85 if(dir) 86 { 87 rt_pwm_set(pwm_dev, fl_for, maxSpd, (rt_int32_t)sL); 88 rt_pwm_set(pwm_dev, fl_back, maxSpd, 0); 89 rt_pwm_set(pwm_dev, fr_for, maxSpd, (rt_int32_t)sR); 90 rt_pwm_set(pwm_dev, fr_back, maxSpd, 0); 91 } 92 else 93 { 94 rt_pwm_set(pwm_dev, fl_for, maxSpd, 0); 95 rt_pwm_set(pwm_dev, fl_back, maxSpd, (rt_int32_t)sL); 96 rt_pwm_set(pwm_dev, fr_for, maxSpd, 0); 97 rt_pwm_set(pwm_dev, fr_back, maxSpd, (rt_int32_t)sR); 98 } 99 100 rt_thread_mdelay(1); 101} 102 103 104// 小車旋轉(zhuǎn) 105void MotorControl::rotateBot(int dir, float spd) { 106 float s = spd * maxSpd; 107 if (dir < 0) 108 { 109 s *= -1; 110 } 111 if(dir) 112 { 113 // Clockwise 114 rt_pwm_set(pwm_dev, fl_for, maxSpd, (rt_int32_t)s); 115 rt_pwm_set(pwm_dev, fl_back, maxSpd, 0); 116 rt_pwm_set(pwm_dev, fr_for, maxSpd, 0); 117 rt_pwm_set(pwm_dev, fr_back, maxSpd, (rt_int32_t)s); 118 } 119 else 120 { 121 // Counter Clockwise 122 rt_pwm_set(pwm_dev, fl_for, maxSpd, 0); 123 rt_pwm_set(pwm_dev, fl_back, maxSpd, (rt_int32_t)s); 124 rt_pwm_set(pwm_dev, fr_for, maxSpd, (rt_int32_t)s); 125 rt_pwm_set(pwm_dev, fr_back, maxSpd, 0); 126 } 127 rt_thread_mdelay(1); 128} 129 130//Turn off both motors 131void MotorControl::stopMotors() 132{ 133 rt_pwm_set(pwm_dev, fl_for, maxSpd, 0); 134 rt_pwm_set(pwm_dev, fl_back, maxSpd, 0); 135 rt_pwm_set(pwm_dev, fr_for, maxSpd, 0); 136 rt_pwm_set(pwm_dev, fr_back, maxSpd, 0); 137} 一共只需要這么一點(diǎn)代碼就可以實(shí)現(xiàn)和 ROS 的連接了,所以其實(shí) ROS 也不是那么神秘,它就是因?yàn)楹?jiǎn)單好用所以才這么受歡迎的。既然 RT-Thread 已經(jīng)配置好了,下一步就是 ROS 的配置了。 2.2 ROS 配置我們把上面 RT-Thread 的固件傳到板子上以后,可以用一個(gè) USB-TTL 一邊和 STM32 控制板的 USART2 連接,另一邊插到 ARM 控制板的 USB 口,接下來(lái)就可以建立連接了,在 ARM 板上輸入命令: 1$ rosrun rosserial_python serial_node.py /dev/ttyUSB0 如果看到下面的輸出,那就成功建立連接了: 1tpl@nanopineoplus2:~$ rosrun rosserial_python serial_node.py /dev/ttyUSB0 2.3 ROS 控制小車2[INFO] [1567239474.258919]: ROS Serial Python Node 3[INFO] [1567239474.288435]: Connecting to /dev/ttyUSB0 at 57600 baud 4[INFO] [1567239476.425646]: Requesting topics... 5[INFO] [1567239476.464336]: Note: publish buffer size is 512 bytes 6[INFO] [1567239476.471349]: Setup publisher on vel_x [std_msgs/Float64] 7[INFO] [1567239476.489881]: Setup publisher on turn_bias [std_msgs/Float64] 8[INFO] [1567239476.777573]: Note: subscribe buffer size is 512 bytes 9[INFO] [1567239476.785032]: Setup subscriber on cmd_vel [geometry_msgs/Twist] 既然已經(jīng)成功建立連接了,下一步就是寫小車控制的代碼了。我們先初始化一個(gè)工作區(qū)間: 1$ mkdir catkin_workspace && cd catkin_workspace 2$ catkin_init_workspace 接下來(lái)創(chuàng)建一個(gè)軟件包: 1$ cd src 2$ catkin_create_pkg my_first_pkg rospy 這樣就會(huì)自動(dòng)在 src 目錄創(chuàng)建一個(gè) ROS 軟件包了。我們?cè)?catkin_workspace/src/my_first_pkg/src 目錄下新建一個(gè)文件 ros_cmd_vel_pub.py: 1#!/usr/bin/python 2 3import rospy 4from geometry_msgs.msg import Twist 5from pynput.keyboard import Key, Listener 6 7vel = Twist() 8vel.linear.x = 0 9 10def on_press(key): 11 12 try: 13 if(key.char == 'w'): 14 print("Forward") 15 vel.linear.x = 0.8 16 vel.angular.z = 0 17 18 if(key.char == 's'): 19 print("Backward") 20 vel.linear.x = -0.8 21 vel.angular.z = 0 22 23 if(key.char == 'a'): 24 print("Counter Clockwise") 25 vel.linear.x = 0 26 vel.angular.z = -0.8 27 28 if(key.char == 'd'): 29 print("Clockwise") 30 vel.linear.x = 0 31 vel.angular.z = 0.8 32 33 return False 34 35 except AttributeError: 36 print('special key {0} pressed'.format(key)) 37 return False 38 39def on_release(key): 40 vel.linear.x = 0 41 vel.angular.z = 0 42 43 return False 44 45# Init Node 46rospy.init_node('my_cmd_vel_publisher') 47pub = rospy.Publisher('cmd_vel', Twist, queue_size=10) 48 49# Set rate 50rate = rospy.Rate(10) 51 52listener = Listener(on_release=on_release, on_press = on_press) 53 54while not rospy.is_shutdown(): 55 print(vel.linear.x) 56 pub.publish(vel) 57 vel.linear.x = 0 58 vel.angular.z = 0 59 rate.sleep() 60 61 if not listener.running: 62 listener = Listener(on_release=on_release, on_press = on_press) 63 listener.start() 這就是我們的 python 控制程序了,可以使用鍵盤的 wasd 控制小車前進(jìn)后退,順時(shí)針、逆時(shí)針旋轉(zhuǎn)。我們需要給它添加可執(zhí)行權(quán)限: 1$ chmod u+x ./ros_cmd_vel_pub.py 這樣就可以編譯軟件包了,在 catkin_worspace 目錄下。 1$ catkin_make 2$ source devel/setup.bash 我們終于就可以啟動(dòng)程序從電腦上控制小車運(yùn)動(dòng)了: 1rosrun my_first_pkg ros_cmd_vel_pub.py 2.4 參考文獻(xiàn)可以看到用 ROS 實(shí)現(xiàn)小車控制其實(shí)代碼量并不算多,只需要在自己小車原有的代碼上發(fā)布一些話題,告訴 ROS 小車當(dāng)前的狀態(tài),并且訂閱一個(gè)話題接收 ROS 的控制指令就可以了。 · ros-pibot:https://github.com/wuhanstudio/ros-pibot 3 RT-Thread 無(wú)線連接 ROS3.1 rosserial 配置其實(shí)無(wú)線連接和有線連接幾乎是一模一樣的,只不過(guò)是先用 ESP8266 使自己的控制板能連上網(wǎng),然后用 tcp 連接和 ROS 通信,關(guān)于 RT-Thread 使用 ESP8266 上網(wǎng)的教程可以參照 官網(wǎng):https://www.rt-thread.org/document/site/application-note/components/at/an0014-at-client/,非常詳細(xì)了,我這里就不重復(fù)了。確保開發(fā)板有網(wǎng)絡(luò)連接后,我們就可以在 rosserial 里面配置為使用 tcp 連接:file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml16216\wps16.png 我們只需要在上一部分的 main.cpp 里添加一行代碼: 1// 設(shè)置 ROS 的 IP 端口號(hào) 2nh.getHardware()->setConnection("192.168.1.210", 11411); 3 4// 添加在節(jié)點(diǎn)初始化之前 5nh.initNode(); 開發(fā)板就能通過(guò) tcp 連接和 ROS 通信了,非常方便。 3.2 ROS 配置由于我們使用了 tcp 連接,所以 ROS 上自然也要開啟一個(gè)服務(wù)器了,之前是使用的串口建立連接,現(xiàn)在就是使用 tcp 了: 1$ rosrun rosserial_python serial_node.py tcp 其他的代碼完全不需要改變,這樣我們就實(shí)現(xiàn)了一個(gè) ROS 無(wú)線控制的小車了。 3.3 參考文獻(xiàn)· RT-Thread 使用 ESP8266 上網(wǎng): · https://www.rt-thread.org/document/site/application-note/components/at/an0014-at-client/ 4 總結(jié)這里再總結(jié)一下,其實(shí) RT-Thread 使用 rosserial 軟件包和 ROS 建立連接非常簡(jiǎn)單,只需要在自己小車原有代碼的基礎(chǔ)上發(fā)布一些消息,告訴 ROS 小車當(dāng)前的狀態(tài),以及訂閱來(lái)自 ROS 的控制指令就可以了。 |