ROS学习笔记

一.创建 ROS 工作空间

mkdir -p xxx_ws/src(必须得有 src)
cd xxx_ws
catkin_make

1.进入 xxx_ws启动 vscode

code .

快捷键 ctrl + shift + B 调用编译,选择:catkin_make:build

可以点击配置设置为默认,修改.vscode/tasks.json 文件

{
// 有关 tasks.json 格式的文档,请参见
    // https://go.microsoft.com/fwlink/?LinkId=733558
    "version": "2.0.0",
    "tasks": [
        {
            "label": "catkin_make:debug", //代表提示的描述性信息
            "type": "shell",  //可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
            "command": "catkin_make",//这个是我们需要运行的命令
            "args": [],//如果需要在命令后面加一些后缀,可以写在这里,比如-DCATKIN_WHITELIST_PACKAGES=“pac1;pac2”
            "group": {"kind":"build","isDefault":true},
            "presentation": {
                "reveal": "always"//可选always或者silence,代表是否输出信息
            },
            "problemMatcher": "$msCompile"
        }
    ]
}

2.创建 ROS 功能包

选定 src 右击 ---> create catkin package

设置包名 添加依赖

roscpp rospy std_msgs

3.使用C++编写程序实现

1.进入 ros 包的 src 目录编辑源文件
cd 自定义的包
2.创建文件.cpp C++源码实现(文件名自定义)
#include "ros/ros.h"

int main(int argc, char *argv[])
{
    //执行 ros 节点初始化
    ros::init(argc,argv,"hello");
    //创建 ros 节点句柄(非必须)
    ros::NodeHandle n;
    //控制台输出 hello world
    ROS_INFO("hello world!");

    return 0;
}

4.编辑 ros 包下的 Cmakelist.txt文件

add_executable(步骤3的源文件名
  src/源文件名.cpp
)
target_link_libraries(源文件名
  ${catkin_LIBRARIES}
)

5.进入工作空间目录并编译

cd 自定义空间名称
catkin_make

生成 build devel ....

6.执行

先启动命令行1:

roscore

再启动命令行2:

cd 工作空间
source ./devel/setup.bash
rosrun 包名 C++节点

2.话题通信概念**

以发布订阅的方式实现不同节点之间数据交互的通信模式。

作用

用于不断更新的、少逻辑处理的数据传输场景。

  1. 1.编写发布方实现;

    在功能包下的src目录下创建.cpp

    .包含头文件 
    #include "ros/ros.h"
    #include "std_msgs/String.h" //普通文本类型的消息
    #include <sstream>
    
    int main(int argc, char  *argv[])
    {   
        //设置编码
        setlocale(LC_ALL,"");
    
        //2.初始化 ROS 节点:命名(唯一)
        // 参数1和参数2 后期为节点传值会使用
        // 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
        ros::init(argc,argv,"talker");
        //3.实例化 ROS 句柄
        ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能
    
        //4.实例化 发布者 对象
        //泛型: 发布的消息类型
        //参数1: 要发布到的话题
        //参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
        ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);
    
        //5.组织被发布的数据,并编写逻辑发布数据
        //数据(动态组织)
        std_msgs::String msg;
        // msg.data = "你好啊!!!";
        std::string msg_front = "Hello 你好!"; //消息前缀
        int count = 0; //消息计数器
    
        //逻辑(一秒10次)
        ros::Rate r(1);
    
        //节点不死
        while (ros::ok())
        {
            //使用 stringstream 拼接字符串与编号
            std::stringstream ss;
            ss << msg_front << count;
            msg.data = ss.str();
            //发布消息
            pub.publish(msg);
            //加入调试,打印发送的消息
            ROS_INFO("发送的消息:%s",msg.data.c_str());
    
            //根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;
            r.sleep();
            count++;//循环结束前,让 count 自增
            //暂无应用
            ros::spinOnce();
        }
    
    
        return 0;
    }
    
2. 配置 CMakeLists.txt
add_executable(Hello_pub
  src/Hello_pub.cpp
)
target_link_libraries(Hello_pub
  ${catkin_LIBRARIES}
)

三、话题通信自定义msg调用(C++)

0.vscode 配置

将前面生成的 head 文件路径配置进 c_cpp_properties.json 的 includepath属性:

"/xxx/yyy工作空间/devel/include/**" //配置 head 文件的路径 

1.发布方创建

#include "ros/ros.h"
#include "demo02_talker_listener/Person.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");

    //1.初始化 ROS 节点
    ros::init(argc,argv,"talker_person");

    //2.创建 ROS 句柄
    ros::NodeHandle nh;

    //3.创建发布者对象
    ros::Publisher pub = nh.advertise<demo02_talker_listener::Person>("chatter_person",1000);

    //4.组织被发布的消息,编写发布逻辑并发布消息
    demo02_talker_listener::Person p;
    p.name = "sunwukong";
    p.age = 2000;
    p.height = 1.45;

    ros::Rate r(1);
    while (ros::ok())
    {
        pub.publish(p);
        p.age += 1;
        ROS_INFO("我叫:%s,今年%d岁,高%.2f米", p.name.c_str(), p.age, p.height);

        r.sleep();
        ros::spinOnce();
    }



    return 0;
}
注:订阅编写逻辑和发布一致

2.配置 CMakeLists.txt

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

 CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
 
add_executable(person_talker src/person_talker.cpp)

add_dependencies(person_talker ${PROJECT_NAME}_generate_messages_cpp)

target_link_libraries(person_talker
  ${catkin_LIBRARIES}
)

3执行话题:

1.启动 roscore;

roscore

2.启动发布节点;

cd xx_ws

source ./devel/setup.bash 

rosrun plumbing_pub_sub demo03_pub_person

3.启动订阅节点。

rosrun plumbing_pub_sub demo04_sub_person

启动节点信息图:

rqt_graph 

二.服务通信的创建

在src目录下创建功能包,功能包下新建 srv 目录,添加 xxx.srv 文件 src->plumbing_server_client->srv->Addints.srv

1.内容:

srv 文件中请求和响应使用---分割

# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum

2.编辑配置文件

package.xml中添加编译依赖与执行依赖

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

CMakeLists.txt编辑 srv 相关配置

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs
add_service_files(
  FILES
  AddInts.srv
)
generate_messages(
  DEPENDENCIES
  std_msgs
)

服务通信自定义srv调用

0.配置vscode

/demo03_ws/devel/include/piumbing_server_client$

pwd

在配置c_cpp_properies.json 文件,把上一步打印出的路径复制进来即可。

1.创建服务端文件:

在功能包下src目录下创建demo01_server.cpp文件

/*
    需求: 
        编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
        服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
        客户端再解析

    服务器实现:
        1.包含头文件
        2.初始化 ROS 节点
        3.创建 ROS 句柄
        4.创建 服务 对象
        5.回调函数处理请求并产生响应
        6.由于请求有多个,需要调用 ros::spin()

*/
#include "ros/ros.h"
#include "demo03_server_client/AddInts.h"

// bool 返回值由于标志是否处理成功
bool doReq(demo03_server_client::AddInts::Request& req,
          demo03_server_client::AddInts::Response& resp){
    int num1 = req.num1;
    int num2 = req.num2;

    ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2);

    //逻辑处理
    if (num1 < 0 || num2 < 0)
    {
        ROS_ERROR("提交的数据异常:数据不可以为负数");
        return false;
    }

    //如果没有异常,那么相加并将结果赋值给 resp
    resp.sum = num1 + num2;
    return true;


}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"AddInts_Server");
    // 3.创建 ROS 句柄
    ros::NodeHandle nh;
    // 4.创建 服务 对象
    ros::ServiceServer server = nh.advertiseService("AddInts",doReq);
    //ros打印语句
    ROS_INFO("服务已经启动....");
    //     5.回调函数处理请求并产生响应
    //     6.由于请求有多个,需要调用 ros::spin()
    ros::spin();
    return 0;
}

2.配置 CMakeLists.txt

add_executable(demo01_server src/demo01_server.cpp)

add_dependencies(demo01_server ${PROJECT_NAME}_gencpp)

target_link_libraries(demo01_server
  ${catkin_LIBRARIES}
)

同理穿件客户端消息

3.执行

流程:

  • 需要先启动服务:rosrun 包名 服务
  • 然后再调用客户端 :rosrun 包名 客户端 参数1 参数2
  • 测试服务端消息 在ws中 rosserver call addints 加tab补齐

常用命令

  • rosnode : 操作节点
  • rostopic : 操作话题
  • rosservice : 操作服务
  • rosmsg : 操作msg消息
  • rossrv : 操作srv消息
  • rosparam : 操作参数

image-20210924215636228

绕Z:偏航

绕X:翻滚

绕Y:俯仰

话题订阅配置文件:

src>plumbing>package.xml

 <buildtool_depend>catkin</buildtool_depend>
  <build_depend>geometry_msgs</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>turtlesim</build_depend>
  <build_export_depend>geometry_msgs</build_export_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>geometry_msgs</exec_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>turtlesim</exec_depend>

实现简单的服务调用:cly@cly-virtual-machine:~$ rosservice call /spawn

常用的api

ros::init_options::AnonymousName 解决节点重命名问题

latch(可选),如果设置为ture,会保存发布方的最后一条信息,并且新的订阅对象连接到发布方时,发布方会将这条信息发送给订阅者。

在导航订阅中,可以将地图发布对象的latch设置为ture,并且发布方只发布一次数据,每当订阅者连接时,将地图数据发送给订阅者(只发布一次),提高数据的发送效率。

C++

1.时刻

获取时刻,或是设置指定时刻:

注意:1,时刻与持续时间可以执行加减2,持续时间之间也可以执行加减3.时刻之间值可以相减,不可以相加

ros::init(argc,argv,"hello_time");
ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
ros::Time right_now = ros::Time::now();//将当前时刻封装成对象
//获取距离 1970年01月01日 00:00:00 的秒数,以此为时间参考系
ROS_INFO("当前时刻:%.2f",right_now.toSec());
ROS_INFO("当前时刻:%d",right_now.sec);//获取距离 1970年01月01日 00:00:00 的秒数

//设置时间
ros::Time t1(100,100000000);// 参数1:秒数  参数2:纳秒
ROS_INFO("t1=%.2f",t1.toSec()); //100.10
//第2种:直接传入 double 类型的秒数
ros::Time t2(100.3);
ROS_INFO("t2=%.2f",t2.toSec()); //100.30

//3.持续时间
ros::Time start = ros::Time::now();
ROS_INFO("当前时刻:%.2f",start.toSec());
ros::Duration du(10);//持续10秒钟,参数是double类型的,以秒为单位
du.sleep();//按照指定的持续时间休眠
ros::Time end = ros::Time::now();
ROS_INFO("当前时刻:%.2f",end.toSec());

//ROS中提供了时间与时刻的运算:
ros::Time begin= ros::Time::now();
ros::Duration du1(10);
//ros::Duration du2(20);
  //ROS_INFO("当前时刻:%.2f",now.toSec());
 //1.time 与 duration 运算
ros::Time stop = begin + du1;
    //ros::Time stop = now - du1;
ROS_INFO("当前时刻之前:%.2f",begin.toSec());
ROS_INFO("当前时刻之后:%.2f",stop.toSec());
//PS: time 与 time 不可以相加运算

ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:

//回调函数
void cd(const ros::TimerEvent& event){
    ROS_INFO("-------------");

ROS_INTO("-----定时器-----");
 ros::Timer timer = nh.createTimer(ros::Duration(0.5),cd);//只执行一次
 ros::spin();

ROS中的源文件的调用

自定义源文件的配置:
//118
include_directories(
include
  ${catkin_INCLUDE_DIRS}
)
//123
add_library(head_src
  include/${PROJECT_NAME}/head.h
  src/hello.cpp
)

//137
add_executable(use_hello src/ues_hello.cpp)

//147
add_dependencies(head_src ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(use_hello ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
target_link_libraries(head_src
  ${catkin_LIBRARIES}
)
target_link_libraries(use_hello
  head_src
  ${catkin_LIBRARIES}
)

4.ROS元功能包

首先:新建一个功能包,不需要添加依赖

然后:修改package.xml ,内容如下:

 <exec_depend>被集成的功能包</exec_depend>
 .....
 <export>
   <metapackage />
 </export>

最后:修改 CMakeLists.txt,内容如下:

cmake_minimum_required(VERSION 3.0.2)
project(demo)
find_package(catkin REQUIRED)
catkin_metapackage()//这个是函数的调用

注:CMakeLists.txt 中不可以有换行。把注释全部删了,只留这四行。

5.1.2 静态坐标变换

1.创建功能包

创建项目功能包依赖于

tf2 tf2_ros tf2_geometry_msgs roscpp rospy std_msgs geometry_msgs

查看方式:1.rviz

rostopic list

rostopic echo /tf_static 

动态坐标转换:打开小乌龟的节点

先订阅后发布。

//计算son1和son2的相对关系,函数为lookupTransform
参数1:目标坐标系
参数2:源坐标系
参数3:ros::Time(0)  取间隔最短的两个坐标关系帧计算的相对关系
返回值geometry_msgs::TransformStamped 源相对于目标坐标系的相对关系

5.2 rosbag

在ROS中关于数据的留存以及读取实现,提供了专门的工具: rosbag。

gazebo 启动异常以及解决

  • 问题1:VMware: vmw_ioctl_command error Invalid argument(无效的参数)

    解决:

    echo "export SVGA_VGPU10=0" >> ~/.bashrc

    source .bashrc

  • 问题2:[Err] [REST.cc:205] Error in REST request

    解决:sudo gedit ~/.ignition/fuel/config.yaml

    然后将url : https://api.ignitionfuel.org使用 # 注释

    再添加url: https://api.ignitionrobotics.org

  • 问题3:启动时抛出异常:[gazebo-2] process has died [pid xxx, exit code 255, cmd.....

    解决:killall gzserverkillall gzclient

image-20211012214751699

6.2 URDF集成Rviz基本流程

实现流程:

  1. 准备:新建功能包,导入依赖
  2. 核心:编写 urdf 文件
  3. 核心:在 launch 文件集成 URDF 与 Rviz
  4. 在 Rviz 中显示机器人模型
T1、解决urdf中含有中文字符导致出现:UnicodeEncodeError: ‘ascii‘ codec can‘t encode characters in position:

在根目录下:/opt/ros/melodic/lib/python2.7/dist-packages

新建一个sitecustomize.py文件

cd /opt/ros/melodic/lib/python2.7/dist-packages
sudo gedit sitecustomize.py

在其中添加内容:

#coding=utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')

重启ROS即可解决。

先写urdf 再配置launch

<mesh filename="package://urdf01_rviz/meshes/xxx.stl"/>

image-20211015110326772

6.6.1 URDF与Gazebo基本集成流程

urdf xacro gazebo_ros gazebo_ros_control gazebo_plugins

7.2.1 导航实现01_SLAM建图

安装相关功能包:

sudo apt install ros-melodic-gmapping
sudo apt install ros-melodic-map-server
sudo apt install ros-melodic-navigation

tupain

posted @ 2021-11-01 11:08  骑蜗牛环球  阅读(289)  评论(0)    收藏  举报