直线插补 圆弧插补 步进电机二维直线插补圆弧插补控制算法 C语言 STM32移植

void line_interp(int x0, int y0, int x1, int y1) {
    int dx = abs(x1 - x0);
    int dy = -abs(y1 - y0);
    int sx = x0 < x1 ? 1 : -1;
    int sy = y0 < y1 ? 1 : -1;
    int err = dx + dy;
    
    while(1) {
        step_motor(X_AXIS, x0);
        step_motor(Y_AXIS, y0);
        if(x0 == x1 && y0 == y1) break;
        int e2 = 2 * err;
        if(e2 >= dy) {
            err += dy;
            x0 += sx;
        }
        if(e2 <= dx) {
            err += dx;
            y0 += sy;
        }
    }
}

这其实是改良版Bresenham算法,亮点在误差项计算。err变量像个裁判,决定哪个轴该动。当X轴误差累积超过阈值就动Y轴,反之亦然。注意dx取绝对值而dy带负号,这种不对称处理让算法在八象限都适用。

圆弧插补就刺激多了,得跟三角函数打交道。分享个极坐标实现的骚操作:

void arc_interp(float cx, float cy, float r, float start_ang, float end_ang) {
    float step_angle = 0.1; // 角度分辨率
    uint16_t steps = (uint16_t)(fabs(end_ang - start_ang)/step_angle);
    
    for(int i=0; i<steps; i++) {
        float theta = start_ang + i*step_angle;
        int x = cx + r * cos(theta);
        int y = cy + r * sin(theta);
        
        // 运动到目标点
        move_to(x, y); 
        
        // 动态调整步进延迟
        uint32_t delay = calc_delay(current_speed);
        DWT_Delay_us(delay); // 使用STM32的DWT精确延时
    }
}

这里藏着个坑:直接用三角函数计算每个点会产生累积误差。实测发现步进0.5度时,半径误差会累积到3个脉冲以上。后来改用DDA(数字微分分析)算法才解决:

void dda_arc(float xc, float yc, float xe, float ye) {
    float x = xc, y = yc;
    float dx = xe - xc, dy = ye - yc;
    float steps = fmax(fabs(dx), fabs(dy));
    
    if(steps == 0) return;
    
    float x_inc = dx / steps;
    float y_inc = dy / steps;
    
    for(int i=0; i<=steps; i++) {
        set_motor_position((int)x, (int)y);
        x += x_inc;
        y += y_inc;
        while(!tim3_update_flag); // 等待定时器触发
        tim3_update_flag = 0;
    }
}

移植到STM32要注意定时器配置。TIM3的ARR寄存器控制脉冲频率,CNT循环产生中断。这里有个性能优化点:直接操作寄存器比用HAL库快3倍以上:

// 定时器初始化
void TIM3_Init(uint32_t arr) {
    __HAL_RCC_TIM3_CLK_ENABLE();
    TIM3->ARR = arr; // 自动重装载值
    TIM3->PSC = 72-1; // 72MHz主频
    TIM3->DIER |= TIM_DIER_UIE; // 使能更新中断
    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
    TIM3->CR1 |= TIM_CR1_CEN;
}

// 中断服务函数里放脉冲生成代码
void TIM3_IRQHandler(void) {
    if(TIM3->SR & TIM_SR_UIF) {
        TIM3->SR &= ~TIM_SR_UIF;
        // 这里处理步进电机脉冲输出
        STEP_PORT->ODR ^= STEP_PIN; // 翻转步进引脚
    }
}

实测发现,当脉冲频率超过20kHz时,中断方式会有丢步现象。后来改用DMA+PWM模式才稳定跑到50kHz,关键在配置DMA循环模式传输预计算的脉冲序列。

最后提醒新手:电机运动曲线一定要做加减速处理!突然启停会让步进电机失步。推荐使用S型曲线算法,比梯形加速平滑30%以上。附上速度规划代码片段:

float s_curve(float t, float total_time) {
    // 归一化时间
    float x = t / total_time;
    // S曲线函数
    return 0.5f - 0.5f * cosf(x * M_PI);
}

调参时用示波器看脉冲波形,能看到明显的光滑过渡。记得根据电机扭矩调整加速度参数,别让算法成了空中楼阁。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐