2. 圆弧插补任务框架搭建

任务目标

在RTOS环境中创建一个独立的圆弧插补任务,该任务能够接收圆弧加工参数(起点、终点、圆心、方向),完成参数解析与合法性校验,并在任务循环中按周期输出当前插补点坐标。本案例聚焦任务框架与参数解析层,不涉及具体插补算法实现。

环境依赖

  • 任意RTOS(本案例以FreeRTOS API为例,接口与其他RTOS高度相似)
  • C语言编译器(GCC/Keil/IAR均可)
  • 串口或日志输出通道(用于观察任务运行状态)

数据结构与任务框架

圆弧插补任务通过消息队列接收加工指令,解析后的参数存入任务私有结构体。任务循环以固定周期(1ms)运行,每次迭代调用插补点计算函数(本案例以简化的角度增量法示意),并通过调试串口输出当前点坐标。

```c #include #include #include #include #include "FreeRTOS.h" #include "task.h" #include "queue.h" #define ARC_TASK_PERIOD_MS 1 #define ARC_QUEUE_LENGTH 4 #define PI 3.14159265358979f /* 圆弧加工指令(由外部通过消息队列发送) */ typedef struct { float sx, sy; /* 起点坐标 */ float ex, ey; /* 终点坐标 */ float cx, cy; /* 圆心坐标 */ uint8_t dir; /* 0=顺时针, 1=逆时针 */ } ArcCommand_t; /* 解析后的圆弧参数 */ typedef struct { float radius; float start_angle; float end_angle; float step_angle; /* 每周期增量角 */ float current_angle; float center_x, center_y; uint8_t valid; } ParsedArc_t; static QueueHandle_t xArcQueue; static ParsedArc_t xParsedArc; /* 圆弧参数解析与校验 */ static uint8_t ARC_ParseCommand(const ArcCommand_t *cmd, ParsedArc_t *out) { float dx_s = cmd->sx - cmd->cx; float dy_s = cmd->sy - cmd->cy; float dx_e = cmd->ex - cmd->cx; float dy_e = cmd->ey - cmd->cy; float r_s = sqrtf(dx_s*dx_s + dy_s*dy_s); float r_e = sqrtf(dx_e*dx_e + dy_e*dy_e); /* 半径一致性校验(允许0.01mm公差) */ if (fabsf(r_s - r_e) > 0.01f) { return 0; } out->radius = r_s; out->center_x = cmd->cx; out->center_y = cmd->cy; out->start_angle = atan2f(dy_s, dx_s); out->end_angle = atan2f(dy_e, dx_e); /* 计算总转角 */ float total_angle = out->end_angle - out->start_angle; if (cmd->dir == 0) { /* 顺时针:角度递减 */ while (total_angle > 0) total_angle -= 2*PI; } else { /* 逆时针:角度递增 */ while (total_angle < 0) total_angle += 2*PI; } /* 1ms周期,插补总时间按100ms规划(100步完成圆弧) */ out->step_angle = total_angle / 100.0f; out->current_angle = out->start_angle; out->valid = 1; return 1; } /* 根据当前角度计算插补点 */ static void ARC_GetInterpolatedPoint(const ParsedArc_t *arc, float *x, float *y) { *x = arc->center_x + arc->radius * cosf(arc->current_angle); *y = arc->center_y + arc->radius * sinf(arc->current_angle); } /* 圆弧插补任务主体 */ static void vArcTask(void *pvParameters) { ArcCommand_t rxCmd; float px, py; uint32_t step_count = 0; for (;;) { /* 尝试接收新指令(非阻塞) */ if (xQueueReceive(xArcQueue, &rxCmd, 0) == pdTRUE) { if (ARC_ParseCommand(&rxCmd, &xParsedArc)) { step_count = 0; printf("[ARC] New command accepted. Radius=%.2f\r\n", xParsedArc.radius); } else { printf("[ARC] Error: Invalid arc parameters!\r\n"); } } /* 若当前有有效圆弧,则执行插补步进 */ if (xParsedArc.valid && step_count < 100) { ARC_GetInterpolatedPoint(&xParsedArc, &px, &py); printf("[ARC] Step %lu: X=%.2f Y=%.2f\r\n", step_count, px, py); xParsedArc.current_angle += xParsedArc.step_angle; step_count++; if (step_count >= 100) { xParsedArc.valid = 0; printf("[ARC] Arc complete.\r\n"); } } vTaskDelay(pdMS_TO_TICKS(ARC_TASK_PERIOD_MS)); } } /* 任务创建与队列初始化 */ void ARC_Init(void) { xArcQueue = xQueueCreate(ARC_QUEUE_LENGTH, sizeof(ArcCommand_t)); memset(&xParsedArc, 0, sizeof(xParsedArc)); xTaskCreate(vArcTask, "ArcTask", 256, NULL, 2, NULL); } /* 外部发送圆弧指令的接口 */ void ARC_SendCommand(float sx, float sy, float ex, float ey, float cx, float cy, uint8_t dir) { ArcCommand_t cmd = {sx, sy, ex, ey, cx, cy, dir}; xQueueSend(xArcQueue, &cmd, 0); } ```

运行与验证

调用方式:在RTOS启动后调用ARC_Init()创建任务,然后通过ARC_SendCommand()发送一条圆弧指令。例如:

```c /* 在某个任务或main函数中 */ ARC_Init(); ARC_SendCommand(0, 10, 10, 0, 0, 0, 0); /* 起点(0,10) 终点(10,0) 圆心(0,0) 顺时针 */ ```

预期输出:串口依次打印约100行插补点坐标,首行接近X=0.00 Y=10.00,末行接近X=10.00 Y=0.00,中间点位于以(0,0)为圆心、半径10的圆弧上。若发送半径不一致的参数(如起点到圆心距离≠终点到圆心距离),任务打印Error: Invalid arc parameters!且不执行插补。

通过判定标准:

  1. 任务创建后无崩溃,周期输出正常;
  2. 合法圆弧指令被接受,输出点坐标满足(x-cx)²+(y-cy)²≈R²;
  3. 首末点与起点终点一致(误差<0.01);
  4. 非法参数被拒绝并打印错误信息。

关键说明

本案例将圆弧总插补步数固定为100步(对应100ms完成),实际工程中应根据进给速度与周期动态计算步数。角度归一无用while循环处理跨象限问题,确保顺时针/逆时针方向正确。任务框架可直接替换插补算法函数ARC_GetInterpolatedPoint()的内部实现,无需改动任务调度与通信结构。