ROS入門(一)——基本概念和話題


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安裝

    1. 添加ROS軟件源。

      $ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
      
    2. 添加密鑰。

      $ sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
      
    3. 更新軟件源。

      $ sudo apt-get update
      
    4. 安裝ROS。

      $ sudo apt-get install ros-melodic-desktop-full
      $ sudo apt-get dist-upgrade
      
    5. 初始化rosdep。

      $ sudo rosdep init
      $ rosdep update
      
    6. 設置環境變量。

      $ echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
      
    7. 安裝rosinstall。

      $ sudo apt install python-rosinstall python-rosinstall-generator python-wstool build-essential
      
    8. 例程。

      $ 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM