当前位置: 首页 > news >正文

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联合抓波形,得出来的效果还是不错的,会有一点点超调量 

相关文章:

FPGA实现电机转速PID控制

通过纯RTL实现电机转速PID控制&#xff0c;包括电机编码器值读取&#xff0c;电机速度、正反转控制&#xff0c;PID算法&#xff0c;卡尔曼滤波&#xff0c;最终实现对电机速度进行控制&#xff0c;使其能够渐近设定的编码器目标值。 一、设计思路 前面通过SOPC之NIOS Ⅱ实现电…...

C++中的volatile

volatile的本意是“易变的”&#xff0c;是一种类型修饰符&#xff0c;用它声明的类型变量表示可以被某些编译器未知的因素更改&#xff0c;比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量&#xff0c;编译器对访问该变量的代码就不再进行优化&#xff0c;从而可…...

数学建模--一维插值法的多种插值方式的Python实现

目录 1.算法流程步骤 2.算法核心代码 3.算法效果展示 1.算法流程步骤 #算法的核心就是利用scipy中的interpolate来完成工作 #一共是5种一维插值算法形式: #插值方法&#xff1a;1.阶梯插值 2.线性插值 3.2阶样条插值 4.3阶样条插值 #"nearest"阶梯插值 #"zero&…...

爱校对:让法律、医疗、教育行业的文本更加无懈可击

在今天这个信息爆炸的世界里&#xff0c;文本准确性成了法律、医疗和教育这些严谨行业中一个不能忽视的要点。一个小错误可能造成严重的后果&#xff0c;甚至影响人们的生命和事业。这正是为什么更多的专业人士开始选择使用“爱校对”来确保他们的文档、研究和通讯无懈可击。 法…...

使用pip下载第三方软件包报错超时处理方法

报错如下&#xff1a; WARNING: Retrying (Retry(total4, connectNone, readNone, redirectNone, statusNone)) after connection broken by ‘ReadTimeoutEr ror(“HTTPSConnectionPool(host‘files.pythonhosted.org’, port443): Read timed out. (read timeout15)”)’: /p…...

计算古坐标——基于GPlates Web Service的坐标点重建

Gplates客户端和在线门户&#xff0c;pygplates和gplately是存在内在联系的应用&#xff0c;它们主要实现可视化&#xff0c;输入板块模型和化石点的现今坐标信息&#xff0c;在GPlates中可视化呈现&#xff0c;点位的坐标计算并不展现。而rgplates利用R语言提供了直接进行坐标…...

智安网络|加强软件供应链安全保障:共同抵御威胁的关键路径

在当今数字化时代&#xff0c;软件供应链安全成为了一个备受关注的话题。各行各业都依赖于软件产品和服务来支持其业务运营。然而&#xff0c;随着供应链的不断扩大和复杂化&#xff0c;软件供应链安全问题也日益突出。那么应该如何解决&#xff1f; 首先&#xff0c;软件供应…...

华为Mate 60系列发售,北斗卫星通信技术进一步深入大众消费市场

近日&#xff0c;华为Mate 60系列手机在没有举办发布会的情况下在官方商城突然上架开售&#xff0c;人气火爆。 值得一提的是&#xff0c;华为Mate60 Pro支持卫星通话&#xff0c;无地面网络时&#xff0c;也能拨打和接听卫星电话&#xff0c;还可自由编辑卫星消息。华为 Mate6…...

Grad-CAM,即梯度加权类激活映射 (Gradient-weighted Class Activation Mapping)

Grad-CAM&#xff0c;即梯度加权类激活映射 (Gradient-weighted Class Activation Mapping)&#xff0c;是一种用于解释卷积神经网络决策的方法。它通过可视化模型对于给定输入的关注区域来提供洞察。 原理: Grad-CAM的关键思想是将输出类别的梯度&#xff08;相对于特定卷积…...

程序发布——使用pyinstaller打包识别程序为exe可执行文件 详解

当我们使用python完成项目开发后,必然面对着如何将自己的程序分享给其他人使用,这就离不开程序的打包工作。对于大多数人而言,我们还是使用windows电脑居多,因此我们在大多数场景是需要将程序打包为exe的可执行文件。笔者将在本章节详细介绍使用pyinstaller进行打包的全过程…...

Docker 使用

简介 Docker是一个开源的容器引擎&#xff0c;它有助于更快地交付应用。 Docker可将应用程序和基础设施层隔离&#xff0c;并且能将基础设施当作程序一样进行管理。使用 Docker可更快地打包、测试以及部署应用程序&#xff0c;并可以缩短从编写到部署运行代码的周期。 Docker…...

电脑c盘变红满了怎么清理?4个方法轻松清理!

“我的电脑才用了不到一年&#xff0c;现在就已经满了&#xff01;电脑c盘变红满了应该怎么清理呢&#xff1f;有什么方法能帮我清理的干净一点吗&#xff1f;希望大家给我出出主意。” 随着我们使用电脑时间的增多&#xff0c;电脑C盘可能会变得满满当当&#xff0c;这会不仅会…...

【UE 材质】实现角度渐变材质、棋盘纹理材质

目标 步骤 一、角度渐变材质 1. 首先通过“Mask”节点将"Texture Coordinate" 节点的R、G通道分离 2. 通过“RemapValueRange”节点将0~1范围映射到-1~1 可以看到此时R通道效果&#xff1a; G通道效果&#xff1a; 继续补充如下节点 二、棋盘纹理材质 原视频链接&…...

[深度学习]1. 深度学习知识点汇总

本文记录了我在学习深度学习的过程中遇到过的不懂的知识点&#xff0c;为了方便翻阅&#xff0c;故将其发表于此&#xff0c;随时更新&#xff0c;供大家参考。 深度学习常见知识点 1. 测试精度和训练精度 在深度学习中&#xff0c;测试精度和训练精度是两个重要的指标&#…...

鲁棒优化入门(6)—Matlab+Yalmip两阶段鲁棒优化通用编程指南(上)

0.引言 上一篇博客介绍了使用Yalmip工具箱求解单阶段鲁棒优化的方法。这篇文章将和大家一起继续研究如何使用Yalmip工具箱求解两阶段鲁棒优化(默认看到这篇博客时已经有一定的基础了&#xff0c;如果没有可以看看我专栏里的其他文章)。关于两阶段鲁棒优化与列与约束生成算法的原…...

golang通过gorm操作sqlite设置主键自增

在 Golang 中使用 GORM 操作 SQLite 数据库时&#xff0c;可以通过以下步骤设置主键自增&#xff1a; 首先&#xff0c;确保已经安装了 GORM 和 SQLite 的驱动程序。你可以使用以下命令安装它们&#xff1a; go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite导入所…...

基于Spring Boot的企业门户网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的企业门户网站设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java springboot…...

Json解析流程

一、拿到了题库 分析一下可以定义的 1、序号&#xff0c;用来区分题目数&#xff0c;每个题有唯一的序号 2、题目&#xff0c;就是下图的Q 3、预设的回答&#xff0c;下图的A 分析完我可以知道有三个字段&#xff0c;分别是int index、string Q、string A。 二、把字段丢到…...

Mybatis 动态SQL – 使用choose标签动态生成条件语句

之前我们介绍了if,where标签的使用&#xff1b;本篇我们需要在if,where标签的基础上介绍如何使用Mybatis提供的choose标签动态生成条件语句。 如果您对if,where标签动态生成条件语句不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybat…...

http接口自动化测试框架实现

目录 一、测试需求描述 二、实现方法 三、Excel表格样式 四、实现代码&#xff08;代码才是王道&#xff0c;有注释很容易就能看明白的&#xff09; 一、测试需求描述 对服务后台一系列的http接口功能测试。 输入&#xff1a;根据接口描述构造不同的参数输入值 输出&…...

Android逆向学习(三)vscode修改smali绕过vip

Android逆向学习&#xff08;三&#xff09;vscode修改smali绕过vip 写在前面 这是吾爱的第二个作业&#xff0c;主要就是要修改smali代码&#xff0c;其实smali代码我感觉没有必要去学&#xff0c;当然主要是我本来就会汇编语言&#xff0c;基本上汇编语言都是一样的&#x…...

代码随想录训练营第38天|62.不同路径,63.不同路径II

代码随想录训练营第38天|62.不同路径&#xff0c;63.不同路径II 62.不同路径文章思路代码 63.不同路径II文章思路代码 总结 62.不同路径 文章 代码随想录|0062.不同路径 思路 d p [ i ] [ j ] { 1 , i 0 ∧ j 0 d p [ i − 1 ] [ j ] d p [ i ] [ j − 1 ] , e l s e \b…...

BlueStore BlueFS rocksdb 关联性梳理

Tag: ceph 12.2.4 BlueStore空间初始化 BlueStore磁盘空间管理 总述 OSD挂载目录基于文件系统管理&#xff0c;Slow、WAL、DB空间区域基于裸盘管理&#xff1b;Slow区域&#xff1a;此类空间主要用于存储对象数据&#xff0c;由BlueStore管理&#xff0c;其中分配于BlueFS空…...

PgSQL-并行查询系列-介绍[译]

PgSQL-并行查询系列-介绍 现代CPU模型拥有大量的CPU核心。多年来&#xff0c;数据库应用程序都是并发向数据库发送查询的。查询处理多个表的行时&#xff0c;若可以使用多核&#xff0c;则可以客观地提升性能。PgSQL 9.6引入了并行查询的新特性&#xff0c;开启并行查询后可以大…...

Linux以系统服务的方式启动Kafka(其他服务同理)

最终效果&#xff1a; 先回顾命令行的启动方式&#xff1a; kafka的启动 进入kafka的安装目录 1、首先启动zookeeper服务&#xff1a; bin/zookeeper-server-start.sh config/zookeeper.properties2、再启动kafka bin/kafka-server-start.sh config/server.properties &…...

成都瀚网科技有限公司:抖店的评论会消失吗?

抖店是抖音推出的电子商务平台。很多用户在购物后都会对产品进行评价。但有时用户可能会发现抖店评论缺失&#xff0c;让用户产生一些疑惑和困惑。本文将围绕这个问题提供一些答案和解决方案。 1.为什么抖店评论不见了&#xff1f; 首先需要明确的是&#xff0c;抖店评论消失可…...

优先级队列priority_queue以及仿函数的使用

目录 优先级队列priority_queuepriority_queue的模拟实现仿函数 优先级队列priority_queue 优先级队列priority_queue是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它默认第一个元素总是它所包含的元素中最大的 优先级队列默认使用vector作为底层存储数据的…...

java+ssm+mysql水费管理系统

项目介绍&#xff1a; 使用javassmmysql开发的用户水费管理系统&#xff0c;系统包含超级管理员&#xff0c;系统管理员、用户角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理、用户管理、用水管理&#xff08;用水记录、缴费提醒&#xff09;、水费…...

搭建最简单的SpringBoot项目

1、创建maven项目 2、引入父pom <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.15</version> </parent> 3、引入springboot-web依赖 <dependency…...

Windows系统手动重新生成性能计数器

症状 使用性能监视器工具时&#xff0c;某些计数器可能缺失或不包含计数器数据。 性能计数器库可能已损坏&#xff0c;需要重新生成。 应用程序日志中可能会出现以下错误&#xff1a; Log Name: Application Source: Microsoft-Windows-IIS-W3SVC-PerfCounters Event ID…...