FPGA实现电机转速PID控制

2023-11-05

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FPGA实现电机转速PID控制 的相关文章

  • Linux驱动程序DMA传输到PC作为主机的PCIe卡

    我正在开发一个 DMA 例程 将数据从 PC 传输到 PCIe 卡上的 FPGA 我阅读了 DMA API txt 和 LDD3 ch 15 详细信息 但是 我不知道如何从 PC 到 PCIe 卡上的一致 iomem 块进行 DMA 传输
  • 「HDLBits题解」Gates4

    本专栏的目的是分享可以通过HDLBits仿真的Verilog代码 以提供参考 各位可同时参考我的代码和官方题解代码 或许会有所收益 题目链接 Gates4 HDLBits module top module input 3 0 in out
  • UIO 设备上的 mmap EINVAL 错误

    在尝试使用 UIO 而不是直接映射后 我在 Xilinx Zynq 上映射物理内存时遇到问题 dev mem 虽然计划是以普通用户身份运行应用程序 而不是root这仍在运行root 显然 第一个映射成功 其余映射到同一个文件描述符12 de
  • 确定监听某个端口的进程pid

    正如标题所示 我正在运行多个游戏服务器 并且每个服务器都有相同的name但不同PID和port数字 我想匹配PID正在监听某个端口的服务器 然后我想终止这个进程 我需要它来完成我的 bash 脚本 这可能吗 因为在网上还没有找到解决方案 您
  • ESP10B 锁定连接器

    ESP10B 锁定连接器 ESP10B 电机新增内容包括双极型号标准 NEMA 尺寸 17 23 和 34 的步进电机现在包括输出扭矩范围从 61 盎司英寸到 1291 盎司英寸的双极型号 该电机配有带锁定连接器的尾缆 可轻松连接 每转可步
  • 您可以使用类 C 语言对 FPGA 进行编程吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 在大学里 我用类似 C 的语言编写了 FPGA 不过 我也知道人们通常使用 Verilog 或 VHD
  • 如何在 Verilog 中综合 While 循环?

    我尝试设计一个 Booth 乘法器 它在所有编译器中运行良好 包括 Modelsim Verilogger Extreme Aldec Active Hdl 和 Xilinx Isim 我知道模拟和综合是两个不同的过程 而且只有少数Veri
  • mysql.server 启动时出现 PID 错误?

    我刚刚尝试使用自制程序 在 Mac OS X 10 6 上 安装 MySQL 但我在第一个障碍时遇到了问题 当尝试手动启动服务器 mysql server start 时 出现以下错误 ERROR Manager of pid file q
  • verilog $readmemh 对于 50x50 像素 RGB 图像花费太多时间

    我正在尝试编译用于 FPGA 编程的 verilog 代码 我将在其中实现 VGA 应用程序 我使用 QuartusII 和 Altera 我正在尝试正确使用 readmemh 来逐像素获取图片 现在 我已经使用 matlab 将图片转换为
  • 我们可以在 C 或 SystemVerilog 中使用 ifdef MACROS 中的条件吗?

    我想要那样的东西 ifdef N O gt N I define GREATER 1 else define LESSER 1 endif 但做不到 有什么解决方案或阅读吗 我很努力地想要做到这一点 但是却做不到 Verilog 不提供这样
  • 赋值语句中的“others=>'0'”是什么意思?

    cmd register process rst n clk begin if rst n 0 then cmd r lt others gt 0 elsif clk event and clk 1 then cmd r lt end if
  • 在 Verilog 设计中产生时钟故障

    我正在使用 Verilog 设计芯片 我有一个 3 位计数器 我希望当计数器处于第 8 次循环时 应该有一个时钟故障 之后就可以正常工作了 在 Verilog 设计中产生时钟故障的可能方法是什么 在时钟信号上注入毛刺的一种方法是使用forc
  • 为什么 Verilog 不被视为编程语言? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 教授在课堂上说学生不应该说他们学会了用Verilog 编程 他说 Verilog 之类的东西不是用来编程的 而是用来设计的 那么 Verilog
  • AppleScript:从应用程序隐藏/获取进程名称

    我想隐藏最前面的应用程序 我知道您可以使用以下语法隐藏进程 tell application System Events set visible of process to false end tell 我知道如何获取最前面的应用程序 pa
  • VIM 高亮匹配开始/结束

    我正在尝试找到一个插件 它将突出显示与 Verilog 匹配的开始 结束语句 VIM 可以使用花括号 方括号 但不能使用它的开始 结束 我希望 VIM 突出显示正确的开始到正确的结束 在我看来 最好的选择是使用 matchit 该脚本是 v
  • 如何在 Verilog 中推断 Block RAM

    我在一个项目中遇到了一个非常具体的问题 这个问题已经困扰我好几天了 我有以下 RAM 模块的 Verilog 代码 module RAM param clk addr read write clear data in data out pa
  • 使用正则表达式进行 Verilog 端口映射

    我有一个很长的端口映射 我想在其中替换一堆 SignalName i with SignalName SignalName i 我想我可以用正则表达式轻松地做到这一点 但我无法弄清楚如何做到这一点 有任何想法吗 假设 SignalData
  • 如何通过VBA获取当前Excel实例的进程ID,而不使用标题?

    如何获取运行 VBA 代码的当前 Excel 实例的进程 ID 我不想通过标题中的名称来请求它 当我有两个或多个具有相同标题的 Excel 实例时 这会导致问题 您可以使用此方法来获取当前进程ID Declare Function GetC
  • Mysql 连接到服务器:用户 root@localhost 的访问被拒绝

    edit9 是否有可能我只是缺少文件夹的一些权限 我真的非常非常感谢更多的建议 edit3 由于这篇文章没有得到足够的回复 而且这绝对是至关重要的 我尽快完成这件事 我重建了我的帖子以显示我认为到目前为止我已经扣除的内容 注意 通过许多不同
  • 如何在bash中列出所有后台pid

    要么我无法正确表达我的搜索 要么答案不容易找到 但我正在尝试找出如何列出我的所有后台任务 PID 例如 到目前为止 我发现要列出我们使用的最后一个 PID 但现在我想列出之前任务的 PID 如果存在 但我找不到如何做到这一点 最终我想列出我

随机推荐

  • Wireshark数据抓包分析之传输层协议(TCP协议)

    目录 预备知识 1 TCP协议的由来 2 TCP端口 3 TCP三次握手 3 1 第一次握手 3 2 第二次握手 3 3 第三次握手 4 TCP四次断开 5 TCP重置 实验目的 实验环境 实验步骤一 1 配置服务器端 2 配置客户端 3
  • 微信小程序web-view使用说明,及链接打不开问题

    开发微信小程序时 有时会需要在小程序内打开网页链接 这时就需要用到 web view 标签 web view 是小程序上用来承载网页的容器 且每个页面只能有一个 web view 它会自动铺满整个页面 并覆盖其他组件 目前个人类型的小程序上
  • Requests入门

    前言 爬虫三大库 Requests Lxml BeautifulSoup Requests库的官方文档指出 让HTTP服务于人类 Requests库的作用就是请求网站获取网页数据的 今天我们来了解一下Requests库 如果感觉博主的文章还
  • 最新最全GPT-3模型网络结构详细解析

    最近 GPT3很火 现在有很多讲GPT 3的文章 比如讲解它可以做什么 思考它的带来的影响 可视化其工作方式 看了这些文章并不足以详细了解GPT 3模型 仍然需要认真研究相关论文和博客 因此 本文主要目标 帮助其他人对GPT 3体系结构有一
  • 放炮罚计算器软件

    放炮罚计算器软件 放炮罚 又称为百胡 红胡 告胡子 跑胡子 煨胡子 是湖南人喜欢的一种字牌娱乐活动 据说起源于双峰 又称为双峰跑胡子 放炮罚由于其灵活多变 惊险刺激 而广受湖南人民的喜爱 街头巷尾随处可见在玩放炮罚的广大兄弟姐妹 这种字牌游
  • Linux进程之调度器

    1 Linux调度器的原理 Linux调度器 Linux Scheduler 负责管理这一进程在CPU上运行时的资源分配 它根据选定策略和所估算的进程行为 考量各种因素的权重 对等待在运行队列的进程按优先级排列 从而决定哪个进程能够接下来获
  • base64加密解密

    String random UUID randomUUID toString replaceAll substring 0 8 随机八位数字字母结合字符串 System out println random 2458ec59 String
  • 期货开户收费政策非常合理

    需要大家支付的费用由两部分组成 一部分是保证金 另一部分是费率 保证金和费率都由交易所收取 收取的费用是固定的 因为后期大家投资的项目是不一样的 所以需要大家准备的费用肯定也不一样 除了交易所所收取的费用以外 还包括了开户公司所收取的费用
  • 51单片机原理图

    51单片机 TOC
  • ANDROID APP的页面布局(Part I)

    做一个好的APP自然是不能缺少一个好的漂亮的且合理的页面布局了 ANDORID里面支持的布局大致上有下列即种 根据界面的需要使用不同的布局可达到事半功倍的效果 这个跟做HMTL的页面的原理是一样 好的页面看起来就是舒服 而且容易维护 1 L
  • Lambda表达式与函数式编程

    文章目录 函数式编程 Stream流 概述 为什么学 函数式编程思想 Lambda表达式 概述 Lambda表达式的前身 省略规则 Stream流 概述 案例数据准备 创建流 中间操作 终结操作 reduce归并 注意事项 Optional
  • C语言运算符优先级(超详细)

    转自 http blog csdn net huangblog article details 8271791 每当想找哪个运算符优先级高时 很多时候总是想找的就没有 真让人气愤 现在 终于有个我个人觉得非常全的 分享给大家 欢迎拍砖 C语
  • 前端开发面试题及答案整理(合集)

    前端开发面试题及答案 1 对Web标准以及W3C的理解与认识 答 标签闭合 标签小写 不乱嵌套 提高搜索机器人搜索几率 使用外链CSS和JS脚本 结构行为表现的分离 文件下载与页面速度更快 内容能被更多的用户所访问 内容能被更广泛的设备所访
  • Qt 助手 assistant 单独运行 及 字体设置

    曾经在 Qt creator上 不知道点击了哪里 Qt 助手也是可以单独运行的 这样就可以不需要安装字体了 但是 一直没有找到这个重现的规则 或者快捷键 1 运行Qt 助手 assistant linux 所在目录 xxxxxx Qt5 1
  • java调用 Myeclipse用jax-ws创建的webservice具体方法(三)

    首先需要下载所需的jar包 webservices所需全部jar包下载 点击打开链接 直接上代码 import java net MalformedURLException import java net URL import java r
  • 基于亚奈奎斯特采样和SOMP算法的平板脉冲响应空间插值matlab仿真

    目录 1 算法运行效果图预览 2 算法运行软件版本 3 部分核心程序 4 算法理论概述 5 算法完整程序工程 1 算法运行效果图预览 2 算法运行软件版本 matlab2022a 3 部分核心程序 fine regular grid NSa
  • Could not resolve placeholder 'jdbc.driverClassName' in string value "${jdbc.driverClassName}

    org springframework beans factory BeanDefinitionStoreException Invalid bean definition with name dataSource defined in f
  • $.ajaxFileUpload上传文件出现错误...问题总结

    1 加载报错 ajaxfileupload js 1 Uncaught ReferenceError jQuery is not defined 上传报错 Uncaught TypeError ajaxFileUpload is not a
  • C++ Pat甲级1003 Emergency (25 分)图+dfs

    1003 Emergency 25 分 As an emergency rescue team leader of a city you are given a special map of your country The map sho
  • FPGA实现电机转速PID控制

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