fastDDS学习记录

一. Windows系统下fastDDS使用

  1. 官网下载fastDDS,已经是编译好的二级制安装文件,安装好之后,对应的fastDDS gen和需要的环境变量已经自动装好了。
  2. 直接按照网上教程,先写一个idl后缀文件,用来设置传输的数据类型
struct HelloSeven
{
    string sevenData;
};
  1. 然后运行:fastddsgen -example CMake HelloSevenPubSubMain.idl,不出意外,报错了
    报错1

    查了一下,cl.exe文件已经装好了,应该是cl.exe的环境变量没设置好,搜了一下,github上有一样的问题,答案里有解决方案。可以通过vs里的命令行(工具—>命令行—>开发者命令工具)来执行这个指令,成功,生成了一系列文件。
    VS命令行工具.png
  2. 这代表已经用fastddsgen工具生成了工程文件了,下一步就是编译这个工程文件。摸索了好几次,还是用最原始的方法,就是先拿cmake软件make一下这个工程,报错,报的错是系统没有openssl的环境变量,查了一下,系统没有装openssl,去官网下载,开始装了个light版,不行,必须装完整版。装完之后,添加三个环境变量OPENSSL_ROOT_DIR OPENSSL_CRYPTO_LIBRARY OPENSSL_INCLUDE_DIR
    再编译,通过了。
    参考代码如下
mkdir build
cd build
cmake ..
  1. 中间cmake --build时,还有报错,“MSBuild version 17.6.3+07e294721 for .NET Framework
    MSBUILD : error MSB1009: 项目文件不存在。”在系统变量path中添加msbuild的路径“C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild”就好了。
  2. 用vs打开工程,生成解决方案,生成了一个exe文件,叫HelloSevenPubSubMain.exe和一个lib文件。
  3. 运行示例项目,用这个exe文件给上不同的参数,分别运行publisher和subscriber,成功


    publisher.png

    subscriber.png

2. 用docker运行fastdds

在Windows上安装docker-desktop。然后去下载fastdds的image,fastdds官网有,下载下来是一个tar文件。然后把这个image载入进docker中,在tar文件所在文件夹运行:

docker load -i "ubuntu-fastdds <FastDDS-Version>.tar"

双引号里是tar文件的名称,改成实际文件的名称。
这时候去docker-desktop里面去看,可以看到image里已经有了这个image了。然后就是用这个image启动一个container,运行指令

docker run -it --rm --network=host --ipc=host <docker-image>

其中的<docker-image>用实际image名替换,可以去docker-desktop里去复制,

复制image名
这样就启动了这个fastdds的image。network和host的两个参数,是为了容器之间共享内存,可以使得容器之间通过共享内存通讯,详见官方说明1.1. Leveraging Fast DDS SHM in Docker deployments — Fast DDS 2.13.1 documentation (eprosima.com)
再根据官网指示,运行一个image内部自带的例程

root@docker-desktop:/usr/local/eprosima/fastrtps/examples/cpp/dds/HelloWorldExample/bin# tmux new-session "./DDSHelloWorldExample publisher 0 1000" \; split-window "./DDSHelloWorldExample subscriber" \; select-layout even-vertical

效果如下

例程运行效果
这是在同一个container中subscriber和publisher同时运行并通讯。
接着测试了开启两个container分别做publisher和subscriber,也成功进行了通讯。
例程参考1.2. Fast DDS Image — Fast DDS 2.13.1 documentation (eprosima.com)

FastDDS共享内存shm模式

按照官方教程1.1. Leveraging Fast DDS SHM in Docker deployments — Fast DDS 2.13.2 documentation (eprosima.com),测试有docker端参与的共享内存方式下的dds通讯。
用官方的这个docker配置方法

docker run -it --rm --network=host --ipc=host --name cont1 <image name>

启动俩容器之后,运行共享内存专用测试程序HelloWorldExampleSharedMem,俩容器正常收发,通讯正常。
用--ipc=shareable和--ipc=container:cont1方式启动俩互相共享内存的容器后,

docker run -it --rm --network=host --ipc=shareable --name cont1 <image name>
docker run -it --rm --network=host --ipc=container:cont1 <image name>

共享内存的dds程序可以跑通,俩容器正常收发。
之前启动容器时,没有加--network=host,跑这个共享内存的fastdds就不通。看来跟网络还有关系,所以必须网络配置成host模式。在上边链接里的官方教程中也有相关说明。
两个容器的共享内存通讯通了,但是测试容器和windows共享内存跑FastDDS还是不行,应该是因为Windows和docker容器中间还隔了一个wsl,并没有共享内存。

两容器通讯

在默认bridge的网络模式下启动两个容器,hello worldexample运行,两容器通讯成功。

容器和wsl2的互联互通

  1. 在默认bridge网络模式下启动容器,然后在wsl2上启动DDSHelloWorldExample,作为publisher,在容器上运行subscriber,无法连通。
  2. 用host网络模式下启动容器,然后测试与wsl2进行dds通讯,还是无法联通。

linux下docker 容器运行fastDDS

ubuntu的宿主机运行一个FastDDS的docker容器,容器网络设置为host模式,即启动时候用

docker run -it --rm --network=host --ipc=host <docker-image>

在局域网另一台的Windows机中运行helloworldexample程序,一边发布,一边订阅,容器中程序成功实现同Windows系统通讯。注意要关闭防火墙,或者单独设置防火墙规则。

三.用ros常用的一组msg文件构建fastDDS的发布与订阅c++工程

拿到如下的接口数据结构描述文件夹

F:.
│  CMakeLists.txt
│  darknetf.txt
│  package.xml
│  
└─msg
        DeadZoneOccupancyGrid.msg
        GirdNormalInfo.msg
        GirdNormalInfo2.msg
        LeaderVehicle.msg
        LqEntryState.msg
        LqOccupancyGrid.msg
        NoObstacleFlag.msg
        OccupancyGridInfo.msg
        PercepVehicleState.msg
        TargetGlobal.msg
        TargetGlobalInfo.msg
        TargetLocal.msg
        TargetLocalInfo.msg
        UnderWaterOccupancyGrid.msg
        VehiclePose.msg
        WaterOccupancyGrid.msg

数据是嵌套构造的,我们需要的最外层数据是WaterOccupancyGrid.msg定义的数据。从msg文件生成idl可以通过ros2系统packages构建过程附带完成,也可以通过构建脚本手动生成,在chat某某T的协助写,编写了python脚本,将msg文件批量转换为了idl文件。

import os

# 定义类型映射关系
type_mapping = {
    'bool': 'boolean',
    'int8': 'int8',
    'uint8': 'uint8',
    'int16': 'int16',
    'uint16': 'uint16',
    'int32': 'int32',
    'uint32': 'uint32',
    'int64': 'int64',
    'uint64': 'uint64',
    'float32': 'float',
    'float64': 'double',
    'string': 'string'
}

def convert_msg_type_to_idl_type(msg_type):
    """将ROS 2的消息类型转换为IDL类型"""
    if msg_type in type_mapping:
        return type_mapping[msg_type]
    elif msg_type.endswith("[]"):  # 处理数组
        base_type = msg_type[:-2]
        return f"sequence<{type_mapping.get(base_type, base_type)}>"
    else:
        return msg_type  # 自定义类型保持不变

def convert_msg_file_to_idl(msg_file, idl_file):
    """将一个.msg文件转换为.idl文件"""
    print(f"Converting {msg_file} to {idl_file}")
    with open(msg_file, 'r', encoding='utf-8') as msg_f, open(idl_file, 'w', encoding='utf-8') as idl_f:
        msg_name = os.path.splitext(os.path.basename(msg_file))[0]
        
        # 写入IDL文件的头
        idl_f.write(f"module {msg_name} {{\n")
        idl_f.write("  struct Msg {\n")

        # 读取每一行并转换为IDL格式
        for line in msg_f:
            line = line.strip()
            if not line or line.startswith('#'):  # 忽略空行和注释
                continue
            parts = line.split()
            if len(parts) < 2:
                print(f"Skipping invalid line: {line}")
                continue
            msg_type, msg_field = parts[0], parts[1]
            idl_type = convert_msg_type_to_idl_type(msg_type)
            idl_f.write(f"    {idl_type} {msg_field};\n")
        
        # 写入结构体和模块结束符
        idl_f.write("  };\n")
        idl_f.write("};\n")
        print(f"Finished writing {idl_file}")

def convert_all_msg_files_to_idl(msg_dir, idl_dir):
    """将一个目录下的所有.msg文件转换为.idl文件"""
    if not os.path.exists(idl_dir):
        os.makedirs(idl_dir)
    
    for root, dirs, files in os.walk(msg_dir):
        for file in files:
            if file.endswith('.msg'):
                msg_file = os.path.join(root, file)
                idl_file = os.path.join(idl_dir, file.replace('.msg', '.idl'))
                print(f"Converting {msg_file} to {idl_file}")
                convert_msg_file_to_idl(msg_file, idl_file)

if __name__ == "__main__":
    # 设置msg文件目录和生成的idl文件目录
    msg_directory = './msg'  # 你的.msg文件目录
    idl_directory = './idl'  # 输出.idl文件的目录

    # 执行转换
    convert_all_msg_files_to_idl(msg_directory, idl_directory)

执行完转换之后,还需要解决数据结构在不同idl文件之间嵌套的问题,本文第一章节的内容是针对单个idl文件的工程生成,需要进行针对性的修正工作,才能针对多idl文件生成工程。

  1. 要将嵌套引用的idl文件include进idl文件中,然后要保证同一个工程的module名相同,struct名区分,
#include "GirdNormalInfo.idl" //结构体的第一个元素GirdNormalInfo是自定义数据格式,需要include该结构体所在的GirdNormalInfo.idl文件
module WaterOccupancyGrid {
  struct GirdNormalInfo2 {
    GirdNormalInfo info;
    uint16 us_height;
    uint16 uc_target_type;
  };
};
  1. fastddsgen指令使用时,需要将所有用到的idl文件全部放入指令中
fastddsgen -example CMake GirdNormalInfo.idl UniHeader.idl OccupancyGridInfo.idl VehiclePose.idl GirdNormalInfo2.idl WaterOccupancyGrid.idl

这样就生成了发布和订阅的工程源文件,再通过本文第一章节的编译生成过程,就能实现发布和订阅功能了。中间有一些小问题,调试后发布和订阅功能正常。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容