• micro-ROS中对消息的内存管理


    1.背景

    我在之前的一篇文章【在VSCode下利用PlateFormIO开发Arduino的MicroROS遇到的一些问题】中的第10点中,提到一个问题:为啥在使用自定义消息类型时,有时候需要调用 micro_ros_utilities_create_message_memory 函数来对消息对象进行内存申请,而有时候不用呢?

    2.答案

    其实答案就在这篇官方的指导/说明文章中:【Handling messages memory in micro-ROS】

    是否需要对消息类型进行内存申请,取决于消息的成员类型。

    2.1.基本类型及其数组,不需要

    假如你的消息的成员类型是基本类型(Basic type)及其数组类型(Array type),比如bool、byte、char、float32,bool[n]、byte[n]、char[n]、float32[n]等等,那就不用额外进行内存申请的操作,因为这些基本类型在实例化时就已经明确了空间大小,系统直接帮忙分配好了内存。

    bool bool_test
    byte byte_test
    char char_test
    float32 float32_test
    float64 double_test
    int8 int8_test
    uint8 uint8_test
    int16 int16_test
    uint16 uint16_test
    int32 int32_test
    uint32 uint32_test
    int64 int64_test
    uint64 uint64_test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.1.序列类型(复合类型、复合序列类型),需要

    但是,假如消息的成员类型为:序列类型(Sequence type)、包含序列的复合类型(Compound type)、复合序列类型(Sequences of compound types),那就需要手动申请内存空间了。当然,假如复合类型中的成员全是基本类型,那也不用手动申请。
    上面提到的这些类型,之所以需要人为去申请内存,原因很简单:系统不知道你要多大的空间。
    就拿int32的序列int32[](注意,这个不是数组类型,中括号中间没有具体的数值)来说,在micro-ROS中,该类型经过解析后,得到的是这样一个结构体:

    typedef struct rosidl_runtime_c__int32__Sequence
    {
      int32_t* data;    /* The pointer to an array of int32 */
      size_t size;      /* The number of valid items in data */
      size_t capacity;  /* The number of allocated items in data */
    } rosidl_runtime_c__int32__Sequence;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    由于size、capacity都是未知的,那么系统如何知道要申请多大的内存空间并把指针值赋予data呢?
    因此需要程序员自己手动申请并赋值一下。(这里解释一下上述结构体中的size、capacity的区别,类比电池的话,size表示剩余电量/可用电量,capacity表示电池总容量。这样设计,估计是为了在实例化一次这个对象后,能够对这个对象反复利用)

    3.内存申请方法

    在这个【Handling messages memory in micro-ROS】文章中提到,在micro-ROS中处理消息的内存有两种方式:手动申请(Manual allocation)、辅助申请(micro-ROS utilities)。

    3.1.手动申请(Manual allocation)

    这个就是要求对数据结构的各个成员进行数据填充、内存分配。比如对上面的rosidl_runtime_c__int32__Sequence类型,可以这样子初始化:

    rosidl_runtime_c__int32__Sequence values;
    
    values.capacity = 100;
    values.data = (int32_t*) malloc(mymsg.values.capacity * sizeof(int32_t));
    values.size = 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样子操作比较繁琐,更好的是下面的方法。

    3.1.工具辅助(micro-ROS utilities)

    在micro-ROS中,官方提供了一些函数及结构,可以让我们相对便捷地对消息类型进行内存管理。
    这里看一下例子:

    mypackage__msg__MyComplexType mymsg;
    
    static micro_ros_utilities_memory_conf_t conf = {0};
    
    micro_ros_utilities_memory_rule_t rules[] = {
      {"multiheaders", 4},
      {"multiheaders.frame_id", 60},
      {"name", 10}
    };
    conf.rules = rules;
    conf.n_rules = sizeof(rules) / sizeof(rules[0]);
    
    // member named "values" of MyComplexType will have the default max_basic_type_sequence_capacity
    
    bool success = micro_ros_utilities_create_message_memory(
      ROSIDL_GET_MSG_TYPE_SUPPORT(mypackage, msg, MyComplexType),
      &mymsg,
      conf
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    其中,mypackage__msg__MyComplexType类型是这样子的:

    typedef struct mypackage__msg__MyComplexType
    {
      std_msgs__msg__Header__Sequence multiheaders;
      rosidl_runtime_c__int32__Sequence values;
      double duration;
      int8 coefficients[10];
      rosidl_runtime_c__String name;  // equal to rosidl_runtime_c__char__Sequence
    } mypackage__msg__MyComplexType;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    header的类型是这样的:

    typedef struct std_msgs__msg__Header
    {
      builtin_interfaces__msg__Time stamp;
      rosidl_runtime_c__String frame_id;
    } std_msgs__msg__Header;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.1.1.规则的定义

    可以看到,针对mypackage__msg__MyComplexType的成员multiheaders,指定下面的内存申请规则:

    ...
    micro_ros_utilities_memory_rule_t rules[] = {
      {"multiheaders", 4}, // 对序列进行长度(capacity)的申请
      {"multiheaders.frame_id", 60}, // frame_id是字符串,进行60字节的申请
      ...
    };
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    rule的具体的写法应该是

     {"对象成员名称",  Sequence的capacity大小}
    
    • 1

    在这里插入图片描述

    需要注意的是,序列成员的成员可以直接写,而不用序列号,比如上面的"multiheaders.frame_id"就不用写成"multiheaders.data[0].frame_id"之类的。

    另外,对于未在rules中指定的序列类型成员,会按照micro_ros_utilities_memory_conf_t的max_string_capacity、max_ros2_type_sequence_capacity、max_basic_type_sequence_capacity来进行申请,假如需要覆盖默认值micro_ros_utilities_memory_conf_default,可以这样操作:

    static micro_ros_utilities_memory_conf_t conf = {0};
    
    conf.max_string_capacity = 50;
    conf.max_ros2_type_sequence_capacity = 5;
    conf.max_basic_type_sequence_capacity = 5;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.1.2.规则的使用

    定义好规则之后,当调用 micro_ros_utilities_create_message_memory 函数时,应该是对Sequence类型的capacity进行赋值,然后再根据此capacity进行实际内存的计算+申请。(可能会涉及递归过程?)
    大概看一下源码,应该是的。具体实现过程有空再分析分析。
    在这里插入图片描述


    参考:
    【Handling messages memory in micro-ROS】

  • 相关阅读:
    【LeetCode】No.101. Symmetric Tree -- Java Version
    js——高阶函数、闭包、递归以及浅拷贝和深拷贝
    webpack5 - 之 生产环境的优化(dist压缩包、copy静态资源、缓存、代码切片、多线程打包、抽离重复代码 与 最小化 entry chunk)
    【LeetCode】1979. 找出数组的最大公约数
    Android修行手册 - POI操作Excel常用样式(字体,背景,颜色,Style)
    大数据Flink(七十九):SQL 的容错(Checkpoint)
    【虚拟机】VMware的NAT模式、桥接模式、仅主机模式
    week3 map
    研发效能工程实践-精益需求管理
    【JavaScript】内置数学对象、日期对象学习笔记
  • 原文地址:https://blog.csdn.net/joyopirate/article/details/133611859