micro-ROS / micro_ros_setup

Support macros for building micro-ROS-based firmware.
Apache License 2.0
336 stars 128 forks source link

rcl_executor_spin stuck on freeRTOS #667

Open N-K1998 opened 9 months ago

N-K1998 commented 9 months ago

Issue template

I am trying to define a pub/sub node on microros. Using the following code the execution stops at rclc_executor_spin. Is there something wrong with the initialization?

/* timer callback */
void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
    (void) last_call_time;
    if (timer != NULL) {
        RCSOFTCHECK(rcl_publish(&publisher, &pub_msg, NULL));
        printf("Publishing: %d\n", pub_msg.data);
        pub_msg.data++;
    }
}

static void microros_thread_custom( void *pvParameters ){
    // set custom transport
    rmw_uros_set_custom_transport(
        false, // Framing disable here
        (void *) NULL, // transport->args
        shm_transport_open,
        shm_transport_close,
        shm_transport_write,
        shm_transport_read);

    // Get empty allocator to define a custom one
    rcl_allocator_t custom_allocator = rcutils_get_zero_initialized_allocator();
    rclc_support_t support;

    // Set custom allocation methods for freeRTOS
    custom_allocator.allocate = microros_allocate;
    custom_allocator.deallocate = microros_deallocate;
    custom_allocator.reallocate = microros_reallocate;
    custom_allocator.zero_allocate =  microros_zero_allocate;

    // Set default allocator to custom
    if (!rcutils_set_default_allocator(&custom_allocator)){
        printf("# Setting custom allocator as default: FAILED\r\n");
        return;
    }

    // get the default allocator
    rcl_allocator_t allocator = rcl_get_default_allocator();

    // create init_options
    RCCHECK(rclc_support_init(&support, 0, NULL, &allocator)); // argc:0, argv: NULL

    // create node
    rcl_node_t node;
    RCCHECK(rclc_node_init_default(&node, "int32_publisher_subscriber_rclc", "", &support));

    // topics
    const char * pub_topic_name = "freeRTOS_publisher";
    const char * sub_topic_name = "linux_publisher";

    // create publisher
    RCCHECK(rclc_publisher_init_default(
        &publisher,
        &node,
        ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
        pub_topic_name)); // topic name

    // create subscriber
    RCCHECK(rclc_subscription_init_default(
        &subscriber,
        &node,
        ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
        sub_topic_name));

    // create timer,
    rcl_timer_t 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();
    unsigned int num_handles = 1 + 1; // 1 timer + 1 sub callback
    RCCHECK(rclc_executor_init(&executor, &support.context, num_handles, &allocator));
    RCCHECK(rclc_executor_add_timer(&executor, &timer));
    RCCHECK(rclc_executor_add_subscription(&executor, &subscriber, &sub_msg, &subscription_callback, ON_NEW_DATA)); // sub

    pub_msg.data = 0;

    RCCHECK(rclc_executor_spin(&executor));

    // fini functions
    RCCHECK(rcl_subscription_fini(&subscriber, &node));
    RCCHECK(rcl_publisher_fini(&publisher, &node));
    RCCHECK(rcl_node_fini(&node));
    RCCHECK(rcl_timer_fini(&timer));

    return;
}
pablogs9 commented 9 months ago

Avoid using rclc_executor_spin in embedded systems, it can freeze your scheduler.

Use this approach:

while(1){
   delay execution for letting other tasks to run
   spin some the rclc executor
}
N-K1998 commented 9 months ago

Just a question. If I define a custom transport in the microros thread task like this:

static void microros_thread_custom( void *pvParameters ){
    // set custom transport
    rmw_uros_set_custom_transport(
        false, // Framing disable here
        (void *) NULL, // transport->args
        shm_transport_open,
        shm_transport_close,
        shm_transport_write,
        shm_transport_read);
    // whatever code

}

And then in the transport_read i call a blocking task operation like:

size_t shm_transport_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err){
        // Whatever
        vTaskDelay(x10second);
        // Whatever
}

Does this operation affect the execution of the main thread? The main thread won't execute until the end of the delay inside the read or will it normally continue its execution?

pablogs9 commented 9 months ago

yes, if you block in the read operation it will block the spin.

N-K1998 commented 9 months ago

And also the main thread that has set the custom transport?

pablogs9 commented 9 months ago

no, the read operation happens inside the spin, so only is blocked the task that runs the spin.