FPGA实现电机转速PID控制

news2025/1/10 2:49:36

         通过纯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)
begin
    if (~reset_n)
    begin
        // PWM
        high_dur <= 0;
        total_dur <= 0;
        
        // MOTOR
        motor_movement <= 1'b0;
        motor_direction <= 1'b1;
        motor_fast_decay <= 1'b1;
    end
    else if (en)
    begin
        total_dur <= 32'd2500;
        high_dur  <= speeddata;
        {motor_fast_decay, motor_direction, motor_movement} <= {1'b1,direction,1'b1};
    end
 
// 方向控制
always @(*)
begin
    if (motor_fast_decay)
    begin  
        if (motor_movement)
        begin
            if (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};
        end
        else
            {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)
begin
    if (~reset_n)
    begin
        tick <= 1;
    end
    else if (tick >= total_dur)
    begin
        tick <= 1;
    end
    else
        tick <= tick + 1;
end
 
always @(posedge clock)
begin
    PWM_OUT <= (tick <= high_dur) ? 1'b1 : 1'b0;
end

三、速度控制模块

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

 //速度控制逻辑
    always @(posedge clk or negedge reset_n) begin
        if (~reset_n) begin
            SpeedParam <= 32'd0;
            direction_reg <= 0;
        end else begin
            //计算PWM参数  CYCLE_WIDTH_MINI = 32'd50;CYCLE_WIDTH_MAX  = 32'd2500;
            if (Speed_in > 32'd0) begin
                SpeedParam <= CYCLE_WIDTH_MINI + (Speed_in * (CYCLE_WIDTH_MAX - CYCLE_WIDTH_MINI) / 32'd100);
		    end else if(Speed_in < 32'd0) begin
				SpeedParam <= CYCLE_WIDTH_MINI + ((-Speed_in) * (CYCLE_WIDTH_MAX - CYCLE_WIDTH_MINI) / 32'd100);
            end else begin
                SpeedParam <= 32'd0;
            end

            //设置电机的转向
            direction_reg <= Speed_in[31] ? 0 : 1;
        end
    end

四、PID控制模块  

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

    // 将Kp, Ki, Kd转化为定点数表示
	parameter KP = 32'd60; 			// 0.06
    parameter KI = 32'd1; 				// 0.001
    parameter KD = 32'd3400; 			// 3.4
	parameter 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) begin
		if (~reset_n) begin
			clk_25m <= 0;
		end else begin
			clk_25m <= ~clk_25m;
		end
	end

    always @(posedge clk_25m or negedge reset_n) begin
        if (~reset_n) begin
            error 	  <= 0;
            integral   <= 0;
				speed 	  <= 0;
        end else begin
				
			error <= targetDistance - currentDistance;

			// Calculate control output
            p <= error * KP;
            i <= integral * KI;
            d <= (error - prev_error) * KD;
			controlOutput <= (p + i + d) / SCALE_FACTOR;
				
            // 将控制输出限制在电机速度范围内
            //speed <= initialSpeed + controlOutput;
			if(controlOutput > $signed(32'd100)) begin
				speed <= $signed(32'd100);
			end else if(controlOutput < $signed(-32'd100)) begin
				speed <= $signed(-32'd100);
			end else begin
				speed <= controlOutput;
			end
				
			//integral <= integrallimit + error;
			if(integral_next >= $signed(32'd800)) begin
				integral <= $signed(32'd800);
			end else if(integral_next <= $signed(-32'd800)) begin
				integral <= $signed(-32'd800);
			end else begin
				integral <= integral_next;
			end
        end
    end
	
	// 更新下次迭代的前一次误差和积分
	always @(posedge clk_25m or negedge reset_n) begin
	    if(~reset_n) begin
		    prev_error <= 0;
        end else if(error!= prev_error) begin
			prev_error <= error;
		end else begin
		     prev_error <= prev_error;
		end
	end

	assign 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) 
begin
    if(PULSE_XOR != PULSE_XOR_PREVIOUS)                             
    begin                                                              
        DO_PULSE <= 1'b1;                                              
        PULSE_XOR_PREVIOUS <= PULSE_XOR;
    end
    else begin                                                         
        DO_PULSE <= 1'b0;
    end
    
end

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

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

七、实验效果

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

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

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

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

相关文章

Python 快速输出接下来几个月的月末日期

# pip install arrow 安装库&#xff08;若已安装 可忽略&#xff09; import arrowEnd_of_month_date [] # 月末日期 for i in range(100):if arrow.now().shift(daysi).format(YYYY-MM-DD)[-2:] 01: # 若日期为 1 号 则返回前一天的日期End_of_month_date.append(arrow.now…

C# winform控件和对象双向数据绑定

实现目的&#xff1a; 控件和对象双向数据绑定 实现结果&#xff1a; 1. 对象值 -> 控件值 2. 控件值 -> 对象值 using System; using System.Windows.Forms;namespace ControlDataBind {public partial class MainForm : Form{People people new People();public Mai…

尚硅谷大数据项目《在线教育之离线数仓》笔记006

视频地址&#xff1a;尚硅谷大数据项目《在线教育之离线数仓》_哔哩哔哩_bilibili 目录 第11章 数仓开发之ADS层 P087 P088 P089 P090 P091 P092 P093 P094 P095 P096 P097 P098 P099 P100 P101 P102 P103 P104 P105 P106 P107 P108 P109 P110 P111 …

RK开发板的USB连接(Ubuntu)

一、安装连接工具 sudo apt-get install putty 二、启动putty工具 sudo putty 三、连接usb&#xff0c;并查看相关的信息 # 查看接入的是否有usb ls /dev/tty* 显示如下&#xff1a;&#xff08;含有usb接口&#xff1a; /dev/ttyUSB0&#xff09; /dev/tty /dev/tty23 /d…

浏览器跨标签通信

写在前面 今天说一下跨标签通信的一种实现方式&#xff0c;首先跨标签指的是同一个浏览器中不同标签之间进行数据通信&#xff0c;也就是说比如在第一个标签写了一个数据&#xff0c;在另一个标签里面进行数据的使用&#xff0c;而且是实时的 先说现象 当我点击发送消息给标签二…

深度学习:基于循环神经网络RNN实现语句生成

目录 1 循环神经网络介绍 1.1 什么是循环神经网络 1.2 RNN的网络结构 1.3 RNN的工作原理 ​编辑 1.4 RNN的应用场景 2 基于RNN实现语句生成 2.1 句子生成介绍 2.2 基于pytorch实现语句生成 2.3 完整代码 2.4 该模型的局限 3 总结 1 循环神经网络介绍 1.1 什么是循环…

【rar密码】rar压缩包密码忘记了,如何解密?

Rar压缩包设置了加密&#xff0c;在解压、修改密码等操作时都需要输入正确的密码。忘记了密码就很麻烦了&#xff0c;如果真的忘记密码&#xff0c;唯一的方法就是找回密码了。 我们自己想要找回密码&#xff0c;最直接简单的就是自己尝试密码。如果压缩包密码是自己设置的&am…

IDEA报错:Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found

问题&#xff1a; 使用IDEA新建spring boot项目&#xff0c;报错如下&#xff1a; Plugin org.springframework.boot:spring-boot-maven-plugin: not found解决办法&#xff1a; 1.在本地maven仓库中找到spring-boot-maven-plugin的版本号 2.在pom.xml文件中添加对应的版本…

ubuntu 22.04安装cuda、cudnn、conda、pytorch

1、cuda 视频连接 https://www.bilibili.com/video/BV1bW4y197Mo/?spm_id_from333.999.0.0&vd_source3b42b36e44d271f58e90f86679d77db7cuda 11.8 https://developer.nvidia.com/cuda-toolkit-archive点击进入 https://developer.nvidia.com/cuda-11-8-0-download-arc…

win10安装vmware17 pro和centos7及配置

1.下载地址 https://www.bear20.com/pcwin/153/725878653.htmlhttps://www.ittel.cn/archives/12099.html #以下许可证 选择其一即可 4A4RR-813DK-M81A9-4U35H-06KND NZ4RR-FTK5H-H81C1-Q30QH-1V2LA JU090-6039P-08409-8J0QH-2YR7F 4Y09U-AJK97-089Z0-A3054-83KLA 4C21U-2KK9Q-…

Vue-router路由

配置路由 相当于SpringMVC的Controller 路径然后&#xff0c;跳转到对应的组件 一键生成前端项目文档

虹科方案 | 利用扩展型 NAS 存储加速 AI/ML 工作负载

一、AI&ML变革日常生活 AI&#xff08;人工智能&#xff09;和ML&#xff08;机器学习&#xff09;的发展正逐渐渗透到我们的日常生活中&#xff0c;为我们带来了翻天覆地的变化。从智能手机中的语音助手到智能家居设备&#xff0c;AI的应用让我们的生活更加便捷和智能化。…

UWB高精度定位系统源码 UWB室内定位技术 工作考勤、电子围栏、历史轨迹回放、巡检巡查

现代制造业厂区面积大、人员数量多、物资设备不断增加&#xff0c;随着工业信息化技术的发展&#xff0c;大型制造企业中对人员、车辆、物资的管理要求越来越细致。 智慧工厂高精度定位管理系统使用UWB室内定位技术&#xff0c;通过在厂区安装定位基站&#xff0c;为人员或设备…

Spring Boot 整合 Shiro(后端)

1 Shiro 什么是 Shiro 官网&#xff1a; http://shiro.apache.org/ 是一款主流的 Java 安全框架&#xff0c;不依赖任何容器&#xff0c;可以运行在 Java SE 和 Java EE 项目中&#xff0c;它的主要作用是对访问系统的用户进行身份认证、 授权、会话管理、加密等操作。 …

【juc】ReentrantReadWriteLock之缓存(仅当学习)

目录 一、说明二、代码示例2.1 pom依赖2.2 示例代码2.3 实体类 三、示例截图 一、说明 1.针对于读多写少的情况 2.先查缓存&#xff0c;没有再去查库 二、代码示例 2.1 pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"h…

leetcode 第 361 场周赛

2843. 统计对称整数的数目 核心思想&#xff1a;枚举每一个数是否是对称整数,第一种写法为python写法&#xff0c;第二种为一般写法我觉得更好&#xff0c;非常有思想性。 2844. 生成特殊数字的最少操作 核心思想&#xff1a;枚举特殊数字结尾的几种可能。其实自己做的时候一…

postman9.12.汉化版(附有下载链接)

想用英文版本的可以直接点击下载最新版本 这里直接付上9.12.2版本的下载链接&#xff0c;如果大家要下载别的版本&#xff0c;可以直接修改链接里面的版本号即可 &#xff0c;下面是汉化包下载 链接&#xff1a;https://pan.baidu.com/s/1izK3HfqlfXJdq6KIYeJ2zw?pwdpetk 提…

MySQL加密的几种常见方式

MySQL提供了多种加密方式来保护数据的安全性。下面是几种常见的MySQL加密方式&#xff1a; 密码加密&#xff1a; MySQL5.7及以上版本使用SHA-256算法对密码进行加密。这种加密方式更安全&#xff0c;可以防止密码泄露。 之前的MySQL版本使用SHA-1算法进行密码加密。这种加密方…

解决DNS服务器未响应错误的方法

​当你将设备连接到家庭网络或具有互联网接入功能的Wi-Fi热点时,由于各种原因,互联网连接可能无法正常工作。本文中的说明适用于Windows 10、Windows 8和Windows 7。 无法连接到DNS服务器的原因 故障的一类与域名系统有关,域名系统是世界各地互联网提供商使用的分布式名称…

[C++][pcl]pcl安装后测试代码3

测试环境&#xff1a; vs2019 pcl1.12.1 代码&#xff1a; #include<iostream> #include <thread>#include <pcl/common/common_headers.h> #include <pcl/features/normal_3d.h> #include <pcl/io/pcd_io.h> #include <pcl/visualizatio…