• 简述 K60 直流电机闭环控制


    仅用于备份本人所写笔记,如有错误或不完善之处还请包含。转载请注明出处!

    程序使用逐飞科技 K60 库 V2.0.3

    电机速度闭环控制,简单来说就是将电机的输出量(速度)通过编码器反馈到控制端(单片机),然后对该输出量和设定的输入量进行比对,接着经过 PID 运算,将运算结果输入给电机的过程。

    电机速度闭环一个显而易见的好处就是,可以让小车在不同的路段动态调整小车的速度,如果参数调整的好,可以让小车极大的提升对赛道的适应性能,减少小车跑完一圈所需的时间。

    闭环控制思路比较简单,首先需要获取编码器的值,每 4ms 取一次值(可以设定其他时间),然后将取到的值进行相应的计算,得出电机当前速度。

    代码示例:

    const float unit_pulse = 1783.0;  // 小车轮胎旋转一周(360°)的脉冲
    const float motor_radius = 0.063;  // 电机轮胎半径(米)
    
    // 编码器初始化
    void EncoderInit(void) {
        ftm_quad_init(ftm2);  // 初始化 ftm2 为正交解码
    
        port_init_NoAlt(B18, PULLUP);  // 上拉
        port_init_NoAlt(B19, PULLUP);  // 上拉
    }
    
    // 读取编码器值
    float EncoderRead(void) {
        float data = 0;
    
        data = ftm_quad_get(ftm2);  // 获取编码器的脉冲值
        ftm_quad_clean(ftm2);       // 清除正交解码的脉冲值
    
        return data;
    }
    
    // 计算电机当前速度
    // 输入值为运行该程序的间隔时间
    float GetMotorSpeed(uint16 run_time_ms) {
        float motor_speed = 0;
    
        encoder.finalValue +=
            0.25f * (encoder.value - encoder.finalValue);  // 一阶低通滤波
        encoder.motorRounds =
            (encoder.finalValue * run_time_ms) / unit_pulse;  // 电机每 1s 转的圈数
        encoder.motorDistance =
            encoder.motorRounds * (2 * PI * motor_radius);  // 电机运行长度 m
        motor_speed = encoder.motorDistance / 1;            // 电机速度 m/s
    
        return motor_speed;
    }
    

    计算出速度后,把该变量赋值给 PID 计算,算出需要输出给电机的占空比。PID 计算公式可以根据实际情况稍作修改优化。

    代码示例:

    // 电机 pid 程序
    void MotorPid(void) {
        static float err, last_err, expect_pwm;
    
        // 请根据实际情况修改 KP、KI 参数大小
        motor_pid.kp = 0.1;
        motor_pid.ki = 0.2;
    
        err = motor.expectSpeed - encoder.motorSpeed;
        expect_pwm += motor_pid.kp * (err - last_err) + motor_pid.ki * err;
        last_err = err;
    
        // 电机占空比限幅
        // 请根据实际情况修改最大和最小 PWM 参数值
        if (expect_pwm > 260) expect_pwm = 260;
        if (expect_pwm <= 0) expect_pwm = 0;
    
        // 将值转换为正整数类型
        motor.expectDutyRatio = (uint16)(expect_pwm);
    }
    

    PID 返回期望占空比赋值给电机控制函数:

    // 电机控制程序
    void MotorControl(void) {
        MotorPid();
        MotorPWM(0, motor.expectDutyRatio);
    }
    

    这样就实现了简单的电机速度闭环控制。当然这只算是比较基础的闭环,若要实现复杂功能,还需进行大量优化。

    如下为本文闭环全部代码:

    /*******************************************************************************************
     * COPYRIGHT NOTICE
     * Copyright (c) 2019, ZhangXiao
     * All rights reserved.
     *
     * 本文件仅供参考交流,未经允许不得用于商业用途!
     *
     * File           : motor.c
     * Author         : ZhangXiao
     * Blog           : www.cnblogs.com/zhangxiaochn
     * Version        : v1.0.1
     * Date           : 2019-08-06
     * Software       : IAR 7.70.1
     * Description    : 电机相关程序以及 PID、编码器程序等
     * note           : None
    ********************************************************************************************/
    
    #include "motor.h"
    #include "headfile.h"
    
    const float unit_pulse = 1783.0;  // 小车轮胎旋转一周(360°)的脉冲
    const float motor_radius = 0.063;  // 电机轮胎半径(米)
    
    // 电机 pid 程序结构体参数
    struct MOTOR_PID motor_pid = {0.1, 0.2, 0, 0, 0, 0, 0, 0};
    
    // 编码器结构体
    struct ENCODER encoder = {0, 0, 0, 0, 0};
    
    // 电机结构体
    struct MOTOR motor = {0, 0, 0};
    
    // 电机初始化
    // FTM1_CH0_PIN 端口为 A12
    // FTM1_CH1_PIN 端口为 A13
    // 电机初始化频率默认为 10k,请根据实际情况更改频率大小
    void MotorInit(void) {
        ftm_pwm_init(ftm1, ftm_ch0, 10 * 1000, 0);
        ftm_pwm_init(ftm1, ftm_ch1, 10 * 1000, 0);
    }
    
    // 电机 PWM 控制
    void MotorPWM(uint16 pwm_1, uint16 pwm_2) {
        ftm_pwm_duty(ftm1, ftm_ch0, pwm_1);  // A12 端口
        ftm_pwm_duty(ftm1, ftm_ch1, pwm_2);  // A13 端口
    }
    
    // 编码器初始化
    void EncoderInit(void) {
        ftm_quad_init(ftm2);  // 初始化 ftm2 为正交解码
    
        port_init_NoAlt(B18, PULLUP);  // 上拉
        port_init_NoAlt(B19, PULLUP);  // 上拉
    }
    
    // 读取编码器值
    float EncoderRead(void) {
        float data = 0;
    
        data = ftm_quad_get(ftm2);  // 获取编码器的脉冲值
        ftm_quad_clean(ftm2);       // 清除正交解码的脉冲值
    
        return data;
    }
    
    // 计算电机当前速度
    // 输入值为运行该程序的间隔时间
    float GetMotorSpeed(uint16 run_time_ms) {
        float motor_speed = 0;
    
        encoder.finalValue +=
            0.25f * (encoder.value - encoder.finalValue);  // 一阶低通滤波
        encoder.motorRounds =
            (encoder.finalValue * run_time_ms) / unit_pulse;  // 电机每 1s 转的圈数
        encoder.motorDistance =
            encoder.motorRounds * (2 * PI * motor_radius);  // 电机运行长度 m
        motor_speed = encoder.motorDistance / 1;            // 电机速度 m/s
    
        return motor_speed;
    }
    
    // 计算电机运行距离
    // 输入值为运行该程序的间隔时间
    float GetMotorDistance(uint16 run_time_ms) {
        return encoder.motorDistance / run_time_ms;
    }
    
    // 编码器相关参数计算
    // 输入值为运行该程序的间隔时间
    // 程序建议每 4ms 运行一次,即 Encoder_Calculate(4);
    void EncoderCalculate(uint16 run_time_ms) {
        uint16 run_time = 1000 / run_time_ms;  // 每秒钟运行此程序次数
    
        encoder.value = EncoderRead();                 // 读取编码器的值
        encoder.motorSpeed = GetMotorSpeed(run_time);  // 计算电机当前速度
        motor.distance += GetMotorDistance(run_time);  // 获取电机距离
    }
    
    // 电机 pid 程序
    void MotorPid(void) {
        static float err, last_err, expect_pwm;
    
        // 请根据实际情况修改 KP、KI 参数大小
        motor_pid.kp = 0.1;
        motor_pid.ki = 0.2;
    
        err = motor.expectSpeed - encoder.motorSpeed;
        expect_pwm += motor_pid.kp * (err - last_err) + motor_pid.ki * err;
        last_err = err;
    
        // 电机占空比限幅
        // 请根据实际情况修改最大和最小 PWM 参数值
        if (expect_pwm > 260) expect_pwm = 260;
        if (expect_pwm <= 0) expect_pwm = 0;
    
        // 将值转换为正整数类型
        motor.expectDutyRatio = (uint16)(expect_pwm);
    }
    
    // 电机控制程序
    void MotorControl(void) {
        MotorPid();
        MotorPWM(0, motor.expectDutyRatio);
    }
    
    
  • 相关阅读:
    Dev中出现Error 0: 操作成功完成。
    找出一个二维数组中的鞍点,即该位置上的元素在该行上最大、在该列上最小。也可能没有没有鞍点。
    oracle 日常删除多余数据
    java程序练习:输入数字转换成中文输出(金额相关)
    linux压缩和解压文件命令
    一篇文章搞定Git——Git代码管理及使用规范
    读书心得(1)-20191108
    回表查询的说明
    Java 8新特性解读
    java-try catch中return在finally之前 还是之后执行
  • 原文地址:https://www.cnblogs.com/zhangxiaochn/p/11225487.html
Copyright © 2020-2023  润新知