机器人操作系统ROS 编程开发--详细总结

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: 最近工作涉及到自动驾驶的,需要学习ROS,学习中总结了一些知识点,分享给大家。机器人操作系统ROS,是一种分布式处理框架(又名Nodes),ROS常用C++和python编程语言开发;(这里项目开发采用C++ 11版本)。ROS的点对点设计以及服务和节点管理器等机制,可以分散由计算机视觉和语音识别等功能带来的实时计算压力,能够适应多机器人遇到的挑战。ROS免费并且开源。

最近工作涉及到自动驾驶的,需要学习ROS,学习中总结了一些知识点,分享给大家。

ROS基本介绍

 机器人操作系统ROS,是一种分布式处理框架(又名Nodes),ROS常用C++和python编程语言开发;(这里项目开发采用C++ 11版本)。ROS的点对点设计以及服务和节点管理器等机制,可以分散由计算机视觉和语音识别等功能带来的实时计算压力,能够适应多机器人遇到的挑战。ROS免费并且开源。


ROS常用的概念

1node: 节点. 节点就是一些直行运算任务的进程。节点之间是通过传送消息进行通讯的;ROS中,通常来讲我们写的c++程序主函数所在的程序称为一个节点;

2message: 消息.机器人需要传感器,传感器采集到的信息,即这儿的message.(如:位置消息,温度、湿度等);消息以一种发布/订阅的方式传递;

3topic: 话题.node交换Messages的命名总线异步通讯机制,传输消息;

4package: 是组织ROS代码的最基本单位,每一个Package都可以包括库文件,可执行文件,脚本及其它的一些文件。

5workSpace: 工作空间 用来存放很多不同package的。

6roslaunch: 启动文件,其目的是一次性启动多个节点s。

7Master: 节点管理器,ROS名称服务,帮助节点找到彼此。

8publish : 发布器,把相关的信息发送到topic

9、subscribe: 订阅器,订阅相应的topic,接收话题的信息


ROS的结构是怎样的?

ROS分为两层,底层是操作系统层,上层则是广大用户编写提供的各种功能不同的软件包,比如定位导航,行动规划等等。

所以ROS实际上可以看成是一个中间层,提供和重新封装了底层硬件调用的API,这些重新封装的API称为客户端库,运用这些库可是实现硬件调用,以此实现各种不同的功能,如使用激光雷达扫描生成周围环境的2D地图……

   ROS框架基于集中式拓扑图结构,它的进程(即节点,ROS以节点形式进行通信,以此实现功能)是分布式的,进程分布在各个功能不同的功能包里面。


ROS发布消息-- publish

1、流程

  • 初始化 ROS 系统
  • 在 ROS 网络内广播中,我们将要在 chatter 话题上,发布 std_msgs/String 类型的消息
  • 以每秒 10 次的频率(可以设置修改)在 chatter 上发布消息

2、Ros发布信息 例子

/**
 *本程序演示了通过ROS系统简单发送消息。
*/
#include "ros/ros.h"                         //要使用ROS,得包含这个头文件
#include "std_msgs/String.h"                 //导入 String类型的头文件
#include <sstream>                           //c++自带的头文件 实现输入输出流等
int main(int argc, char **argv)
{
  ros::init(argc, argv, "talker");          //ros初始化,talker就是node(节点)的名字
  ros::NodeHandle n;                        //为这个进程的节点创建一个句柄。
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
                                           //定义要publish(发布)信息的对象
  ros::Rate loop_rate(10);                 //发布的信息的快慢 速度为10Hz
  int count = 0;
  while (ros::ok())
  {
    std_msgs::String msg;
    std::stringstream ss;
    ss << "hello world " << count;                
    msg.data = ss.str();
    ROS_INFO("%s", msg.data.c_str());    //可以理解为ROS里的printf()
    chatter_pub.publish(msg);            //用来发布信息(msg中的内容)
    ros::spinOnce();                     //这个函数是用于接收器,是检测一次
    loop_rate.sleep();
    ++count;
  }
  return 0;
}

image.gif

3、程序重点降解:

ros::NodeHandle n;

  • 第一个创建的 NodeHandle 会为节点进行初始化,最后一个销毁的 NodeHandle 则会释放该节点所占用的所有资源

ros:: Publisher chatter_pub = n.advertise < std_msgs::String>("chatter", 1000);

  • n.advertise 通过NodeHandle的对象n告诉ROS系统我要创建一个可以发布信息的对象
  • < std_msgs::String>告诉ROS我要发布的是标准信息中的String类型,chatter 通讯时的topic(主题)
  • 1000这个数字的意思是要缓冲的信息数量
  • advertise返回一个 ros::Publisher 对象,它有两个作用:

    1) 它有一个 publish() 成员函数可以让你在topic上发布消息;

     2) 如果消息类型不对,它会拒绝发布。

4、为什么需要缓冲呢?

这发布和接收之间并不是瞬间进行的,发布消息和接收到消息之间的时间差。缓冲区接收最新的信息放到信息序列的最后。即缓冲区的信息的数据结构是queue。第一条来的信息在序列满了的情况下会被第一个丢弃。

ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

1000这个数字的意思是要缓冲的信息数量


ROS订阅消息subscribe

1、流程

  • 初始化ROS系统
  • 订阅 chatter 话题
  • 进入自循环,等待消息的到达
  • 当消息到达,调用 chatterCallback() 函数

Ros订阅信息 例子

/**
* 本程序演示了通过ROS系统简单接收消息。
*/
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg)
  //是一个回调函数,当接收到 chatter 话题的时候就会被调用。{
ROS_INFO("I heard: [%s]", msg->data.c_str());//msg->data就是一个std::string类型的量
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");//节点的名字换成了listener
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
                                //定义接收器,topic话题:chatter,第三个参数chatterCallback称为回调函数
ros::spin();            //会使程序在循环中,一直检测有没有接收到新的消息
return 0;
}

image.gif

3、注意点

  1. 如果ROS遇到了相同的节点名字那么他会停止掉旧节点的名字然后使用新节点的那个程序(这时候旧的节点如果 有ros::ok(),那么他会就变得不OK了 = = 。这是ros::ok()返回false的第三种情况)
  2. 注意node的名字得独一无二,但是topic的名字得和你想接收的信息的topic一样!
  3. chatterCallback称为回调函数,接收器每一次接收到消息,就会调用名字为它的函数;一般命名为...Callback这样一看就知道这是ROS使用得回调函数
  4. std_msgs::String对象msg,类包含数据成员data,调用方式为msg.data。如果类的指针叫msg,那么调用该成员的方式是msg->data


ROS 编译程序

1、流程

  • 编辑CMakeLists文件(指定要编译的文件)
  • 再使用catkin_make编译

2、编译例子:

来到编写好程序的包目录中

cd ~/catkin_ws/src/pub_sub_test/

image.gif

编辑CMakeLists文件

gedit CMakeLists.txt

image.gif

在后面添加如下内容:

add_executable(pub_string src/pub_string.cpp)
target_link_libraries(pub_string ${catkin_LIBRARIES})

image.gif

第一行表示我们要编译add_executable表示我们要添加一个可执行文件,

pub_string是这个可执行文件的名字

src/pub_string.cpp指定要编译的源文件的位置.

第二行target_link_libraries表示我们要将可执行文件链接到一个库,我们要使用ROS当然是要链接到ROS的库了,

pub_string指定要链接可执行文件的名字,后面是指定要链接的库的名字.

来到工作空间中,执行catkin_make命令

cd ~/catkin_ws/
catkin_make

image.gif


执行ROS程序方法1:

方法1:

打开第一个terminal,执行roscore命令(打开rosmaster 服务器)

roscore

image.gif

roscore是为了让各种节点之间能够沟通用的

打开第二个terminal,进入工作空间,执行source devel/setup.bash 命令,

cd ~/catkin_ws/
source devel/setup.bash

image.gif

使用rosrun 命令运行程序(例如:执行pub_string.cpp):

rosrun pub_sub_test pub_string

image.gif

第一个参数:程序pub_string.cpp所在的包(package)的名字

第二个参数:运行程序的名称


方法2: 使用roslaunch来运行程序

特点:

可以便捷开启多个节点,自动开启rosmaster服务

区别:

    • 方法一在使用rosrun之前,我们一定得需要启动rosmaster,即开启一个窗口输入roscore,来开启rosmaster 服务器;
    • 运行roslaunch文件后rosmaster会自动启动.当然你关闭了roslaucn之后rosmaster也会关闭

    流程:

      1. 来到程序所在包的文件夹目录下,新建一个名字叫launch 的文件夹
      2. 在launch中建立的一个文件,名字可以随意,后缀必须是launch
      3. 编译程序
      4. 编辑launch文件
      5. 通过roslaunch,执行节点程序

      详细说明:

        1. 编辑launch文件

        格式: <

        launch>
            <node name="pub_string" pkg = "pub_sub_test" type = "pub_string" output = "screen">
            </node>
            <node name="pub_int8" pkg = "pub_sub_test" type = "pub_int8" output = "screen">
            </node>
        </launch>

        image.gif

        roslaunch使用的是xml语言,launch文件的内容是跑一个node简单的形式

        <launch>,<\launch>表示launch文件的开始和结束

        <node ....>表示接下来输入node相关的内容,比如说首先输入的是node的名字,这个东西一般和type后面输入的内容一样,

        type需要被赋值为节点对应的可执行文件的名字,

        name则是节点的名字.具体区别是你在CMakeLists.txt文件里编译文件的命令

        output = "screen"  设置通过print()输出的信息,打印到命令窗口中,默认时关闭的

        通过roslaunch,执行节点程序

        格式:

        关键字:roslaunch

        第一个参数: xx.launch 所在的包的名称

        第二个参数: 要执行的.launch 的文件



        ROS发布和接收图

        image.gif编辑

        rosbag

        简介

          1. rosbag 指命令行中数据包相关命令;
          2. rosbag 主要用于记录、回放、分析 rostopic 中的数据。它可以将指定 rostopic 中的数据记录到 .bag 后缀的数据包中,便于对其中的数据进行离线分析和处理。
          3. 对于 subscribe 某个 topic 的节点来说,它无法区分这个 topic 中的数据到底是实时获取的数据还是从 rosbag 中回放的数据。这就有助于我们基于离线数据快速重现曾经的实际场景,进行可重复、低成本的分析和调试。

          rosbag 的命令  

          image.gif编辑


          rosbag录制:

          录制所有发布出来的话题,此时默认将话题保存在一个以当时时间戳命名的文件夹中:

          $ rosbag record –a

          录制指定话题:

          $ rosbag record /topic1 /topic12


          rosbag回放:

          基本功能:

          $ rosbag play <your bagfile name>

          等待一定时间之后发布bag文件中的内容

          $ rosbag play <your bagfile name> -d <delay time>

          按一定频率回放,-r选项用来设定消息发布速率,如下面命令则表示以3倍原始速率发布话题

          $ rosbag play -r 3 <your bagfile name>

          回放指定话题:

          $ rosbag play <your bagfile name> --topics <topics>


          rosbag检查和回放

          1. rosbag info指令可以显示数据包中的信息:

          rosbag  info filename.bag

          这些信息包括 topic 的名称、类型和 message 数量。

          2) 接下来回放数据包中的 topic。

          首先在turtle_teleop_key 所在的终端窗口中按Ctrl+C退出该键盘控制节点。保留turtlesim节点继续运行。在终端中bag文件所在目录下运行以下命令:

          rosbag play <bagfile>

          就能够回放出 bag 中包含的 topic 内容了。


          如果想改变消息的发布速率,可以用下面的命令

          rosbag play -r 2 <bagfile>

          这时的轨迹相当于以两倍的速度通过按键发布控制命令时产生的轨迹。 -r 后面的数字对应播放速率。


          如果希望 rosbag 循环播放,可以用命令

          rosbag play -l  <bagfile>  # -l == --loop


          如果只播放感兴趣的 topic ,则用命令

          rosbag play <bagfile> --topic /topic1

          在上述播放命令执行期间,空格键可以暂停播放。


          CMakeLists

          简介:

          ROS中创建软件包所依赖的文件CMakeList.txtcatkin_make会根据你写的CMakeList.txt来配置编译软件包。

          格式:

          所需CMake版本                  cmake_minimum_required(VERSION 2.8.3)

          软件包名称                         project()

          查找构建此包所需的包      find_package()

          消息 / 服务 / 动作生成器     add_message_files(),add_service_files(),add_action_files

          消息 / 服务 / 动作生成        generate_messages()

          指定包构建的消息导出        catkin_package()

          要建立的库 / 可执行文件   add_library() / add_executable() / target_link_libraries())

          例如:

          image.gif编辑

          其中:add_executable(read_param src/show_param.cpp)和target_link_libraries(read_param ${catkin_LIBRARIES})  是新添加的,指定可执行文件

          详细解释:

          1.所需CMake版本

          catkin_make的底层是使用cmake进行编译的,这里指定cmake的版本(最低版本)

          2.软件包名称

          project(package_name)

          在使用catkin_create_pkg创建包时,后面跟的参数(包名)就是此处的package_name

          在CMakeList.txt后面的部分可以使用 ${PROJECT_NAME} 来使用此参数

          3. 查找此包创建时所需要的其它包(依赖包)

          可以将所依赖的包写成下面的形式:

          find_package(catkin REQUIRED)

          find_package(roscpp REQUIRED)

          find_package(rospy REQUIRED)

          find_package(std_msgs REQUIRED)

          其中catkin是创建每个包所必须的依赖项,创建包时所依赖的其它项又可以将其组成catkin的组件,所以上面可以总写为:

          find_package(catkin REQUIRED COMPONENTS

           roscpp

           rospy

           std_msgs

          )

          上面中的roscpprospystd_msgs 是运行程序所需要的依赖包

          roscpp: 用C++ 语言进行ros开发要用到的包

          rospy: 用python 语言进行ros开发要用到的包

          std_msgs: 基本的数据类型int stringfloat、double等的依赖包

          4.add_message_files()

          像message service 和action的定义需要在catkin_package()之前

          ## Generate messages in the 'msg' folder

          # add_message_files(

          #   FILES

          #   Message1.msg

          #   Message2.msg

          # )

          比如:

          add_message_files(

           FILES Num1.msg Num2.msg

          )

          5.catkin_package()

          用来向编译系统指明catkin-specific的信息,格式如下:

          catkin_package(

            INCLUDE_DIRS include  # 此项打开之后该软件包的include文件可以被其它包所引用

            LIBRARIES ${PROJECT_NAME} #同理

            CATKIN_DEPENDS roscpp nodelet

            DEPENDS eigen opencv)

          6.包含文件目录

          include_directories(include ${catkin_INCLUDE_DIRS})  

          其中include是指包含本软件包下的头文件, ${catkin_INCLUDE_DIRS}是指ROS下其它包的头文件,include需要写在${catkin_INCLUDE_DIRS}前面。

          7.生成可执行文件

          #其中talker为将要生成的二进制文件,在ros所有的包中必须是独一无二不能重复的,src/talker.cpp为需要编译的源文件

          add_executable(talker src/talker.cpp)  

          target_link_libraries(talker ${catkin_LIBRARIES})

          生成的可执行文件会存放在./devel/lib/*pack_name*/下:


          问题与解决方案总结

          缺失依赖库

          image.gif编辑

          解决思路:

          1.在错误原因中,找到错误代码端,分析,确定缺失的包

          2.在程序所在包下,添加依赖包

          添加依赖包,详细说明:

          思路:

          1. 一个是包目录下的的CMakeLists.txt,添加依赖包
          2. 然后在位于同一位置的package.xml中,添加添加依赖包

          例如

          1. 打开的CMakeLists.txt,发现就在最前面几行,有下面的内容。
          find_package(catkin REQUIRED COMPONENTS
            roscpp
            rospy
            std_msgs
          )

          image.gif

          括号中的内容正好一一对应我们创建包时添加的依赖项,在后面添加geometry_msgs,变成下面的样子,保存退出。

          find_package(catkin REQUIRED COMPONENTS
            roscpp
            rospy
            std_msgs
            geometry_msgs
          )

          image.gif

           

          打开位于同一目录下的package.xml

          <build_depend>roscpp</build_depend>
           <build_depend>rospy</build_depend>
            <build_depend>std_msgs</build_depend>
            <build_export_depend>roscpp</build_export_depend>
            <build_export_depend>rospy</build_export_depend>
            <build_export_depend>std_msgs</build_export_depend>
            <exec_depend>roscpp</exec_depend>
            <exec_depend>rospy</exec_depend>
            <exec_depend>std_msgs</exec_depend>

          image.gif

          发现std_msgs, rospy, roscpp,每个出现了三次,所以只我们需要按照这个文档里相同的语法让geometry_msgs出现三次就行了。更改之后该文件同样位置变成下面的内容:

          <build_depend>roscpp</build_depend>
            <build_depend>rospy</build_depend>
            <build_depend>std_msgs</build_depend>
            <build_depend>geometry_msgs</build_depend>
            <build_export_depend>roscpp</build_export_depend>
            <build_export_depend>rospy</build_export_depend>
            <build_export_depend>std_msgs</build_export_depend>
            <build_export_depend>geometry_msgs</build_export_depend>
            <exec_depend>roscpp</exec_depend>
            <exec_depend>rospy</exec_depend>
            <exec_depend>std_msgs</exec_depend>
            <exec_depend>geometry_msgs</exec_depend>
            <exec_depend>geometry_msgs</exec_depend>

          image.gif

          保存退出。这时候再用catkin_make编译,就成功了。改变上面两个文档的内容就相当于我们在创建包时添加了依赖项geometry_msgs。


          catkin_make编译错误

          问题:

          Could not find a package configuration file provided by

           "gazebo_ros_control" with any of the following names:

             gazebo_ros_controlConfig.cmake

          gazebo_ros_control-config.cmake

          分析

          提示缺少“gazebo_ros_control”功能包,

          解决方案:

          sudo apt-get install ros-kinetic-gazebo-ros-control

            问题:

            Could not find a package configuration file provided by "move_base_msgs"

             with any of the following names:

               move_base_msgsConfig.cmake

            move_base_msgs-config.cmake

            分析

            提示缺少“gazebo_ros_control”功能包

            解决方案:

            sudo apt-get install ros-kinetic-move-base-msgs

              问题:

              alsa/asoundlib.h: No such file or directory

              分析

              缺少一个库,libasound2-dev

              解决方案:

              sudo apt-get install libasound2-dev

              希望对你有帮助。

              相关实践学习
              使用ROS创建VPC和VSwitch
              本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
              阿里云资源编排ROS使用教程
              资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 产品详情:https://www.aliyun.com/product/ros/
              相关文章
              |
              17天前
              |
              Ubuntu 机器人 Linux
              |
              3天前
              |
              前端开发 测试技术 调度
              移动应用与系统:探索开发与操作系统的奥秘####
              【10月更文挑战第22天】 本文深入剖析了移动应用的开发流程与移动操作系统的核心原理,揭示了两者如何相互依存、共同推动移动互联网的发展。从应用架构设计到操作系统性能优化,全方位解读移动生态的技术细节,为开发者和用户提供有价值的参考。 ####
              12 5
              |
              1天前
              |
              搜索推荐 前端开发 测试技术
              移动应用与系统:探索开发之道与操作系统的演进#### 一、
              【10月更文挑战第24天】 本文将带你深入探索移动应用开发的全过程,从构思到上架的每一个细节。同时,我们还将回顾移动操作系统的发展历程,分析当前主流系统的技术特点和未来趋势。无论你是开发者还是普通用户,都能在这里找到感兴趣的内容。 #### 二、
              8 1
              |
              5天前
              |
              安全 Android开发 数据安全/隐私保护
              移动应用与系统:探索开发趋势与操作系统革新#### 一、
              【10月更文挑战第20天】 本文旨在剖析当前移动应用开发的热门趋势,并探讨移动操作系统的最新进展与未来展望。通过梳理从原生应用到跨平台开发的转变,以及主流操作系统如iOS和Android的技术创新,本文为开发者提供了一份详尽的行业指南,助力他们在快速迭代的移动科技领域保持领先。 #### 二、
              18 2
              |
              10天前
              |
              传感器 机器人 数据处理
              ROS 编程入门的介绍
              【10月更文挑战第13天】ROS(Robot Operating System)是一种开源的机器人软件框架,广泛用于机器人开发中。通过使用 ROS,开发者可以轻松创建和管理机器人应用程序。在本节中,我们将介绍如何创建一个 ROS 功能包并实现一些基本功能。
              53 3
              |
              25天前
              |
              安全 测试技术 数据库
              Python编程--sys模块及OS模块简单用例
              Python编程--sys模块及OS模块简单用例
              16 1
              |
              17天前
              |
              传感器 数据可视化 机器人
              【ROS速成】半小时入门机器人ROS系统简明教程之可视化系统(三)
              半小时入门机器人ROS系统简明教程之可视化系统
              |
              17天前
              |
              机器人
              【ROS速成】半小时入门机器人ROS系统简明教程之安装测速(二)
              半小时入门机器人ROS系统简明教程之安装测速
              |
              18天前
              |
              算法 调度 UED
              探索操作系统中的多线程编程
              【8月更文挑战第78天】在数字世界的复杂迷宫中,操作系统扮演着至关重要的角色。本文旨在揭开操作系统中多线程编程的神秘面纱,引导读者理解其概念、实现及应用。通过深入浅出的方式,我们将探讨如何在程序设计中运用多线程,以及这一技术如何优化软件性能和提升用户体验。文章将结合具体代码示例,展示多线程在实际应用中的魔力。无论你是编程新手还是资深开发者,这篇文章都将为你提供新的视角和思考路径。
              |
              23天前
              |
              人工智能 安全 机器人
              Dify开发Agent对接钉钉机器人
              这篇文章详细讲解了如何在Dify平台上开发一个Agent并与钉钉机器人集成,实现自动化消息处理和响应功能。
              112 0