鱼香ROS社区
    • 版块
    • 最新
    • 未解决
    • 已解决
    • 群组
    • 注册
    • 登录
    紧急通知:禁止一切关于政治&VPN翻墙等话题,发现相关帖子会立马删除封号
    提问前必看的发帖注意事项: 社区问答规则(小鱼个人)更新 | 高质量帖子发布指南

    使用Micro-ROS报错

    已定时 已固定 已锁定 已移动 未解决
    综合问题
    micro-ros 话题通信 求助
    1
    2
    702
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • W
      winstonmanapple
      最后由 编辑

      使用CubeMX+IDE开发
      下面是我freertos的Default代码

      void StartDefaultTask(void *argument)
      {
        /* USER CODE BEGIN StartDefaultTask */
      
        // 0. 上电自检打印
        printf("\r\n[SYSTEM] Power On.\r\n");
        printf("[SYSTEM] Stack Size: %d bytes\r\n", (int)(3000 * 4));
      
        // 1. 初始化底层 (串口/内存)
        printf("[Init] Connecting to Agent...\r\n");
        while(Micro_ROS_Patch_Init(&huart3) != 1)
        {
            printf("[Error] Patch Init Failed.\r\n");
            osDelay(500);
        }
      
        // 2. 初始化分配器
        allocator = rcl_get_default_allocator();
      
        // 3. 握手连接 Agent
        rcl_ret_t rc_support = rclc_support_init(&support, 0, NULL, &allocator);
        while (rc_support != RCL_RET_OK)
        {
            printf("."); // 打印点点表示正在连接
            HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); // 快闪板载LED
            osDelay(200);
            // 重试
            rc_support = rclc_support_init(&support, 0, NULL, &allocator);
        }
        printf("\r\n[Init] Agent Connected! (rc=%d)\r\n", (int)rc_support);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 灭灯
      
        // 4. 创建节点
        rclc_node_init_default(&node, "stm32_led_node", "", &support);
      
        // 5. 创建订阅者 (Reliable)
        rcl_ret_t rc_sub = rclc_subscription_init_default(
            &subscriber,
            &node,
            ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, String),
            "led_control"
        );
        if (rc_sub != RCL_RET_OK) {
            printf("[Fatal] Sub Init Failed: %d\r\n", (int)rc_sub);
            for(;;) osDelay(100);
        }
      
        // 6. 配置消息内存
        std_msgs__msg__String__init(&sub_msg);
        sub_msg.data.data = msg_buffer;
        sub_msg.data.capacity = sizeof(msg_buffer);
        sub_msg.data.size = 0;
      
        // 7. 初始化执行器
        rcl_ret_t rc_exec = rclc_executor_init(&executor, &support.context, 4, &allocator);
        if (rc_exec != RCL_RET_OK) {
            printf("[Fatal] Executor Init Failed: %d\r\n", (int)rc_exec);
            for(;;) osDelay(100);
        }
      
        // 8. 添加订阅者
        rcl_ret_t rc_add = rclc_executor_add_subscription(
              &executor,
              &subscriber,
              &sub_msg,
              &Led_Control_Callback,
              ON_NEW_DATA
          );
        if (rc_add != RCL_RET_OK) {
            printf("[Fatal] Add Sub Failed: %d\r\n", (int)rc_add);
            for(;;) osDelay(100);
        }
      
        printf("[Init] System Ready. Entering Loop...\r\n");
      
        // ==========================================================
        // 主循环
        // ==========================================================
        for(;;)
        {
            // Spin
            rcl_ret_t rc = rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
      
            if (rc == RCL_RET_OK) {
                // 正常: 收到消息处理完毕
                // printf("OK "); // 刷屏太快,建议屏蔽
            }
            else if (rc == RCL_RET_TIMEOUT) {
                // 正常: 没有消息
            }
            else {
                // 🔴 真正的运行时错误捕获
                printf("[Error] Spin: %d\r\n", (int)rc);
      
                osDelay(100);
            }
      
            osDelay(10);
        }
        /* USER CODE END StartDefaultTask */
      }
      

      之后报错,下面是报错日志

      [Init] Agent Connected! (rc=0)
      [Init] System Ready. Entering Loop...
      [Error] Spin: 900
      [Error] Spin: 900
      [Error] Spin: 900
      [Error] Spin: 900
      [Error] Spin: 900
      [Error] Spin: 900
      [Error] Spin: 900
      
      1 条回复 最后回复 回复 引用 0
      • W
        winstonmanapple
        最后由 编辑

        问题解决了,源头出在一个补丁函数:

        void * microros_reallocate(void * pointer, size_t size, void * state){
          return NULL; // 静态策略下通常不使用 realloc
        }
        

        简单来说,就是micro-ROS 试图申请或调整内存时,需要用到这个函数,而我的函数直接返回了NULL值,导致了micro-ROS 无法调整内存
        为此,求助LLM,对函数做出以下修改:

        void * microros_reallocate(void * pointer, size_t size, void * state){
          // 情况 1: 如果指针为空,这就相当于 malloc (这是 Micro-ROS 初始化最常用的情况)
          if (pointer == NULL) {
            return pvPortMalloc(size);
          }
        
          // 情况 2: 如果大小为 0,这就相当于 free
          if (size == 0) {
            vPortFree(pointer);
            return NULL;
          }
        
          // 情况 3: 真正的 realloc (调整大小)
          // 由于 FreeRTOS 的 heap_4 不支持 realloc,我们需要手动“搬家”
          // ⚠️ 警告:我们不知道旧数据块的大小,这是一个风险点。
          // 但 Micro-ROS 通常用它是为了扩大数组。
          // 这里的策略是:申请新内存 -> 假定旧数据有效 -> 释放旧内存
          
          void * new_ptr = pvPortMalloc(size);
          if (new_ptr != NULL) {
              // 这里的拷贝长度是个玄学,因为我们不知道旧块有多大。
              // 但对于 wait_set 这种结构,通常是安全的,或者我们可以只拷贝一部分。
              // 为了安全起见,我们假设旧数据也很重要,拷贝 size 长度 (可能会读越界,但在 RAM 连续的 MCU 上通常没事)
              // 更安全的做法是引入记录内存大小的机制,但太复杂。
              // 这里采用最简单的“能跑就行”策略:
              memcpy(new_ptr, pointer, size); // 注意:如果 size < old_size,会截断;如果 size > old_size,可能会读到垃圾数据
              vPortFree(pointer);
          }
          return new_ptr;
        }
        

        重新编译烧录,问题解决

        1 条回复 最后回复 回复 引用 0
        • 第一个帖子
          最后一个帖子
        皖ICP备16016415号-7
        Powered by NodeBB | 鱼香ROS