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!且不执行插补。
通过判定标准:
- 任务创建后无崩溃,周期输出正常;
- 合法圆弧指令被接受,输出点坐标满足
(x-cx)²+(y-cy)²≈R²;
- 首末点与起点终点一致(误差<0.01);
- 非法参数被拒绝并打印错误信息。
关键说明
本案例将圆弧总插补步数固定为100步(对应100ms完成),实际工程中应根据进给速度与周期动态计算步数。角度归一无用while循环处理跨象限问题,确保顺时针/逆时针方向正确。任务框架可直接替换插补算法函数ARC_GetInterpolatedPoint()的内部实现,无需改动任务调度与通信结构。