24年电赛——自动行驶小车(H题)MSPM0G3507-编码电机驱动与通用PID

news2024/9/24 5:23:05

一、编码电机驱动

        编码电机的详情可以查看此篇文章:

stm32平衡小车--(1)JGB-520减速电机+tb6612(附测试代码)_jgb520-CSDN博客

        简单来说,编码电机的驱动主要是给一个 PWM 和一个正负级就能驱动。PWM 的大小决定转速快慢,电机的两个电源正负极不同决定了旋转的方向不同。

1、PWM设置:

        PWM 的值可以在图形化配置界面进行配置,同时也可以通过如下函数进行设置:第一个参数是定时器,第二个是捕获比较值,第三个是通道。

void DL_Timer_setCaptureCompareValue(
    GPTIMER_Regs *gptimer, uint32_t value, DL_TIMER_CC_INDEX ccIndex);

        如下是设置占空比的一个函数。第一个参数是捕获比较值,第二个参数是设置通道,我是用一个定时器输出两路 PWM,所以需要两个通道。因为设置的是下降沿触发,系统主频是80MHz,进行了10000分频,所以计数值就是8000。duty = 7999 - 7999 * (duty/3200.0);就可以将捕获比较值从8000映射到3200。然后下面 if 来判断设置PWM输出的通道。

// 设置电机PWM占空比,取值范围为3200~0,对应占空比为100%~0%
void Motor_SetPwm(float duty,uint8_t channel) {

    duty = 7999 - 7999 * (duty/3200.0);

    if(channel==0){
        DL_TimerA_setCaptureCompareValue(PWM_MOTOR_INST , duty, DL_TIMER_CC_0_INDEX );  // 设置定时器捕获比较值
    }else {
        DL_TimerA_setCaptureCompareValue(PWM_MOTOR_INST , duty, DL_TIMER_CC_1_INDEX );  // 设置定时器捕获比较值
    }
}

        PWM 图形化配置:

 2、电机方向设置

        此函数与上面的参数一致。这两个函数分别用来设置引脚低电平和高电平。

DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
DL_GPIO_setPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);

        下面函数时根据驱动电机。duty 值为正,电机正转,duty 值为负,电机反转;channel 值为0,驱动右轮,channel 值为1,驱动左轮。

// 电机前进控制函数
void Set_Speed(float duty, uint8_t channel) {
    if(channel==0){     //判断左右轮
        if(duty>0){     //判断正反转
            Motor_SetPwm(duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);
        }else if(duty<0){
            Motor_SetPwm(-duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
        }else {
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);
        }
    }else {
        if(duty>0){
            Motor_SetPwm(duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR1_PORT, GPIO_MOTOR_PIN_FR1_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FR2_PORT, GPIO_MOTOR_PIN_FR2_PIN);
        }else if(duty<0){
            Motor_SetPwm(-duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR2_PORT, GPIO_MOTOR_PIN_FR2_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FR1_PORT, GPIO_MOTOR_PIN_FR1_PIN);
        }else {
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR1_PORT, GPIO_MOTOR_PIN_FR1_PIN);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR2_PORT, GPIO_MOTOR_PIN_FR2_PIN);
        }
    }
}

二、通用PID

        其中增量式 PID 不需要初始阈值,其输出主要是与上次误差与上上次误差有关,适合调节稳定速度;

        位置式 PID 需要初始阈值,其输出的结果与历史的状态有关,适合将速度维持在某个定值。

pid.c

#include "pid.h"
#include <math.h>

// 初始化PID结构体
void PID_Init(PID *s_PID, PID_VAR_TYPE set_point, PID_VAR_TYPE Proportion,
              PID_VAR_TYPE Integral, PID_VAR_TYPE Derivative)
{
    // 初始化PID参数
    s_PID->SetPoint = set_point;            // 设定目标值
    s_PID->Proportion = Proportion;         // 比例系数
    s_PID->Integral = Integral;             // 积分系数
    s_PID->Derivative = Derivative;         // 微分系数
    s_PID->Error = 0;                       // 当前误差
    s_PID->LastError = 0;                   // 上一次误差
    s_PID->PrevError = 0;                   // 上上次误差
    s_PID->SumError = 0;                    // 总误差
    s_PID->LastResult = 0;                  // 上一次计算结果
    s_PID->Result = 0;                      // 当前计算结果
    s_PID->OutMax = DEFAULT_PID_OUT_MAX;    // 输出上限
    s_PID->OutMin = DEFAULT_PID_OUT_MIN;    // 输出下限
    s_PID->IntegralMax = DEFAULT_PID_INTEGRAL_OUT_MAX;  // 积分输出上限
    s_PID->IntegralMin = DEFAULT_PID_INTEGRAL_OUT_MIN;  // 积分输出下限
}

// 设置PID的目标值
void PID_SetPoint(PID *s_PID, PID_VAR_TYPE set_point)
{
    s_PID->SetPoint = set_point;
}

// 设置PID的输出范围
void PID_SetOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin)
{
    s_PID->OutMax = outMax;
    s_PID->OutMin = outMin;
}

// 设置PID的积分输出范围
void PID_SetIntegralOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin)
{
    s_PID->IntegralMax = outMax;
    s_PID->IntegralMin = outMin;
}

// 增量型PID计算
PID_VAR_TYPE Increment_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point)
{
    s_PID->LastResult = s_PID->Result;   // 保存上一次的计算结果

    // 计算误差
    s_PID->Error = s_PID->SetPoint - now_point;

    // PID计算
    s_PID->Result =
        s_PID->LastResult +
        s_PID->Proportion * (s_PID->Error - s_PID->LastError) +  // 比例项
        s_PID->Integral * s_PID->Error +                          // 积分项
        s_PID->Derivative * (s_PID->Error - 2 * s_PID->LastError + s_PID->PrevError);  // 微分项

    s_PID->PrevError = s_PID->LastError;   // 更新上上次误差
    s_PID->LastError = s_PID->Error;       // 更新上一次误差

    // 限制输出范围
    if (s_PID->Result > s_PID->OutMax)
        s_PID->Result = s_PID->OutMax;
    else if (s_PID->Result < s_PID->OutMin)
        s_PID->Result = s_PID->OutMin;

    return s_PID->Result;
}

// 位置型PID计算
PID_VAR_TYPE Position_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point)
{
    s_PID->LastResult = s_PID->Result;   // 保存上一次的计算结果

    // 计算误差
    s_PID->Error = s_PID->SetPoint - now_point;
    s_PID->SumError += s_PID->Error;    // 更新总误差

    // 限制积分输出范围
    PID_VAR_TYPE IOutValue = s_PID->SumError * s_PID->Integral;
    if (IOutValue > s_PID->IntegralMax)
        IOutValue = s_PID->IntegralMax;
    else if (IOutValue < s_PID->IntegralMin)
        IOutValue = s_PID->IntegralMin;

    // PID计算
    s_PID->Result =
        s_PID->Proportion * s_PID->Error +      // 比例项
        IOutValue +                            // 积分项
        s_PID->Derivative * (s_PID->Error - s_PID->LastError);  // 微分项

    s_PID->PrevError = s_PID->LastError;   // 更新上上次误差
    s_PID->LastError = s_PID->Error;       // 更新上一次误差

    // 限制输出范围
    if (s_PID->Result > s_PID->OutMax)
        s_PID->Result = s_PID->OutMax;
    else if (s_PID->Result < s_PID->OutMin)
        s_PID->Result = s_PID->OutMin;

    return s_PID->Result;
}

// 综合型PID计算
PID_VAR_TYPE PID_Cal(PID *s_PID, PID_VAR_TYPE now_point)
{
    s_PID->LastResult = s_PID->Result;   // 保存上一次的计算结果

    // 计算误差
    s_PID->Error = s_PID->SetPoint - now_point;
    s_PID->SumError += s_PID->Error;    // 更新总误差

    // 限制积分输出范围
    PID_VAR_TYPE IOutValue = s_PID->SumError * s_PID->Integral;
    if (IOutValue > s_PID->IntegralMax)
        IOutValue = s_PID->IntegralMax;
    else if (IOutValue < s_PID->IntegralMin)
        IOutValue = s_PID->IntegralMin;

    // PID计算
    s_PID->Result =
        s_PID->Proportion * (s_PID->Error + IOutValue) +   // 比例项
        s_PID->Derivative * (s_PID->Error - s_PID->LastError);  // 微分项

    s_PID->PrevError = s_PID->LastError;   // 更新上上次误差
    s_PID->LastError = s_PID->Error;       // 更新上一次误差

    // 限制输出范围
    if (s_PID->Result > s_PID->OutMax)
        s_PID->Result = s_PID->OutMax;
    else if (s_PID->Result < s_PID->OutMin)
        s_PID->Result = s_PID->OutMin;

    return s_PID->Result;
}

pid.h

#ifndef __PID_H
#define __PID_H	  

//PID输出范围宏定义
#define DEFAULT_PID_OUT_MAX               ( 3200)
#define DEFAULT_PID_OUT_MIN               (-3200)
//PID积分范围宏定义
#define DEFAULT_PID_INTEGRAL_OUT_MAX      ( 400)
#define DEFAULT_PID_INTEGRAL_OUT_MIN      (-400)

//PID变量类型宏定义
#define PID_VAR_TYPE                            float     //int

//定义结构体
typedef struct
{
    PID_VAR_TYPE SetPoint;      // 设定目标 
    PID_VAR_TYPE Proportion;    // 比例常数
    PID_VAR_TYPE Integral;      // 积分常数
    PID_VAR_TYPE Derivative;    // 微分常数 
    PID_VAR_TYPE SumError;      // 积分和
    PID_VAR_TYPE Error;         // 误差
    PID_VAR_TYPE LastError;     // 上次误差 
    PID_VAR_TYPE PrevError;     // 前一次误差   
    PID_VAR_TYPE LastResult;    // 上次计算结果
    PID_VAR_TYPE Result;        // 当前计算结果
    PID_VAR_TYPE OutMax;        // 输出限幅最大值
    PID_VAR_TYPE OutMin;        // 输出限幅最小值
    PID_VAR_TYPE IntegralMax;   // 积分限幅最大值
    PID_VAR_TYPE IntegralMin;   // 积分限幅最小值
} PID;

extern void PID_Init(PID *s_PID, PID_VAR_TYPE set_point,
                        PID_VAR_TYPE Proportion,
                        PID_VAR_TYPE Integral,
                        PID_VAR_TYPE Derivative);  //PID初始化

extern void PID_SetOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin);          //设置PID输出范围
extern void PID_SetIntegralOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin);  //设置PID积分范围
extern void PID_SetPoint(PID *s_PID, PID_VAR_TYPE set_point);                               //设置目标值

extern PID_VAR_TYPE Increment_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point);  //增量式PID计算
extern PID_VAR_TYPE Position_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point);   //位置式PID计算
extern PID_VAR_TYPE PID_Cal(PID *s_PID, PID_VAR_TYPE now_point);            //比例外置式PID

#endif 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1976329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI PC处理器架-低功耗、NPU算力、大模型

AI PC处理器架构变化&#xff1a;ARM低功耗、引入NPU算力、大模型落地端侧 ARM架构以简洁的指令集设计&#xff0c;快速执行每条命令&#xff0c;实现低功耗下的高效性能。其核心理念是节能和效率&#xff0c;为电池驱动设备提供了理想选择。相较之下&#xff0c;x86架构虽指令…

Linux--shell脚本语言—/—<1>

一、shell简介 Shell是一种程序设计语言。作为命令语言&#xff0c;它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令&#xff1b;作为程序设计语言&#xff0c;它定义了各种变量和参数&#xff0c;并提供了许多在高级语言中才具有的控制结构&am…

【practise】大数相加、大数相乘

通常&#xff0c;我们的int、long long类型都有最大的数字上限&#xff0c;也就是说再大了会有溢出问题&#xff0c;那么很大的数字是怎么进行运算的呢&#xff1f; 其中一种方法是把很大的数字转变成字符串存放到string中&#xff0c;然后用代码对字符串进行处理&#xff0c;…

PHP最新可用获取QQ昵称API接口源码_非第三方

PHP最新可用获取QQ昵称API接口源码&#xff0c;运行环境为php7-8都可以&#xff0c;内容为直接调用QQ空间接口 在需要展示QQ昵称处&#xff0c;直接调用以下函数就可以。 例如&#xff1a;get_qq_nick(123456)就会直接输出123456的qq号昵称。 API源码下载&#xff1a;QQ昵称AP…

【C语言】字符函数和字符串函数详解

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html &#x1f381;代码托管:黄灿灿 (huang-cancan-xbc) - Gitee.com ⚙️操作环境:Visual Studio 2022 目录 一、引言 内容一览 一、strlen …

一拖三无线充底座-带给你极致的便利生活

随着科技的不断进步&#xff0c;无线充电技术已经逐渐渗透到我们日常生活的方方面面&#xff0c;一拖三无线充底座作为其中的佼佼者&#xff0c;以其高效、便捷的特点受到广大用户的青睐。本文将从电磁感应原理、多线圈设计、频率匹配、电能传输、功率分配以及充电管理六个方面…

【Rust日报】终端表格查看工具

[new ver] Tabiew v0.6.1 Tabiew 是一个轻量级的、基于终端的应用程序&#xff0c;用于查看和查询分隔符分隔值格式的文档&#xff0c;例如 CSV 或 TSV 文件。 功能如下&#xff1a; &#x1f4ca; 表格视图: 通过易于导航的表格视图来探索数据。&#x1f50d; 工作表视图: 深入…

深入剖析隐私安全测试:在数字化时代的守护者

大家好&#xff0c;我是一名_全栈_测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家关注我&#xff0c;和我一起【分享测试知识&#xff0c;交流测试技术&#xff0c;趣聊行业热点】。 一、引言 在日新月异的数字化浪潮中&…

FPGA开发——基于Quartus的工程建立以及下载仿真全过程

一、概述 在使用了这么久的FPGA之后&#xff0c;才发现前面光发了各个设计的设计文件和测试问价&#xff0c;还没有发过怎么利用我们编写的代码进行仿真和使用软件进行工程建立和下载验证的过程&#xff0c;今天就补发一篇Quartus的工程建立以及下载仿真全过程。 二、文件夹建…

程序执行的环境和预处理

目录&#xff1a; 思维导图 一 程序的翻译环境 二 程序的执行环境 三 C 语言程序的编译和链接 四 预定义符号 五 预处理指令 六 宏和函数对比 七 预处理操作符# 和 ## 八 命令定义 九 预处理指令#include 十 预处理指令#undef 十一 条件编译 WeChat_2024073122290…

JavaScript 打印 V 和倒 V 图案的程序(Program to print V and inverted-V pattern)

倒 V 型模式&#xff1a;给定 n 的值&#xff0c;打印倒 V 型模式。示例&#xff1a; 输入&#xff1a;n 5 输出 &#xff1a; E D D C C B B A A 输入&#xff1a;n 7 输出 &#xff1a; G F F E E D D C C B B A…

中药养发护发

按照中医理论,头发和肝肾有密切联系,肝主血,肾藏精, 其华在发,肝肾强健,上荣于头,则毛发乌黑浓密. 中药育发的应用 以当归,天麻,桑疹子养血润发,配合干姜祛风活血,能通畅经络, 加快循环,激活毛囊,能促进皮肤组织营养成分吸收和废弃物的排泄,改善 头发生态. 用苦参 皂角 清热化…

yum仓库的制作与使用

目录 前言&#xff1a; 1 查看系统内核 2 获取网络源 3 搭建yum网络仓库 4 rpm包的下载 4.1 将rpm包下载至本地 4.2 对下载的rpm包进行备份 5 制作本地yum源 5.1 软件仓库制作工具createrepo 5.2 使用createrepo创建本地yum仓库 6 搭建docker本地仓库 前言&#x…

【Dash】使用 HTML 和 CSS 创建图表

一、Styling Your App The examples in the previous section used Dash HTML Components to build a simple app layout, but you can style your app to look more professional. This section will give a brief overview of the multiple tools that you can use to enhan…

Scalable Diffusion Models with Transformers(DIT)代码笔记

完整代码来源&#xff1a;DiT DiT模型主要是在diffusion中&#xff0c;使用transformer模型替换了UNet模型&#xff0c;使用class来控制图像生成。 根据论文&#xff0c;模型越大&#xff0c;patch size 越小&#xff0c;FID越小。 模型越大&#xff0c;参数越多&#xff0c;pa…

3.罗马数字转数字

3.罗马数字转数字 题目题目分析 题目 题目分析 基础思路是先想到键值对&#xff0c;然后遍历字符串利用键值对匹配来计算值&#xff0c;要注意处理IV&#xff0c;IX之类需要将当前处理字符与前一个字符进行比较。 class Solution { public:int romanToInt(string s) {//这个函…

项目负责人视角:结构化思考在竞赛中的力量

项目负责人视角&#xff1a;结构化思考在竞赛中的力量 前言结构化思考的定义与重要性结构化思考的五个关键步骤1. 明确问题2. 问题分解3. 优先级排序4. 制定行动计划5. 执行与监控 结构化思考的实际应用案例结语 前言 在这个充满挑战和机遇的时代&#xff0c;项目管理已成为企业…

Nero Lens 智图 - 适用于 iOS 和 iPadOS 的专业图片处理 App

首先是手机端的无损放大 App&#xff1a;Nero Lens 智图&#xff0c;适用于 iOS 和 iPadOS&#xff0c;不仅可以放大&#xff0c;还有多种 AI 图片增强功能。 使用这款 App 可以通过 AI 模型智能放大可达 400%&#xff0c;还有老照片去划痕、上色&#xff0c;抠图移除背景、照…

数论第四节:不定方程(习题)

文章目录 例1例2例3例4例5 例1 解下列不定方程&#xff1a; ( a ) 15 x 25 y 100 (a)15x25y100 (a)15x25y100 ( b ) 306 x − 360 y 630 (b)306x-360y630 (b)306x−360y630 (a)解: (15,25)5&#xff0c;且5|100&#xff0c;所以方程有解。 方程两边同时除以(15,24)&…

53 SSLVPN IP 接入

(1) 配置接口IP地址 # 根据组网图中规划的信息,配置各接口的IP地址,具体配置步骤如下。 <Device> system-view [Device] interface gigabitethernet 1/2/5/1 [Device-GigabitEthernet1/2/5/1] ip address 1.1.1.2 255.255.255.0 [Device-GigabitEthernet1/2/…