Fastdds学习分享_xtpes_发布订阅模式及rpc模式

news2025/2/6 4:14:40

        在之前的博客中我们介绍了dds的大致功能,与组成结构。本篇博文主要介绍的是xtypes.分为理论和实际运用两部分.理论主要用于梳理hzy大佬的知识,对于某些一带而过的部分作出更为详细的阐释,并在之后通过实际案例便于理解。案例分为普通发布订阅模式与rpc模式。原博客地址:https://zhuanlan.zhihu.com/p/700132625

目录

xtypes是什么?

自定义类型相关的发送/接收接口

数据筛选

类型规范是不同DDS产品互联互通的基础

        静态模式

优势

劣势

类型描述

2.1. 类型描述

Nested

key

为什么 @key 重要?

id

optional

Extensibility

基础类型

interface

容器

使用流程

1.发布订阅模式

2.命令详细说明

3.RPC模式

这里先写一个demo

server

client

域ID

QoS

Transport

Timeout(RPC 调用超时)

Topic

Threading

FastDDS RPC 可配置参数总结


xtypes是什么?

        xtypes是 DDS(Data Distribution Service) 的一个扩展,提供了一种动态和静态数据类型管理机制.以数据为中心是DDS与其他消息中间件的一个重要的区别。它类似于ros的.msg文件但是更为强大。xtypes使得DDS表现的像能够理解业务数据一样。在hzy大佬的博客中总结了以下几点特性:

  • 自定义类型相关的发送/接收接口

  • 即提交给DDS和从DDS中获取的是主题关联的自定义数据结构对象。

  • 优势
    • 序列化/反序列化的工作从应用下沉到中间件,由中间件考虑端序/对齐/不同语言类型的转换;
    • 类型检查,在编译期即可检查出部分问题;
  • 劣势
    • 使用复杂,即便是简单的收发也需要IDL编译器编译支持代码;
  • 数据筛选

  • DDS提供类似于数据库的实时数据存储与查询的功能,包括:

    • 将主题数据按照key值组织,比如订阅端可以仅读取特定key值的数据;
    • 内容过滤,即订阅端可以配置只关心某个成员范围之间的值,DDS将自动过滤不属于这个范围的主题数据;
  • 类型规范是不同DDS产品互联互通的基础

    • 产品遵循相同的规范使得能够支持的数据类型互认;
    • 数据样本序列化方式规范使得A厂家的DDS产品序列化的数据可以由B厂家的DDS产品反序列化还原成相同类型的样本数据;

我们来理解一下是什么意思,首先自定义类型的发送/接收接口是什么意思?fastdds支持两种模式,静态模式需要对应的idl文件通过fastddsgen生成.hpp与xxxtypes.hpp文件。同时xtpes也支持动态类型来发送和接受数据此时无需idl文件.

        静态模式

        我们来简单看一下普通的静态模式的idl文件是如何编写的:

module state_and_error {
    // 错误码请求
    @extensibility(MUTABLE)
    struct ErrorCode {
        string code;                   // 错误码 (如 "1001", "1002")
    };

    // 错误码解析响应
    @extensibility(MUTABLE)
    struct ErrorCodeReply {
        string description;            // 错误码的解析描述
        string suggestion;             // 修复建议
    };

    // 错误处理接口
    interface ErrorHandle {
        
        ErrorCodeReply analyze_error(in ErrorCode error);
    };
}

        上面的module就类似于C++里面的namespace,里面还有个state碍于篇幅我就没放进来,看个原理就可以了。他这里面的消息单元就是用类似结构体的方式来进行编写的。extensibility这些后面会讲到,它用于支持数据扩展性。包括后面的interface,这些都会在后面的篇幅中讲到。这里看完了静态模式,我们来观看一下动态模式是如何编写的:

#include <fastdds/xtypes/dynamic_types/DynamicTypeBuilder.hpp>
#include <fastdds/xtypes/dynamic_types/DynamicData.hpp>

// 1. 创建动态数据类型
DynamicTypeBuilder* builder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
builder->add_member(0, "id", DynamicTypeBuilderFactory::get_instance()->create_int32_type());
builder->add_member(1, "name", DynamicTypeBuilderFactory::get_instance()->create_string_type());
DynamicType_ptr myType = builder->build();

// 2. 创建 `DynamicData` 数据对象
DynamicData* myData = DynamicDataFactory::get_instance()->create_data(myType);
myData->set_int32_value(0, 42);
myData->set_string_value(1, "Example");

// 3. 发送数据
dds_writer->write(myData);

        这个就相当于一个写在idl文件一个写在了程序里但是他们序列化都需要fastcdr支持。以下是静态和动态的一个对比表:

对比项静态 xtypes(IDL 编译)动态 xtypes(运行时创建)
定义方式通过 .idl 文件定义运行时动态定义
是否需要编译 IDL✅ 需要❌ 不需要
数据结构变化❌ 不能在运行时修改✅ 运行时可修改
类型检查✅ 编译期检查❌ 运行时检查
适合的应用场景实时性高、结构固定结构不固定、跨 DDS 版本兼容
序列化方式DDS CDR(默认高效)可用 JSON、CBOR、DDS CDR
性能更快(直接访问编译好的类型)稍慢(需要运行时解析类型)
ROS 2适配性✅ 是 ROS 2 默认方式❌ 目前 ROS 2 不支持动态 xtypes

        我这边建议使用静态模式,因为对于rpc模式来说,动态模式并不支持interface,并且他在传递性能上较动态模式更弱。但是如果你的数据结构是 “动态的” ,在运行时种类随时可能变化时动态模式也是较好的选择。但是有mutable其实也可以用静态的。

优势

  • 优势
    • 序列化/反序列化的工作从应用下沉到中间件,由中间件考虑端序/对齐/不同语言类型的转换;
    • 类型检查,在编译期即可检查出部分问题;

        这一部分是什么意思呢?在前面我们说了他的序列化是由fastcdr中间件完成的,对于我们程序编写就不用考虑序列化问题,但这也存在一个问题。比如说如果没有自定义序列化插件,将 Protobuf之类的序列化方式转换为 DDS 兼容的数据格式,那么他就不支持其他序列化协议。这种耦合有其好处也有其坏处。像ros1这种没有将序列化下沉到中间件而是用应用层来处理的,就可以通过sfinea机制来让他兼容protobuf.有好有坏吧。类型检查这些也不必多说,常规操作。

 劣势

  • 劣势
    • 使用复杂,即便是简单的收发也需要IDL编译器编译支持代码;

        这个怎么说呢,就是常用的静态 xtypes 使用复杂,即使只是简单的消息传输,也需要 IDL 编译。而且他编译器还挺搞的,dds版本很多有些编译器支持这种dds但是不支持其他dds。有些时候有些数据结构他最新的,自己版本的编译器又不支持。升级上去,可能自己的代码有些编译就会报错。建议用稳定的就行了别折腾了。

        下面的两种,在下文中会有提及,这里就不展开讲了。

类型描述

        

2.1. 类型描述

        类型描述定义开发语言无关的各种类型的语言以及结构,具体包含的类型参见上图,协议中规定DDS主题能够关联的数据类型只包括:结构体struct以及联合体union,其他类型则作为这两种聚合类型的成员。

        除了常规的类型/成员定义外,类型系统中还为类型或者成员添加了一些标签来提供额外的信息,常见的几个标签参见下表。

标签作用对象说明
Extensibility类型用于表明该类型的可扩展性,详见2.2.
Nested类型是否直接关联到DDS主题
key成员表明成员是否为键值
optional成员表明成员是否为可选
id成员指定成员的唯一ID
boundstring/sequence/map成员表明变长结构的长度上界,主要用于空间管理

        在我前面的例子中我们可以看到我只写了拓展性,因为这些其实都不是必填的,他们都是一些可选条件。如果我们要加上限制的话,我们可以这样写,看实际需要来写吧。

        

module state_and_error {
    @extensibility(APPENDABLE)
    struct State {
        @key int32 status;  // `status` 作为唯一标识
        @id(1) double current_x;
        @id(2) double current_y;
        @id(3) double current_theta;
        @optional double linear_velocity;  // 这个成员是可选的
        @optional double angular_velocity;  // 这个成员也是可选的
        @bound(255) string feedback_message;  // 限制字符串最大长度为 255
    };
};

下面来详细讲一下,这些标签。

Nested

        他是一个类型标签(Annotation),它用于指示该类型是否可以直接用作 DDS 主题(Topic),或者它是否只能作为其他数据类型的成员如果一个struct或union被标记为 @Nested,它不能直接作为 DDS 主题(Topic)发布或订阅,只能作为其他 struct 的成员来使用了。如果不加@Nested,默认情况下struct可以直接作为 DDS 主题使用。以下是代码案例:

struct Position {
    double x;
    double y;
    double z;
};

@Nested
struct State {
    int32 status;
    Position pos; // `@Nested` 使 `State` 只能作为 `struct` 的成员
};

key

        在 DDS 里,DDS 通过@key识别数据实例(Instance),@key 相同的数据会被认为是同一个对象,可以更新,不是新的消息。如果你不加@key,DDS 认为你的数据是无状态的消息流(类似于 UDP 广播),而如果你加了@key,DDS 就会把数据当作唯一标识的实例(类似数据库的主键)这句话怎么理解呢?当没有加@key的时候:

struct SensorData {
    int32 id;
    double temperature;
};

        DDS 认为所有SensorData消息是“独立的消息流”,不会追踪id是否重复。每个消息就像 UDP 广播,没有“实例管理”机制,接收方无法分辨两个数据是否属于同一个传感器。

加@key:

struct SensorData {
    @key int32 id;  // 传感器的唯一标识
    double temperature;
};

DDS 现在认为id相同的数据是同一个“实例”,它会:

  1. 缓存最后一次收到的 id = 1 的数据(类似数据库的 UPDATE)。也就是说如果 State 结构体有 @key id,那么 DDS 会按 id 分别存储不同的实例。如果 DDS 订阅者(Subscriber)已经收到 id = 1 的数据,再次收到 id = 1 的新数据时,DDS 只会 更新 id = 1 的数据,不会新增新的条目
  2. 自动删除旧数据(可以配置数据历史策略)。DDS 允许你配置“数据历史策略”(History QoS),决定保留多少条历史记录。如果配置KEEP_LAST(1) DDS 只会保存每个id的最新数据,旧数据会自动被删除。如果配置KEEP_ALL  DDS 会保留所有历史数据,不删除。
  3. 允许 QueryCondition 进行实例查询,比如“只订阅 id = 2 的数据”。

这里展示一下怎么配置只保留最新的

DataReaderQos qos;
qos.history().kind = KEEP_LAST_HISTORY_QOS;
qos.history().depth = 1;  // 只保留最新的一条数据
reader->set_qos(qos);
为什么 @key 重要?

        如果你加了@key,DDS 知道哪些数据属于同一个实例,可以做增量更新,而不是简单的消息广播。这句话就是说

  • 如果你加了@key,DDS 就会按照 key(通常是 id)来管理数据。
  • @key 让 DDS 认为 id 相同的数据是同一个对象的“状态更新”,可以进行增量更新(类似数据库的UPDATA)。

        如果你不加@key,每个消息都是“独立的”,无法做基于 ID 的筛选、历史记录管理或 QoS 策略。但是如果@key类型相同,其他类型不同,如果拓展性没有设置mutable那么就会报错。

id

        用于mutable可扩展性模式,确保新旧版本字段顺序不同也能正确解析数据。不会影响实例管理。如果不加@id,DDS 解析数据时只能按字段顺序匹配,无法正确解析字段新增、删除或重排的情况。这句话怎么理解呢?因为拓展性的mutable允许添加新的数据,那么就需要@id确保新旧版本的数据结构,即使字段顺序不同,DDS 仍然可以正确解析,而不会误解数据格式如果不加 @id,DDS 只能按照字段的顺序解析数据,这意味着:如果字段的顺序改变,旧版本可能解析错字段,导致数据错误。如果字段被删除或新增,旧版本可能会崩溃或丢弃数据。这样,即使新版本的数据结构发生了变化,旧版本仍然可以解析它能识别的字段,不会因字段顺序变化而导致错误!

举个例子

//旧数据
@extensibility(MUTABLE)
struct State {
    int32 status;
    double x;
    double y;
};
//新数据
@extensibility(MUTABLE)
struct State {
    int32 status;
    double y;  // ⚠️ 位置发生变化!
    double x;  // ⚠️ 位置发生变化!
};

这样就会出问题,但是如果加了@id呢?

@extensibility(MUTABLE)
struct State {
    @id(1) int32 status;
    @id(2) double x;
    @id(3) double y;
};


@extensibility(MUTABLE)
struct State {
    @id(1) int32 status;
    @id(3) double y;  // 位置变化了,但 `@id(3)` 让 DDS 知道它是 `y`
    @id(2) double x;  // 位置变化了,但 `@id(2)` 让 DDS 知道它是 `x`
};

这样就没问题了

optional

        他是在旧版本里面使用的,但是现在有拓展性的mutable,就没那么重要了。但是如果某个字段在新版本中可能为空,但旧版本的解析器不允许null值,optional让新系统的发布者可以选择是否发送该字段,避免影响旧系统。optional允许你在不影响旧版本的情况下逐步添加新功能。也就是说大部分时间是没用的。

Extensibility

        这一部分hzy大佬讲的非常详细,引用他的原文即可。需要了解更多dds知识的可以去上面博客去看看原博客,写的很不错。但是注意大佬写的是DDS规范,规范是一个宽泛的概念,各版本的dds具体实现可能略有不同。

DDS可扩展性分为3种,详见下表,为什么取名叫“类型演进”,因为基于APPENDABLE/MUTABLE可扩展性类型,原有系统无需做任何的代码、配置的修改,即可与新的系统(使用迭代后的新的数据类型)进行数据交互。

可扩展性说明
FINAL不可扩展,类型结构必须完全一致才能相互交换数据,用于保护已有系统。
APPENDABLE可追加,这种类型是默认的类型,新的类型是基于老的类型在后面添加成员得到,这种模式下新老数据结构关联的主题能够相互交换数据。
MUTABLE可随意变换,新的类型可将老的类型重新排序组合以及添加新的成员得到,这种模式下新老数据结构关联的主题能够相互交换数据。

FINAL可扩展性示意图

上图中下面蓝色部分代表已有运行系统,上面的橙色部分代表新建的系统,新建的发布/订阅应用将位置信息从原有的2个坐标修改为3个坐标,此时由于原有系统设置为FINAL的保护状态,新的应用无法集成到老的系统中去。

APPENDABLE可扩展性示意图

上图中下面蓝色部分代表已有运行系统,上面的橙色部分代表新建的系统,新建的发布/订阅应用将位置信息从原有的2个坐标修改为3个坐标,此时由于类型系统设置为APPENDABLE可扩展状态,老的应用不修改任何的配置以及代码,即可把新的发布/订阅应用集成到原有的系统中,老的订阅者(右下)将接收到新的发布者发布的数据,其中多出的z成员将被忽略,而新的订阅者应用(左上)将接收到老的发布端者发布的数据,其中缺少的z成员将赋予默认的值。

MUABLE可扩展性示意图

上图中下面蓝色部分代表已有运行系统,上面的橙色部分代表新建的系统,新建的发布/订阅应用将位置信息将原有的x、y坐标打乱并在中间插入一个新的成员z,此时由于类型系统设置为MUTABLE可扩展状态,老的应用不修改任何的配置以及代码,即可把新的发布/订阅应用集成到原有的系统中,老的订阅者(右下)将接收到新的发布者发布的数据,其中多出的z成员将被忽略,而新的订阅者应用(左上)将接收到老的发布端者发布的数据,其中缺少的z成员将赋予默认的值。

介绍到这里可能会产生一个疑问:既然能够支持MUTABLE类型,那所有的类型都设计成可变的类型,系统的可扩展性不就可以得到保证吗,为什么还需要支持前面两个类型?答案总结在下面的这张不同类型的优劣势中,不同类型可扩展性实现的关键技术在数据序列化中介绍。

可扩展性优势劣势
FINAL1、首先是安全,类似于Java里面把一个类声明为final禁止其他类型继承扩展;2、固定结构下数据序列化/反序列化效率高无可扩展性
MUTABLE具备很好的可扩展性结构可变带来底层序列化/反序列化需要携带更多的额外信息,导致效率变低
APPENDABLE1、具备一定的可扩展性;2、接近于固定结构序列化/反序列化效率高可扩展性有限

基础类型

idl和C++用的基本类型差不多:

类型描述示例
boolean布尔值(truefalseboolean is_active;
char单个字符(ASCII)char letter;
octet8-bit 无符号整数octet small_value;
int88-bit 有符号整数int8 small_number;
uint88-bit 无符号整数uint8 small_number;
int1616-bit 有符号整数int16 medium_number;
uint1616-bit 无符号整数uint16 medium_number;
int3232-bit 有符号整数int32 large_number;
uint3232-bit 无符号整数uint32 large_number;
int6464-bit 有符号整数int64 very_large_number;
uint6464-bit 无符号整数uint64 very_large_number;
float32-bit 单精度浮点数float temperature;
double64-bit 双精度浮点数double precise_value;

interface

        interface用于 DDS RPC(远程过程调用),类似 ROS 的 Service。后面会详细介绍

容器

FastDDS 的 xtypes 支持容器类型(Collection Types),包括:

  • sequence<T>(可变长度序列)
  • array<T, N>(固定大小数组)
  • map<K, V, N>(键值对映射,部分 DDS 实现支持)

    例子如下:

struct SensorReadings {
    sequence<float, 10> temperatures;  // 最多存储 10 个温度值
};
SensorReadings data;
data.temperatures().resize(5); // 运行时调整大小


struct Position {
    array<float, 3> coordinates;  // 3D 坐标 (x, y, z)
};
Position pos;
pos.coordinates()[0] = 1.0;
pos.coordinates()[1] = 2.0;
pos.coordinates()[2] = 3.0;


struct SensorMapping {
    map<string<10>, float, 5> sensor_data;  // 最多存储 5 个传感器数据
};
SensorMapping mapping;
mapping.sensor_data()["temperature"] = 36.5;
mapping.sensor_data()["humidity"] = 45.0;

使用流程

        

1.发布订阅模式

        我们先写idl文件,然后进入fastddsgen文件夹。

运行命令

./fastddsgen -language C++ path/to/xxx.idl -d path/to/output/

2.命令详细说明

参数作用示例
-language C++指定生成 C++ 代码(默认是 C++)./fastddsgen -language C++ xxx.idl
-d <output_path>指定输出目录./fastddsgen -d /home/user/generated_code xxx.idl
-replace覆盖旧文件,重新生成代码./fastddsgen -replace xxx.idl
-example <OS>生成完整示例(可选 Linux, Windows, Mac)./fastddsgen -example Linux xxx.idl
-help显示帮助信息./fastddsgen -help

        他会生成一系列代码。在写发布者订阅者的时候,需要.hpp文件与xxxPubSubTypes.hpp.首先需要注册类型,这里就是注册给cdr序列化协议的。

TypeSupport type_support(new Destination::Destination_sitePubSubType());
participant->register_type(type_support); 

之后就可以定义数据结构了

Destination::Destination_site data;
data.x(0);
data.y(0);

3.RPC模式

这里先写一个demo

module robot_control {
    interface RobotService {
        string get_status();
        boolean move_to(in double x, in double y, out string response_msg);
    };
};

然后我们用

fastddsgen -example C++ robot_service.idl -d /home/user/generated_code/

注意一下,有些版本他是用fastrpcgen来编译idl,需要注意一下。

他会生成

robot_controlRobotServiceProxy.hpp   // RPC 客户端(Proxy)
robot_controlRobotServiceServer.hpp  // RPC 服务端(Server)
robot_controlRobotServiceImpl.hpp    // 需要用户实现的服务逻辑
robot_controlRobotService.cxx        // FastDDS RPC 底层实现
robot_controlRobotServicePubSubTypes.hpp  // 数据类型支持

server

#include "robot_controlRobotServiceServer.hpp"

class RobotServiceImpl : public robot_control::RobotServiceServer
{
public:
    // 实现 get_status() 方法
    void get_status(::eprosima::fastdds::dds::StringType& _return) override
    {
        _return = "Robot is running";  // 返回状态信息
    }

    // 实现 move_to() 方法,返回是否移动成功
    bool move_to(double x, double y, ::eprosima::fastdds::dds::StringType& response_msg) override
    {
        std::cout << "Moving to: (" << x << ", " << y << ")" << std::endl;
        
        if (x >= 0 && y >= 0) // 只允许正坐标
        {
            response_msg = "Move successful!";
            return true; // 移动成功
        }
        else
        {
            response_msg = "Invalid target position.";
            return false; // 移动失败
        }
    }
};

int main()
{
    RobotServiceImpl robot_service;
    if (robot_service.run())
    {
        std::cout << "RPC Server is running..." << std::endl;
        while (true) { } // 保持运行
    }
    return 0;
}

        在 FastDDS RPC 生成的 C++ 代码中,IDL 里定义的返回值 在生成的 C++ 代码中会 被转换为void,并使用 out 参数_return 传递结果。这是 FastDDS RPC 代码生成的特性,用于避免额外的拷贝,提高性能。

client

#include "robot_controlRobotServiceProxy.hpp"

int main()
{
    robot_control::RobotServiceProxy client;
    if (client.run())
    {
        std::cout << "Connected to RPC Server!" << std::endl;

        // 远程调用 get_status()
        eprosima::fastdds::dds::StringType status;
        client.get_status(status);
        std::cout << "Robot Status: " << status << std::endl;

        // 远程调用 move_to(),获取返回值
        eprosima::fastdds::dds::StringType response_msg;
        bool result = client.move_to(10.5, 20.8, response_msg);

        std::cout << "Move Result: " << (result ? "Success" : "Failure") << std::endl;
        std::cout << "Server Response: " << response_msg << std::endl;

        client.stop();
    }
    return 0;
}

        在RPC模式下你无需创建主题,域参与者,qos之类的。fastddsrpc内部都会帮你搞定,你只要拥有相同的头文件即可。

普通 DDS 需要手动做的事情FastDDS RPC 自动管理
创建 DomainParticipant✅ FastDDS 自动创建
定义 Topic✅ FastDDS 自动创建
创建 PublisherSubscriber✅ FastDDS 自动创建
管理 RequestReply 的序列化✅ FastDDS 自动管理
匹配 ClientServerDomain ID✅ FastDDS 内部处理

        但与自动管理并不代表你不能设置,比如:

域ID

client.set_domain_id(5);  // 修改 Domain ID
server.set_domain_id(5);

QoS

FastDDS 允许你设置 QoS,控制 RPC 的可靠性、历史记录等。例如:

  • RELIABLE_RELIABILITY_QOS(可靠传输,确保请求不丢失)
  • KEEP_LAST_HISTORY_QOS(保留最近的 N 条历史记录)
  • TRANSIENT_LOCAL_DURABILITY_QOS(即使 Server 断开,Client 仍然能获取数据)
eprosima::fastdds::dds::QoSSettings qos;
qos.reliability(eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS);
qos.history(eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS);
client.set_qos(qos);

Transport

        默认情况下,FastDDS 使用 UDP 进行通信。如果你想强制使用 TCP,可以这样配置:

eprosima::fastdds::dds::TransportConfig transport;
transport.use_tcp(true);
client.set_transport(transport);

Timeout(RPC 调用超时)

        如果Client调用Server超时(Server可能崩溃或网络异常),默认 FastDDS 不会一直等待,可以设置超时时间:

client.set_timeout(std::chrono::milliseconds(5000));  // 5 秒超时

如果 5 秒内 Server没有响应,RPC 调用会失败并返回错误

Topic

client.set_topic_name("MyCustomTopic");

如果你想同时运行多个不同的 RPC 服务,可以用不同的Topic进行隔离

Threading

eprosima::fastdds::dds::ThreadSettings threads;
threads.use_separate_thread(true);  // 每个 RPC 请求使用单独线程
client.set_threading(threads);

        默认情况下,FastDDS 使用单线程模式,你可以改为多线程,提高吞吐量。如果你的 RPC 请求处理速度较慢,建议开启多线程模式,以支持高并发调用。

FastDDS RPC 可配置参数总结

参数作用示例
Domain ID指定 RPC 运行的 DDS 领域client.set_domain_id(5);
QoS设置可靠性、持久性client.set_qos(qos);
Transport指定 TCP/UDP 传输client.set_transport(transport);
Timeout设置调用超时client.set_timeout(std::chrono::milliseconds(5000));
Topic手动指定 Topic 名称client.set_topic_name("MyCustomTopic");
Threading设定是否使用多线程client.set_threading(threads);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2293578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开发板上Qt运行的环境变量的三条设置语句的详解

在终端中运行下面三句命令用于配置开发板上Qt运行的环境变量&#xff1a; export QT_QPA_GENERIC_PLUGINStslib:/dev/input/event1 export QT_QPA_PLATFORMlinuxfb:fb/dev/fb0 export QT_QPA_FONTDIR/usr/lib/fonts/设置成功后可以用下面的语句检查设置成功没有 echo $QT_QPA…

语言月赛 202412【顽强拼搏奖的四种发法】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202412] 顽强拼搏奖的四种发法 题目描述 在 XCPC 竞赛里&#xff0c;会有若干道题目&#xff0c;一支队伍可以对每道题目提交若干次。我们称一支队伍对一道题目的一次提交是有效的&#xff0c;当且仅当&#xff1a; 在本次提交…

自定义数据集 使用scikit-learn中svm的包实现svm分类

引入必要的库 import numpy as np from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.svm import SVC from sklearn.metrics import accuracy_score, classification_report 生成自定义数据集 X, y ma…

有用的sql链接

『SQL』常考面试题&#xff08;2——窗口函数&#xff09;_sql的窗口函数面试题-CSDN博客 史上最强sql计算用户次日留存率详解&#xff08;通用版&#xff09;及相关常用函数 -2020.06.10 - 知乎 (zhihu.com) 1280. 学生们参加各科测试的次数 - 力扣&#xff08;LeetCode&…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.27 NumPy+Pandas:高性能数据处理的黄金组合

2.27 NumPyPandas&#xff1a;高性能数据处理的黄金组合 目录 #mermaid-svg-x3ndEE4hrhO6WR6H {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-x3ndEE4hrhO6WR6H .error-icon{fill:#552222;}#mermaid-svg-x3ndEE4hr…

第一个3D程序!

运行效果 CPP #include <iostream> #include <fstream> #include <string> #include <cmath>#include <GL/glew.h> #include <GLFW/glfw3.h> #include <glm/glm.hpp> #include <glm/gtc/type_ptr.hpp> #include <glm/gtc/…

NeuralCF 模型:神经网络协同过滤模型

实验和完整代码 完整代码实现和jupyter运行&#xff1a;https://github.com/Myolive-Lin/RecSys--deep-learning-recommendation-system/tree/main 引言 NeuralCF 模型由新加坡国立大学研究人员于 2017 年提出&#xff0c;其核心思想在于将传统协同过滤方法与深度学习技术相结…

第二十三章 MySQL锁之表锁

目录 一、概述 二、语法 三、特点 一、概述 表级锁&#xff0c;每次操作锁住整张表。锁定粒度大&#xff0c;发生锁冲突的概率最高&#xff0c;并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。 对于表级锁&#xff0c;主要分为以下三类&#xff1a; 1. 表锁 2. 元数…

【Uniapp-Vue3】获取用户状态栏高度和胶囊按钮高度

在项目目录下创建一个utils文件&#xff0c;并在里面创建一个system.js文件。 在system.js中配置如下代码&#xff1a; const SYSTEM_INFO uni.getSystemInfoAsync();// 返回状态栏高度 export const getStatusBarHeight ()> SYSTEM_INFO.statusBarHeight || 15;// 返回胶…

通向AGI之路:人工通用智能的技术演进与人类未来

文章目录 引言:当机器开始思考一、AGI的本质定义与技术演进1.1 从专用到通用:智能形态的范式转移1.2 AGI发展路线图二、突破AGI的五大技术路径2.1 神经符号整合(Neuro-Symbolic AI)2.2 世界模型架构(World Models)2.3 具身认知理论(Embodied Cognition)三、AGI安全:价…

将ollama迁移到其他盘(eg:F盘)

文章目录 1.迁移ollama的安装目录2.修改环境变量3.验证 背景&#xff1a;在windows操作系统中进行操作 相关阅读 &#xff1a;本地部署deepseek模型步骤 1.迁移ollama的安装目录 因为ollama默认安装在C盘&#xff0c;所以只能安装好之后再进行手动迁移位置。 # 1.迁移Ollama可…

Java自定义IO密集型和CPU密集型线程池

文章目录 前言线程池各类场景描述常见场景案例设计思路公共类自定义工厂类-MyThreadFactory自定义拒绝策略-RejectedExecutionHandlerFactory自定义阻塞队列-TaskQueue&#xff08;实现 核心线程->最大线程数->队列&#xff09; 场景1&#xff1a;CPU密集型场景思路&…

使用开源项目:pdf2docx,让PDF转换为Word

目录 1.安装python 2.安装 pdf2docx 3.使用 pdf2docx 转换 PDF 到 Word pdf2docx&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 环境&#xff1a;windows电脑 1.安装python Download Python | Python.org 最好下载3.8以上的版本 安装时记得选择上&#…

蓝桥杯思维训练营(四)

文章目录 小红打怪494.目标和 小红打怪 小红打怪 思路分析&#xff1a;可以看到ai的范围较大&#xff0c;如果我们直接一个个进行暴力遍历的话&#xff0c;会超时。当我们的攻击的次数越大的时候&#xff0c;怪物的血量就会越少&#xff0c;这里就有一个单调的规律在里面&…

尝试把clang-tidy集成到AWTK项目

前言 项目经过一段时间的耕耘终于进入了团队开发阶段&#xff0c;期间出现了很多问题&#xff0c;其中一个就是开会讨论团队的代码风格规范&#xff0c;目前项目代码风格比较混乱&#xff0c;有的模块是驼峰&#xff0c;有的模块是匈牙利&#xff0c;后面经过讨论&#xff0c;…

【学习笔记】深度学习网络-正则化方法

作者选择了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰写的《Deep Learning》(人工智能领域的经典教程&#xff0c;深度学习领域研究生必读教材),开始深度学习领域学习&#xff0c;深入全面的理解深度学习的理论知识。 在之前的文章中介绍了深度学习中用…

介绍一下Mybatis的底层原理(包括一二级缓存)

表面上我们的就是Sql语句和我们的java对象进行映射&#xff0c;然后Mapper代理然后调用方法来操作数据库 底层的话我们就涉及到Sqlsession和Configuration 首先说一下SqlSession&#xff0c; 它可以被视为与数据库交互的一个会话&#xff0c;用于执行 SQL 语句&#xff08;Ex…

WordPress使用(1)

1. 概述 WordPress是一个开源博客框架&#xff0c;配合不同主题&#xff0c;可以有多种展现方式&#xff0c;博客、企业官网、CMS系统等&#xff0c;都可以很好的实现。 官网&#xff1a;博客工具、发布平台和内容管理系统 – WordPress.org China 简体中文&#xff0c;这里可…

BUUCTF_[安洵杯 2019]easy_web(preg_match绕过/MD5强碰撞绕过/代码审计)

打开靶场&#xff0c;出现下面的静态html页面&#xff0c;也没有找到什么有价值的信息。 查看页面源代码 在url里发现了img传参还有cmd 求img参数 这里先从img传参入手&#xff0c;这里我发现img传参好像是base64的样子 进行解码&#xff0c;解码之后还像是base64的样子再次进…

C基础寒假练习(4)

输入带空格的字符串&#xff0c;求单词个数、 #include <stdio.h> // 计算字符串长度的函数 size_t my_strlen(const char *str) {size_t len 0;while (str[len] ! \0) {len;}return len; }int main() {char str[100];printf("请输入一个字符串: ");fgets(…