ROS入門(一)——基本概念和話題
iwehdio的博客園:https://www.cnblogs.com/iwehdio/
1、環境搭建
-
虛擬機 VMware
VMware Workstation Pro 15.0.0 Build 10134415
https://download3.vmware.com/software/wkst/file/VMware-workstation-full-15.0.0-10134415.exe -
操作系統 Ubuntu 18.04.4 LTS 桌面64位
http://mirrors.aliyun.com/ubuntu-releases/18.04.4/ -
ROS安裝
-
添加ROS軟件源。
$ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' -
添加密鑰。
$ sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 -
更新軟件源。
$ sudo apt-get update -
安裝ROS。
$ sudo apt-get install ros-melodic-desktop-full $ sudo apt-get dist-upgrade -
初始化rosdep。
$ sudo rosdep init $ rosdep update -
設置環境變量。
$ echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc -
安裝rosinstall。
$ sudo apt install python-rosinstall python-rosinstall-generator python-wstool build-essential -
例程。
$ roscore $ rosrun turtlesim turtlesim_node $ rosrun turtlesim turtle_teleop_key
-
-
ubuntu 下的 Linux 命令:
- Ubuntu 終端快捷鍵:ctrl + alt + T。
- 查看當前路徑:pwd 。
- 切換 / 進入路徑:cd 路徑 。
- 返回上一級目錄:cd .. 。
- 創建新文件夾:mkdir 文件夾 。
- 查看路徑下的文件:ls 。
- 創建新文件:touch 文件名 。
- 剪切文件到新路徑:mv 文件名 新路徑 。
- 復制文件到新路徑:cp 文件名 新路徑 。
- 刪除文件:rm 文件名 。
- 刪除文件夾:rm -r 文件夾 。
- 提升當前用戶權限:sudo 。
- 幫助指令:--help 。
-
在 linux 下運行 C++ 文件:
- 首先在終端進入文件目錄下。
- 編譯:
g++ 文件名.cpp -o 編譯文件名。 - 運行:
./編譯文件名。 - 如果是運行 python 文件直接:
python 文件名.py。 - 注意 ROS 下的是 python2 版本。
2、ROS核心概念
-
ROS = 通信機制 + 開發工具 + 應用功能 + 生態系統,目的是提高機器人開發中軟件的復用率。
-
節點與節點管理器:
-
話題通信(異步通信,無反饋):
-
服務通信(同步通信,有反饋):
-
話題和服務的區別:
-
參數(存儲在參數服務器中):適合存儲靜態數據。
-
ROS文件系統:
3、ROS基本使用
- 命令行工具:
- 單擊 tab 可補全命令,雙擊 tab 可顯示信息。
roscore:啟動節點服務器。rqt_graph:顯示系統計算圖。rosnode 具體指令:顯示系統中的節點信息,后接具體指令。- list:獲取所有的節點列表。
- info /節點名:查看某個節點的信息。
rostopic 具體指令:顯示系統中的話題信息,后接具體指令。- list:獲取所有的話題列表。
- pub /話題名 消息類型 具體數據:獲取話題傳輸的數據。
- pub 后可跟 -r 頻率 ,表示一秒中發布的次數,不設置則話題只會發布一次。
rosmsg show 消息類型:顯示小數類型的具體數據格式。rosservice 具體指令:顯示系統中的服務信息,后接具體指令。- list:獲取所有的話題列表。
- call /服務名 服務內容:設置服務內容,調用服務,返回服務名稱。
rosbag 具體指令:使用話題存儲數據。- record -a -o 文件名:記錄數據。-a 表示記錄所有數據,-o 表示將數據存儲到指定文件名的壓縮包中。
- play 文件名:復現記錄的數據。
- ROS 工作空間:
- src:代碼空間。源代碼和功能包等。
- build:編譯空間。編譯中間文件。
- devel:開發空間。可執行文件。
- install:安裝空間。安裝結果。
- 創建工作空間:
mkdir -p ~/工作空間名/src。cd ~/工作空間名/src。catkin_init_workspace。
- 編譯工作空間:
cd ~/工作空間名/。編譯目錄要回到工作空間根目錄。catkin_make。后加 install 會產生 install 文件夾。
- 設置環境變量:
source devel/setup.bash。
- 檢測環境變量:
echo $ROS_PACKAGE_PATH。
- 創建功能包:
catkin_create_pkg 功能包名 依賴包。比如依賴 C++、python 要寫 roscpp、rospy,依賴標准消息格式要寫 std_msgs。- 創建功能包要在 src 目錄中操作。
- 編譯功能包:
- 回到工作空間根目錄下編譯工作空間。
- 同一個工作空間中不允許存在同名功能包。不同工作空間中可以存在同名功能包。
- 配置文件:
- package.xml:功能包的相關信息和依賴包。
- CMakeLists.txt:描述功能包的編譯規則。
3、話題的實現
-
發布者 Publisher 的實現:
-
創建功能包(工作空間src目錄下):
catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim。 -
初始化 ROS 節點,賦予節點名稱。
-
向 ROS Master 注冊節點信息,設置消息類型,傳入話題名稱和隊列(用於緩沖數據)長度。
-
創建消息數據,並循環發布。
-
C++ 實現:
-
首先需要將文件放入功能包下的目錄。
-
配置 CMakeLists.txt 中的編譯規則。
- 在 Build 模塊下,參數空格分隔。
- 設置生成好可執行文件名和需要編譯的代碼:
add_executable(velocity_publisher src/velocity_publisher.cpp)。 - 設置生成的可執行文件所鏈接的庫:
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})。
-
回到工作空間的根目錄下。
-
編譯工作空間。
$ catkin_make $ source devel/setup.bash- 可將 source devel/setup.bash 添加到 home 下的隱藏文件 .bashrc 的最底部作為環境變量。這樣不必每次都調用該指令。
-
運行功能包下的 C++ 文件。
$ roscore $ rosrun turtlesim turtlesim_node $ rosrun learning_topic velocity_publisher
-
-
C++ 源代碼:
/** * 該例程將發布turtle1/cmd_vel話題,消息類型geometry_msgs::Twist */ #include <ros/ros.h> #include <geometry_msgs/Twist.h> int main(int argc, char **argv) { // ROS節點初始化 ros::init(argc, argv, "velocity_publisher"); // 創建節點句柄 ros::NodeHandle n; // 創建一個Publisher,發布名為/turtle1/cmd_vel的topic,消息類型為geometry_msgs::Twist,隊列長度10 ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10); // 設置循環的頻率 ros::Rate loop_rate(10); int count = 0; while (ros::ok()) { // 初始化geometry_msgs::Twist類型的消息 geometry_msgs::Twist vel_msg; vel_msg.linear.x = 0.5; vel_msg.angular.z = 0.2; // 發布消息 turtle_vel_pub.publish(vel_msg); ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]", vel_msg.linear.x, vel_msg.angular.z); // 按照循環頻率延時 loop_rate.sleep(); } return 0; }
-
-
Python 實現:
-
不需要設置編譯,但要確認 .py 文件的屬性中,可被作為可執行文件運行。
-
其他流程類似,注意最后運行時一定要在文件名后加 .py 后綴。
-
在 Python 中 ROS 節點初始化中的節點名、函數名要一致。
-
"/turtle1/cmd_vel" 是 turtlesim 默認訂閱的的運動控制話題。
-
Python 源代碼:
#!/usr/bin/env python # -*- coding: utf-8 -*- # 該例程將發布turtle1/cmd_vel話題,消息類型geometry_msgs::Twist import rospy from geometry_msgs.msg import Twist def velocity_publisher2(): # ROS節點初始化 rospy.init_node('velocity_publisher2', anonymous=True) # 創建一個Publisher,發布名為/turtle1/cmd_vel的topic,消息類型為geometry_msgs::Twist,隊列長度10 turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10) #設置循環的頻率 rate = rospy.Rate(10) while not rospy.is_shutdown(): # 初始化geometry_msgs::Twist類型的消息 vel_msg = Twist() vel_msg.linear.x = 0.5 vel_msg.angular.z = 0.2 # 發布消息 turtle_vel_pub.publish(vel_msg) rospy.loginfo("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]", vel_msg.linear.x, vel_msg.angular.z) # 按照循環頻率延時 rate.sleep() if __name__ == '__main__': try: velocity_publisher2() except rospy.ROSInterruptException: pass
-
-
-
訂閱者 Subsceiber 的實現:
-
初始化 ROS 節點並賦予節點名。
-
定於所需要的話題名、隊列長度和回調函數。
-
循環等待話題消息,接收到消息后進入回調函數。
-
在回調函數中進行消息處理。
-
"/turtle1/pose" 是 turtlesim 默認發布的的運動姿態話題。
-
Python 實現:
#!/usr/bin/env python # -*- coding: utf-8 -*- # 該例程將訂閱/turtle1/pose話題,消息類型turtlesim::Pose import rospy from turtlesim.msg import Pose def poseCallback(msg): rospy.loginfo("Turtle pose: x:%0.6f, y:%0.6f", msg.x, msg.y) def pose_subscriber(): # ROS節點初始化 rospy.init_node('pose_subscriber', anonymous=True) # 創建一個Subscriber,訂閱名為/turtle1/pose的topic,注冊回調函數poseCallback rospy.Subscriber("/turtle1/pose", Pose, poseCallback) # 循環等待回調函數 rospy.spin() if __name__ == '__main__': pose_subscriber()
-
-
話題消息的定義與使用(自定義話題消息):
-
創建 msg 文件夾並在其下創建 Person.msg 文件(
touch Person.msg),並消息格式為:string name uint8 sex uint8 age uint8 unknown = 0 uint8 male = 1 uint8 female = 2 -
在 package.xml 中添加功能包依賴。即編譯依賴 message_generation,運行依賴 message_runtime。
<build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend> -
在 CMakeLists.txt 中添加編譯選項。
-
在 find_package() 中添加
message_generation。 -
定義消息接口,在 Declare ROS messages, services and actions 下。
add_message_files(FILES Person.msg) generate_messages(DEPENDENCIES std_msgs)- 第一行表示將 Person.msg 作為一個消息接口,第二行表示該消息接口的依賴。
-
在 catkin_package 中添加運行時依賴。
catkin_package( CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime )- 去掉 CATKIN_DEPENDS 前的注釋,並在其后添加 message _runtime。
-
-
創建發布者和訂閱者(C++實現):
-
發布者:
/** * 該例程將發布/person_info話題,自定義消息類型learning_topic::Person */ #include <ros/ros.h> #include "learning_topic/Person.h" int main(int argc, char **argv) { // ROS節點初始化 ros::init(argc, argv, "person_publisher"); // 創建節點句柄 ros::NodeHandle n; // 創建一個Publisher,發布名為/person_info的topic,消息類型為learning_topic::Person,隊列長度10 ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10); // 設置循環的頻率 ros::Rate loop_rate(1); int count = 0; while (ros::ok()) { // 初始化learning_topic::Person類型的消息 learning_topic::Person person_msg; person_msg.name = "Tom"; person_msg.age = 18; person_msg.sex = learning_topic::Person::male; // 發布消息 person_info_pub.publish(person_msg); ROS_INFO("Publish Person Info: name:%s age:%d sex:%d", person_msg.name.c_str(), person_msg.age, person_msg.sex); // 按照循環頻率延時 loop_rate.sleep(); } return 0; } -
訂閱者:
/** * 該例程將訂閱/person_info話題,自定義消息類型learning_topic::Person */ #include <ros/ros.h> #include "learning_topic/Person.h" // 接收到訂閱的消息后,會進入消息回調函數 void personInfoCallback(const learning_topic::Person::ConstPtr& msg) { // 將接收到的消息打印出來 ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d", msg->name.c_str(), msg->age, msg->sex); } int main(int argc, char **argv) { // 初始化ROS節點 ros::init(argc, argv, "person_subscriber"); // 創建節點句柄 ros::NodeHandle n; // 創建一個Subscriber,訂閱名為/person_info的topic,注冊回調函數personInfoCallback ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback); // 循環等待回調函數 ros::spin(); return 0; }
-
-
在 CMakeLists.txt 中設置編譯規則。處理之前的設置可執行文件和編譯代碼、設置鏈接庫以外,還需要添加依賴項。
add_executable(person_publisher src/person_publisher.cpp) target_link_libraries(person_publisher ${catkin_LIBRARIES}) add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp) add_executable(person_subscriber src/person_subscriber.cpp) target_link_libraries(person_subscriber ${catkin_LIBRARIES}) add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp) -
再進行編譯並運行發布者和訂閱者。
$ cd ~/workspace0 $ catkin_make $ roscore $ rosrun learning_topic person_subscriber $ rosrun learning_topic person_publisher -
如果在建立連接后,關閉 ROS Master(即roscore命令行),二者的連接不會中斷。
-
-
創建發布者和訂閱者(Python實現):
-
發布者:
#!/usr/bin/env python # -*- coding: utf-8 -*- # 該例程將發布/person_info話題,自定義消息類型learning_topic::Person import rospy from learning_topic.msg import Person def velocity_publisher(): # ROS節點初始化 rospy.init_node('person_publisher', anonymous=True) # 創建一個Publisher,發布名為/person_info的topic,消息類型為learning_topic::Person,隊列長度10 person_info_pub = rospy.Publisher('/person_info', Person, queue_size=10) #設置循環的頻率 rate = rospy.Rate(10) while not rospy.is_shutdown(): # 初始化learning_topic::Person類型的消息 person_msg = Person() person_msg.name = "Tom"; person_msg.age = 18; person_msg.sex = Person.male; # 發布消息 person_info_pub.publish(person_msg) rospy.loginfo("Publsh person message[%s, %d, %d]", person_msg.name, person_msg.age, person_msg.sex) # 按照循環頻率延時 rate.sleep() if __name__ == '__main__': try: velocity_publisher() except rospy.ROSInterruptException: pass -
訂閱者:
#!/usr/bin/env python # -*- coding: utf-8 -*- # 該例程將訂閱/person_info話題,自定義消息類型learning_topic::Person import rospy from learning_topic.msg import Person def personInfoCallback(msg): rospy.loginfo("Subcribe Person Info: name:%s age:%d sex:%d", msg.name, msg.age, msg.sex) def person_subscriber(): # ROS節點初始化 rospy.init_node('person_subscriber', anonymous=True) # 創建一個Subscriber,訂閱名為/person_info的topic,注冊回調函數personInfoCallback rospy.Subscriber("/person_info", Person, personInfoCallback) # 循環等待回調函數 rospy.spin() if __name__ == '__main__': person_subscriber() -
無論是 C++ 還是 Python 都要記得引入生成的 Person 消息類型。
-
參考:古月ROS入門21講:https://www.bilibili.com/video/BV1zt411G7Vn?p=21
iwehdio的博客園:https://www.cnblogs.com/iwehdio/
