Apollo Cyber​​ RT框架文档——2、API 教程

TUTORIALS—教程

Cyber​​ RT API教程

1、Talker-Listener

Cyber​​RT API 演示的第一部分是了解Talker / Listener示例。

以下是三个基本概念:

  • 示例的节点(基本单元)
  • 读取器(读取消息的功能)
  • 写入器(写入消息的功能)
create a node(创建节点)

在Cyber​​RT框架中,节点是最基本的单元,类似于的角色handle。创建特定的功能对象(编写器,读取器等)时,需要基于现有节点实例创建它。

节点创建界面如下:

std::unique_ptr<Node> apollo::cyber::CreateNode(const std::string& node_name, const std::string& name_space = "");

参数:

  • node_name:节点名称,全局唯一标识符
  • name_space:节点所在空间的名称
  • name_space默认为空。它是与node_name串联的空间的名称。格式为/namespace/node_name

返回值

  • 指向Node的专有智能指针

错误条件

  • cyber::Init()未调用时,系统处于未初始化状态,无法创建节点,返回nullptr
Create a writer

编写器是Cyber​​RT中用于发送消息的基本工具。每个writer对应于具有特定数据类型的通道。编写器由CreateWriter节点类中的接口创建。

接口列表如下:

template <typename MessageT>
   auto CreateWriter(const std::string& channel_name)
       -> std::shared_ptr<Writer<MessageT>>;
template <typename MessageT>
   auto CreateWriter(const proto::RoleAttributes& role_attr)
       -> std::shared_ptr<Writer<MessageT>>;

参数:

  • channel_name:要写入的通道的名称
  • MessageT:要写出的消息类型

返回值

  • 指向Writer对象的共享指针
Create a reader

这个reader是网络中用于接收消息的基本工具。创建reader时,必须将其绑定到回调函数。当新消息到达频道时,将调用回调。reader是由CreateReader节点类的接口创建的。

接口列表如下:

template <typename MessageT>
auto CreateReader(const std::string& channel_name, const std::function<void(const std::shared_ptr<MessageT>&)>& reader_func)
    -> std::shared_ptr<Reader<MessageT>>;

template <typename MessageT>
auto CreateReader(const ReaderConfig& config,
                  const CallbackFunc<MessageT>& reader_func = nullptr)
    -> std::shared_ptr<cyber::Reader<MessageT>>;

template <typename MessageT>
auto CreateReader(const proto::RoleAttributes& role_attr,
                  const CallbackFunc<MessageT>& reader_func = nullptr)
-> std::shared_ptr<cyber::Reader<MessageT>>;

参数:

  • MessageT:要阅读的消息类型
  • channel_name:要从中接收的频道的名称
  • reader_func:处理消息的回调函数

返回值

  • 指向Reader对象的共享指针
代码示例
Talker(cyber / examples / talker.cc)
#include "cyber/cyber.h"
#include "cyber/proto/chatter.pb.h"
#include "cyber/time/rate.h"
#include "cyber/time/time.h"
using apollo::cyber::Rate;
using apollo::cyber::Time;
using apollo::cyber::proto::Chatter;
int main(int argc, char *argv[]) {
  // init cyber framework
  apollo::cyber::Init(argv[0]);
  // create talker node
  std::shared_ptr<apollo::cyber::Node> talker_node(
      apollo::cyber::CreateNode("talker"));
  // create talker
  auto talker = talker_node->CreateWriter<Chatter>("channel/chatter");
  Rate rate(1.0);
  while (apollo::cyber::OK()) {
    static uint64_t seq = 0;
    auto msg = std::make_shared<apollo::cyber::proto::Chatter>();
    msg->set_timestamp(Time::Now().ToNanosecond());
    msg->set_lidar_timestamp(Time::Now().ToNanosecond());
    msg->set_seq(seq++);
    msg->set_content("Hello, apollo!");
    talker->Write(msg);
    AINFO << "talker sent a message!";
    rate.Sleep();
  }
  return 0;
}
Listener(cyber / examples / listener.cc)
#include "cyber/cyber.h"
#include "cyber/proto/chatter.pb.h"
void MessageCallback(
    const std::shared_ptr<apollo::cyber::proto::Chatter>& msg) {
  AINFO << "Received message seq-> " << msg->seq();
  AINFO << "msgcontent->" << msg->content();
}
int main(int argc, char *argv[]) {
  // init cyber framework
  apollo::cyber::Init(argv[0]);
  // create listener node
  auto listener_node = apollo::cyber::CreateNode("listener");
  // create listener
  auto listener =
      listener_node->CreateReader<apollo::cyber::proto::Chatter>(
          "channel/chatter", MessageCallback);
  apollo::cyber::WaitForShutdown();
  return 0;
}
Bazel BUILD file(cyber/samples/BUILD)
cc_binary(
    name = "talker",
    srcs = [ "talker.cc", ],
    deps = [
        "//cyber",
        "//cyber/examples/proto:examples_cc_proto",
    ],
)

cc_binary(
    name = "listener",
    srcs = [ "listener.cc", ],
    deps = [
        "//cyber",
        "//cyber/examples/proto:examples_cc_proto",
    ],
)
构建和运行
  Build: bazel build cyber/examples/
  • 在不同的终端中运行Talker/LIstener:
  ./bazel-bin/cyber/examples/talker 
  ./bazel-bin/cyber/examples/listener
  • 检查结果:您应该看到消息在Listener上打印出来。

2、Service Creation and Use(服务创建和使用)

简介

在自动驾驶系统中,除了模块发送或接收消息外,还有很多场景需要模块通信。服务是节点之间的另一种通信方式。与信道不同,服务实现two-way通信,例如,节点通过发送请求获得响应。本节通过示例介绍Cyber​​RT API 模块中的service

Demo - Example (演示-示例)

问题:创建来回传递Driver.proto的客户端-服务器模型。当客户端发送请求时,服务器解析/处理该请求并返回响应。

该演示的实现主要包括以下步骤。

Define request and response messages (定义请求和响应消息)

cyber中的所有消息均采用protobuf格式。任何带有序列化/反序列化接口的protobuf消息都可以用作服务请求和响应消息。Driverexamples.proto中用作本示例中的服务请求和响应:

// filename: examples.proto
syntax = "proto2";
package apollo.cyber.examples.proto;
message Driver {
    optional string content = 1;
    optional uint64 msg_id = 2;
    optional uint64 timestamp = 3;
};
Create a service and a client (创建服务和客户端)
// filename: cyber/examples/service.cc
#include "cyber/cyber.h"
#include "cyber/examples/proto/examples.pb.h"

using apollo::cyber::examples::proto::Driver;

int main(int argc, char* argv[]) {
  apollo::cyber::Init(argv[0]);
  std::shared_ptr<apollo::cyber::Node> node(
      apollo::cyber::CreateNode("start_node"));
  auto server = node->CreateService<Driver, Driver>(
      "test_server", [](const std::shared_ptr<Driver>& request,
                        std::shared_ptr<Driver>& response) {
        AINFO << "server: I am driver server";
        static uint64_t id = 0;
        ++id;
        response->set_msg_id(id);
        response->set_timestamp(0);
      });
  auto client = node->CreateClient<Driver, Driver>("test_server");
  auto driver_msg = std::make_shared<Driver>();
  driver_msg->set_msg_id(0);
  driver_msg->set_timestamp(0);
  while (apollo::cyber::OK()) {
    auto res = client->SendRequest(driver_msg);
    if (res != nullptr) {
      AINFO << "client: response: " << res->ShortDebugString();
    } else {
      AINFO << "client: service may not ready.";
    }
    sleep(1);
  }

  apollo::cyber::WaitForShutdown();
  return 0;
}
Bazel build file(构建文件)
cc_binary(
    name = "service",
    srcs = [ "service.cc", ],
    deps = [
        "//cyber",
        "//cyber/examples/proto:examples_cc_proto",
    ],
)
编译和运行
  • 建立服务/客户:bazel build cyber/examples/…

  • 运行:./ bazel-bin / cyber / examples / service

  • 检查结果:你可以在apollo / data / log / service.INFO中看到以下内容

I1124 16:36:44.568845 14965 service.cc:30] [service] server: i am driver server
I1124 16:36:44.569031 14949 service.cc:43] [service] client: response: msg_id: 1 timestamp: 0
I1124 16:36:45.569514 14966 service.cc:30] [service] server: i am driver server
I1124 16:36:45.569932 14949 service.cc:43] [service] client: response: msg_id: 2 timestamp: 0
I1124 16:36:46.570627 14967 service.cc:30] [service] server: i am driver server
I1124 16:36:46.571024 14949 service.cc:43] [service] client: response: msg_id: 3 timestamp: 0
I1124 16:36:47.571566 14968 service.cc:30] [service] server: i am driver server
I1124 16:36:47.571962 14949 service.cc:43] [service] client: response: msg_id: 4 timestamp: 0
I1124 16:36:48.572634 14969 service.cc:30] [service] server: i am driver server
I1124 16:36:48.573030 14949 service.cc:43] [service] client: response: msg_id: 5 timestamp: 0
注意事项

注册服务时,请注意不允许重复的服务名称

注册服务器和客户端时应用的节点名称也不应重复

3、Param parameter service

Parameter Service 参数服务简介

参数服务被用于节点之间共享的数据,并提供了诸如基本操作setgetlist。参数服务基于Service实现,并包含服务(service)和客户端(client)。

Parameter Object 参数对象
Supported Data types 支持的数据类型

通过 cyber 传递的所有参数都是apollo::cyber::Parameter对象,下表列出了5种受支持的参数类型。

参数类型| C ++数据类型| protobuf数据类型:———— :————- |--------– 
Parameter type | C++ data type | protobuf data type :————- | :————- | :————– 
apollo::cyber::proto::ParamType::INT | int64_t | int64 apollo::cyber::proto::ParamType::DOUBLE | 
double | double apollo::cyber::proto::ParamType::BOOL | bool |bool 
apollo::cyber::proto::ParamType::STRING | std::string | string 
apollo::cyber::proto::ParamType::PROTOBUF | std::string | string 
apollo::cyber::proto::ParamType::NOT_SET | - | -

除了上述5种类型外,Parameter还支持使用protobuf对象作为传入参数的接口。执行序列化后处理该对象,并将其转换为STRING类型以进行传输。

创建参数对象

支持的构造函数:

Parameter();  // Name is empty, type is NOT_SET
explicit Parameter(const Parameter& parameter);
explicit Parameter(const std::string& name);  // type为NOT_SET
Parameter(const std::string& name, const bool bool_value);
Parameter(const std::string& name, const int int_value);
Parameter(const std::string& name, const int64_t int_value);
Parameter(const std::string& name, const float double_value);
Parameter(const std::string& name, const double double_value);
Parameter(const std::string& name, const std::string& string_value);
Parameter(const std::string& name, const char* string_value);
Parameter(const std::string& name, const std::string& msg_str,
          const std::string& full_name, const std::string& proto_desc);
Parameter(const std::string& name, const google::protobuf::Message& msg);

使用Parameter对象的示例代码:

Parameter a("int", 10);
Parameter b("bool", true);
Parameter c("double", 0.1);
Parameter d("string", "cyber");
Parameter e("string", std::string("cyber"));
// proto message Chatter
Chatter chatter;
Parameter f("chatter", chatter);
std::string msg_str("");
chatter.SerializeToString(&msg_str);
std::string msg_desc("");
ProtobufFactory::GetDescriptorString(chatter, &msg_desc);
Parameter g("chatter", msg_str, Chatter::descriptor()->full_name(), msg_desc);
接口和数据读取

接口列表:

inline ParamType type() const;
inline std::string TypeName() const;
inline std::string Descriptor() const;
inline const std::string Name() const;
inline bool AsBool() const;
inline int64_t AsInt64() const;
inline double AsDouble() const;
inline const std::string AsString() const;
std::string DebugString() const;
template <typename Type>
typename std::enable_if<std::is_base_of<google::protobuf::Message, Type>::value, Type>::type
value() const;
template <typename Type>
typename std::enable_if<std::is_integral<Type>::value && !std::is_same<Type, bool>::value, Type>::type
value() const;
template <typename Type>
typename std::enable_if<std::is_floating_point<Type>::value, Type>::type
value() const;
template <typename Type>
typename std::enable_if<std::is_convertible<Type, std::string>::value, const std::string&>::type
value() const;
template <typename Type>
typename std::enable_if<std::is_same<Type, bool>::value, bool>::type
value() const;

有关如何使用这些接口的示例:

Parameter a("int", 10);
a.Name();  // return int
a.Type();  // return apollo::cyber::proto::ParamType::INT
a.TypeName();  // return string: INT
a.DebugString();  // return string: {name: "int", type: "INT", value: 10}
int x = a.AsInt64();  // x = 10
x = a.value<int64_t>();  // x = 10
x = a.AsString();  // Undefined behavior, error log prompt
f.TypeName();  // return string: chatter
auto chatter = f.value<Chatter>();
参数服务

如果一个节点要向其他节点提供参数服务,则需要创建一个ParameterService

/**
 * @brief Construct a new ParameterService object
 *
 * @param node shared_ptr of the node handler
 */
explicit ParameterService(const std::shared_ptr<Node>& node);

由于所有参数都存储在参数服务对象中,因此可以直接在ParameterService中操纵参数,而无需发送服务请求。

设置参数:

/**
 * @brief Set the Parameter object
 *
 * @param parameter parameter to be set
 */
void SetParameter(const Parameter& parameter);

获取参数:

/**
 * @brief Get the Parameter object
 *
 * @param param_name
 * @param parameter the pointer to store
 * @return true
 * @return false call service fail or timeout
 */
bool GetParameter(const std::string& param_name, Parameter* parameter);

获取参数列表:

/**
 * @brief Get all the Parameter objects
 *
 * @param parameters pointer of vector to store all the parameters
 * @return true
 * @return false call service fail or timeout
 */
bool ListParameters(std::vector<Parameter>* parameters);

参数客户端
如果一个节点要使用其他节点的参数服务,则需要创建一个ParameterClient

/**
 * @brief Construct a new ParameterClient object
 *
 * @param node shared_ptr of the node handler
 * @param service_node_name node name which provide a param services
 */
ParameterClient(const std::shared_ptr<Node>& node, const std::string& service_node_name);

您还可以执行SetParameterGetParameterListParameters在“ 参数服务”下进行了提及。

Demo - example(演示-示例)

#include "cyber/cyber.h"
#include "cyber/parameter/parameter_client.h"
#include "cyber/parameter/parameter_server.h"

using apollo::cyber::Parameter;
using apollo::cyber::ParameterServer;
using apollo::cyber::ParameterClient;

int main(int argc, char** argv) {
  apollo::cyber::Init(*argv);
  std::shared_ptr<apollo::cyber::Node> node =
      apollo::cyber::CreateNode("parameter");
  auto param_server = std::make_shared<ParameterServer>(node);
  auto param_client = std::make_shared<ParameterClient>(node, "parameter");
  param_server->SetParameter(Parameter("int", 1));
  Parameter parameter;
  param_server->GetParameter("int", &parameter);
  AINFO << "int: " << parameter.AsInt64();
  param_client->SetParameter(Parameter("string", "test"));
  param_client->GetParameter("string", &parameter);
  AINFO << "string: " << parameter.AsString();
  param_client->GetParameter("int", &parameter);
  AINFO << "int: " << parameter.AsInt64();
  return 0;
}
编译和运行
  • Build(构建): bazel build cyber/examples/…

  • Run(运行): ./bazel-bin/cyber/examples/paramserver

4、Log API(日志)

日志库

cyber 日志库建立在glog之上。需要包括以下头文件:

#include "cyber/common/log.h"
#include "cyber/init.h"
日志配置

默认全局配置路径:cyber / setup.bash

以下配置可以由devloper修改:

export GLOG_log_dir=/apollo/data/log
export GLOG_alsologtostderr=0
export GLOG_colorlogtostderr=1
export GLOG_minloglevel=0
登录初始化

在代码条目处调用Init方法以初始化日志:

apollo::cyber::cyber::Init(argv[0]) is initialized.
If no macro definition is made in the previous component, the corresponding log is printed to the binary log.
日志输出宏

日志库封装在日志打印宏中。相关的日志宏的用法如下:

ADEBUG << "hello cyber.";
AINFO  << "hello cyber.";
AWARN  << "hello cyber.";
AERROR << "hello cyber.";
AFATAL << "hello cyber.";
日志格式

格式为

 <MODULE_NAME>.log.<LOG_LEVEL>.<datetime>.<process_id>
关于日志文件

当前,与默认glog唯一不同的输出行为是模块的不同日志级别将被写入同一日志文件。

5、Building a module based on Component(基于组件构建模块)

关键概念
1.组件

该组件是Cyber​​ RT提供的用于构建应用程序模块的基类。每个特定应用模块可以继承组件类和定义自己Init和Proc功能,因此它可以被加载到 Cyber 框架。

2.二进制VS组件

有两种选择可将Cyber​​ RT框架用于应用程序:

  • 基于二进制:将应用程序单独编译成二进制文件,通过创建自己的Reader和Writer与其他 cyber 模块进行通信。

  • 基于组件:将应用程序编译到共享库中。通过继承Component类并编写相应的dag描述文件,Cyber​​
    RT框架将动态加载和运行应用程序。

基本组件接口
  • 组件的Init()功能类似于执行算法初始化的主要功能。

  • 组件的Proc()功能类似于阅读器的回调函数,该函数在消息到达时由框架调用。

使用组件的优点
  • 可以通过启动文件将组件加载到不同的进程中,并且部署非常灵活。

  • 组件可以通过修改dag文件来更改接收到的通道名称,而无需重新编译。

  • 组件支持接收多种类型的数据。

  • 组件支持提供多种融合策略。

3.Dag 文件格式

dag文件示例:

# Define all coms in DAG streaming.
module_config {
    module_library : "lib/libperception_component.so"
    components {
        class_name : "PerceptionComponent"
        config {
            name : "perception"
            readers {
                channel: "perception/channel_name"
            }
        }
    }
    timer_components {
        class_name : "DriverComponent"
        config {
            name : "driver"
            interval : 100
        }
    }
}
  • module_library:如果要加载.so库,则根目录是cyber的工作目录(与的相同目录setup.bash

  • components&timer_component:选择需要加载的基本组件类类型。

  • class_name:要加载的组件类的名称

  • name:加载的class_name作为加载示例的标识符

  • readers:当前组件接收的数据,支持1-3个数据通道。

演示-例子
Common_component_example(cyber/examples/common_component_example/*)

标头定义(common_component_example.h)

#include <memory>

#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "cyber/examples/proto/examples.pb.h"

using apollo::cyber::examples::proto::Driver;
using apollo::cyber::Component;
using apollo::cyber::ComponentBase;

class Commontestcomponent : public Component<Driver, Driver> {
 public:
  bool Init() override;
  bool Proc(const std::shared_ptr<Driver>& msg0,
            const std::shared_ptr<Driver>& msg1) override;
};
CYBER_REGISTER_COMPONENT(Commontestcomponent)

Cpp文件实现(common_component_example.cc)

#include "cyber/examples/common_component_smaple/common_component_example.h"

#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"

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

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

Timer_component_example(网络/示例/ timer_component_example / *)
标头定义(timer_component_example.h)

Timer_component_example(cyber/examples/timer_component_example/*)

标头定义(timer_component_example.h)

#include <memory>

#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "cyber/component/timer_component.h"
#include "cyber/examples/proto/examples.pb.h"

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

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

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

Cpp文件实现(timer_component_example.cc)

#include "cyber/examples/timer_component_example/timer_component_example.h"

#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "cyber/examples/proto/examples.pb.h"

bool TimertestComponent::Init() {
  driver_writer_ = node_->CreateWriter<Driver>("/carstatus/channel");
  return true;
}

bool TimertestComponent::Proc() {
  static int i = 0;
  auto out_msg = std::make_shared<Driver>();
  out_msg->set_msg_id(i++);
  driver_writer_->Write(out_msg);
  AINFO << "timertestcomponent: Write drivermsg->"
        << out_msg->ShortDebugString();
  return true;
}
编译和运行

使用timertestcomponent作为示例:

Build(构建): bazel build cyber/examples/timer_component_smaple/…

Run(运行): mainboard -d cyber/examples/timer_component_smaple/timer.dag

注意事项

需要注册组件才能通过SharedLibrary加载类。注册界面如下所示:

CYBER_REGISTER_COMPONENT(DriverComponent)

如果在注册时使用名称空间,则还需要在dag文件中定义名称空间时添加名称空间。

  • Component和TimerComponent的配置文件不同,请注意不要混淆两者。

6、Launch(启动)

cyber_launch是Cyber​​ RT框架的启动器。它根据启动文件启动多个主板,并根据dag文件将不同的组件加载到不同的主板中。cyber_launch支持两种方案,可在子进程中动态加载组件或启动二进制程序。

启动文件格式
<cyber>
    <module>
        <name>driver</name>
        <dag_conf>driver.dag</dag_conf>
        <process_name></process_name>
        <exception_handler>exit</exception_handler>
    </module>
    <module>
        <name>perception</name>
        <dag_conf>perception.dag</dag_conf>
        <process_name></process_name>
        <exception_handler>respawn</exception_handler>
    </module>
    <module>
        <name>planning</name>
        <dag_conf>planning.dag</dag_conf>
        <process_name></process_name>
    </module>
</cyber>

模块:每个加载的组件或二进制文件都是一个模块

  • name是加载的模块名称

  • dag_conf是组件的相应dag文件的名称

  • process_name是主板进程一旦启动的名称,process_name的相同组件将被加载并在同一进程中运行。

  • exception_handler是流程中发生异常时的处理程序方法。该值可以是下面列出的退出或重新生成。

    • 退出,这意味着当当前进程异常退出时,整个进程需要停止运行。

    • 重生,异常退出后需要重新启动当前进程。开始这个过程。如果没有空的东西,那就意味着没有治疗。可由用户根据过程的具体条件进行控制

计时器

7、Timer

8、Time API

9、Record file: Read and Write

10、C++ API Directory

1、Node
2、Writer
3、Client
4、Parameter
5、Timer
6、Time
7、Duration
8、Rate
9、RecordReader
10、RecordWriter
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值