FPGA实现电机转速PID控制

         通过纯RTL实现电机转速PID控制,包括电机编码器值读取,电机速度、正反转控制,PID算法,卡尔曼滤波,最终实现对电机速度进行控制,使其能够渐近设定的编码器目标值。

一、设计思路

        前面通过SOPC之NIOS Ⅱ实现电机转速PID控制(调用中断函数)对电机实现了PID控制,然后就可以按照其设计方式将上层的C语言实现的PID控制部分等全部转换成Verilog代码,最终实现纯RTL进行PID控制。
        在前文中,电机PWM控制,电机方向和编码器值的获取,卡尔曼滤波是通过Verilog语言编写,而电机速度控制、PID控制是通过Nios Ⅱ系统中的软件部分实现的,因此需要编写电机速度模块,实现对电机PWM控制模块传入速度信息;编写PID控制模块,实现对电机速度模块速度的校正。
        Nios Ⅱ中采用Avalon总线对各个底层Verilog代码进行读取和写入数据,因此也要对电机控制,电机方向和编码器值获取相关代码进行修改
        按照思路画出大概框图如下:

二、PWM控制模块

在前文PWM模块中由于需要Avalon总线的控制,因此有片选信号、片选地址、读写地址等变量,而转为纯RTL后只需要输入方向以及PWM值就可以,因此需要对前文代码进行修改

reg motor_movement;         // 电机运动,1为开始、0为停止
reg motor_direction;        // 电机转向,1为向前、0为向后
reg motor_fast_decay;       // 电机减速,1为快制动、0为慢制动always @(posedge clock or negedge reset_n)
beginif (~reset_n)begin// PWMhigh_dur <= 0;total_dur <= 0;// MOTORmotor_movement <= 1'b0;motor_direction <= 1'b1;motor_fast_decay <= 1'b1;endelse if (en)begintotal_dur <= 32'd2500;high_dur  <= speeddata;{motor_fast_decay, motor_direction, motor_movement} <= {1'b1,direction,1'b1};end// 方向控制
always @(*)
beginif (motor_fast_decay)begin  if (motor_movement)beginif (motor_direction){DC_Motor_IN2, DC_Motor_IN1, PWM} <= {1'b1, 1'b0, PWM_OUT};else{DC_Motor_IN2, DC_Motor_IN1, PWM} <= {1'b0, 1'b1, PWM_OUT};endelse{DC_Motor_IN2, DC_Motor_IN1, PWM} <= {1'b1, 1'b1, 1'b0};end// PWM 转速控制
reg             PWM_OUT;
reg     [31:0] total_dur;       // 总持续时间
reg     [31:0] high_dur;        // 高位时间,决定电机转速,控制 PWM 占空比,值越高,占空比越大,转速越快
reg     [31:0] tick;            // 计数器always @(posedge clock or negedge reset_n)
beginif (~reset_n)begintick <= 1;endelse if (tick >= total_dur)begintick <= 1;endelsetick <= tick + 1;
endalways @(posedge clock)
beginPWM_OUT <= (tick <= high_dur) ? 1'b1 : 1'b0;
end

三、速度控制模块

速度控制模块的主要任务就是将PID模块传入的速度信息,转换为PWM值传入PWM控制模块,并根据速度的正负值计算电机的正转反转

 //速度控制逻辑always @(posedge clk or negedge reset_n) beginif (~reset_n) beginSpeedParam <= 32'd0;direction_reg <= 0;end else begin//计算PWM参数  CYCLE_WIDTH_MINI = 32'd50;CYCLE_WIDTH_MAX  = 32'd2500;if (Speed_in > 32'd0) beginSpeedParam <= CYCLE_WIDTH_MINI + (Speed_in * (CYCLE_WIDTH_MAX - CYCLE_WIDTH_MINI) / 32'd100);end else if(Speed_in < 32'd0) beginSpeedParam <= CYCLE_WIDTH_MINI + ((-Speed_in) * (CYCLE_WIDTH_MAX - CYCLE_WIDTH_MINI) / 32'd100);end else beginSpeedParam <= 32'd0;end//设置电机的转向direction_reg <= Speed_in[31] ? 0 : 1;endend

四、PID控制模块  

首先要将PID参数中的小数进行缩放转为定点数;
然后因为用了50MHz的时钟,时钟周期是10ns,可能导致KP、KI、KD算不完,因此将其进行分频为25MHz;
然后就是对 KP、KI、KD进行计算,输出PID结果,即电机速度;
最后为了防止电机速度和累积误差过大,对其进行限幅。

    // 将Kp, Ki, Kd转化为定点数表示parameter KP = 32'd60; 			// 0.06parameter KI = 32'd1; 				// 0.001parameter KD = 32'd3400; 			// 3.4parameter SCALE_FACTOR = 1000;  //缩放因子reg signed [31:0] error;reg signed [31:0] prev_error;reg signed [31:0] integral;reg signed [31:0] speed;reg signed [31:0] controlOutput;reg signed [31:0] p;reg signed [31:0] i;reg signed [31:0] d;wire signed [31:0] integral_next = integral + error;reg clk_25m;   always @(posedge clk or negedge reset_n) beginif (~reset_n) beginclk_25m <= 0;end else beginclk_25m <= ~clk_25m;endendalways @(posedge clk_25m or negedge reset_n) beginif (~reset_n) beginerror 	  <= 0;integral   <= 0;speed 	  <= 0;end else beginerror <= targetDistance - currentDistance;// Calculate control outputp <= error * KP;i <= integral * KI;d <= (error - prev_error) * KD;controlOutput <= (p + i + d) / SCALE_FACTOR;// 将控制输出限制在电机速度范围内//speed <= initialSpeed + controlOutput;if(controlOutput > $signed(32'd100)) beginspeed <= $signed(32'd100);end else if(controlOutput < $signed(-32'd100)) beginspeed <= $signed(-32'd100);end else beginspeed <= controlOutput;end//integral <= integrallimit + error;if(integral_next >= $signed(32'd800)) beginintegral <= $signed(32'd800);end else if(integral_next <= $signed(-32'd800)) beginintegral <= $signed(-32'd800);end else beginintegral <= integral_next;endendend// 更新下次迭代的前一次误差和积分always @(posedge clk_25m or negedge reset_n) beginif(~reset_n) beginprev_error <= 0;end else if(error!= prev_error) beginprev_error <= error;end else beginprev_error <= prev_error;endendassign speedout = speed;

五、电机方向和编码器值的获取

电机编码器值要根据电机方向进行自增和自减,因此要先通过AB方波确认电机方向

reg  DO_PULSE;                      //用于存储输出的电机脉冲信号
wire PULSE_XOR;                     //用于存储PHASE_A和PHASE_B进行异或结果
reg  PULSE_XOR_PREVIOUS;            //上一次的PULSE_XOR值
reg  DIRECTION;                     //用于存储电机方向信号
reg  DIRECT_PATCH;                  //用于存储DIRECT异或PHASE_A后取反的结果//解码方向信号
always @(posedge DI_PHASE_A) DIRECTION <= DI_PHASE_B;                    //当有DI_PHASE_A的上升沿,将DI_PHASE_B的值赋给DIRECTION  
always @(posedge DI_PHASE_B) DIRECT_PATCH <= ~(DIRECTION ^ DI_PHASE_A);  //当有DI_PHASE_B的上升沿,将DIRECT和DI_PHASE_A进行异或后取反赋值给DIRECT_PATCH 
assign DO_DIRECT = DIRECTION | DIRECT_PATCH;                             //将DIRECTION和DIRECT_PATCH进行与运算 //解码脉冲信号
assign PULSE_XOR = DI_PHASE_A ^ DI_PHASE_B;                         
always @(posedge DI_SYSCLK) 
beginif(PULSE_XOR != PULSE_XOR_PREVIOUS)                             begin                                                              DO_PULSE <= 1'b1;                                              PULSE_XOR_PREVIOUS <= PULSE_XOR;endelse begin                                                         DO_PULSE <= 1'b0;endend

 获取电机方向后,对其进行计数,得到电机编码器的值并将其输出

always @(posedge clock or negedge reset_n)
beginif(~reset_n) begincounter_enable <= 0;endelse if (counter_enable) beginread_data <= pulse_counter;end  
endalways @(posedge clock)
beginif(DO_PULSE ) beginif(motor_direction) begin                         //如果电机正转  if(pulse_counter < $signed(32'h8000))pulse_counter <= pulse_counter + 1;      //counter随电机传回的脉冲数累加   endelse if(!motor_direction) begin                  //如果电机反转if(pulse_counter > $signed(-32'h8000))pulse_counter <= pulse_counter - 1;      //counter随着电机传回的脉冲数递减    endelsepulse_counter <= 0;                                end
end  

七、实验效果

整体的系统框图如下所示,左右两个电机的方波经过卡尔曼滤波后输入到motor_measure中,先获取其电机方向,然后对电机编码器进行计数并将左右两电机的编码器数值取平均值输入到PID控制模块中,与ISSP输入的目标编码器值进行计算出速度信息,将速度信息输入到set_speed中计算方向和PWM参数,输入到PWM控制模块进行电机控制

通过signal tap和ISSP联合抓波形,得出来的效果还是不错的,会有一点点超调量 

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

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

相关文章

算法笔记:点四叉树

点四叉树是一种用于主要是针对空间点存储与索引的树形数据结构在点四叉树中&#xff0c;空间被分割成四个矩形&#xff0c;四个不同的多边形对应于SW、NW、SE、NE四个象限 1 基本操作 1.1 初始化 创建一个根节点&#xff0c;该节点代表整个二维空间区域 1.2 插入点 当一个新…

oracle 自定义存储过程(非常简单明了)

语法说明 CREATE OR REPLACE PROCEDURE 存储过程名字 ( 参数1 IN %TYPE, 参数2 IN %TYPE, 参数3 OUT %TYPE) IS 变量1 %TYPE; 变量2 %TYPE; BEGIN存储过程执行语句块 END 存储过程名字;举例说明 1.举一个简单的例子 定义存储过程 easyProcedure 入参为 两个数 出参为 他们的…

自己公司开发的ERP系统,怎么对接京东,淘宝等这些电商平台?

得益于互联网基建的成熟及快速发展的电子商贸经济&#xff0c;我国线上零售市场快速增长&#xff0c;2022年全国线上零售额达到13.79万亿元&#xff0c;占社会消费品零售总额的比重为27.2%&#xff0c;也就是说每卖出三件零售商品&#xff0c;就有一件是从线上销售。中大型零售…

P1116 车厢重组(冒泡排序)

题目描述 在一个旧式的火车站旁边有一座桥&#xff0c;其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢&#xff0c;如果将桥旋转 180 180 180 度&#xff0c;则可以把相邻两节车厢的位置交换&#xff0c;用这种方法可以重新排列车厢的顺序…

手撸任意层神经网络-读从文本s.txt取网络结构初始化neuralNetwork

现代c++读取文本文件,文本文件:"s.txt"中有字符串,如:"{2,4,3,1}",获取数字如:2,4,3,1赋值给变量 vectov<int>La; #include <iostream> #include <vector> #include <fstream> //#include <sstream> #include <random&…

【GPT,Flask】用Python Flask结合OpenAI的GPT API构建一个可自主搭建的内容生成应用网站

【背景】 自己构建模型并进行训练需要很高的知识,技能和资源门槛。如今,通过OpenAI提供的API,则可以快速通过GPT能力构建可以提供内容生成服务的在线网站。这套框架可以提供给用户,用户可以利用该框架在自己的环境(比如自己的公司内)构建内容生成服务。你也可以自己上线…

mac建议装双系统吗,详细分析苹果电脑双系统的利弊

mac建议装双系统吗&#xff0c;Mac电脑上安装双系统有哪些利弊呢&#xff0c;一起来看看吧&#xff01; 苹果Mac电脑安装双系统利&#xff1a; 1、用来办公更加方便&#xff1a;苹果系统功能也是很强大的&#xff0c;但是用来办公非常不方便&#xff0c;是由于一些常用的exe软…

QTableView合并单元格

QtableView的功能 QTableView是Qt框架提供的用于显示表格数据的类。它是基于MVC&#xff08;模型-视图-控制器&#xff09;设计模式的一部分&#xff0c;用于将数据模型和界面视图分离。 以下是一些QTableView的主要特点和功能&#xff1a; 1. 显示表格数据&#xff1a; QTa…

奥本海默

&#xff08;1&#xff09;命 电影一开头&#xff0c;奥本海默不擅长做实验。但你看宿命凑巧不凑巧&#xff0c;奥本海默成了曼哈顿计划的工程负责人&#xff0c;偏偏要他一次性爆炸成功。 电影一开头&#xff0c;奥本海默因为不擅长做实验被导师指出引起其他同学哄堂大笑&…

无涯教程-JavaScript - DATE函数

描述 DATE函数返回特定日期的序列号。 语法 DATE (year, month, day)争论 Argument描述Required/Optionalyear year参数的值可以包含1-4位数字。 Excel会根据计算机使用的日期系统解释年份参数。 默认情况下,Microsoft Excel for Windows使用1900日期系统。 请参阅下面的注…

vue如何获取组件的动态长宽 useResizeObserver用法

工作中遇到了一个需求&#xff0c;tab个数要根据页面的大小动态变化&#xff0c;研究了下&#xff0c;分享下&#xff1a; import { useResizeObserver } from vueuse/core;const tabRef ref();const getData (el) > {if (!el || !el?.length) return;console.log(el[0]…

分类算法系列②:KNN算法

目录 KNN算法 1、简介 2、原理分析 数学原理 相关公式及其过程分析 距离度量 k值选择 分类决策规则 3、API 4、⭐案例实践 4.1、分析 4.2、代码 5、K-近邻算法总结 &#x1f343;作者介绍&#xff1a;准大三网络工程专业在读&#xff0c;努力学习Java&#xff0c;涉…

c++宏定义 define的三个特殊符号#,##,#@

原文链接 x##y表示什么&#xff1f;表示x连接y&#xff0c;举例说&#xff1a; int n Conn(123,456); /* 结果就是n123456;/ char str Conn(“asdf”, “adf”); /结果就是 str “asdfadf”;/ 再来看#x&#xff0c;其实就是给x加上单引号&#xff0c;结果返回是一个const c…

QT连接数据库

目录 数据库 数据库基本概念 常用的数据库 SQLite3基础 SQLite特性&#xff1a; QT连接数据库 1.1 QT将数据库分为三个层次 1.2 实现数据库操作的相关方法 sql语句&#xff08;常用&#xff09; 1&#xff09;创建表格 2&#xff09;删除表格 3&#xff09;插入记录 …

JAVA 时间戳

时间戳&#xff08;Timestamp&#xff09;是一个表示特定时间点的数值&#xff0c;通常指的是自某个固定的起始时间&#xff08;如1970年1月1日00:00:00 UTC&#xff09;以来经过的秒数或毫秒数。 在 Java 中&#xff0c;可以使用 System.currentTimeMillis() 方法获取当前的时…

定时脚本自动自动将文件push到git

写脚本 绝对路径 环境注意 写python&#xff0c;bash脚本执行调用 py程序 定制crontab -e 日志要指定输入文件中 项目地址 https://gitee.com/stdev_1/sshpi10/ bash脚本 #!/bin/bash 设置要监控的仓库路径 #path~/github/ #watch_dir“/home/pi/gittest/ipset/sshpi10” p…

期权交易策略及案例的基本策略有哪些?

目前我国上市交易的期权品种日益丰富&#xff0c;期权的基础的交易方法是建立相应头寸再反向平仓&#xff0c;赚取权利金差价&#xff0c;也可以持有期权到期行权。除了基础的交易方法之外&#xff0c;期权还有一些组合策略&#xff0c;下文介绍期权交易策略及案例的基本策略有…

修改部署后的SSM项目配置文件

修改部署后的SSM项目配置文件 环境配置&#xff1a;window10&#xff0c;JDK8&#xff0c;项目采用SSM编写&#xff0c;部署在tomcat上&#xff0c;maven打包是打成war包 1、找到配置文件 打开tomcat目录 D:\Programming\apache-tomcat-8.5.58\webapps\MyPRO\WEB-INF\class…

docker安装redis实操记录

1.Docker拉取镜像 docker pull redis2.Docker挂载配置文件 创建挂载文件夹 mkdir -p /home/redis/data下载默认配置文件 redis.conf 3.启动redis 容器 docker run --restartalways --log-opt max-size100m --log-opt max-file2 -p 6379:6379 --name redis -v /home/redi…

react16之前diff算法的理解和总结

此篇文章所讨论的是 React 16 以前的 Diff 算法。而 React 16 启用了全新的架构 Fiber&#xff0c;相应的 Diff 算法也有所改变&#xff0c;本片不详细讨论Fiber。 fiber架构是为了支持react进行可中断渲染&#xff0c;降低卡顿&#xff0c;提升流畅度。 react16之前的版本&…