zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    完美解决php无法上传大文件代码
    完美解决php无法上传大文件源代码
    完美解决php无法上传大文件源码
    IfcCartesianTransformationOperator3D
    IfcCartesianTransformationOperator3DnonUniform
    IfcCartesianTransformationOperator2D
    IfcCartesianTransformationOperator2DnonUniform
    IfcCartesianTransformationOperator
    IfcCartesianPointList3D
    IfcCartesianPointList2D
  • 原文地址:https://www.cnblogs.com/iwehdio/p/12721349.html
Copyright © 2011-2022 走看看