Skip to content

NotEnoughMemoryException while publishing "long" msg #187

Closed

Description

@DorBenHarush

Hi, I'm trying to publish long massages (Images) with micro-ros. I received this error:
terminate called after throwing an instance of 'eprosima::fastcdr::exception::NotEnoughMemoryException' what(): Not enough memory in the buffer stream Aborted (core dumped)
I checked the meaning and according to eProsima it means that my msg buffer memory is too small.
The buffer default is very small, I tried to publish a string and got the error for string longer than 2 characters.
How can I increase the buffer size?

Regards,
Dor.

Activity

  1. pablogs9 commented on Sep 7, 2020

    @pablogs9
    Member

    Hello @DorBenHarush, please can you put here a list of command and/or code to reproduce this issue?
    FastCDR is not used in micro-ROS firmware so I'm not understanding something here.
    Please let us replicate this issue by providing a comprehensive guide to follow your steps.

  2. DorBenHarush commented on Sep 7, 2020

    @DorBenHarush
    Author

    Hi, @pablogs9 this is the code that generates the error. It works if the string is empty or for short strings like "Hi".
    I used the normal commands for configure, build and flash (esp32).
    CODE:

    #include <stdio.h>
    #include <unistd.h>
    
    #include <rcl/rcl.h>
    #include <rcl/error_handling.h>
    #include <std_msgs/msg/string.h>
    
    #include <rclc/rclc.h>
    #include <rclc/executor.h>
    
    #ifdef ESP_PLATFORM
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #endif
    
    #define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){printf("Failed status on line %d: %d. Aborting.\n",__LINE__,(int)temp_rc);vTaskDelete(NULL);}}
    #define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){printf("Failed status on line %d: %d. Continuing.\n",__LINE__,(int)temp_rc);}}
    
    rcl_publisher_t publisher;
    std_msgs__msg__String msg;
    
    void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
    {
    	UNUSED(last_call_time);
    	if (timer != NULL) {
    		printf("sent.\n");
    		RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
    	}
    }
    
    void appMain(void * arg)
    {
    	rcl_allocator_t allocator = rcl_get_default_allocator();
    	rclc_support_t support;
    
    	// create init_options
    	RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
    
    	// create node
    	rcl_node_t node = rcl_get_zero_initialized_node();
    	RCCHECK(rclc_node_init_default(&node, "freertos_String_publisher", "", &support));
    
    	// create publisher
    	RCCHECK(rclc_publisher_init_default(
    		&publisher,
    		&node,
    		ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, String),
    		"freertos_String_publisher"));
    
    	// create timer,
    	rcl_timer_t timer = rcl_get_zero_initialized_timer();
    	const unsigned int timer_timeout = 1000;
    	RCCHECK(rclc_timer_init_default(
    		&timer,
    		&support,
    		RCL_MS_TO_NS(timer_timeout),
    		timer_callback));
    
    	// create executor
    	rclc_executor_t executor = rclc_executor_get_zero_initialized_executor();
    	RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
    
    	unsigned int rcl_wait_timeout = 100;   // in ms
    	RCCHECK(rclc_executor_set_timeout(&executor, RCL_MS_TO_NS(rcl_wait_timeout)));
    	RCCHECK(rclc_executor_add_timer(&executor, &timer));
    
    	msg.data.data = "hello";
    
    	while(1){
    		rclc_executor_spin_some(&executor, 100);
    		usleep(100000);
    	}
    
    	// free resources
    	RCCHECK(rcl_publisher_fini(&publisher, &node))
    	RCCHECK(rcl_node_fini(&node))
    
      	vTaskDelete(NULL);
    }
    
  3. pablogs9 commented on Sep 7, 2020

    @pablogs9
    Member

    You are not initializing the string structure nor allocating memory for it.

    Try something like:

    #define STR_SIZE 20
    
    std_msgs__msg__String msg;
    
    msg.data.capacity = STR_SIZE;
    msg.data.size = 0;
    msg.data.data = (char*) malloc(STR_SIZE*sizeof(char));
    
    char * my_string = "hello";
    memcpy(msg.data.data, my_string, strlen(my_string));
    msg.data.size = strlen(my_string);
    

    You also have convenience functions like std_msgs__msg__String__init to do this initialization, please check them along with your workspace.

    Please close if resolved

  4. self-assigned this
    on Sep 7, 2020
  5. DorBenHarush commented on Sep 17, 2020

    @DorBenHarush
    Author

    Hi @pablogs9 , I'm trying to publish an Image massage and I have some questions. when I try to publish the full image the publisher returns 1 , so I reduced the size to several times and it worked with 800 bytes. Do you know why?
    secondly I couldn't publish from inside the timer callback only from main.

    Regards,
    Dor.

    CODE:

    #include <esp_event_loop.h>
    #include <esp_log.h>
    #include <esp_system.h>
    #include <nvs_flash.h>
    #include <sys/param.h>
    #include <string.h>
    
    #include <stdio.h>
    #include <unistd.h>
    
    #include <rcl/rcl.h>
    #include <rcl/error_handling.h>
    #include <sensor_msgs/msg/image.h>
    
    #include <rclc/rclc.h>
    #include <rclc/executor.h>
    
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "boards.h"
    
    #define STRING_CAPACITY 20
    
    static const char *TAG = "example:take_picture";
    
    #define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){printf("Failed status on line %d: %d. Aborting.\n",__LINE__,(int)temp_rc);vTaskDelete(NULL);}}
    #define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){printf("Failed status on line %d: %d. Continuing.\n",__LINE__,(int)temp_rc);}}
    
    rcl_publisher_t publisher;
    sensor_msgs__msg__Image* msg;
    camera_fb_t *pic;
    
    
    static camera_config_t camera_config = {
        .pin_pwdn = CAM_PIN_PWDN,
        .pin_reset = CAM_PIN_RESET,
        .pin_xclk = CAM_PIN_XCLK,
        .pin_sscb_sda = CAM_PIN_SIOD,
        .pin_sscb_scl = CAM_PIN_SIOC,
    
        .pin_d7 = CAM_PIN_D7,
        .pin_d6 = CAM_PIN_D6,
        .pin_d5 = CAM_PIN_D5,
        .pin_d4 = CAM_PIN_D4,
        .pin_d3 = CAM_PIN_D3,
        .pin_d2 = CAM_PIN_D2,
        .pin_d1 = CAM_PIN_D1,
        .pin_d0 = CAM_PIN_D0,
        .pin_vsync = CAM_PIN_VSYNC,
        .pin_href = CAM_PIN_HREF,
        .pin_pclk = CAM_PIN_PCLK,
    
        //XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
        .xclk_freq_hz = 20000000,
        .ledc_timer = LEDC_TIMER_0,
        .ledc_channel = LEDC_CHANNEL_0,
    
        .pixel_format = PIXFORMAT_YUV422, //YUV422,GRAYSCALE,RGB565,JPEG
        .frame_size = FRAMESIZE_QQVGA,    //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
    
        .jpeg_quality = 12, //0-63 lower number means higher quality
        .fb_count = 1       //if more than one, i2s runs in continuous mode. Use only with JPEG
    };
    
    void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
    {
    	UNUSED(last_call_time);
    	if (timer != NULL) {
    		
    	}
    }
    
    static esp_err_t init_camera()
    {
        //initialize the camera
        esp_err_t err = esp_camera_init(&camera_config);
        if (err != ESP_OK)
        {
            ESP_LOGE(TAG, "Camera Init Failed");
            return err;
        }
    
        return ESP_OK;
    }
    
    void appMain(void * arg)
    {
        init_camera();
        rcl_allocator_t allocator = rcl_get_default_allocator();
    	rclc_support_t support;
    	msg = sensor_msgs__msg__Image__create();
    	// create init_options
    	RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
    
    	// create node
    	rcl_node_t node = rcl_get_zero_initialized_node();
    	RCCHECK(rclc_node_init_default(&node, "freertos_picture_publisher", "", &support));
    
    	// create publisher
    	RCCHECK(rclc_publisher_init_default(
    		&publisher,
    		&node,
    		ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, Image),
    		"freertos_picture_publisher"));
    
    	// create timer,
    	rcl_timer_t timer = rcl_get_zero_initialized_timer();
    	const unsigned int timer_timeout = 1000;
    	RCCHECK(rclc_timer_init_default(
    		&timer,
    		&support,
    		RCL_MS_TO_NS(timer_timeout),
    		timer_callback));
    
    	// create executor
    	rclc_executor_t executor = rclc_executor_get_zero_initialized_executor();
    	RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
    
    	unsigned int rcl_wait_timeout = 1000;   // in ms
    	RCCHECK(rclc_executor_set_timeout(&executor, RCL_MS_TO_NS(rcl_wait_timeout)));
    	RCCHECK(rclc_executor_add_timer(&executor, &timer));
    
    	//takes picture in yuv422 format
    	pic = esp_camera_fb_get();	
    	
    	//set height, width, step and is_bigedian
    	msg->height = pic->height;
    	msg->width = pic->width;
    	msg->is_bigendian = 0;
    	msg->step = pic->width * 2;
    
    	//set pixel format and frame id
    	char * encoding = "yuv422";
    	msg->encoding.data = (char*)heap_caps_malloc(strlen(encoding) + 1, MALLOC_CAP_8BIT); 
    	msg->header.frame_id.capacity = STRING_CAPACITY;
    	memset(msg->encoding.data, 0, strlen(encoding) + 1);
    	memcpy(msg->encoding.data, encoding, strlen(encoding));
    	msg->encoding.size = strlen(encoding) + 1;
    
    	char* frame_id = "AI_thinker_image";
    	msg->header.frame_id.data = (char*)heap_caps_malloc(strlen(frame_id) + 1, MALLOC_CAP_8BIT);
    	msg->header.frame_id.capacity = STRING_CAPACITY;
    	memset(msg->header.frame_id.data, 0, strlen(frame_id) + 1);
    	memcpy(msg->header.frame_id.data, frame_id, strlen(frame_id));
    	msg->header.frame_id.size = strlen(frame_id) + 1;
    
    
    	//allocate memory for data
    	msg->data.data = (uint8_t*)heap_caps_malloc(pic->len, MALLOC_CAP_8BIT);
    	msg->data.capacity = pic->len;
    	msg->data.size = 1000;//msg->step * pic->height ;
    	printf("copying data...\n");
    	memcpy(msg->data.data, pic->buf, pic->len);
    	if(msg->data.data == NULL){
    		printf("failed to allocate memory. restating in 5 sec.\n");
    		vTaskDelay(5000 / portTICK_RATE_MS);
    		esp_restart();
    	}	
    	//msg->header.stamp ?
    
    	printf("height: %d \n", msg->height);
    	printf("width: %d \n", msg->width);
    	printf("pixel format %s \n", msg->encoding.data);
    	printf("is bigendian %d \n", msg->is_bigendian);
    	printf("picture step %d \n", msg->step);
    	printf("frame id %s \n", msg->header.frame_id.data);
    	printf("buffer length %d \n", msg->data.size);
    	printf("pic->len %d \n", pic->len);
    
    	
    	while(1){
    		rclc_executor_spin_some(&executor, 1000);
    		usleep(100000);
    		//publish picture
    		printf("publisher return value: %d \n", rcl_publish(&publisher, msg, NULL));
           	vTaskDelay(1000 / portTICK_RATE_MS);
    	}
    
        // free resources
    	RCCHECK(rcl_publisher_fini(&publisher, &node))
    	RCCHECK(rcl_node_fini(&node))
    	heap_caps_free(msg->encoding.data);
    	heap_caps_free(msg->header.frame_id.data);
    	heap_caps_free(msg->data.data);
    	sensor_msgs__msg__Image__destroy(msg);
    	vTaskDelete(NULL);
    }
  6. pablogs9 commented on Sep 17, 2020

    @pablogs9
    Member

    Some points:

    • You are not setting msg->encoding.data.capacity.
    • You are setting capacity to STRING_CAPACITY but the real capacity is the memory that you have allocated, which is based in strlen().
    • You are setting msg->data.size = 1000 but you are copying pic->len bytes, here you should set the capacity to the length of the memory that you are allocating and the size to the actual size of the byte array that you are copying. Also, I think that it should be checked if the size is less than the capacity.
    • I guess that the msg->data.data == NULL check should be done just after the memory allocation because allocation can fail and you are setting memory before checking the correctness of the allocation.

    There are some other important points to take into account when dealing with big messages. First of all, you should be aware that the default MTU for the UDP packages that the micro-ROS middleware sends is 512 Bytes. The middleware also has a "historic" which is by default 4. So, the internal middleware layer uses a buffer (from static memory) of 512x4 = 2048 Bytes. Check this for more info

    Whit that said, you have to be aware that if you are using a reliable publisher (rclc_publisher_init_default()) and the message length is greater than the MTU, it will be fragmented into the "historic" slots available. In the other case, if you are using best effort publisher (rclc_publisher_init_best_effort()) the message length must be less than the MTU.

    So, it is important to know the maximum size of your messages, and then you can configure the RMW in the colcon.meta file of your application. See a sample here.

    If you want to tune your RMW buffers for publication it should looks like:

    {
        "names": {
            "rmw_microxrcedds": {
                "cmake-args": [
                       ...
                    "-DRMW_UXRCE_STREAM_HISTORY=4",
                       ...
                ]
            },
            "microxrcedds_client": {
                "cmake-args": [
                       ...
                    "-DUCLIENT_UDP_TRANSPORT_MTU=512",
                       ...
                ]
            }
        }
    }

    Take care of the formatting of this JSON, it often fails just because of an error in a comma.

    You have here two options:

    • Increase the MTU, so the client will send bigger packages to the agent
    • Increase the history, so the client will have more available "historic" slots to fragment you whole message into 512 chunks that will be sent to the agent

    Of course, you can also increase both.

  7. DorBenHarush commented on Sep 17, 2020

    @DorBenHarush
    Author

    I changed the parameters as you suggested but it didn't work. Furthermore, I found out that my defaults were 512 and 1.
    800 bytes msg works with 512 and 1 so I don't know what the problem is.

  8. pablogs9 commented on Sep 17, 2020

    @pablogs9
    Member

    800 Bytes works with 512 Bytes of MTU and 1 RMW_UXRCE_STREAM_HISTORY? So, I have not understood the problem... Is your client publishing? Can you receive data using ros2 topic echo ...?

  9. DorBenHarush commented on Sep 17, 2020

    @DorBenHarush
    Author

    Yes, I receive the data.
    Screenshot from 2020-09-17 15-09-55

  10. pablogs9 commented on Sep 17, 2020

    @pablogs9
    Member

    Ok cool, can we close the issue or you have another doubt?

  11. DorBenHarush commented on Sep 17, 2020

    @DorBenHarush
    Author

    the problem is that I cant publish data bigger than 800 bytes even if i increase the parameters to 10 and 4096.

  12. pablogs9 commented on Sep 17, 2020

    @pablogs9
    Member

    One more thing, when you modify the colcon.meta file are you cleaning all builds?

    Usually when modifying the micro-ROS configuration you have to do:

    ros2 run micro_ros_setup build_firmware.sh clean  #clean is only valid for the ESP32 port
    ros2 run micro_ros_setup configure_firmware.sh [config]
    ros2 run micro_ros_setup build_firmware.sh

    You can check if your changes has been effective looking:
    include/rmw_microxrcedds_c/config.h and
    include/uxr/client/config.h

    please make sure that in those files you see the values that you have set in your colcon.metas

  13. DorBenHarush commented on Sep 18, 2020

    @DorBenHarush
    Author

    I checked files and the values didnt change even after doing the steps you mentioned, so I chnaged the values inside
    include/rmw_microxrcedds_c/config.h and
    include/uxr/client/config.h
    to make sure that values are correct and it didnt work.

  14. pablogs9 commented on Sep 18, 2020

    @pablogs9
    Member

    These files are not intended to be modified, please try to rebuild all from scratch and modify the colon.metas just before the creation step in order to generate these headers correctly.

    I'm going to check why they are not being modified following the step I wrote above.

    Keep me updated about your progress, please.

  15. DorBenHarush commented on Sep 18, 2020

    @DorBenHarush
    Author

    It didnt work.
    This is my workflow:
    create_firmware -> add camera app and extensions(Kproject) -> configure camera as app to run -> menuconfig & building -> flash

    25.9:
    Hi @pablogs9, Do you know why the config.h files only half correct?

  16. pablogs9 commented on Sep 29, 2020

    @pablogs9
    Member

    Hello @DorBenHarush, I am testing this issue and I have prepared this example based on yours:
    https://github.com/micro-ROS/freertos_apps/blob/feature/add_image_publisher/apps/image_publisher/app.c

    I have a few questions:

    • Which are the resolution f the camera, width and height?
    • How many bytes per pixel does it have?
  17. DorBenHarush commented on Sep 30, 2020

    @DorBenHarush
    Author

    Hi @pablogs9,

    • Which are the resolution f the camera, width and height?
      Height - 120 Width - 160.
    • How many bytes per pixel does it have?
      I'm using YUV422 pixel format so 16-bit per pixel. source
  18. DorBenHarush commented on Oct 1, 2020

    @DorBenHarush
    Author

    Hello @DorBenHarush, I am testing this issue and I have prepared this example based on yours:
    https://github.com/micro-ROS/freertos_apps/blob/feature/add_image_publisher/apps/image_publisher/app.c

    Did it work?

  19. pablogs9 commented on Oct 1, 2020

    @pablogs9
    Member

    Hello @DorBenHarush, I just have tested it in the ESP32 check the last commit. Well if the image es 120x160 pixels and we have 2 B per pixel this makes a message of approx 38400 bytes, if the MTU by default is 512 B we need an XRCE historic greater than 75. You can see this configuration here.

    Due to your question, we have found an issue (we are currently investigating) in the middleware handling of fragmentation. So I would like you to test a specific branch with the fix. Having a micro-ROS workspace you can use this fix branch with:

    cd firmware/mcu_ws/eProsima/Micro-XRCE-DDS-Client
    git checkout fix/fragments_available

    Using this app with the fix/fragments_available branch of the Micro XRCE-DDS Client middleware I have this:

    image

    You can see the agent receiving and publishing the topic data, the ESP32 console output with publisher return value: 0 and a ros2 topic hz /freertos_image_publisher at 1 Hz.

    Please try this fix and give support to us.

  20. DorBenHarush commented on Oct 1, 2020

    @DorBenHarush
    Author

    It didn't work I'm sill getting 1 as a return value.
    I tested it on esp32cam and esp32 both didn't work

  21. pablogs9 commented on Oct 1, 2020

    @pablogs9
    Member

    Hello @DorBenHarush.

    Here you can find a screencast with instructions for building the application with the correct branches:

    https://asciinema.org/a/ljRBzoNwOIxH8x6eg020tgjYU

    And here you can see the operation:

    https://asciinema.org/a/It2rmJ5ZqMHYpCqyva9hKtkN3

    Please follow these steps and tell me if it works for you.

  22. DorBenHarush commented on Oct 1, 2020

    @DorBenHarush
    Author

    Thank you for the insructions, I'll check the fix again in a few days.

    07.10.2020:
    Hi @pablogs9, it works now.

  23. DorBenHarush commented on Oct 12, 2020

    @DorBenHarush
    Author

    Hi @pablogs9, why when I try to use this configuration the config.h still wont form in the right way?

        "names": {
            "rmw_microxrcedds": {
                "cmake-args": [
                    "-DRMW_UXRCE_MAX_NODES=1",
                    "-DRMW_UXRCE_MAX_PUBLISHERS=1",
                    "-DRMW_UXRCE_MAX_SUBSCRIPTIONS=0",
                    "-DRMW_UXRCE_MAX_SERVICES=0",
                    "-DRMW_UXRCE_MAX_CLIENTS=0",
                    "-DRMW_UXRCE_MAX_HISTORY=12",
                ]
            },
            "microxrcedds_client": {
                "cmake-args": [
                 	"-DUCLIENT_UDP_TRANSPORT_MTU=4096",
    
                ]
    	}
        } 
    }
    
  24. pablogs9 commented on Oct 13, 2020

    @pablogs9
    Member

    Because your JSON is malformed, check the ending comma in args:

        "names": {
            "rmw_microxrcedds": {
                "cmake-args": [
                    "-DRMW_UXRCE_MAX_NODES=1",
                    "-DRMW_UXRCE_MAX_PUBLISHERS=1",
                    "-DRMW_UXRCE_MAX_SUBSCRIPTIONS=0",
                    "-DRMW_UXRCE_MAX_SERVICES=0",
                    "-DRMW_UXRCE_MAX_CLIENTS=0",
                    "-DRMW_UXRCE_MAX_HISTORY=12" <- here you had a comma
                ]
            },
            "microxrcedds_client": {
                "cmake-args": [
                 	"-DUCLIENT_UDP_TRANSPORT_MTU=4096" <- here you had a comma
    
                ]
    	}
        } 
    }
  25. DorBenHarush commented on Oct 14, 2020

    @DorBenHarush
    Author

    Hi @pablogs9, the file config.h is still incorrect even when the JSON file is correct. Default values only.

  26. pablogs9 commented on Oct 14, 2020

    @pablogs9
    Member

    Here you have a sample of how to do this:

    https://asciinema.org/a/VIi73Ck8hazq5xdAf4txSfzmt

    Keypoints:

    • In 10:57 you have the first build with RMW_UXRCE_MAX_HISTORY set to 1. Both meta and config.h is shown.
    • In 12:14 I modify the value, then I clean the build, reconfigure it and build it again, so at the end, I check that config.h is modified.

    Please close when you resolve your issue or ask if you have more doubts.

    Thanks!

  27. DorBenHarush commented on Oct 17, 2020

    @DorBenHarush
    Author

    It works now with this configuration :

    {
        "names": {
            "rmw_microxrcedds": {
                "cmake-args": [
                    "-DRMW_UXRCE_MAX_NODES=1",
                    "-DRMW_UXRCE_MAX_PUBLISHERS=1",
                    "-DRMW_UXRCE_MAX_SUBSCRIPTIONS=0",
                    "-DRMW_UXRCE_MAX_SERVICES=0",
                    "-DRMW_UXRCE_MAX_CLIENTS=0",
                    "-DRMW_UXRCE_MAX_HISTORY=12"
                ]
            },
            "microxrcedds_client": {
                "cmake-args": [
                       
                    "-DUCLIENT_UDP_TRANSPORT_MTU=4096",
                      
                ]
            }
        }
    }
    

    Thank you so much for the help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions