【Apollo研究ノート】——サイバーRT作成コンポーネント

ここに画像の説明を挿入

序文

この記事はCyber​​ RTの研究記録であり、記事中に不正確、不完全、欠落している部分があるかもしれません。ご指摘ください。
コースアドレス: https://apollo.baidu.com/community/course/outline/329?activeId=10200
詳細については、以下を参照してください:
[1] Apollo Spark プロジェクト学習ノート - 講義 3 (Apollo Cyber​​ RT モジュール)詳しい解説と実戦)https://blog.csdn.net/sinat_52032317/article/details/126924375
[2] 【アポロ・スパーク計画】——サイバー基本構想 | 通信の仕組み
https://blog.csdn.net /sinat_52032317/article/details /131878429?spm=1001.2014.3001.5501
[3] 第 1 章: Cyber​​ RT の基本的な導入と実践https://apollo.baidu.com/community/article/1093
[4] 第 2 章:サイバーRTコミュニケーションメカニズムの分析と実践https://apollo.baidu.com/community/article/1094
[5] 第3章:コンポーネントの認知と実践https://apollo.baidu.com/community/article/1103説明この記事はセクション 1 ~ 4 から始まります。

の example-component の例はサンプルに基づいており、
TEST1.Component のケースと TEST2.TimerComponent のケースはSpark プロジェクトの例に基づいています。

関連コードの仕上げ

リンク: https://pan.baidu.com/s/1ENgXE4yQ1v4nJRjcfZtd8w?pwd=ht4c抽出コード: ht4c

0. 前提知識

この部分については、第 3 章: コンポーネントの認識と実践https://apollo.baidu.com/community/article/1103で詳しく説明されています。

0.1 コンポーネントとは

Apollo の Cyber​​ RT フレームワークは、コンポーネントの概念に基づいて構築されています。各コンポーネントは、一連の入力を処理して出力データを生成する Cyber​​ RT フレームワークの特定のアルゴリズム モジュールであり、コンポーネントに対応する DAG ファイルを使用することで、Cyber​​ RT はモジュールの動的な読み込みを実現できます。

0.2 コンポーネントの種類

コンポーネントは 2 つのカテゴリに分類されます。1 つはメッセージ駆動型Component(つまり、メッセージが到着した場合にのみ proc() が呼び出されます)、もう 1 つは定期的に呼び出されますTimerComponentタイミング スケジューリング モジュールはメッセージの送受信にバインドされていないため、ユーザーはメッセージを読み取るためのリーダーを作成する必要があります。複数のメッセージを読み取る必要がある場合は、複数のリーダーを作成できます。

  • Component最大 4 方向のメッセージ フュージョンをサポートできるメッセージ フュージョン メカニズムを提供します。複数のチャネルからデータを読み取る場合、最初のチャネルがメイン チャネルとして使用されます。メインチャネルにメッセージが到着すると、Cyber​​ RT はコンポーネントの Proc() を呼び出してデータ処理を実行します。
  • TimerComponentメッセージ フュージョンは提供しません。コンポーネントとは異なり、TimeComponent の Proc() 関数はメイン チャネルに基づいてトリガーされませんが、一定の間隔でシステムによって呼び出されます。開発者は構成ファイルで呼び出し間隔を決定できます。

0.3 コンポーネントの作成とワークフロー

1.ヘッダー ファイルをインクルードします
2.クラスを定義し、Component または time Component を継承します。Component の機能要件に従って、Component または TimeComponent を継承することを選択します。
3. Init() 関数と Proc() 関数を書き換えます。Init() 関数はコンポーネントのロード時に実行され、ノードの作成、ノード リーダーの作成、ノード ライターの作成など、コンポーネントの初期化に使用されます。 Proc()関数 コンポーネントの機能を実現するためのコア関数であり、コンポーネントのコアロジック機能が実現されます。
4. Cyber​​ RT にコンポーネントを登録します。コンポーネントが Cyber​​ RT に登録されている場合のみ、Cyber​​ RT は動的にロードできます。そうでない場合、Cyber​​ RT は動的ロード中にエラーを報告します。

2.1 ヘッダー ファイルの具体的な実装を参照してください。

0.4 コンポーネントのロード方法

Cyber​​ RT では、すべてのコンポーネントは独立した .so ファイルにコンパイルされ、Cyber​​ RT は開発者が提供する設定ファイルに従ってオンデマンドで対応するコンポーネントをロードします。したがって、開発者は、Cyber​​ RT がコンポーネントを正しくロードして実行できるように、.so ファイルの構成ファイル、.dag ファイル、および .launch ファイルを作成する必要があります。

Cyber​​ RT は、コンポーネントをロードして起動する 2 つの方法を提供します。つまり、cyber_launchツールを使用して
コンポーネントに対応する起動ファイルを起動する方法と、メインボードを使用してコンポーネントに対応する dag ファイルを起動する方法です。

cyber_launch ツールは dag ファイルとバイナリ ファイルを起動でき、メインボードは起動 dag ファイルを実行します。

0.5 コンポーネントの利点

  • 起動ファイルを設定することで異なるプロセスに読み込むことができ、柔軟に導入できます。
  • DAG ファイルを構成することで、パラメーター構成、スケジュール戦略、およびチャネル名を変更できます。
  • 複数のタイプのメッセージを受信でき、複数のメッセージ融合戦略を備えています。
  • インターフェイスはシンプルで、Cyber​​ フレームワークによって動的にロードできるため、より柔軟で使いやすくなっています。

アルゴリズム コンポーネントを作成して開始するには、次の 4 つの手順を実行する必要があります。

  • コンポーネントのディレクトリ構造を初期化します。
  • コンポーネントクラスの実装
  • 設定ファイル
  • 起動コンポーネント

1. コンポーネントのディレクトリ構造を初期化します。

example-component を例に挙げます(以下の場合はタイマー部分を一時的に無視してください)。

├── BUILD
├── cyberfile.xml
├── example-components.BUILD
├── example.dag
├── example.launch
├── proto
│   ├── BUILD
│   └── examples.proto
└── src
    ├── BUILD
    ├── common_component_example.cc
    ├── common_component_example.h
    ├── timer_common_component_example.cc
    └── timer_common_component_example.h
  • C++ ヘッダー ファイル: common_component_example.h
  • C++ ソース ファイル: common_component_example.cc
  • Bazel ビルド ファイル: BUILD
  • DAG ファイル:example.dag
  • 起動ファイル:examples.launch

2. コンポーネントクラスを実装する

2.1 ヘッダファイル

common_component_example.h の実装には次の手順があります。

  • ヘッダーファイルをインクルードする
  • テンプレート クラスに基づいて Componentコンポーネント クラスが派生しますCommonComponentSample
  • 派生クラスで独自のInit sum関数を定義しますProc Proc入力データ型を指定する必要があります。
  • CYBER_REGISTER_COMPONENTマクロ定義を使用して、コンポーネント クラスをグローバルに使用可能なものとして登録します。
#pragma once
#include <memory>

#include "cyber/component/component.h"
#include "example_components/proto/examples.pb.h"
  //  CommonComponentSample类不能被继承
class CommonComponentSample : public apollo::cyber::Component<example::proto::Driver, example::proto::Driver> {
    
    
   //有几个数据就有几个example::proto::Driver
    public:
    bool Init() override;
    bool Proc(const std::shared_ptr<example::proto::Driver>& msg0,
        const std::shared_ptr<example::proto::Driver>& msg1) override;
};
CYBER_REGISTER_COMPONENT(CommonComponentSample)

テンプレート クラスはComponentで定義されていますcyber/component/component.h

template <typename M0 = NullType, typename M1 = NullType,
          typename M2 = NullType, typename M3 = NullType>
class Component : public ComponentBase {
    
    
 public:
  Component() {
    
    }
  ~Component() override {
    
    }

  /**
   * @brief init the component by protobuf object.
   *
   * @param config which is defined in 'cyber/proto/component_conf.proto'
   *
   * @return returns true if successful, otherwise returns false
   */
  bool Initialize(const ComponentConfig& config) override;
  bool Process(const std::shared_ptr<M0>& msg0, const std::shared_ptr<M1>& msg1,
               const std::shared_ptr<M2>& msg2,
               const std::shared_ptr<M3>& msg3);

 private:
  /**
   * @brief The process logical of yours.
   *
   * @param msg0 the first channel message.
   * @param msg1 the second channel message.
   * @param msg2 the third channel message.
   * @param msg3 the fourth channel message.
   *
   * @return returns true if successful, otherwise returns false
   */
  virtual bool Proc(const std::shared_ptr<M0>& msg0,
                    const std::shared_ptr<M1>& msg1,
                    const std::shared_ptr<M2>& msg2,
                    const std::shared_ptr<M3>& msg3) = 0;
};

コードからわかるように、Component クラスは最大4 つのテンプレート パラメーターを受け入れ、各テンプレート パラメーターは Proc 関数で定期的に受信されて処理される入力メッセージ タイプを表します。

2.2 ソースファイル

ソース ファイルの場合common_component_example.ccInitProcこれら 2 つの関数を実装する必要があります。

#include "example_components/src/common_component_example.h"

bool CommonComponentSample::Init() {
    
    
    AINFO << "Commontest component init";
    return true;
}

bool CommonComponentSample::Proc(const std::shared_ptr<example::proto::Driver>& msg0,
                                    const std::shared_ptr<example::proto::Driver>& msg1) {
    
    
    AINFO << "Start common component Proc [" << msg0->msg_id() << "] ["
        << msg1->msg_id() << "]";
    return true;
}

2.3 BUILDファイルの作成

最終的にライブラリに基づいてcommon_component_example_lib共有ライブラリファイルが生成されlibcommon_component_example.so、その共有ライブラリがCyber​​ RTスケジューラのメインボードによって動的にロードされて実行されることがわかります。

load("//tools:cpplint.bzl", "cpplint")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")

package(default_visibility = ["//visibility:public"])

cc_binary(
    name = "libcomponent_examples.so",
    linkshared = True,
    linkstatic = True,
    deps = [
        ":timer_common_component_example_lib",
        ":common_component_example_lib"
    ],
)

cc_library(
    name = "timer_common_component_example_lib",
    srcs = ["timer_common_component_example.cc"],
    hdrs = ["timer_common_component_example.h"],
    visibility = ["//visibility:private"],
    alwayslink = True,
    deps = [
        "//cyber",
        "//example_components/proto:examples_cc_proto",
    ],
)

cc_library(
    name = "common_component_example_lib",
    srcs = ["common_component_example.cc"],
    hdrs = ["common_component_example.h"],
    visibility = ["//visibility:private"],
    alwayslink = True,
    deps = [
        "//cyber",
        "//example_components/proto:examples_cc_proto",
    ],
)

cpplint()

3. 設定ファイルのセットアップ

3.1 DAGファイルの設定

DAG 設定ファイルは、Cyber​​ RT スケジューラ メインボードのダイナミック ローディング モジュールの最終設定ファイルであり、DAG 依存関係設定ファイル (例: example.dag) で次の項目を設定します。

  • チャンネル名: チャンネルの名前を入力します。
  • ライブラリ パス: このコンポーネントによって生成された共有ライブラリ パス
  • クラス名: このコンポーネントクラスの名前
# Define all coms in DAG streaming.
    module_config {
# 共享库文件路径
    module_library : "/opt/apollo/neo/packages/example-components-dev/latest/lib/libcomponent_examples.so"
    timer_components {
        class_name : "TimerCommonComponentSample"
        config {
            name : "CommonComponent"
            # 消息频率:10ms
            interval : 10
        }
      }

    components {
        # 组件类名称,一定不能写错,否则mainboard无法动态创建CommonComponentSample组件对象
        class_name : "CommonComponentSample"
        config {
             # 模块名
            name : "example"
            readers {
                channel: "/apollo/channel_example/driver_test"
            }
            readers {
                channel: "/apollo/channel_example/driver_test2"
            }
        }
      }
    }

3.2 起動スタートアップファイルの設定

起動スタートアップファイル (example.launch) で、次の項目を設定します。

  • name コンポーネントの名前
  • dag_conf 前の手順で構成した DAG ファイルのパス
  • process_name コンポーネントを実行するときのプロセス名
<cyber>
    <component>
        <name>common</name>
        <dag_conf>/apollo/cyber/examples/common_component_example/common.dag</dag_conf>
        <process_name>common</process_name>
    </component>
</cyber>

4. コンポーネントを起動します

次のコマンドを使用してコンポーネントをコンパイルします。

buildtool build --packages example_components

実行(推奨)

cyber_launch start example_components/example.launch

ここに画像の説明を挿入
または走る

mainboard -d example_components/example.dag

ここに画像の説明を挿入

[example]  I0318 21:31:18.881103  7620 timer_common_component_example.cc:35] [mainboard]timer_component_example: Write same drivermsg to mutliple channel->msg_id: 95
[example]  I0318 21:31:18.890939  7613 common_component_example.cc:26] [mainboard]Start common component Proc [96] [95][95]
[example]  I0318 21:31:18.890986  7612 timer_common_component_example.cc:35] [mainboard]timer_component_example: Write same drivermsg to mutliple channel->msg_id: 96
[example]  I0318 21:31:18.900918  7621 common_component_example.cc:26] [mainboard]Start common component Proc [97] [96][96]
[example]  I0318 21:31:18.900992  7618 timer_common_component_example.cc:35] [mainboard]timer_component_example: Write same drivermsg to mutliple channel->msg_id: 97
[example]  I0318 21:31:18.911090  7617 common_component_example.cc:26] [mainboard]Start common component Proc [98] [97][97]

ps: 上記の情報はログにも表示されます。

同時に別の端末でCyber​​_monitorを開きます

cyber_monitor

ここに画像の説明を挿入
上記のコードをいくつか調整してチャネルを追加しました(ここでは赤でデータが流入していないことを示します。理由は後で分析されます)。

TimerComponentのコードを変更した後、コンパイル後、cyber_launchに進むと、cyber_monitorの3番目の部分でデータの流入が確認できます コンポーネントとTimerComponentが別のチャネルを占有している場合、cyber_monitorの起動は次のようになります
ここに画像の説明を挿入
。下の図。
ここに画像の説明を挿入

TEST1.コンポーネントケース

このセクションでは、2 つのチャネル メッセージの融合を実現するための単純なコンポーネント インスタンスを実装し、2 つのチャネル メッセージの番号を画面端末に順番に出力します。

ディレクトリ構造を作成する

以下のディレクトリを参照して作成してください

apollo_workspace
|--test
   |--common_component_example
   |  |--BUILD    // bazel编译文件
   |  |--driver_writer.cc    // 向driver channel中写消息的writer
   |  |--chatter_writer.cc   // 向chatter channel中写消息的writer
   |  |--common_component_example.cc    // component 源文件
   |  |--common_component_example.h    // component 头文件
   |  |--common.dag    // component 配置文件
   |  |--common.launch    // component launch文件
   |--proto
      |--BUILD    // protobuf的编译文件
      |--examples.proto    // protobuf
   |--BUILD
   |--test.BUILD
   |--cyberfile.xml

protoファイルとBUILDファイル

プロト

syntax = "proto2";  // proto版本

package apollo.cyber.test.proto; // proto命名空间

message Chatter {
    
    
  optional uint64 timestamp = 1;
  optional uint64 lidar_timestamp = 2;
  optional uint64 seq = 3;
  optional bytes content = 4;
};

message Driver {
    
    
  optional string content = 1;
  optional uint64 msg_id = 2;
  optional uint64 timestamp = 3;
};

建てる

load("@rules_proto//proto:defs.bzl", "proto_library") 
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("//tools:python_rules.bzl", "py_proto_library")

package(default_visibility = ["//visibility:public"])

cc_proto_library(
    name = "examples_cc_proto",
    deps = [
        ":examples_proto",
    ],
)

proto_library(
    name = "examples_proto",
    srcs = ["examples.proto"],
)

load("@rules_proto//proto:defs.bzl", "proto_library") proto 関連のコンパイル ルール
load("@rules_cc//cc:defs.bzl", "cc_proto_library")C++ 関連のコンパイル ルール
load("//tools:python_rules.bzl", "py_proto_library")Python 関連のコンパイル ルール、apollo でカスタマイズ

ドライバ/チャ​​タライタの実装

チャッターライター.cc

#include "cyber/cyber.h"
#include "test/common_component_example/proto/examples.pb.h" //自己的路径
#include "cyber/time/rate.h"
#include "cyber/time/time.h"

using apollo::cyber::Rate;
using apollo::cyber::Time;
using apollo::cyber::test::proto::Chatter; //命名空间

int main(int argc, char *argv[]) {
    
    
  apollo::cyber::Init(argv[0]);
  auto talker_node = apollo::cyber::CreateNode("chatter_writer");
  // 创建writer,写Chatter类型消息
  auto talker = talker_node->CreateWriter<Chatter>("/apollo/chatter");
  // 创建计时器
  Rate rate(3.0);

  std::string content("apollo_prediction");
  while (apollo::cyber::OK()) {
    
    
    static uint64_t seq = 0;
    auto msg = std::make_shared<Chatter>();  // Chatter的智能指针
    msg->set_timestamp(Time::Now().ToNanosecond()); // 时间戳
    msg->set_lidar_timestamp(Time::Now().ToNanosecond());
    msg->set_seq(seq++);
    msg->set_content(content + std::to_string(seq - 1));
    talker->Write(msg); // 将数据写入channel
    AINFO << "/apollo/chatter sent message, seq=" << (seq - 1) << ";";
    // 每秒3次
    rate.Sleep();
  }
  return 0;
}

ドライバー_ライター.cc

#include "cyber/cyber.h"
#include "test/common_component_example/proto/examples.pb.h"
#include "cyber/time/rate.h"
#include "cyber/time/time.h"

using apollo::cyber::Rate;
using apollo::cyber::Time;
using apollo::cyber::test::proto::Driver;

int main(int argc, char *argv[]) {
    
    
  // 初始化cyber
  apollo::cyber::Init(argv[0]);
  // 创建node
  auto talker_node = apollo::cyber::CreateNode("driver_writer");
  // 创建writer,写Driver类型消息
  auto talker = talker_node->CreateWriter<Driver>("/apollo/driver");
  // 新建计时器
  Rate rate(2.0);
  std::string content("apollo_test");
  while (apollo::cyber::OK()) {
    
    
    static uint64_t seq = 0;
    auto msg = std::make_shared<Driver>();
    // 创建一个Driver类型的消息并填入数据
    msg->set_timestamp(Time::Now().ToNanosecond());
    msg->set_msg_id(seq++);
    msg->set_content(content + std::to_string(seq - 1));
    talker->Write(msg);
    AINFO << "/apollo/driver sent message, seq=" << (seq - 1) << ";";
    // 每秒2次
    rate.Sleep();
  }
  return 0;
}

コンポーネントの実装

common_component_example.h

#pragma once
#include <memory>

#include "cyber/component/component.h"
#include "test/common_component_example/proto/examples.pb.h"

using apollo::cyber::Component;
using apollo::cyber::ComponentBase;
using apollo::cyber::test::proto::Driver;
using apollo::cyber::test::proto::Chatter;

// 有两个消息源,继承以Driver和Chatter为参数的Component模版类
class CommonComponentSample : public Component<Driver, Chatter> {
    
    
 public:
  bool Init() override;
  // Proc() 函数的两个参数表示两个channel中的最新的信息
  bool Proc(const std::shared_ptr<Driver>& msg0,
            const std::shared_ptr<Chatter>& msg1) override;
};
// 将CommonComopnentSample注册在cyber中
CYBER_REGISTER_COMPONENT(CommonComponentSample)

Component<Driver, Chatter>ここでは、2 つのチャネルの 2 つの形式のメッセージが継承されており、Proc() 関数のパラメーターがそれらに対応していることがわかります。同様に、継承された場合Component<Driver, Chatter, Driver>、Proc() 関数は次のようになります。Proc(const std::shared_ptr<Driver>& msg0, const std::shared_ptr<Chatter>& msg1, const std::shared_ptr<Driver>& msg2)

common_component_example.cc

#include "test/common_component_example/com_component_test/common_component_example.h"
// 在加载component时调用
bool CommonComponentSample::Init() {
    
    
  AINFO << "Commontest component init";
  return true;
}
// 在主channel,也就是Driver有消息到达时调用
bool CommonComponentSample::Proc(const std::shared_ptr<Driver>& msg0,
                                 const std::shared_ptr<Chatter>& msg1) {
    
    
  // 将两个消息的序号格式化输出
  AINFO << "Start common component Proc [" << msg0->msg_id() << "] ["
        << msg1->seq() << "]";
  return true;
}

設定ファイル

DAGファイル

module_config {
    
    
    module_library : "/opt/apollo/neo/packages/test-dev/latest/lib/libcommon_component_example.so"
    components {
    
    
        class_name : "CommonComponentSample"
        config {
    
    
            name : "common"
            readers {
    
    
                channel: "/apollo/driver"
            }
            readers {
    
    
                channel: "/apollo/chatter"
            }
        }
    }
 }
  • module_libraryComponent:コンパイルされたファイルの格納ディレクトリを指します.so
    * components: Component の種類を示しますが、これ以外にもcomponents別の種類がありますtimer_componentので、次の例で説明します。
  • class_nameComponent:この場合、ロードされたクラス名を示しますCommonComponentSample
  • name:Cyber​​上でロードされたクラスの識別名を示します。
  • readers: コンポーネントによって読み取られるチャネルを示します。これは、継承された基本クラスによって読み取られる型に対応します。
    起動ファイル
<cyber>
    <module>
        <name>common</name>
        <dag_conf>/apollo_workspace/test/common_component_example/com_component_test/common.dag</dag_conf>
        <process_name>common</process_name>
    </module>
</cyber>

<name>: Component Cyber​​ に読み込まれた識別名を示します。DAG ファイルの名前フィールドに対応します。
<dag_conf>:DAG設定ファイルのパスを示します。: 起動後のスレッド名を示します。このスレッドでは
<process_name>同じスレッド名が実行されます。component

ビルドファイル

建てる

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")

package(default_visibility = ["//visibility:public"])

cc_binary(
    name = "libcommon_component_example.so",
    linkshared = True,
    linkstatic = True,
    deps = [":common_component_example_lib"],
)

cc_library(
    name = "common_component_example_lib",
    srcs = ["common_component_example.cc"],
    hdrs = ["common_component_example.h"],
    visibility = ["//visibility:private"],
    deps = [
        "//cyber",
        "//test/common_component_example/proto:examples_cc_proto",       // 路径按自己的改
    ],
    alwayslink = True,
)

cc_binary(
    name = "driver_writer",
    srcs = ["driver_writer.cc"],
    linkstatic = True,
    deps = [
        "//cyber",
        "//test/common_component_example/proto:examples_cc_proto",
    ],
)

cc_binary(
    name = "chatter_writer",
    srcs = ["chatter_writer.cc"],
    linkstatic = True,
    deps = [
        "//cyber",
        "//test/common_component_example/proto:examples_cc_proto",
            ],
)

filegroup(
    name = "conf",
    srcs = [
        ":common.dag",
        ":common.launch",
    ],
)

install(
    name = "install",
    data = [
        ":conf",
    ],
    runtime_dest = "test/bin",
    library_dest = "test/lib",
    data_dest = "test/common_component_example/conf",
    targets = [
        ":chatter_writer",
        ":driver_writer",
        "libcommon_component_example.so",
    ],
)

install_src_files(
    name = "install_src",
    src_dir = ["."],
    dest = "test/src/common_component_example",
    filter = "*",  
)

cpplint()

コンパイル

 buildtool build -p test/

installキーワード:runtime_dest実行可能ファイルの場所library_destライブラリ ファイルの場所data_dest.dag/.launch ファイルの場所

パッケージ管理 BUILD ファイル内の deps を忘れずに変更してください。

走る

コンパイル後、Cyber​​ を実行してコンポーネントをロードします。前述したように、Cyber​​ は設定ファイルに従ってコンポーネントをロードします。Cyber​​ は、コンポーネントをロードする 2 つの方法を提供します。

方法 1. メインボードを使用して開始します。mainboard -d <path/to/dag>この例では、実行するコマンドは次のとおりです。

mainboard -d test/common_component_example/common.dag

方法 2. cyber_launch を使用して開始します。cyber_launch start <path/to/launch>この例では、実行するコマンドは次のとおりです。

cyber_launch start test/common_component/common.launch

3つの端末を起動する

# 第一个
export GLOG_alsologtostderr=1
./bazel-bin/test/common_component_example/driver_writer
# 第二个
export GLOG_alsologtostderr=1
./bazel-bin/test/common_component_example/chatter_writer
# 第三个
export GLOG_alsologtostderr=1
cyber_launch start test/common_component_example/common.launch

ここに画像の説明を挿入ここに画像の説明を挿入
Cyber​​_monitor は関連するチャネル情報を確認できます。
ここに画像の説明を挿入
CommonComponentSample はメイン チャネル情報を受信するたびに Proc() 関数を実行し、Proc() 関数はメッセージ フュージョン ロジックを実行して 2 つのメッセージの番号を順番に画面に出力します。

CommonComponentSample で画面に出力される情報を見ると、メインチャネル情報 (Driver 情報) の番号が順番に増加し、非メインチャネル情報 (Chatter 情報) の番号が欠落または重複していることがわかります。コンポーネントの Proc() 関数 実行はメイン チャネル メッセージが到着した場合にのみトリガーされ、Proc() 関数は実行時にフュージョン チャネルの最新のメッセージをすべて読み取ります。

TEST2.TimerComponent の場合

TimerDriverSampleこのセクションでは、前のケースの 2 つの Writer を置き換えるために、2 つの TimerComponents、つまりとを実装しますTimerChatterSample

ディレクトリを作成する

apollo_workspace
|--test
   |--timer_component_example
      |--BUILD
      |--timer_chatter.h    // TimerChatterSample 头文件
      |--timer_chatter.cc    // TimerChatterSample 源文件
      |--timer_driver.h    // TimerDriverSample 头文件
      |--timer_driver.cc    // TimerDriverSample 源文件
      |--driver.dag    // TimerDriverSample 配置文件
      |--driver.launch    // TimerDriverSample launch文件
      |--chatter.dag    // TimerChatterSample 配置文件
      |--chatter.launch    // TimerChatterSample launch文件

protoファイルとBUILDファイル

TEST1からファイルを継承します

TimerComponent の実装

timer_chatter.h

#include <memory>
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "cyber/component/timer_component.h"
#include "test/common_component_example/proto/examples.pb.h"

using apollo::cyber::Component;
using apollo::cyber::ComponentBase;
using apollo::cyber::TimerComponent;
using apollo::cyber::Writer;
using apollo::cyber::test::proto::Chatter;

class TimerChatterSample : public TimerComponent {
    
    
 public:
  bool Init() override;
  bool Proc() override;

 private:
  std::shared_ptr<Writer<Chatter>> chatter_writer_ = nullptr;
};
CYBER_REGISTER_COMPONENT(TimerChatterSample)
  • 基本クラスをTimeChatterComponent継承する必要があり、コード構造は通常の Component とほぼ同じであることがわかります。TimerComponent
  • 違いは、データ ソースがないため、テンプレート パラメーターがないことです。

timer_chatter.cc

#include "test/timer_component_example/timer_chatter.h"
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "test/common_component_example/proto/examples.pb.h"


bool TimerChatterSample::Init() {
    
    
  chatter_writer_ = node_->CreateWriter<Chatter>("/apollo/chatter");
  return true;
}

bool TimerChatterSample::Proc() {
    
    
  static int i = 0;
  auto out_msg = std::make_shared<Chatter>();
  out_msg->set_seq(i++);
  chatter_writer_->Write(out_msg);
  AINFO << "timer_chatter: Write chattermsg->"
        << out_msg->ShortDebugString();
  return true;
}

TimerChatter は Init() で Writer を初期化し、Proc() で Channel に情報を書き込みます。

タイマー_ドライバー.h

#include <memory>
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "cyber/component/timer_component.h"
#include "test/common_component_example/proto/examples.pb.h"

using apollo::cyber::Component;
using apollo::cyber::ComponentBase;
using apollo::cyber::TimerComponent;
using apollo::cyber::Writer;
using apollo::cyber::test::proto::Driver;

class TimerDriverSample : public TimerComponent {
    
    
 public:
  bool Init() override;
  bool Proc() override;

 private:
  std::shared_ptr<Writer<Driver>> driver_writer_ = nullptr;
};
CYBER_REGISTER_COMPONENT(TimerDriverSample)

タイマー_ドライバー.cc

#include "test/timer_component_example/timer_driver.h"
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "test/common_component_example/proto/examples.pb.h"
bool TimerDriverSample::Init() {
    
    
  driver_writer_ = node_->CreateWriter<Driver>("/apollo/driver");
  return true;
}

bool TimerDriverSample::Proc() {
    
    
  static int i = 0;
  auto out_msg = std::make_shared<Driver>();
  out_msg->set_msg_id(i++);
  driver_writer_->Write(out_msg);
  AINFO << "timer_driver: Write drivermsg->"
        << out_msg->ShortDebugString();
  return true;
}

設定ファイル

おしゃべりの日

module_config {
    
    
    module_library : "/opt/apollo/neo/packages/test-dev/latest/lib/libtimer_chatter.so"
    timer_components {
    
    
        class_name : "TimerChatterSample"
        config {
    
    
            name : "timer_chatter"
            interval : 400
        }
    }
}
  • 間隔: TimerComponent が Proc() を実行する間隔を示します。この構成では 400 ミリ秒ごとに実行されます。
  • データ融合がないため、リーダーフィールドがありません
  • 残りの構成は通常のコンポーネントと同じです

おしゃべり.起動

<cyber>
    <module>
        <name>timer_chatter</name>
        <dag_conf>/opt/apollo/neo/packages/test-dev/latest/timer_component_example/conf/chatter.dag</dag_conf>
        <process_name>timer_chatter</process_name>
    </module>
</cyber>

ドライバーの日

module_config {
    module_library : "/opt/apollo/neo/packages/test-dev/latest/lib/libtimer_driver.so"
    timer_components {
        class_name : "TimerDriverSample"
        config {
            name : "timer_driver"
            interval : 200
        }
    }
}

ドライバー.起動

<cyber>
    <module>
        <name>timer_driver</name>
        <dag_conf>/opt/apollo/neo/packages/test-dev/latest/timer_component_example/conf/driver.dag</dag_conf>
        <process_name>timer_driver</process_name>
    </module>
</cyber>

ビルドファイル

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")

package(default_visibility = ["//visibility:public"])

cc_binary(
    name = "libcommon_component_example.so",
    linkshared = True,
    linkstatic = True,
    deps = [":common_component_example_lib"],
)

cc_library(
    name = "common_component_example_lib",
    srcs = ["common_component_example.cc"],
    hdrs = ["common_component_example.h"],
    visibility = ["//visibility:private"],
    deps = [
        "//cyber",
        "//test/common_component_example/proto:examples_cc_proto",
    ],
    alwayslink = True,
)

cc_binary(
    name = "driver_writer",
    srcs = ["driver_writer.cc"],
    linkstatic = True,
    deps = [
        "//cyber",
        "//test/common_component_example/proto:examples_cc_proto",
    ],
)

cc_binary(
    name = "chatter_writer",
    srcs = ["chatter_writer.cc"],
    linkstatic = True,
    deps = [
        "//cyber",
        "//test/common_component_example/proto:examples_cc_proto",
            ],
)

filegroup(
    name = "conf",
    srcs = [
        ":common.dag",
        ":common.launch",
    ],
)

install(
    name = "install",
    data = [
        ":conf",
    ],
    runtime_dest = "test/bin",
    library_dest = "test/lib",
    data_dest = "test/common_component_example/conf",
    targets = [
        ":chatter_writer",
        ":driver_writer",
        "libcommon_component_example.so",
    ],
)

install_src_files(
    name = "install_src",
    src_dir = ["."],
    dest = "test/src/cyberatest",
    filter = "*",
)

cpplint()

パッケージ管理 BUILD ファイル内の deps を忘れずに変更してください。

走る

実装した 2 つの TimerComponent は、前のケースで定期的にメッセージを書き込む 2 つの Writer を置き換えるために使用できます。起動方法は、TimerComponent が構成ファイルを通じて構成できる点を除いて、前のケースと似ています。

3つのターミナルも開きます

# 第一个
export GLOG_alsologtostderr=1
cyber_launch start test/common_component_example/common.launch
# 第二个
export GLOG_alsologtostderr=1
cyber_launch start test/timer_component_example/driver.launch
# 第三个
export GLOG_alsologtostderr=1
cyber_launch start test/timer_component_example/chatter.launch

ここに画像の説明を挿入

CommonComponentSample がメイン チャネル情報を受信するたびに、Proc() 関数が 1 回実行され、Proc() 関数はメッセージ フュージョン ロジックを実行して、2 つのメッセージの番号を順番に画面に出力します。
ここに画像の説明を挿入

TimerChatterSample は、システム タイマーによって Proc() 関数を 400ms/200ms ごとに呼び出し、Proc() 関数は実行されるたびにメッセージを送信します。そして、メッセージの番号を画面に出力します。

参考

[1] https://github.com/ApolloAuto/apollo/blob/master/docs/04_Cyber
​​RT/ Cyber​​RT_Quick_Start_cn.md [2] Cyber​​ RT が新しいコンポーネントを構築
[3] apollo によって導入された Cyber​​ Component (13)
[ 4] サイバー RT の基礎の導入と実践https://apollo.baidu.com/community/article/1093
[5] 第 3 章: コンポーネントの認知と実践https://apollo.baidu.com/community/article/1103

おすすめ

転載: blog.csdn.net/sinat_52032317/article/details/129638015