使用STM32驱动舵机sg90

在这里插入图片描述

一、舵机原理

舵机(Servo Motor)驱动原理基于其内部的反馈控制系统,通常使用PWM(脉宽调制)信号来控制舵机的位置、速度或者扭矩。简单来说,舵机会根据输入的PWM信号的脉宽(占空比)来调整其转动角度。以下是详细的解释:

1. PWM信号控制

舵机的控制原理通常依赖于PWM信号的占空比(脉冲宽度与周期的比例)。PWM信号的周期一般固定为20ms(50Hz),但脉宽可以变化,从而控制舵机的转动角度。

  • 周期:通常为20ms,即50Hz。
  • 脉宽(占空比):通常在1ms到2ms之间变化,表示舵机的转动角度。
    • 1ms:舵机转到0度位置。
    • 1.5ms:舵机转到90度位置(中立位置)。
    • 2ms:舵机转到180度位置。

2. 舵机内部结构

舵机内部通常包括以下几个关键部分:

  • 电机:提供转动力矩。
  • 齿轮系统:将电机的转速转换为适当的扭矩并限制角度。
  • 位置传感器:通常是电位计,用来检测舵机的当前位置。
  • 控制电路:接收PWM信号,并通过反馈控制系统调整电机的转动。

3. 舵机的反馈控制

舵机通过反馈系统(通常是一个电位计)将当前角度反馈给控制电路。当舵机接收到PWM信号时,内部控制系统会判断目标位置和当前角度之间的误差,并根据误差调整电机的转动,直到达到目标位置。这个过程是一个闭环控制系统。

  • 目标角度:由输入的PWM信号的脉宽决定(比如1ms表示0度,1.5ms表示90度,2ms表示180度)。
  • 实际角度:通过位置传感器获取并反馈给控制电路。
  • 误差:目标角度和实际角度之间的差异。
  • 调整:舵机根据误差调整电机的运动,直到实际角度与目标角度一致。

4. 舵机的工作过程

  • 当你向舵机输入一个PWM信号时,舵机会根据脉冲宽度来判断目标位置。
  • 如果舵机当前的角度与目标角度不一致,舵机会驱动电机旋转,直到角度匹配。
  • 一旦到达目标位置,舵机就会停止转动并维持当前位置,直到接收到新的PWM信号。

5. 常见的舵机类型

  • 模拟舵机:根据PWM信号控制角度变化,一旦电机达到目标位置就停止旋转,并维持静止状态。适用于精确的角度控制,但通常不能连续旋转(仅有固定的转动范围,通常是0°到180°)。
  • 数字舵机:具有更高的精度和速度,内部控制电路更为复杂,通常能提供更高的扭矩和更精细的角度控制。

二、舵机控制(不考虑死区)

2.1 舵机控制原理(180°舵机)

PWM信号控制

舵机的控制原理通常依赖于PWM信号的占空比(脉冲宽度与周期的比例)。PWM信号的周期一般固定为20ms(50Hz),但脉宽可以变化,从而控制舵机的转动角度。

周期:通常为20ms,即50Hz。
脉宽(占空比):通常在1ms到2ms之间变化,表示舵机的转动角度。
1ms:舵机转到0度位置。
1.5ms:舵机转到90度位置(中立位置)。

总结:通过单片机设定一个20ms的PWM周期,然后通过不同的PWM占空比,让舵机转到指定位置

在这里插入图片描述

三、舵机死区

3.1 死区介绍

先简单看一下产品参数,这里以mg90为例,产品参数显示,死区是5微秒
5微秒死区:意味着舵机控制系统在信号输入的时间变化小于5微秒时,舵机不会做出任何响应。当PWM信号的脉宽变化不到5微秒时,舵机内部的反馈控制系统认为这是噪声或微小的波动,忽略它,不会做出位置调整
在这里插入图片描述

如何理解死区的存在?
死区简单理解为一个特别微小的变化,可能是主动的驱动或者被动的震动的幅度太小,不足以使舵机转动,我是把他理解为推动一个静止物体的最大静摩擦力即可,当驱动力不足以克服静止物体的最大静摩擦力时候,物体是推不动的。无论死区的原因是什么导致,尽可能大角度的控制才能让舵机精准的转动指定的角度。

3.2 软件减少死区方案

3.2.1 软件滤波(最简单)

死区通常是由于微小的PWM信号变化未能触发舵机响应。可以通过在软件中对输入信号进行平滑处理(如滤波)来减少微小信号的变化,以确保只有足够大的信号变化才会引起舵机动作。

方案:
软件滤波:使用简单的低通滤波器(如滑动平均滤波器)来平滑输入的PWM信号。这样,如果输入信号的变化小于某个阈值,舵机就不会执行任何动作。
比如,可以维护一个平滑后的PWM值列表,只有当新信号与历史平均信号有较大差异时,才调整舵机。

#define DEADZONE_THRESHOLD 10  // 死区阈值
uint16_t prev_pwm = 1500;   // 之前的PWM信号

void SmoothPWM(uint16_t pwm_input) {
    if (abs(pwm_input - prev_pwm) > DEADZONE_THRESHOLD) {
        prev_pwm = pwm_input;
        Servo_SetPWM(pwm_input);  // 只有超出死区阈值时才设置新的PWM信号
    }
}

优点
能有效减少微小的信号波动对舵机的影响。
可以减少舵机不必要的调整和振动。

3.2.2 死区补偿算法

动态调整目标位置:当控制信号接近死区时,可以设定一个“死区宽容区”,使得当输入信号处于死区时,舵机不会做出响应,直到输入信号超出该范围。

#define DEADZONE_MIN 1400  // 死区下限
#define DEADZONE_MAX 1600  // 死区上限

void Servo_SetAngleWithDeadzone(uint16_t pwm_input) {
    if (pwm_input < DEADZONE_MIN || pwm_input > DEADZONE_MAX) {
        Servo_SetPWM(pwm_input);  // 超过死区范围时才设置PWM
    }
}

优点
死区宽容区使得舵机不会响应微小的信号变化,从而避免频繁的调整和可能的振动。
提高了系统的稳定性,避免了无效的角度调整。

3.2.3 PDI反馈

PID(比例-积分-微分)控制器广泛用于舵机控制,特别是在需要精准控制位置时。为了减少死区带来的影响,可以通过调整PID参数来增加系统对微小变化的响应度,或引入反向积分等技术来避免死区效应。

做过飞思卡尔竞赛的同学应该都了解,车模车头就是一个精密舵机,通过舵机,完成快速转向,如下图车模上部是一个舵机。
想要精确控制需要对舵机增肌PID控制,通过PID补偿current角度和target的差值,这里不做详细分析。
在这里插入图片描述

float Kp = 1.0f;  // 比例增益
float Ki = 0.0f;  // 积分增益
float Kd = 0.1f;  // 微分增益

void PIDControl(uint16_t target_angle, uint16_t current_angle) {
    float error = target_angle - current_angle;
    if (fabs(error) > DEADZONE_MIN) {  // 如果误差大于死区范围才做调整
        // 计算PID控制
        // ...
        uint16_t pwm_output = Kp * error + Ki * integral + Kd * derivative;
        Servo_SetPWM(pwm_output);
    }
}

四、总体代码

下面是基于stm32f1的简单舵机代码

#include "stm32f10x.h"

void TIM2_PWM_Init(void) {
    // 1. 打开TIM2时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    // 2. 打开GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    // 3. 配置PA0为复用推挽输出
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 4. 配置定时器2生成PWM信号
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 20000 - 1;  // 频率20ms (50Hz)
    TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;   // 1MHz计数频率
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    // 5. 配置PWM输出
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_Pulse = 1500;  // 初始占空比,1500us对应90°位置
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    
    // 6. 启动定时器
    TIM_Cmd(TIM2, ENABLE);
    TIM_CtrlPWMOutputs(TIM2, ENABLE);  // 启用PWM输出
}

void Servo_SetAngle(uint16_t angle) {
    // 将角度映射到1ms到2ms的脉冲宽度
    // 0°对应1000us,180°对应2000us
    uint16_t pulse_width = (uint16_t)(1000 + (angle * 1000 / 180));  // 范围1ms - 2ms
    
    // 设置PWM脉冲宽度
    TIM_SetCompare1(TIM2, pulse_width);
}

int main(void) {
    // 初始化系统时钟
    SystemInit();
    
    // 初始化PWM
    TIM2_PWM_Init();
    
    while (1) {
        // 设定舵机角度为90°
        Servo_SetAngle(90);
        // 延时
        for (volatile int i = 0; i < 1000000; i++);
        
        // 设定舵机角度为45°
        Servo_SetAngle(45);
        // 延时
        for (volatile int i = 0; i < 1000000; i++);
        
        // 设定舵机角度为135°
        Servo_SetAngle(135);
        // 延时
        for (volatile int i = 0; i < 1000000; i++);
    }
}

附录视频

等我拍了补上。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值