3、软件限位实现:基于编码器位置的软限位、回零后的限位使能、限位缓冲区的概念

好,咱们接着聊软件限位。硬件限位是底线,但你不能光靠它。为什么?因为撞到硬限位的那一刻,冲击力已经存在了。哪怕你速度再慢,机械结构也会受到一点影响。我见过不少设备,用久了硬限位开关都撞歪了。

软件限位,说白了就是在代码里画两条红线。一条是正向限位,一条是负向限位。电机跑到红线跟前,系统主动刹车。这比撞硬限位温柔多了。

3.1 基于编码器位置的软限位

实现方式其实很简单。你只需要在每次位置更新时,检查一下当前编码器值。

// 伪代码示例
#define POSITIVE_LIMIT  10000  // 正向软限位,单位:脉冲
#define NEGATIVE_LIMIT  -10000 // 负向软限位

int32_t current_position; // 从编码器读取

void check_soft_limit(void) {
    if (current_position >= POSITIVE_LIMIT) {
        motor_stop();
        set_error(ERROR_SOFT_LIMIT_POS);
    }
    else if (current_position <= NEGATIVE_LIMIT) {
        motor_stop();
        set_error(ERROR_SOFT_LIMIT_NEG);
    }
}

嗯,这里要注意。这个检查函数必须放在高速循环里。比如伺服驱动的电流环或者位置环里。我早期犯过一个错,把它放在了一个100ms的慢速任务里。结果电机冲出去老远才触发限位,差点撞坏工件。

我个人习惯把软限位检查放在中断服务程序里。编码器Z相中断或者定时器中断,频率至少1kHz。这样响应时间能控制在1ms以内。

关键点:软限位的响应速度取决于检查频率。频率越高,越安全。

3.2 回零后的限位使能

这里有个坑,我必须要讲。很多新手一上电就把软限位使能了。结果呢?电机还没回零,位置值可能是随机的。比如编码器上电后默认值是0,但实际机械位置可能在中间。这时候软限位一使能,电机可能直接报错,动都动不了。

正确的做法是:回零完成后,再使能软限位

为什么?因为回零操作会建立一个绝对的机械参考点。只有知道了零点,你才能知道正向和负向的边界在哪里。

// 状态机示例
typedef enum {
    STATE_NOT_HOMED,
    STATE_HOMING,
    STATE_HOMED
} system_state_t;

system_state_t state = STATE_NOT_HOMED;

void motion_task(void) {
    switch (state) {
        case STATE_NOT_HOMED:
            // 软限位不使能
            soft_limit_enable = false;
            // 等待回零指令
            break;
            
        case STATE_HOMING:
            // 执行回零流程
            soft_limit_enable = false;
            break;
            
        case STATE_HOMED:
            // 回零完成,使能软限位
            soft_limit_enable = true;
            // 正常运动控制
            break;
    }
}

我曾经在一个项目中,客户抱怨说设备上电后偶尔会自己报错。查了两天才发现,是回零过程中编码器值跳变,触发了软限位。从那以后,我所有的项目都严格遵循「回零后才使能软限位」这个原则。

警告:绝对不要在未回零的状态下使能软限位。这会导致误触发,甚至让设备无法启动。

3.3 限位缓冲区的概念

好,接下来是重点。限位缓冲区,这个概念很多人没听过。但它在实际项目中非常有用。

你想想看,软限位设在了10000脉冲的位置。电机以高速冲过去,在9999脉冲时触发停止。但电机有惯性啊,它不可能瞬间停住。实际停下来时可能已经到了10050脉冲。这就超出了软限位。

限位缓冲区就是解决这个问题的。我们在软限位内部再划出一段区域,叫做缓冲区。比如软限位在10000,缓冲区从9500到10000。当电机进入缓冲区,系统开始减速,而不是急停。

// 带缓冲区的软限位实现
#define SOFT_LIMIT_POS     10000
#define BUFFER_ZONE        500   // 缓冲区大小,单位:脉冲

void check_soft_limit_with_buffer(int32_t pos, int32_t velocity) {
    int32_t buffer_start = SOFT_LIMIT_POS - BUFFER_ZONE;
    
    if (pos >= SOFT_LIMIT_POS) {
        // 到达硬限位,急停
        motor_emergency_stop();
    }
    else if (pos >= buffer_start) {
        // 进入缓冲区,减速
        // 根据距离限位的远近,计算减速曲线
        float distance_to_limit = SOFT_LIMIT_POS - pos;
        float decel_ratio = distance_to_limit / BUFFER_ZONE;
        int32_t target_speed = (int32_t)(velocity * decel_ratio);
        
        if (target_speed < MIN_SPEED) {
            target_speed = 0;
        }
        set_motor_speed(target_speed);
    }
}

这个缓冲区的大小怎么定?我一般按这个公式估算:

参数 说明 推荐值
最大速度 电机运行的最高速度 根据电机规格
减速时间 从最大速度到0的时间 50-200ms
缓冲区大小 缓冲区脉冲数 最大速度 × 减速时间 × 2

举个例子。电机最大速度是3000rpm,配2500线的编码器。每秒脉冲数就是3000/60×2500×4 = 500,000。减速时间设100ms。缓冲区大小就是500,000 × 0.1 × 2 = 100,000脉冲。

为什么要乘以2?这是安全系数。我吃过亏,有一次负载惯性特别大,减速距离比计算的长了一倍。从那以后,我习惯把缓冲区放大一倍。

小技巧:缓冲区不一定是线性的。你可以用S曲线减速,更平滑。但代码复杂度会高一些。

嗯,总结一下今天的重点。软件限位不是简单的if判断,它需要结合回零状态、考虑电机惯性、设计缓冲区。这三个点做好了,你的运动控制系统才算真正安全。

下一章我会讲硬件限位和软件限位的配合策略。到时候会聊到「软硬结合」的实战案例,敬请期待。