ros学习笔记(12.6实时更新中)

0.引言

关于深度学习的学习不能仅仅顾着一个CV领域,也需要很多配套的知识,比如图像学、图像处理、信号处理、图像编码、web、应用端;硬件部分则是ROS、slam、plc、嵌入式
作为一个专硕,没必要太深耕课题,提高知识的广度更有必要,故现在开始学习
大部分内容是基于赵虚左老师的视频和讲义:ros赵虚左

1.安装和配置环境

1.1虚拟机

ros、slam的一些项目大都是基于linux系统的,所以要不双系统要不使用虚拟机,这里建议使用甲骨文的virtualbox,免费开源而且比vm ware轻量化和快捷。然后配置ubuntu的系统,现在主流的Linux系统就是ubuntu。网上有很多现成的教程,记得安装virtualbox tools

1.2ROS安装

首先需要更新 aptapt 是用于从互联网仓库搜索、安装、升级、卸载软件或操作系统的工具。类似于pip和conda

sudo apt update

然后调用,意思是按照官方推荐的配置安装桌面化的ros

sudo apt install ros-noetic-desktop-full

太卡的化记得换源,阿里、豆瓣、中科、清华等等

sudo sh -c ‘. /etc/lsb-release && echo “deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ lsb_release -cs main” > /etc/apt/sources.list.d/ros-latest.list’

1.3测试

在linux里启动三个终端,分别输入:
命令行1键入:roscore 意味着启动ros核心,不管什么ros程序,cpp、py的都需要启用ros

命令行2键入:rosrun turtlesim turtlesim_node(此时会弹出图形化界面)
rosrun xx yy相当于python xx,其中xx代表功能包名或者可以理解为项目名,在这里turtlesim代表我们使用龟龟这个包,yy代表程序名或者可以理解为功能名或者节点名(节点在ros很重要,相当于类,但我感觉ros是面向过程的,结点只相当于一个功能的触发器),在这里turtlesim_node代表召唤乌龟。

命令行3键入:rosrun turtlesim turtle_teleop_key
同上,只是turtle_teleop_key代表键盘输入,可以控制龟龟行走。
三个命令虽然是顺序输入的,但是在terminal中耦合性太强了,所以要三个终端来分别控制。后面学习了launch之后就好了一些,launch就是类似bash。
在这里插入图片描述

2.通信

三种通信模式:话题通信(发布订阅模式),服务通信(请求响应模式),参数服务器(参数共享模式)

2.1 话题通信

在这里插入图片描述
最简单的握手:发布方先发布消息,然后接收方再接受消息,接收方没接受的话,发布的消息放在队列中等待,队列前的先销毁。
发布方:

/*
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)

         PS: 二者需要设置相同的话题


    消息发布方:
        循环发布信息:HelloWorld 后缀数字编号

    实现流程:
        1.包含头文件 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 发布者 对象
        5.组织被发布的数据,并编写逻辑发布数据

*/
// 1.包含头文件 
#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;
}

接收方

/*
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)


    消息订阅方:
        订阅话题并打印接收到的消息

    实现流程:
        1.包含头文件 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 订阅者 对象
        5.处理订阅的消息(回调函数)
        6.设置循环调用回调函数

*/
// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h"

void doMsg(const std_msgs::String::ConstPtr& msg_p){
    ROS_INFO("我听见:%s",msg_p->data.c_str());
    // ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}
int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"listener");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;

    //4.实例化 订阅者 对象
    ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
    //5.处理订阅的消息(回调函数)

    //     6.设置循环调用回调函数
    ros::spin();//循环读取接收的数据,并调用回调函数处理

    return 0;
}

在执行cpp的程序时候,一定要先把配置文件给写好,先编译再运行。

2.2msg打包

不打包的话,使用std_msgs只能使用单一数据类型的字段
ros::Subscriber sub = nh.subscribe<std_msgs::String>(“chatter”,10,doMsg);
ros::Publisher pub = nh.advertise<std_msgs::String>(“chatter”,10);

3.Ros2

一年后终于诈尸更新了
参考:鱼香ros2

3.1ros2和ros1的区别:

1.没有主节点

ros1中的Master节点会将一个系统中的所有功能连接在一起,所有子功能节点的通讯必须要通过Master,当主节点出错时所有功能全部失效。
在这里插入图片描述

2.操作系统

从原来的只支持linux平台变成了支持windows、mac

3.通讯系统

ros1的通讯是基于TCP实现,实时性差、系统开销大,ros2通信直接更换为DDS进行实现

4.应用适配性

Python2到Python3的支持
编译系统的改进(catkin到ament)
C++标准更新到c++11
可以使用相同 API 的进程间和进程内通信

3.2安装

fishros给直接打包了一整套系统,使用指令可以一键安装:

wget http://fishros.com/install -O fishros && . fishros

每次启动终端都自动执行ros的setup.bash,好让系统的环境里有ros2

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc

3.2.2 demo_test

起一个terminal,并且在终端输入

ros2 run demo_nodes_py listener #起一个叫listener 的节点

再起一个terminal输入

ros2 run demo_nodes_cpp talker #起一个叫talker 的节点

3.3g++编译cpp

3.3.1使用make编译
mkdir -p d2lros2/chapt2/basic/ #新建空间
code d2lros2 #vscode打开这个空间
在vscode中新建.cpp并且编写代码
g++ first_ros2_node.cpp 编译.cpp

g++有很多库和头文件找不到,所以在编译的时候要强调部分环境的位置,可以使用Make进行批量化处理cpp文件来生成可执行文件。

安装make

sudo apt install make

在d2lros2/d2lros2/chapt2/basic下新建Makefile,然后将上面的g++编译指令用下面的形式写到Makefile里。
vim Makefile

build:
    g++ first_ros2_node.cpp \
    -I/opt/ros/humble/include/rclcpp/ \
    -I /opt/ros/humble/include/rcl/ \
    -I /opt/ros/humble/include/rcutils/ \
    -I /opt/ros/humble/include/rmw \
    -I /opt/ros/humble/include/rcl_yaml_param_parser/ \
    -I /opt/ros/humble/include/rosidl_runtime_c \
    -I /opt/ros/humble/include/rosidl_typesupport_interface \
    -I /opt/ros/humble/include/rcpputils \
    -I /opt/ros/humble/include/builtin_interfaces \
    -I /opt/ros/humble/include/rosidl_runtime_cpp \
    -I /opt/ros/humble/include/tracetools \
    -I /opt/ros/humble/include/rcl_interfaces \
    -I /opt/ros/humble/include/libstatistics_collector \
    -I /opt/ros/humble/include/statistics_msgs \
    -L /opt/ros/humble/lib/ \
    -lrclcpp -lrcutils \
    -o first_node
    
# 顺便小鱼加个clean指令,用来删掉first_node
clean:
    rm first_node  

保存后执行在终端输入make build来执行make的批处理编译,执行make clean可删掉first_node节点

3.3.2使用cmake编译

之前的makefile文件内部的头文件和库文件都是自己根据报错添加上的,cmake可以自动生成makefile不用一个个写环境依赖
安装:

sudo apt install cmake

在d2lros2/d2lros2/chapt2/basic新建CMakeLists.txt,输入下面内容。

cmake_minimum_required(VERSION 3.22)

project(first_node)

#include_directories 添加特定的头文件搜索路径 ,相当于指定g++编译器的-I参数
include_directories(/opt/ros/humble/include/rclcpp/)
include_directories(/opt/ros/humble/include/rcl/)
include_directories(/opt/ros/humble/include/rcutils/)
include_directories(/opt/ros/humble/include/rcl_yaml_param_parser/)
include_directories(/opt/ros/humble/include/rosidl_runtime_c/)
include_directories(/opt/ros/humble/include/rosidl_typesupport_interface/)
include_directories(/opt/ros/humble/include/rcpputils/)
include_directories(/opt/ros/humble/include/builtin_interfaces/)
include_directories(/opt/ros/humble/include/rmw/)
include_directories(/opt/ros/humble/include/rosidl_runtime_cpp/)
include_directories(/opt/ros/humble/include/tracetools/)
include_directories(/opt/ros/humble/include/rcl_interfaces/)
include_directories(/opt/ros/humble/include/libstatistics_collector/)
include_directories(/opt/ros/humble/include/statistics_msgs/)

# link_directories - 向工程添加多个特定的库文件搜索路径,相当于指定g++编译器的-L参数
link_directories(/opt/ros/humble/lib/)

# add_executable - 生成first_node可执行文件
add_executable(first_node first_ros2_node.cpp)

# target_link_libraries - 为first_node(目标) 添加需要动态链接库,相同于指定g++编译器-l参数
# 下面的语句代替 -lrclcpp -lrcutils
target_link_libraries(first_node rclcpp rcutils)

也可以用下面的代买代替

cmake_minimum_required(VERSION 3.22)
project(first_node)

find_package(rclcpp REQUIRED)
add_executable(first_node first_ros2_node.cpp)
target_link_libraries(first_node rclcpp::rclcpp)

在中断中执行cmake和make build

3.4编译Python

vscode新建second_ros2_node.py

# 导入rclpy库,如果Vscode显示红色的波浪线也没关系
import rclpy
from rclpy.node import Node
# 调用rclcpp的初始化函数
rclpy.init() 
# 调用rclcpp的循环运行我们创建的second_node节点
rclpy.spin(Node("second_node"))

3.5ros工作空间

节点需要存在于功能包当中、功能包需要存在于工作空间当中。
**工作空间定义:**工作空间是包含若干个功能包的目录,一开始大家把工作空间理解成一个文件夹就行了。这个文件夹包含下有src。

cd d2lros2/chapt2/
mkdir -p chapt2_ws/src

ROS工作空间是具有特定结构的目录,一般为以下结构:

.
├── build
├── install
├── log
└── src

4 directories, 0 files

3.5功能包的构建

**功能包定义:**功能包可以理解为存放节点的地方,就是function的集合
使用colcon对功能包进行构建

cd d2lros2/chapt2/
mkdir colcon_test_ws 
cd colcon_test_ws
git clone https://github.com/ros2/examples src/examples -b humble #下载示例代码
colcon build #在colcon_test_ws目录下对examples进行编译

编译完成后会获得工作空间:build install log src

3.6节点构造

cpp

因为节点是工作空间下的功能包中的一部分,所以我们要想创建节点,就要先创建一个工作空间,再创建功能包。
创建工作空间:

cd d2lros2/chapt2/
mkdir -p chapt2_ws/src/

创建功能包:

cd chapt2_ws/src
ros2 pkg create example_cpp --build-type ament_cmake --dependencies rclcpp

–build-type 用来指定该包的编译类型,一共有三个可选项ament_python、ament_cmake、cmake
–dependencies 指的是这个功能包的依赖,这里给了一个ros2的C++客户端接口rclcpp

得到如下目录结构:

.
└── src
└── example_cpp
├── CMakeLists.txt
├── include
│ └── example_cpp
├── package.xml
└── src

py

同理先构建工作空间和创建功能包:

cd chapt2/chapt2_ws/src/
ros2 pkg create example_py  --build-type ament_python --dependencies rclpy

会获得如下目录结构

.
├── example_py
│ └── init.py
├── package.xml
├── resource
│ └── example_py
├── setup.cfg
├── setup.py
└── test
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py

4.通信机制

在DDS中,不同逻辑网络共享物理网络的主要机制称为域(Domain) ID。同一域上的ROS 2节点可以自由地相互发现并发送消息,而不同域上的ROS 2节点则不能。所有ROS 2节点默认使用域ID为0。为了避免在同一网络上运行ROS 2的不同计算机组之间互相干扰,应为每组设置不同的域ID。

域选择1到101是肯定安全的

默认情况下,linux内核使用端口32768-60999作为临时端口。这意味着域ID 0-101 和 215-232 可以安全使用,而不会与临时端口发生冲突。临时端口范围可在Linux中通过在 /proc/sys/net/ipv4/ip_local_port_range 中设置自定义值进行配置。如果使用自定义临时端口范围,则可能需要相应地调整上述数字。
知道端口号和域号可以计算出udp端口号,计算公式如下
Discovery Multicast Port: dmp = 7400 + domain_id * 250
User Multicast Port: ump = dmp + 1
Discovery Unicast Port: dup = dmp + 10 + participant_id * 2
User Unicast Port: uup = dup + 1

域ID到UDP端口号计算器:

http://dev.ros2.fishros.com/doc/Concepts/About-Domain-ID.html#domain-id-to-udp-port-calculator

4.话题通信

话题是ros四大同时方式之一,是最常用的通信方式,话题通信采取的是订阅发布模型。
一个节点发布数据到某个话题上,另外一个节点就可以通过订阅话题拿到数据。
在这里插入图片描述
实例:控制节点创建一个话题发布者,发布控制命令(command)话题,接口类型为字符串(string),控制接点通过发布者发布控制命令(前进、后退、左转、右转、停止)。
在这里插入图片描述

4.1创建功能包和节点

首先创建对话的功能包:

cd chapt3/chapt3_ws/src/
ros2 pkg create example_topic_rclpy  --build-type ament_python --dependencies rclpy

创建节点:touch

cd example_topic_rclpy/example_topic_rclpy
touch topic_subscribe_02.py
touch topic_publisher_02.py

目录结构

.
└── src
└── example_topic_rclcy
├── CMakeLists.txt
├── include
│ └── example_topic_rclpy
└── topic_subscribe_02.py
└── topic_publisher_02.py
├── package.xml
└── src

发布者代码:

from std_msgs.msg import String
import rclpy
from rclpy.node import Node

class NodePublisher02(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info("大家好,我是%s!" % name)
        self.command_publisher_ = self.create_publisher(String,"command", 10) 
        self.timer = self.create_timer(0.5, self.timer_callback)
    
    def timer_callback(self):
        """
        定时器回调函数
        """
        msg = String()
        msg.data = 'backup'
        self.command_publisher_.publish(msg) 
        self.get_logger().info(f'发布了指令:{msg.data}')    #打印一下发布的数据
def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = NodePublisher02("topic_publisher_01")  # 新建一个节点
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

self.create_publisher(String, “sexy_girl”, 10):第一个参数为消息类型,第二个参数为话题的名称,第三个参数为队列长度,
订阅者代码

from std_msgs.msg import String
import rclpy
from rclpy.node import Node

class NodeSubscribe02(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info("大家好,我是%s!" % name)
        # 创建订阅者
        self.command_subscribe_ = self.create_subscription(String,"command",self.command_callback,10)

    def command_callback(self,msg):
        speed = 0.0
        if msg.data=="backup":
            speed = -0.2
        self.get_logger().info(f'收到[{msg.data}]命令,发送速度{speed}')
def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = NodeSubscribe02("topic_subscribe_01")  # 新建一个节点
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

写完代码之后要使用colcon build编译之后才能执行,注意每次更改完代码后都要重新编译,话题名相同的两个发布和订阅才能相互沟通。

使用ros2 run example_topic_rclpy topic_subscribe_02 即ros2 run 工作包名 节点名

对话状态显示工具

rqt_graph工具可以可视化topic之间数据交换的关系,在termial中输入rqt_graph即可:
在这里插入图片描述

常用CLI工具:
返回系统中当前活动的所有主题的列表:ros2 topic list
打印实时话题内容 ros2 topic echo

5.服务通信

服务是ros网络中节点通信的另一种方式.服务基于呼叫-回复模式,以之对比的话题是基于发布-订阅模式.话题可以让所有节点订阅数据流并且持续更新,而服务仅当它们收到客户端的请求才提供数据.
在这里插入图片描述
先建工作包:

ros2 pkg create service --build-type ament_python  --node-name service_01 
colcon build
写完了代码之后再更改setup.py中的节点信息
再colcon build
source /install/setup.bash

客户端

from example_interfaces.srv import AddTwoInts

class ServiceClient02(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info("节点已启动:%s!" % name)
        self.client_ = self.create_client(AddTwoInts,"add_two_ints_srv") 

    def result_callback_(self, result_future):
        response = result_future.result()
        self.get_logger().info(f"收到返回结果:{response.sum}")
    
    def send_request(self, a, b):
        while rclpy.ok() and self.client_.wait_for_service(1)==False:
            self.get_logger().info(f"等待服务端上线....")
            
        request = AddTwoInts.Request()
        request.a = a
        request.b = b
        self.client_.call_async(request).add_done_callback(self.result_callback_)
        
def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = ServiceClient02("service_client_02")  # 新建一个节点
    # 调用函数发送请求
    node.send_request(3,4)
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

服务端

# 导入接口
from example_interfaces.srv import AddTwoInts


class ServiceServer02(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info("节点已启动:%s!" % name)
        self.add_ints_server_ = self.create_service(AddTwoInts,"add_two_ints_srv", self.handle_add_two_ints) 

    def handle_add_two_ints(self,request, response):
        self.get_logger().info(f"收到请求,计算{request.a} + {request.b}")
        response.sum = request.a + request.b
        return response


6.自定义接口

自定义接口的功能包必须为cpp,colcon build后会在local/lib/python3.10/dist-packages/下找到python版本的头文件

ros2 pkg create example_ros2_interfaces --build-type ament_cmake --dependencies rosidl_default_generators geometry_msgs

依赖项rosidl_default_generators必须添加,话题接口放到msg文件夹下,以.msg结尾。服务接口放到srv文件夹下,以srv结尾。

.
├── CMakeLists.txt
├── msg
│ ├── RobotPose.msg
│ └── RobotStatus.msg
├── package.xml
└── srv
└── MoveRobot.srv

接着修改CMakeLists.txt

添加下面的内容

rosidl_generate_interfaces(${PROJECT_NAME}
“msg/RobotPose.msg”
“msg/RobotStatus.msg”
“srv/MoveRobot.srv”
DEPENDENCIES geometry_msgs
)

接着修改package.xml

<buildtool_depend>ament_cmake</buildtool_depend>
rosidl_default_generators
geometry_msgs

<member_of_group>rosidl_interface_packages</member_of_group> #添加这一行

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

7.参数

开环控制:机器人按照预先设定的命令执行任务,但并不会对执行过程中的状态进行反馈和调整
闭环控制:是指机器人通过传感器或其他检测设备获取执行任务过程中的状态信息,将这些信息反馈给控制系统,从而实现对机器人执行任务过程中的实时控制和调整

对节点的一些参数可以进行修改:

ros2 run turtlesim turtlesim_node#启动节点
ros2 param set /turtlesim background_r 44#更改参数数值
ros2 param dump turtlesim --output-dir ./#保存海龟节点的参数到当前目录
ros2 run turtlesim turtlesim_node --ros-args --params-file ./turtlesim.yaml#在启动乌龟结点的时候加载参数
这样打印出的乌龟背景就是青色的

自定义节点和控制

cd chapt4/chapt4_ws/
ros2 pkg create example_parameters_rclpy --build-type ament_python --dependencies rclpy --destination-directory src --node-name parameters_basic 

更改parameters_basic.py

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node

class ParametersBasicNode(Node):
    """
    创建一个ParametersBasicNode节点,并在初始化时输出一个话
    """

    def __init__(self, name):
        super().__init__(name)
        self.get_logger().info(f"节点已启动:{name}!")
        # 声明参数
        self.declare_parameter('rcl_log_level', 0)
        # 获取参数
        log_level = self.get_parameter("rcl_log_level").value
        # 设置参数
        self.get_logger().set_level(log_level)
        # 定时修改
        self.timer = self.create_timer(0.5, self.timer_callback)

    def timer_callback(self):
        """定时器回调函数"""
        # 获取参数
        log_level = self.get_parameter("rcl_log_level").value
        # 设置参数
        self.get_logger().set_level(log_level)
        print(
            f"========================{log_level}=============================")
        self.get_logger().debug("我是DEBUG级别的日志,我被打印出来了!")
        self.get_logger().info("我是INFO级别的日志,我被打印出来了!")
        self.get_logger().warn("我是WARN级别的日志,我被打印出来了!")
        self.get_logger().error("我是ERROR级别的日志,我被打印出来了!")
        self.get_logger().fatal("我是FATAL级别的日志,我被打印出来了!")



def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = ParametersBasicNode("parameters_basic")  # 新建一个节点
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

定参数编译执行

colcon build  --packages-select example_parameters_rclpy
source install/setup.bash
ros2 run example_parameters_rclpy parameters_basic --ros-args -p rcl_log_level:=10

动参数编译执行

ros2 param list#打印当前节点有哪些参数可以设定
ros2 param set /parameters_basic rcl_log_level 40

8.动作

话题适用于节点间单向的频繁的数据传输,服务则适用于节点间双向的数据传递,而参数则用于动态调整节点的设置,动作可以监控和指导全过程
Action的三大组成部分目标、反馈和结果。
目标send_goal:即Action 客户端告诉服务端要做什么,服务端针对该目标要有响应。解决了不能确认服务端接收并处理目标问题

反馈feedback:即Action 服务端告诉客户端此时做的进度如何(类似与工作汇报)。解决执行过程中没有反馈问题
结果result:即Action 服务端最终告诉客户端其执行结果,结果最后返回,用于表示任务最终执行情况。

ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key#乌龟能认识键盘是一个活动
ros2 action list-t#查看有什么可以执行的话题.
ros2 interface show turtlesim/action/RotateAbsolute #查看该话题有什么参数可以更改
ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 1.6}" --feedback

Waiting for an action server to become available…
Sending goal:
theta: 1.5

Feedback:
remaining: -0.0840003490447998

Goal accepted with ID: b368de0ed1a54e00890f1b078f4671c8

Feedback:
remaining: -0.06800031661987305

Feedback:
remaining: -0.05200028419494629

Feedback:
remaining: -0.03600025177001953

Feedback:
remaining: -0.020000219345092773

Feedback:
remaining: -0.004000186920166016

Result:
delta: 0.08000016212463379

Goal finished with status: SUCCEEDED

9.自定义通信接口

除了参数之外,话题、服务和动作(Action)都支持自定义接口,每一种通信方式所适用的场景各不相同,所定义的接口也被分为话题接口、服务接口、动作接口三种。
注意接口功能包类型必须为:ament_cmake

依赖项rosidl_default_generators必须要加

ros2 pkg create example_ros2_interfaces --build-type ament_cmake --dependencies rosidl_default_generators geometry_msgs

在工作包里新建msg,srv文件件,接口文件都各自存放。

├── CMakeLists.txt
├── msg
│ ├── RobotPose.msg
│ └── RobotStatus.msg
├── package.xml
└── srv
└── MoveRobot.srv

接着修改CMakeLists.txt

find_package(rosidl_default_generators REQUIRED)
find_package(geometry_msgs REQUIRED)

添加下面的内容 rosidl_generate_interfaces(${PROJECT_NAME}

“msg/RobotPose.msg”
“msg/RobotStatus.msg”
“srv/MoveRobot.srv”
DEPENDENCIES geometry_msgs
)

接着修改package.xml

<buildtool_depend>ament_cmake</buildtool_depend>
rosidl_default_generators
geometry_msgs
<member_of_group>rosidl_interface_packages</member_of_group> #添加这一行
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

重新编译一遍

话题接口格式:
xxx.msg

int64 num

服务接口格式:xxx.srv

int64 a #请求
int64 b #应答
---
int64 num

动作接口格式:xxx.action

int64 Request #请求消息从动作客户端发送到动作服务器,初始化一个新目标。
---
int64 Result #当目标完成时,结果消息从动作服务器发送到动作客户端。
---
int64 Feedback #反馈消息定期从动作服务器发送到动作客户端,其中包含关于目标的更新。



# 11.位置和姿态
## 11.1位置
直角坐标系下一点的位置信息可以写作矢量的行驶,可以简单的认为就是坐标轴的xyz。
## 11.2姿态
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/84409d99c9f54c479b554d0c0fbb5e80.png)
以点a为原坐标系,可以通过描述ap之间坐标系的关系来表示p的姿态,p轴和a轴的每一个轴都有一定的角度,一共九个角度的余弦值就可以组成旋转矩阵。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/03086dc22c174fa987ceda1b9bceecd9.png)
## 11.3位置+姿态
位置矢量计算位置、旋转矩阵可以用来计算在特定参考系下的姿态,将位置和姿态组合到一起叫做位姿
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值