fastdds的快速使用

1.1。什么是 DDS?

数据分发服务 (DDS)是一种以 数据为中心的通信协议,用于分布式软件应用程序通信。它描述了支持数据提供者和数据消费者之间通信的通信应用程序编程接口 (API) 和通信语义。

由于它是一个以数据为中心的发布订阅 (DCPS) 模型,因此在其实现中定义了三个关键应用实体:发布实体,定义信息生成对象及其属性;订阅实体,它定义了信息消费对象及其属性;和配置实体,定义作为主题传输的信息类型,并使用其服务质量 (QoS) 属性创建发布者和订阅者,确保上述实体的正确性能。

DDS 使用 QoS 来定义 DDS 实体的行为特征。QoS 由单独的 QoS 策略(源自 QoSPolicy 的类型的对象)组成。这些在Policy中描述。

1.1.1。DCPS 概念模型

在 DCPS 模型中,为开发通信应用系统定义了四个基本要素。

出版商。它是负责创建和配置其实现的DataWriters的 DCPS 实体。DataWriter是负责实际发布消息的实体。每个人都有一个分配的主题,在该主题下发布消息。有关详细信息,请参阅发布者。

订户。它是 DCPS 实体,负责接收在其订阅的主题下发布的数据。它为一个或多个DataReader对象提供服务,这些对象负责将新数据的可用性传达给应用程序。有关详细信息,请参阅订阅者。

主题。它是绑定发布和订阅的实体。它在 DDS 域中是唯一的。通过TopicDescription,它允许发布和订阅的数据类型的统一。有关详细信息,请参阅主题。

域。这是用于链接所有发布者和订阅者的概念,属于一个或多个应用程序,它们在不同主题下交换数据。这些参与域的单个应用程序称为DomainParticipant。DDS 域由域 ID 标识。DomainParticipant 定义域 ID 以指定它所属的 DDS 域。具有不同 ID 的两个 DomainParticipants 不知道彼此在网络中的存在。因此,可以创建多个通信通道。这适用于涉及多个DDS应用程序的场景,它们各自的DomainParticipants相互通信,但这些应用程序不得干扰。域参与者充当其他 DCPS 实体的容器,充当 发布者、订阅者和主题实体的工厂,并在域中提供管理服务。有关详细信息,请参阅域。
fastdds的快速使用_第1张图片

1.2. 什么是 RTPS

为支持 DDS 应用程序而开发的实时发布订阅 (RTPS)协议是一种发布订阅通信中间件,它通过 UDP/IP 等尽力传输传输。此外,Fast DDS 还支持 TCP 和共享内存 (SHM) 传输。

它旨在支持单播和多播通信。

在继承自 DDS 的 RTPS 顶部,可以找到域,它定义了一个单独的通信平面。几个域可以同时独立地共存。一个域包含任意数量的RTPSParticipants,即能够发送和接收数据的元素。为此,RTPSParticipants 使用他们的Endpoints:

RTPSWriter:能够发送数据的端点。

RTPSReader:能够接收数据的端点。

RTPSParticipant 可以有任意数量的写入器和读取器端点。
fastdds的快速使用_第2张图片
通信围绕主题进行,主题定义和标记正在交换的数据。主题不属于特定参与者。参与者通过 RTPSWriters 对主题下发布的数据进行更改,并通过 RTPSReaders 接收与其订阅的主题相关的数据。通信单元称为Change,它表示在 Topic 下写入的数据的更新。 RTPSReaders/RTPSWriters在其History上注册这些更改,这是一种用作最近更改缓存的数据结构。

在eProsima Fast DDS的默认配置中,当您通过 RTPSWriter 端点发布更改时,会在后台执行以下步骤:

更改将添加到 RTPSWriter 的历史缓存中。

RTPSWriter 将更改发送到它知道的任何 RTPSReaders。

接收到数据后,RTPSReaders 用新的变化更新他们的历史缓存。

但是,Fast DDS 支持多种配置,允许您更改 RTPSWriters/RTPSReaders 的行为。修改 RTPS 实体的默认配置意味着 RTPSWriters 和 RTPSReaders 之间的数据交换流发生变化。此外,通过选择服务质量 (QoS) 策略,您可以通过多种方式影响这些历史缓存的管理方式,但通信循环保持不变。您可以继续阅读RTPS 层部分,了解更多关于快速 DDS 中 RTPS 协议的实现。

1.3. 编写一个简单的 C++ 发布者和订阅者应用程序

1.3.1。背景

DDS 是实现 DCPS 模型的以数据为中心的通信中间件。该模型基于发布者的开发,这是一个数据生成元素;和一个订阅者,一个数据消费元素。这些实体通过主题进行通信,主题是绑定两个 DDS 实体的元素。发布者在主题下生成信息,订阅者订阅同一主题以接收信息

1.3.2. 先决条件

首先,您需要按照安装手册中列出的步骤安装 eProsima Fast DDS及其所有依赖项。您还需要完成安装手册中列出的安装 eProsima Fast DDS-Gen工具的步骤。此外,本教程中提供的所有命令都针对 Linux 环境进行了概述。

1.3.4。导入链接库及其依赖项

DDS 应用程序需要 Fast DDS 和 Fast CDR 库。根据安装过程,使这些库可用于我们的 DDS 应用程序的过程将略有不同。在 Linux 上,可以在目录/usr/include/fastrtps/和 /usr/include/fastcdr/中分别找到 Fast DDS 和 Fast CDR 的头文件。两者的编译库都可以在目录/usr/lib/中找到

1.3.5。配置 CMake 项目

cmake_minimum_required(VERSION 3.12.4)

if(NOT CMAKE_VERSION VERSION_LESS 3.0)
    cmake_policy(SET CMP0048 NEW)
endif()

project(DDSHelloWorld)

# Find requirements
if(NOT fastcdr_FOUND)
    find_package(fastcdr REQUIRED)
endif()

if(NOT fastrtps_FOUND)
    find_package(fastrtps REQUIRED)
endif()

# Set C++11
include(CheckCXXCompilerFlag)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG OR
        CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11)
    if(SUPPORTS_CXX11)
        add_compile_options(-std=c++11)
    else()
        message(FATAL_ERROR "Compiler doesn't support C++11")
    endif()
endif()

IDL通过生成的数据文件为:

这必须生成以下文件:

HelloWorld.cxx:HelloWorld 类型定义。

HelloWorld.h:HelloWorld.cxx 的头文件。

HelloWorldPubSubTypes.cxx:HelloWorld 类型的序列化和反序列化代码。

HelloWorldPubSubTypes.h:HelloWorldPubSubTypes.cxx 的头文件。

pub端的代码

// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
  2//
  3// Licensed under the Apache License, Version 2.0 (the "License");
  4// you may not use this file except in compliance with the License.
  5// You may obtain a copy of the License at
  6//
  7//     http://www.apache.org/licenses/LICENSE-2.0
  8//
  9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15/**
 16 * @file HelloWorldPublisher.cpp
 17 *
 18 */
 19
 20#include "HelloWorldPubSubTypes.h"
 21
 22#include 
 23#include 
 24#include 
 25#include 
 26#include 
 27#include 
 28
 29using namespace eprosima::fastdds::dds;
 30
 31class HelloWorldPublisher
 32{
 33private:
 34
 35    HelloWorld hello_;
 36
 37    DomainParticipant* participant_;
 38
 39    Publisher* publisher_;
 40
 41    Topic* topic_;
 42
 43    DataWriter* writer_;
 44
 45    TypeSupport type_;
 46
 47    class PubListener : public DataWriterListener
 48    {
 49    public:
 50
 51        PubListener()
 52            : matched_(0)
 53        {
 54        }
 55
 56        ~PubListener() override
 57        {
 58        }
 59
 60        void on_publication_matched(
 61                DataWriter*,
 62                const PublicationMatchedStatus& info) override
 63        {
 64            if (info.current_count_change == 1)
 65            {
 66                matched_ = info.total_count;
 67                std::cout << "Publisher matched." << std::endl;
 68            }
 69            else if (info.current_count_change == -1)
 70            {
 71                matched_ = info.total_count;
 72                std::cout << "Publisher unmatched." << std::endl;
 73            }
 74            else
 75            {
 76                std::cout << info.current_count_change
 77                        << " is not a valid value for PublicationMatchedStatus current count change." << std::endl;
 78            }
 79        }
 80
 81        std::atomic_int matched_;
 82
 83    } listener_;
 84
 85public:
 86
 87    HelloWorldPublisher()
 88        : participant_(nullptr)
 89        , publisher_(nullptr)
 90        , topic_(nullptr)
 91        , writer_(nullptr)
 92        , type_(new HelloWorldPubSubType())
 93    {
 94    }
 95
 96    virtual ~HelloWorldPublisher()
 97    {
 98        if (writer_ != nullptr)
 99        {
100            publisher_->delete_datawriter(writer_);
101        }
102        if (publisher_ != nullptr)
103        {
104            participant_->delete_publisher(publisher_);
105        }
106        if (topic_ != nullptr)
107        {
108            participant_->delete_topic(topic_);
109        }
110        DomainParticipantFactory::get_instance()->delete_participant(participant_);
111    }
112
113    //!Initialize the publisher
114    bool init()
115    {
116        hello_.index(0);
117        hello_.message("HelloWorld");
118
119        DomainParticipantQos participantQos;
120        participantQos.name("Participant_publisher");
121        participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);
122
123        if (participant_ == nullptr)
124        {
125            return false;
126        }
127
128        // Register the Type
129        type_.register_type(participant_);
130
131        // Create the publications Topic
132        topic_ = participant_->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);
133
134        if (topic_ == nullptr)
135        {
136            return false;
137        }
138
139        // Create the Publisher
140        publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);
141
142        if (publisher_ == nullptr)
143        {
144            return false;
145        }
146
147        // Create the DataWriter
148        writer_ = publisher_->create_datawriter(topic_, DATAWRITER_QOS_DEFAULT, &listener_);
149
150        if (writer_ == nullptr)
151        {
152            return false;
153        }
154        return true;
155    }
156
157    //!Send a publication
158    bool publish()
159    {
160        if (listener_.matched_ > 0)
161        {
162            hello_.index(hello_.index() + 1);
163            writer_->write(&hello_);
164            return true;
165        }
166        return false;
167    }
168
169    //!Run the Publisher
170    void run(
171            uint32_t samples)
172    {
173        uint32_t samples_sent = 0;
174        while (samples_sent < samples)
175        {
176            if (publish())
177            {
178                samples_sent++;
179                std::cout << "Message: " << hello_.message() << " with index: " << hello_.index()
180                            << " SENT" << std::endl;
181            }
182            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
183        }
184    }
185};
186
187int main(
188        int argc,
189        char** argv)
190{
191    std::cout << "Starting publisher." << std::endl;
192    int samples = 10;
193
194    HelloWorldPublisher* mypub = new HelloWorldPublisher();
195    if(mypub->init())
196    {
197        mypub->run(static_cast(samples));
198    }
199
200    delete mypub;
201    return 0;
202}

每一部分的功能:

DomainParticipantFactory. 允许创建和销毁 DomainParticipant 对象。

DomainParticipant. 充当所有其他实体对象的容器以及发布者、订阅者和主题对象的工厂。

TypeSupport. 为参与者提供序列化、反序列化和获取特定数据类型的密钥的功能。

Publisher. 它是负责创建 DataWriters 的对象。

DataWriter. 允许应用程序设置要在给定主题下发布的数据的值。

DataWriterListener. 允许重新定义 DataWriterListener 的功能。

派生类的私有成员

类的私有数据成员,hello_数据成员被定义为 HelloWorld类的一个对象,它定义了我们用 IDL 文件创建的数据类型。接下来定义参与者、发布者、主题、DataWriter和数据类型对应的私有数据成员。类的type_对象TypeSupport是将用于在 DomainParticipant 中注册主题数据类型的对象

PubListener通过从该类继承来定义DataWriterListener该类。此类覆盖默认的 DataWriter 侦听器回调,允许在发生事件时执行例程。当检测到新的 DataReader 正在侦听 DataWriter 正在发布的主题时,重写的回调on_publication_matched() 允许定义一系列操作。检测与 DataWriter 匹配的 DataReader的info.current_count_change()这些更改。这是MatchedStatus允许跟踪订阅状态更改的结构中的成员。最后,listener_类的对象被定义为 的实例PubListener

类的公共构造函数和析构函数HelloWorldPublisher定义如下。构造函数将类的私有数据成员初始化nullptr为 ,TypeSupport 对象除外,它被初始化为HelloWorldPubSubType类的实例。类析构函数删除这些数据成员,从而清理系统内存

初始化函数

初始化 HelloWorld 类型hello_结构成员的内容。

通过 DomainParticipant 的 QoS 为参与者分配名称。

使用DomainParticipantFactory创建参与者。

注册 IDL 中定义的数据类型。

为出版物创建主题。

创建发布者。

使用先前创建的侦听器创建 DataWriter。

为了发布,实现了公共成员功能publish()。在 DataWriter 的侦听器回调中,表明 DataWriter 已与侦听发布主题的 DataReader 匹配,数据成员matched_被更新。它包含发现的 DataReader 的数量。因此,当发现第一个 DataReader 时,应用程序开始发布。这只是由 DataWriter 对象写入更改

sub订阅者

// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
  2//
  3// Licensed under the Apache License, Version 2.0 (the "License");
  4// you may not use this file except in compliance with the License.
  5// You may obtain a copy of the License at
  6//
  7//     http://www.apache.org/licenses/LICENSE-2.0
  8//
  9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15/**
 16 * @file HelloWorldSubscriber.cpp
 17 *
 18 */
 19
 20#include "HelloWorldPubSubTypes.h"
 21
 22#include 
 23#include 
 24#include 
 25#include 
 26#include 
 27#include 
 28#include 
 29#include 
 30
 31using namespace eprosima::fastdds::dds;
 32
 33class HelloWorldSubscriber
 34{
 35private:
 36
 37    DomainParticipant* participant_;
 38
 39    Subscriber* subscriber_;
 40
 41    DataReader* reader_;
 42
 43    Topic* topic_;
 44
 45    TypeSupport type_;
 46
 47    class SubListener : public DataReaderListener
 48    {
 49    public:
 50
 51        SubListener()
 52            : samples_(0)
 53        {
 54        }
 55
 56        ~SubListener() override
 57        {
 58        }
 59
 60        void on_subscription_matched(
 61                DataReader*,
 62                const SubscriptionMatchedStatus& info) override
 63        {
 64            if (info.current_count_change == 1)
 65            {
 66                std::cout << "Subscriber matched." << std::endl;
 67            }
 68            else if (info.current_count_change == -1)
 69            {
 70                std::cout << "Subscriber unmatched." << std::endl;
 71            }
 72            else
 73            {
 74                std::cout << info.current_count_change
 75                        << " is not a valid value for SubscriptionMatchedStatus current count change" << std::endl;
 76            }
 77        }
 78
 79        void on_data_available(
 80                DataReader* reader) override
 81        {
 82            SampleInfo info;
 83            if (reader->take_next_sample(&hello_, &info) == ReturnCode_t::RETCODE_OK)
 84            {
 85                if (info.valid_data)
 86                {
 87                    samples_++;
 88                    std::cout << "Message: " << hello_.message() << " with index: " << hello_.index()
 89                                << " RECEIVED." << std::endl;
 90                }
 91            }
 92        }
 93
 94        HelloWorld hello_;
 95
 96        std::atomic_int samples_;
 97
 98    } listener_;
 99
100public:
101
102    HelloWorldSubscriber()
103        : participant_(nullptr)
104        , subscriber_(nullptr)
105        , topic_(nullptr)
106        , reader_(nullptr)
107        , type_(new HelloWorldPubSubType())
108    {
109    }
110
111    virtual ~HelloWorldSubscriber()
112    {
113        if (reader_ != nullptr)
114        {
115            subscriber_->delete_datareader(reader_);
116        }
117        if (topic_ != nullptr)
118        {
119            participant_->delete_topic(topic_);
120        }
121        if (subscriber_ != nullptr)
122        {
123            participant_->delete_subscriber(subscriber_);
124        }
125        DomainParticipantFactory::get_instance()->delete_participant(participant_);
126    }
127
128    //!Initialize the subscriber
129    bool init()
130    {
131        DomainParticipantQos participantQos;
132        participantQos.name("Participant_subscriber");
133        participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);
134
135        if (participant_ == nullptr)
136        {
137            return false;
138        }
139
140        // Register the Type
141        type_.register_type(participant_);
142
143        // Create the subscriptions Topic
144        topic_ = participant_->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);
145
146        if (topic_ == nullptr)
147        {
148            return false;
149        }
150
151        // Create the Subscriber
152        subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);
153
154        if (subscriber_ == nullptr)
155        {
156            return false;
157        }
158
159        // Create the DataReader
160        reader_ = subscriber_->create_datareader(topic_, DATAREADER_QOS_DEFAULT, &listener_);
161
162        if (reader_ == nullptr)
163        {
164            return false;
165        }
166
167        return true;
168    }
169
170    //!Run the Subscriber
171    void run(
172        uint32_t samples)
173    {
174        while(listener_.samples_ < samples)
175        {
176            std::this_thread::sleep_for(std::chrono::milliseconds(100));
177        }
178    }
179};
180
181int main(
182        int argc,
183        char** argv)
184{
185    std::cout << "Starting subscriber." << std::endl;
186    int samples = 10;
187
188    HelloWorldSubscriber* mysub = new HelloWorldSubscriber();
189    if(mysub->init())
190    {
191        mysub->run(static_cast(samples));
192    }
193
194    delete mysub;
195    return 0;
196}

Subscriber. 它是负责创建和配置 DataReader 的对象。

DataReader. 它是负责实际接收数据的对象。它在应用程序中注册标识要读取的数据的主题(TopicDescription)并访问订阅者接收到的数据。

DataReaderListener. 这是分配给数据读取器的侦听器。

DataReaderQoS. 定义 DataReader 的 QoS 的结构。

SampleInfo. 它是伴随每个样本“读取”或“获取”的信息。
从类的私有数据成员开始,值得一提的是数据读取监听器的实现。类的私有数据成员将是参与者、订阅者、主题、数据读取器和数据类型。与数据写入器的情况一样,侦听器实现了要在事件发生时执行的回调。SubListener 的第一个被覆盖的回调是on_subscription_matched(),它类似于on_publication_matched()DataWriter 的回调

void on_subscription_matched(
        DataReader*,
        const SubscriptionMatchedStatus& info) override
{
    if (info.current_count_change == 1)
    {
        std::cout << "Subscriber matched." << std::endl;
    }
    else if (info.current_count_change == -1)
    {
        std::cout << "Subscriber unmatched." << std::endl;
    }
    else
    {
        std::cout << info.current_count_change
                << " is not a valid value for SubscriptionMatchedStatus current count change" << std::endl;
    }
}

第二个被覆盖的回调是on_data_available(). 在此,数据读取器可以访问的下一个接收到的样本被获取并处理以显示其内容。在这里SampleInfo定义了类的对象,它决定了一个样本是否已经被读取或获取。每次读取样本时,接收样本的计数器都会增加

void on_data_available(
        DataReader* reader) override
{
    SampleInfo info;
    if (reader->take_next_sample(&hello_, &info) == ReturnCode_t::RETCODE_OK)
    {
        if (info.valid_data)
        {
            samples_++;
            std::cout << "Message: " << hello_.message() << " with index: " << hello_.index()
                        << " RECEIVED." << std::endl;
        }
    }
}

你可能感兴趣的:(Fast-DDS,自动驾驶,c++,自动驾驶,fastdds)