0. 简介

eProsima Micro XRCE-DDS是一个实现由OMG定义和维护的DDS-XRCE协议的库,其目的是允许资源受限的设备(如微控制器)像任何其他DDS参与者一样与DDS世界进行通信。 它遵循客户端/服务器范例,由两个库组成,Micro XRCE-DDS 客户端和Micro XRCE-DDS Agent。Micro XRCE-DDS 客户端是轻量级实体,旨在在 eXtremely Resource C上编译,而 Micro XRCE-DDS 代理是将客户端与 DDS 世界连接起来的代理
在这里插入图片描述

PX4 已经使用 uXRCE-DDS 中间件,允许在配套计算机上发布和订阅uORB 消息,就像它们是ROS 2主题一样。这提供了 PX4 和 ROS 2 之间快速可靠的集成,并使 ROS 2 应用程序更容易获取车辆信息和发送命令。PX4 使用利用eProsima Micro XRCE-DDS 的XRCE-DDS 实现。我们可以来看看这个中间件以及具体使用

在这里插入图片描述

uXRCE-DDS 中间件由运行在 PX4 上的客户端和运行在配套计算机上的代理组成,它们之间通过串行或 UDP 链路进行双向数据交换。代理充当客户端的代理,使其能够发布和订阅全局 DDS 数据空间中的主题。为了使 PX4 uORB 主题在 DDS 网络上共享,您需要在 PX4 上运行uXRCE-DDS 客户端,并连接到在配套设备上运行的微型 XRCE-DDS 代理。

PX4 uxrce_dds_client内容发布到/从一组定义的 uORB 主题到全局 DDS 数据空间

micro XRCE-DDS-Agent在配套计算机上运行,​​并充当 DDS/ROS 2 网络中客户端的代理。

ros2xrcedds 这是一个用于与 ros2和 XRCEDDS 通信的库。它被用在 ros2duino 中,是一个中间库,可以很容易地适应其他裸平台。

Micro-XRCE-DDS-Client客户端独立版,代理不依赖于客户端代码。它可以独立构建,也可以在 ROS2工作区中构建,或者在 Ubuntu 上以快照包的形式安装。

micro-ROS Agent此存储库包含 Micro-ROS Agent 包。Micro-ROS 代理是包装 Micro XRCE-DDS 代理的 ROS2节点。该节点充当 DDS 网络和单片机内部的 Micro-ROS 节点之间的服务器。它接收和发送来自 Micro-ROS 节点的消息,并跟踪暴露给 ROS2网络的 Micro-ROS 节点。

Micro XRCE-DDS-GenMicroXRCE-DDS Gen 提供了一个工具,可以使用 MicroCDR 序列化库从 idl 文件生成序列化主题代码。虽然这个工具打算与 Micro XRCE-DDS Client 库一起使用,但是生成的代码不依赖于它。


1. XRCE-DDS基础和安装

在这里插入图片描述

Micro XRCE-DDS Gen工具,将*.idl文件配置信息生成对应目标代码;传输层可以使用TCP、UDP、Serial、Custom中的一种;在Client侧,使用MicroCDR库对数据进行序列化/反序列化操作;而在Agent侧,需要用FastCDR对数据进行序列化/反序列化操作。

1.1 安装 eProsima Micro XRCE-DDS 组件

您可以从 这里 下载包含预安装客户端和代理的 Micro XRCE-DDS 和 Fast-DDS Suite Docker 镜像,以及一些编译示例。有关该 Docker 镜像的更多信息,请访问 此处

1.2 独立安装代理

从 GitHub 克隆项目:

$ git config --global http.postBuffer 524288000
$ git clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git
$ cd Micro-XRCE-DDS-Agent
$ mkdir build && cd build

在 Linux 中,在 build 文件夹内执行以下命令:

$ cmake ..
$ make
$ sudo make install

在 Windows 中,首先选择 Visual Studio 版本:

$ cmake -G "Visual Studio 15 2017 Win64" ..
$ cmake --build .
$ cmake --build . --target install

请注意,eProsima Micro XRCE-DDS Agent 可在编译时通过多个 CMake 定义进行配置,具体信息请参考 配置 部分。


完成安装后,务必将 /usr/local/lib 添加到动态加载器链接目录中:

sudo ldconfig /usr/local/lib/
从 Snap 包安装

您也可以将 eProsima Micro XRCE-DDS Agent 作为 Snap 包 安装。只需在控制台执行以下命令:

sudo snap install micro-xrce-dds-agent

要下载对应于 develop 分支的 Snap 镜像,可以在安装命令中添加 --edge 标志。请注意,Snap 包仅适用于 Linux。

1.3 独立安装客户端

从 GitHub 克隆项目:

$ git clone https://github.com/eProsima/Micro-XRCE-DDS-Client.git
$ cd Micro-XRCE-DDS-Client
$ mkdir build && cd build

在 Linux 中,在 build 文件夹内执行以下命令:

$  cmake .. -DUCLIENT_BUILD_EXAMPLES=ON #  编译完成后在example文件夹生成了示例的可执行文件
$ make -j8
$ sudo make install

在这里插入图片描述

同样,在运行之前,请将 /usr/local/lib 添加到动态加载器链接目录中:

sudo ldconfig /usr/local/lib/

在 Windows 中,首先选择 Visual Studio 版本:

$ cmake -G "Visual Studio 15 2017 Win64" ..
$ cmake --build .
$ cmake --build . --target install

要安装 eProsima Micro XRCE-DDS Client 示例,请在 cmake .. 命令行选项中添加 -DUCLIENT_BUILD_EXAMPLES=ON

1.4 安装 Micro XRCE-DDS Gen 工具

首先安装依赖项,克隆项目并构建:

$ git clone https://github.com/eProsima/Micro-XRCE-DDS-Gen.git
$ cd Micro-XRCE-DDS-Gen
$ git submodule init
$ git submodule update
$ ./gradlew assemble

安装完成后,Micro XRCE-DDS-Gen 工具将可用:

$ ./scripts/microxrceddsgen -help

ros转xrce-dds可以使用https://github.com/Pansamic/uxrce-ros2-msgs这个项目。

1.5 同时安装代理和客户端

从 GitHub 克隆项目:

$ git clone https://github.com/eProsima/Micro-XRCE-DDS.git
$ cd Micro-XRCE-DDS
$ mkdir build && cd build

在 Linux 中,在 build 文件夹内执行以下命令:

$ cmake ..
$ make
$ sudo make install

在 Windows 中选择 Visual Studio 版本:

$ cmake -G "Visual Studio 15 2017 Win64" ..
$ cmake --build . --target install

现在,eProsima Micro XRCE-DDS AgenteProsima Micro XRCE-DDS Client 都已安装在系统中。要安装 eProsima Micro XRCE-DDS Gen 工具,请在 cmake .. 命令行选项中添加 -DUXRCE_ENABLE_GEN=ON。要安装示例,请添加 -DUXRCE_BUILD_EXAMPLES=ON

具体和PX4相关的工作已经写的比较明白了,可以参考这篇文章:Ubuntu20.04安装ROS2+ROS2-PX4框架搭建或者 Ubuntu搭建PX4无人机仿真环境(5)

2. 使用 Docker 编译 Micro-XRCE-DDS 代理

为了方便修改 XRCE-DDS 代理,特地编译了一个镜像用于编译代理。以下是详细步骤:

2.1 运行 Docker 镜像

使用以下命令来运行 Docker 镜像:

docker run -itd -p 2019:2019/udp -v agent-build-files:/root/files --name agent-build szc188/micro-xrce-dds-agent-build-env:v2.1.1 /bin/bash

2.2 取消默认的代理

在 VSCode 的终端中执行以下命令,以取消 Git 的默认代理设置:

git config --global --unset http.proxy
git config --global --unset https.proxy

2.3 安装必要的工具

更新包列表并安装 net-tools

apt update
apt install -y net-tools

2.4 拉取源代码

在终端中执行以下命令以克隆 Micro-XRCE-DDS-Agent 的源代码:

cd /root/files
git clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git -b v2.1.1

2.5 编译源代码

进入源代码目录并进行编译:

cd Micro-XRCE-DDS-Agent && mkdir build && cd build
cmake ..
make

2.6 运行代理

使用以下命令来运行编译好的代理:

./MicroXRCEAgent udp4 -p 2019

2.7 修改客户端 Topic 名称以兼容 ROS2 防冲突

为了确保局域网内的 Topic 名称不会重复,需要根据本机的序列号定制 Topic 名称。以下是修改步骤:

2.7.1 修改 FastDDSMiddleware.cpp
  • 打开文件:files/Micro-XRCE-DDS-Agent/src/cpp/middleware/fastdds/FastDDSMiddleware.cpp
  • 搜索 bool FastDDSMiddleware::create_topic_by_xml(
  • 在找到的 if 语句内添加以下代码:
extern std::string chiplink_sn;
std::string str("rt/");
str += chiplink_sn + "/";
str += attrs.topicName;
attrs.topicName = str.c_str();
std::cout << "####### attrs.getTopicName(): " << attrs.getTopicName() << std::endl;
2.7.2 修改 FastDDSEntities.cpp
  • 打开文件:files/Micro-XRCE-DDS-Agent/src/cpp/middleware/fastdds/FastDDSEntities.cpp
  • 搜索 bool FastDDSDataWriter::create_by_xml(const std::string& xml)
  • if 语句中添加以下代码:
extern std::string custom_sn;
std::string str("rt/");
str += custom_sn + "/";
str += attrs.topic.topicName;
attrs.topic.topicName = str.c_str();
std::cout << "####### FastDDSDataWriter.attrs.topic.topicName: " << attrs.topic.topicName << std::endl;
  • 同样修改 bool FastDDSDataReader::create_by_xml(const std::string& xml) 中的 if 语句,添加相同的代码。
2.7.3 修改 microxrce_agent.cpp
  • 打开文件:files/Micro-XRCE-DDS-Agent/microxrce_agent.cpp
  • int main(int argc, char** argv) 上方添加以下代码:
std::string custom_sn = "test_sn";

这样就完成了对客户端 Topic 名称的定制。

3. FreeRTOS + LWIP 工程中移植 Micro-XRCE-DDS

3.1 准备工作

在进行移植之前,准备一个 FreeRTOS + LWIP 工程,并创建两个文件夹:microddsmicrocdr。然后开始移植工作。

3.2 开始移植

1. 文件拷贝

(1)拷贝 Micro-XRCE-DDS-Client 代码

从第三章下载的 Micro-XRCE-DDS-Client 代码包中,复制以下文件到已有工程的 microdds 文件夹中:

  • src 文件夹
  • include 文件夹
  • examples 文件夹

(2)拷贝 Micro-XRCE-DDS-Agent 代码

从 Micro-XRCE-DDS-Agent 代码包中,复制以下文件到已有工程的 microcdr 文件夹中:

  • src 文件夹
  • include 文件夹

完成上述步骤后,已有工程的目录结构应如下所示:

2. 调整代码

(1)修改 config.h.in

microcdr 中找到 config.h.in 文件,将其重命名为 config.h,并根据需要调整文件中的内容。

修改前:

修改文件名后,调整文件中的内容:

(2)删除多余的头文件

microdds/include 文件夹中,删除与 UDP 无关的头文件。

microdds/src 文件夹中,删除与 UDP 无关的源文件,仅保留与 UDP 相关的文件。

删除后的文件内容:

(3)处理 config.h.in

microdds/include 文件夹中,将 config.h.in 修改为 config.h 文件,并手动调整文件内容。

手动修改文件中的内容:

(4)保留必要的例程

microdds/examples 文件夹中,删除其他不必要的例程,仅保留 SubscribeHelloWorld 例程。

3. 例程测试

(1)重命名例程的 main 函数

SubscribeHelloWorld 例程中的 main 文件名和 main 函数重命名,以避免与工程中的其他 main 函数冲突。

(2)调整 main 函数内容

根据项目需求,调整 main 函数的内容,以适应 FreeRTOS + LWIP 环境。

(3)调用示例函数

调整完毕后,在工程的函数中调用 example_main() 来启动示例。

提示:

  • 确保例程的 IP 地址与 Agent 的 IP 地址在同一网段。
  • 确保端口号与 Agent 监听的端口一致。

4. 代码阅读

PublishHelloWorld

#include "HelloWorld.h"

#include <uxr/client/client.h>
#include <ucdr/microcdr.h>

#include <stdio.h> //printf
#include <string.h> //strcmp
#include <stdlib.h> //atoi

#define STREAM_HISTORY  8
#define BUFFER_SIZE     UXR_CONFIG_UDP_TRANSPORT_MTU* STREAM_HISTORY

int main(
        int args,
        char** argv)
{
    // CLI配置, 检查参数数量,确保传入IP和端口有效
    if (3 > args || 0 == atoi(argv[2]))
    {
        printf("usage: program [-h | --help] | ip port [<max_topics>]\n");
        return 0;
    }

    char* ip = argv[1]; // 获取命令行输入的IP地址
    char* port = argv[2]; // 获取命令行输入的端口号
    uint32_t max_topics = (args == 4) ? (uint32_t)atoi(argv[3]) : UINT32_MAX; // 最大主题数,可选参数

    // 传输设置,通过UDP初始化传输结构体
    uxrUDPTransport transport;
    if (!uxr_init_udp_transport(&transport, UXR_IPv4, ip, port))
    {
        printf("Error at create transport.\n"); // 错误处理:创建传输失败
        return 1;
    }

    // 会话设置,初始化会话并创建
    uxrSession session;
    uxr_init_session(&session, &transport.comm, 0xAAAABBBB); // 初始化会话ID
    if (!uxr_create_session(&session))
    {
        printf("Error at create session.\n"); // 错误处理:创建会话失败
        return 1;
    }

    // 流配置,用于设置可靠流的发送和接收缓冲区
    uint8_t output_reliable_stream_buffer[BUFFER_SIZE];
    uxrStreamId reliable_out = uxr_create_output_reliable_stream(&session, output_reliable_stream_buffer, BUFFER_SIZE,
                    STREAM_HISTORY);

    uint8_t input_reliable_stream_buffer[BUFFER_SIZE];
    uxr_create_input_reliable_stream(&session, input_reliable_stream_buffer, BUFFER_SIZE, STREAM_HISTORY);

    // 创建参与者实体
    uxrObjectId participant_id = uxr_object_id(0x01, UXR_PARTICIPANT_ID);
    const char* participant_xml = "<dds>"
            "<participant>"
            "<rtps>"
            "<name>default_xrce_participant</name>" // 参与者名称
            "</rtps>"
            "</participant>"
            "</dds>";
    uint16_t participant_req = uxr_buffer_create_participant_xml(&session, reliable_out, participant_id, 0,
                    participant_xml, UXR_REPLACE);

    // 创建主题实体
    uxrObjectId topic_id = uxr_object_id(0x01, UXR_TOPIC_ID);
    const char* topic_xml = "<dds>"
            "<topic>"
            "<name>HelloWorldTopic</name>" // 主题名称
            "<dataType>HelloWorld</dataType>" // 数据类型
            "</topic>"
            "</dds>";
    uint16_t topic_req = uxr_buffer_create_topic_xml(&session, reliable_out, topic_id, participant_id, topic_xml,
                    UXR_REPLACE);

    // 创建发布者实体
    uxrObjectId publisher_id = uxr_object_id(0x01, UXR_PUBLISHER_ID);
    const char* publisher_xml = ""; // 发布者XML(此处为空)
    uint16_t publisher_req = uxr_buffer_create_publisher_xml(&session, reliable_out, publisher_id, participant_id,
                    publisher_xml, UXR_REPLACE);

    // 创建数据写入器(DataWriter)实体
    uxrObjectId datawriter_id = uxr_object_id(0x01, UXR_DATAWRITER_ID);
    const char* datawriter_xml = "<dds>"
            "<data_writer>"
            "<topic>"
            "<kind>NO_KEY</kind>"
            "<name>HelloWorldTopic</name>" // 数据写入器绑定到主题
            "<dataType>HelloWorld</dataType>"
            "</topic>"
            "</data_writer>"
            "</dds>";
    uint16_t datawriter_req = uxr_buffer_create_datawriter_xml(&session, reliable_out, datawriter_id, publisher_id,
                    datawriter_xml, UXR_REPLACE);

    // 发送创建实体消息并等待状态
    uint8_t status[4];
    uint16_t requests[4] = { participant_req, topic_req, publisher_req, datawriter_req };
    if (!uxr_run_session_until_all_status(&session, 1000, requests, status, 4))
    {
        // 错误处理:如果未能成功创建所有实体
        printf("Error at create entities: participant: %i topic: %i publisher: %i datawriter: %i\n", status[0],
                status[1], status[2], status[3]);
        return 1;
    }

    // 写主题循环,将指定数量的话题写入
    bool connected = true; // 保持连接状态
    uint32_t count = 0; // 计数器
    while (connected && count < max_topics)
    {
        HelloWorld topic = {
            ++count, "Hello DDS world!" // 增加计数并定义消息内容
        };

        ucdrBuffer ub; // 定义一个序列化缓冲区
        uint32_t topic_size = HelloWorld_size_of_topic(&topic, 0); // 获取消息大小
        uxr_prepare_output_stream(&session, reliable_out, datawriter_id, &ub, topic_size); // 准备输出流
        HelloWorld_serialize_topic(&ub, &topic); // 序列化主题对象

        printf("Send topic: %s, id: %i\n", topic.message, topic.index); // 输出发送的信息
        connected = uxr_run_session_time(&session, 1000); // 按时间运行会话
    }

    // 清理资源
    uxr_delete_session(&session); // 删除会话
    uxr_close_udp_transport(&transport); // 关闭UDP传输层

    return 0; // 程序正常结束
}

…详情请参照古月居

UORB和DDS相关的通信机制,主要包括创建数据读写实体,并按照一定的QoS策略进行配置。同时根据主题名称的生成规则与网络流进行分发,通过检测请求结果来判断各个操作是否成功。

#pragma once

#include <uxr/client/client.h>
#include <ucdr/microcdr.h>

#include <uORB/topics/uORBTopics.hpp>

#define TOPIC_NAME_SIZE 128

// 将给定的 orb_id 和实例转换为 uxrObjectId
uxrObjectId topic_id_from_orb(ORB_ID orb_id, uint8_t instance = 0)
{
	if (orb_id != ORB_ID::INVALID) {
		uint16_t id = static_cast<uint8_t>(orb_id) + (instance * UINT8_MAX);
		uxrObjectId topic_id = uxr_object_id(id, UXR_TOPIC_ID); // 生成并返回用于主题标识的对象ID
		return topic_id;
	}

	return uxrObjectId{}; // 返回一个无效的对象ID
}

// 根据客户端命名空间和方向生成主题名称
static bool generate_topic_name(char *topic, const char *client_namespace, const char *direction, const char *name)
{
	if (client_namespace != nullptr) { // 如果客户端命名空间不为空
		int ret = snprintf(topic, TOPIC_NAME_SIZE, "rt/%s/fmu/%s/%s", client_namespace, direction, name);
		return (ret > 0 && ret < TOPIC_NAME_SIZE); // 检查生成是否成功,且没有超过最大缓冲区大小
	}

	// 如果没有客户端命名空间则使用默认格式生成主题名称
	int ret = snprintf(topic, TOPIC_NAME_SIZE, "rt/fmu/%s/%s", direction, name);
	return (ret > 0 && ret < TOPIC_NAME_SIZE);
}

// 创建数据写入者
static bool create_data_writer(uxrSession *session, uxrStreamId reliable_out_stream_id, uxrObjectId participant_id,
			       ORB_ID orb_id, const char *client_namespace, const char *topic_name_simple, const char *type_name,
			       uxrObjectId &datawriter_id)
{
	char topic_name[TOPIC_NAME_SIZE]; // 存放主题名称

	// 生成主题名称
	if (!generate_topic_name(topic_name, client_namespace, "out", topic_name_simple)) {
		PX4_ERR("topic path too long"); // 输出错误日志:主题路径过长
		return false;
	}

	uxrObjectId topic_id = topic_id_from_orb(orb_id); // 从orb_id获取主题ID
	uint16_t topic_req = uxr_buffer_create_topic_bin(session, reliable_out_stream_id, topic_id, participant_id, topic_name,
			     type_name, UXR_REPLACE); // 在会话中创建主题请求


	// 创建发布者
	uxrObjectId publisher_id = uxr_object_id(topic_id.id, UXR_PUBLISHER_ID); 
	uint16_t publisher_req = uxr_buffer_create_publisher_bin(session, reliable_out_stream_id, publisher_id, participant_id,
				 UXR_REPLACE); // 创建发布者请求


	// 数据写入者ID
	datawriter_id = uxr_object_id(topic_id.id, UXR_DATAWRITER_ID);

	uxrQoS_t qos = { // 设置质量服务(QoS)参数
		.durability = UXR_DURABILITY_TRANSIENT_LOCAL, // 确保数据在本地持久
		.reliability = UXR_RELIABILITY_BEST_EFFORT, // 优先发送
		.history = UXR_HISTORY_KEEP_LAST, // 保留最近的数据历史
		.depth = 0, // 深度设为0表示无限制
	};

	uint16_t datawriter_req = uxr_buffer_create_datawriter_bin(session, reliable_out_stream_id, datawriter_id, publisher_id,
				  topic_id, qos, UXR_REPLACE); // 创建数据写入者请求

	// 发送创建实体消息并等待其状态
	uint16_t requests[3] {topic_req, publisher_req, datawriter_req}; // 请求数组
	uint8_t status[3]; // 状态反馈数组

	if (!uxr_run_session_until_all_status(session, 1000, requests, status, 3)) { // 执行会话并检查所有请求的状态
		PX4_ERR("create entities failed: %s, topic: %i publisher: %i datawriter: %i",
			topic_name, status[0], status[1], status[2]); // 输出失败信息
		return false;
	} else {
		PX4_INFO("successfully created %s data writer, topic id: %d", topic_name, topic_id.id); // 成功创建提示
	}

	return true; // 成功返回true
}

// 创建数据读取器
static bool create_data_reader(uxrSession *session, uxrStreamId reliable_out_stream_id, uxrStreamId input_stream_id,
			       uxrObjectId participant_id, uint16_t index, const char *client_namespace, const char *topic_name_simple,
			       const char *type_name, uint16_t queue_depth)
{
	char topic_name[TOPIC_NAME_SIZE]; // 存放主题名称

	// 生成主题名称
	if (!generate_topic_name(topic_name, client_namespace, "in", topic_name_simple)) {
		PX4_ERR("topic path too long"); // 输出错误日志:主题路径过长
		return false;
	}

	uint16_t id = index + 1000; // 给定索引偏移量id,用于唯一标识

	uxrObjectId topic_id = uxr_object_id(id, UXR_TOPIC_ID); // 使用偏移量生成UCPS主题ID
	uint16_t topic_req = uxr_buffer_create_topic_bin(session, reliable_out_stream_id, topic_id, participant_id, topic_name,
			     type_name, UXR_REPLACE); // 创建主题请求


	// 创建订阅者
	uxrObjectId subscriber_id = uxr_object_id(id, UXR_SUBSCRIBER_ID);
	uint16_t subscriber_req = uxr_buffer_create_subscriber_bin(session, reliable_out_stream_id, subscriber_id,
				  participant_id, UXR_REPLACE); // 创建订阅者请求


	// 数据读取器ID
	uxrObjectId datareader_id = uxr_object_id(id, UXR_DATAREADER_ID);

	uxrQoS_t qos = { // 设置质量服务(QoS)参数
		.durability = UXR_DURABILITY_VOLATILE, // 不保证持久性
		.reliability = UXR_RELIABILITY_BEST_EFFORT, // 优先发送
		.history = UXR_HISTORY_KEEP_LAST, // 保留最近的数据历史
		.depth = queue_depth, // 队列深度指定为传入参数
	};

	uint16_t datareader_req = uxr_buffer_create_datareader_bin(session, reliable_out_stream_id, datareader_id,
				  subscriber_id, topic_id, qos, UXR_REPLACE); // 创建数据读取器请求

	uint16_t requests[3] {topic_req, subscriber_req, datareader_req}; // 请求数组
	uint8_t status[3]; // 状态反馈数组

	if (!uxr_run_session_until_all_status(session, 1000, requests, status, 3)) { // 执行会话并检查所有请求的状态
		PX4_ERR("create entities failed: %s %i %i %i", topic_name,
			status[0], status[1], status[2]); // 输出失败信息
		return false;
	}

	uxrDeliveryControl delivery_control{}; // 创建交付控制结构体
	delivery_control.max_samples = UXR_MAX_SAMPLES_UNLIMITED; // 最大样本数量设置为无限制
	uxr_buffer_request_data(session, reliable_out_stream_id, datareader_id, input_stream_id, &delivery_control); // 请求数据接收

	return true; // 成功返回true
}

uORB—> ROS2

ROS 2 需要与 PX4 固件中创建 uXRCE-DDS 客户端模块所使用的消息定义相同,以便正确解析消息。这些定义存储在 ROS 2 接口包 PX4/px4_msgs 中,并由 CI 在主分支和发布分支上自动同步。请注意,PX4 源代码中的所有消息都存在于该存储库中,但只有在PX4-Autopilot/src/modules/uxrce_dds_client/dds_topics.yaml中列出的消息才会作为 ROS 2 主题可用。

如果您正在创建或修改 uORB 消息,您必须手动从您的 PX4 源代码树中更新工作区中的消息。通常,这意味着您需要更新 dds_topics.yaml,克隆接口包,然后通过将 PX4-Autopilot/msg 中的新/修改的消息定义手动同步到其 msg 文件夹中。假设 PX4-Autopilot 位于您的主目录 ~ 中,而 px4_msgs 位于 ~/px4_ros_com/src/ 中,则命令可能是:

rm ~/px4_ros_com/src/px4_msgs/msg/*.msg
cp ~/PX4-Autopilot/mgs/*.msg ~/px4_ros_com/src/px4_msgs/msg/

在这里插入图片描述

5. 参考链接

https://docs.px4.io/main/en/middleware/uxrce_dds.html

https://micro-xrce-dds.docs.eprosima.com/en/latest/installation.html

https://blog.csdn.net/gitblog_01184/article/details/141740487

https://blog.csdn.net/weixin_55944949/article/details/140627640

https://blog.csdn.net/HuskieMe/article/details/129713130

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐