micro-ROS / micro_ros_arduino

micro-ROS library for Arduino
Apache License 2.0
453 stars 117 forks source link

micro-ros + esp32 so slow #1816

Open markilius508 opened 3 months ago

markilius508 commented 3 months ago

I set up a micro-ROS system on an ESP32 to publish three messages simultaneously in a single timer callback with a 20 ms interval.

tf2_msgsmsgTFMessage sensor_msgsmsgJointState nav_msgsmsgOdometry

However, I noticed my motor controller wasn't responding as smoothly as it used to. When I measured the time taken by the timer callback, I was shocked to find it was using up 100 ms just to publish those three messages! For comparison, I tested the same task on my OpenCR board, and it only took 6 ms to complete. Can you help me figure out what's causing this issue?

#include <micro_ros_arduino.h>
#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <micro_ros_utilities/type_utilities.h>
#include <micro_ros_utilities/string_utilities.h>

#include <control_msgs/msg/joint_jog.h>
#include <builtin_interfaces/msg/duration.h>
#include <nav_msgs/msg/odometry.h>
#include <geometry_msgs/msg/transform_stamped.h>
#include <tf2_msgs/msg/tf_message.h>
#include <sensor_msgs/msg/joint_state.h>

#define WHEEL_NUM 2
#define LEFT 0
#define RIGHT 1

rcl_publisher_t publisher;
control_msgs__msg__JointJog msg;
control_msgs__msg__JointJog msg_static;

rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;
rcl_node_t node;
rcl_timer_t timer;

// rcl_subscription_t cmd_vel_sub;
rcl_publisher_t odom_pub;
rcl_publisher_t tf_pub;
rcl_publisher_t joint_state_pub;
rcl_publisher_t dbg_pub;

rcl_timer_t odom_timer;
// geometry_msgs__msg__Twist cmd_vel_msg;
nav_msgs__msg__Odometry odom_msg;
tf2_msgs__msg__TFMessage odom_tf;
geometry_msgs__msg__TransformStamped odom_geo;
sensor_msgs__msg__JointState joint_state_msg;
// std_msgs__msg__Float64 dbg_msg;
// std_msgs__msg__UInt64 dbg_msg;
builtin_interfaces__msg__Duration dbg_msg;

uint8_t my_buffer[1000];

#define LED_PIN 13
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);

#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}

void error_loop(){
  while(1){
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    delay(100);
  }
}

void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
  RCLC_UNUSED(last_call_time);
  if (timer != NULL) {
        struct timespec time_stamp;
        clock_gettime(CLOCK_REALTIME, &time_stamp);

        odom_msg.header.stamp.sec = time_stamp.tv_sec;
        odom_msg.header.stamp.nanosec = time_stamp.tv_nsec;
        RCSOFTCHECK(rcl_publish(&odom_pub, &odom_msg, NULL));

        odom_geo.header.stamp.sec = odom_msg.header.stamp.sec;
        odom_geo.header.stamp.nanosec = odom_msg.header.stamp.nanosec;
        // copy_odomPose_to_tf(&odom_msg, &odom_geo);
        odom_tf.transforms.data = &odom_geo;
        RCSOFTCHECK(rcl_publish(&tf_pub, &odom_tf, NULL));

        joint_state_msg.header.stamp.sec = odom_msg.header.stamp.sec;
        joint_state_msg.header.stamp.nanosec = odom_msg.header.stamp.nanosec;
        for (uint8_t whl_idx = 0; whl_idx < WHEEL_NUM; whl_idx++) {
            joint_state_msg.position.data[whl_idx] = 5.0;
            joint_state_msg.velocity.data[whl_idx] = 5.0;
        }
        RCSOFTCHECK(rcl_publish(&joint_state_pub, &joint_state_msg, NULL));

        struct timespec time_stop;
        clock_gettime(CLOCK_REALTIME, &time_stop);
        dbg_msg.sec = time_stop.tv_sec - time_stamp.tv_sec;
        dbg_msg.nanosec = time_stop.tv_nsec - time_stamp.tv_nsec;
        RCSOFTCHECK(rcl_publish(&dbg_pub, &dbg_msg, NULL));
    }
}

void setup() {
  set_microros_transports();

  delay(2000);

  allocator = rcl_get_default_allocator();

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

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

  // create publisher
  rclc_publisher_init_default(
        &dbg_pub,
        &node,
        ROSIDL_GET_MSG_TYPE_SUPPORT(builtin_interfaces, msg, Duration),
        "dbg");

    rclc_publisher_init_default(
        &odom_pub,
        &node,
        ROSIDL_GET_MSG_TYPE_SUPPORT(nav_msgs, msg, Odometry),
        "odom_raw");

    rclc_publisher_init_default(
        &tf_pub,
        &node,
        ROSIDL_GET_MSG_TYPE_SUPPORT(tf2_msgs, msg, TFMessage),
        "/tf");

    rclc_publisher_init_default(
        &joint_state_pub,
        &node,
        ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, JointState),
        "/joint_states");

  // create timer,
  const unsigned int timer_timeout = 100;
  RCCHECK(rclc_timer_init_default(
    &timer,
    &support,
    RCL_MS_TO_NS(timer_timeout),
    timer_callback));

    // create executor
  RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
  RCCHECK(rclc_executor_add_timer(&executor, &timer));

  static micro_ros_utilities_memory_conf_t conf = {0};
    // conf.max_string_capacity = 20;
    // conf.max_ros2_type_sequence_capacity = 5;
    // conf.max_basic_type_sequence_capacity = 5;

    micro_ros_utilities_create_message_memory(
        ROSIDL_GET_MSG_TYPE_SUPPORT(nav_msgs, msg, Odometry),
        &odom_msg,
        conf);

    micro_ros_utilities_memory_rule_t tf_msgs_rules[] = {
        {"transform", 1}};
    conf.rules = tf_msgs_rules;

    micro_ros_utilities_create_message_memory(
        ROSIDL_GET_MSG_TYPE_SUPPORT(tf2_msgs, msg, TFMessage),
        &odom_tf,
        conf);

    micro_ros_utilities_memory_rule_t joint_state_rules[] = {
        {"name", WHEEL_NUM},
        {"position", WHEEL_NUM},
        {"velocity", WHEEL_NUM},
        {"effort", 0}};

    conf.rules = joint_state_rules;

    micro_ros_utilities_create_message_memory(
        ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, JointState),
        &joint_state_msg,
        conf);

    micro_ros_string_utilities_set(odom_msg.header.frame_id, "odom_frame");
    micro_ros_string_utilities_set(odom_msg.child_frame_id, "base_footprint");

    odom_msg.pose.covariance[0] = 0.001;
    odom_msg.pose.covariance[7] = 0.001;
    odom_msg.pose.covariance[35] = 0.001;

    odom_msg.twist.covariance[0] = 0.0001;
    odom_msg.twist.covariance[7] = 0.0001;
    odom_msg.twist.covariance[35] = 0.0001;

    odom_geo.header.frame_id = odom_msg.header.frame_id;
    odom_geo.child_frame_id = odom_msg.child_frame_id;
    // copy_odomPose_to_tf(&odom_msg, &odom_geo);

    odom_tf.transforms.size = 1;
    odom_tf.transforms.data = &odom_geo;

    micro_ros_string_utilities_set(joint_state_msg.header.frame_id, "base_link");

    joint_state_msg.name.size = WHEEL_NUM;
    joint_state_msg.name.data[LEFT] = micro_ros_string_utilities_set(joint_state_msg.name.data[LEFT], "wheel_left_joint");
    joint_state_msg.name.data[RIGHT] = micro_ros_string_utilities_set(joint_state_msg.name.data[RIGHT], "wheel_right_joint");

    joint_state_msg.position.size = WHEEL_NUM;
    joint_state_msg.velocity.size = WHEEL_NUM;
    joint_state_msg.effort.size = 0;
}

void loop() {
  RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
}
hippo5329 commented 3 months ago

You should set serial baudrate to 921600. Or , 2000000 if your esp32 serial bridge supports, eg ch340.

markilius508 commented 3 months ago

You should set serial baudrate to 921600. Or , 2000000 if your esp32 serial bridge supports, eg ch340.

image

Thank you for your reply. I tried to change baudrate to 2000000 but somehow the maximum achievable rate appeared to be limited to 921600 (second last line in the Terminal Output).

Despite this adjustment, I observed no significant improvement in micro-ROS performance; the time needed for publishing those three messages remained unchanged.

hippo5329 commented 3 months ago

Your timer is 100ms, eq, 10Hz // create timer, const unsigned int timer_timeout = 100;

You can check the rate with "ros2 topic hz /your_topic" .

hippo5329 commented 3 months ago

The maximum topic rate of int32 publisher is around 250Hz on esp32. I use 50Hz for control loop. You should also use low latency kernel on host.

https://github.com/hippo5329/micro_ros_arduino_examples_platformio/wiki

markilius508 commented 3 months ago

@hippo5329 You are absolutely right, 10Hz is too slow. Increasing the publishing frequency and using a low latency kernel on the host are both on my to-do list. Thank you for your recommendation and sharing your repo - I'll definitely check it out.