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判断,它需要结合回零状态、考虑电机惯性、设计缓冲区。这三个点做好了,你的运动控制系统才算真正安全。
下一章我会讲硬件限位和软件限位的配合策略。到时候会聊到「软硬结合」的实战案例,敬请期待。