小白学习pid环控制-概览篇
文章目录
一、前言
- 本人可以算是一个硬件小白(大学逃课),学习这个东西的主要想法是想自己做一个3d打印机,然后去参与相关的工作(其实是自己想做点手办、模型之类的)
- 其次如果做不出来也算是当一个技术积累,补补脑子
- 之前在某鱼那边收了一个套件(丝杠、电机、编码器、限位器),还有看见了一个web开源项目: https://gitee.com/skythinker/pid-simulator-web ,怎么说呢,好奇心压不住了
- 所以本文章可以直接看成一个笔记记录,而不是一些教程,看看就行
- 文章打算从概览、实现、上位机的三个角度进行编写(随缘更新)
二、整体硬件概览
-
实现一个控制类的东西通常需要三种知识的配合
- 机械部分:提供运动实现的基础,通常用于将物理世界的东西限制为几种固定的行动模式
- 驱动部分:包含了实现机械模式的各类电机以及相关配套(比如驱动器)、辅助用的限位开关等
- 控制部分:通常分为了下位机(就当是单片机吧)完成对于驱动部分的信号监测、控制逻辑转电子信号的工作,上位机(给用户用的)完成一些更高维度的逻辑控制,比如3d打印机中的路线G-Code生成,依次发送给下位机、异常处理等工作
-
总的来说笔记本是大脑,下位机将大脑的指令转换为各种电信号,最后物理世界实现固定的一些行为
三、硬件模块介绍
下列硬件是个人觉得比较重要的一些东西(差生文具多),实际上还需要很多的辅助工具来实现目标
3.1 开关电源
- 将交流电转换为固定电压(看厂家)直流电的硬件
- 通常来说先看电压,我这里的输入电压为24v(问卖家),所以买24v的。最好不要买统一输入电压的元器件,否则你还得去买DC-DC的电压转换模块,将24V分别转换到各个元器件支持的电压
- 然后估算电流(通常是查询每个硬件满载电流多大,然后直接加起来,外加一些冗余量即可)。最后根据电压x电流得到开关电源型号。比如我这个是24v最大电流3.1A,整体功率75w的开关电源
- 它通常来说是5个螺丝位置,分别对应零线、火线、地线、直流-,直流+
- 如果怕接错线,记得接完前三个口后,万用表测量最后直流的电压是否正常(或者指示灯是否常亮)
3.2 接近传感器
3.2.1 基本介绍
- 原理有很多,但作用都一样。都是什么东西靠近的时候对外输出一个电压信号(输出什么看原件标签)
- 我这个是默认状态输出24v,当前面有铁片的时候输出0V
- 单片机的通常输入是3.3-5v之间,可以在稍微大一点,但绝对不能是24v。由于手头上没有DC-DC的降压模块了,个人是直接将杜邦线的中间剪断后焊接了一个100k的电阻上去,使用示波器量取电阻两端电压。输入电压降低到6.1v超标但搭配隔离器能用范围
3.2.2 电气连接
- 以下是一个简易的电气连接示意图,线的作用一般可以直接搜索官方
3.2.3 简易测试代码
from machine import Pin
import time
# define limit status
left_limit = 0
right_limit = 0
# define limit pin
left_limit_pin = 12
right_limit_pin = 13
left_pin = Pin(left_limit_pin, Pin.IN)
right_pin = Pin(right_limit_pin, Pin.IN)
# def help function
def read_limit(pin, is_left):
global left_limit
global right_limit
is_rising = (pin.irq().flags() == Pin.IRQ_RISING)
value = 0 if is_rising else 1 # normal hight, emit low
if is_left:
left_limit = value
print("Left", value)
else:
right_limit = value
print("Right", value)
left_pin.irq(lambda pin: read_limit(pin, True)) # 任意沿触发
right_pin.irq(lambda pin: read_limit(pin, False))
while True:
time.sleep(0.1)
3.3 编码器
就当是测距的一种实现仪器就好了
3.3.1 基础介绍
- 我这个是AB编码器,简单的来说有两个LED灯,还有一个带600个槽的纸片,硬件转动的时候每次遮挡LED灯,就会依次发送一个0V(常态5v),这个幅值看厂家
- 关于这个的细节,我找到了一篇很好的文章,这里贴一下https://blog.csdn.net/caoleiwe/article/details/106126237?spm=1001.2014.3001.5506 ,大神文章,以下是他的一张图
3.3.2 硬件维修
这个主要是我买到手的是坏的,怎么输出都是0,或者乱跳
- 整体的电路直接分为了三组
- 电源组,将外部24V转换为内部需要的5v
- 光电组,将光电效应转换为一高一低的电压
- 输出滤波(应该是),降低光电那边的杂波,并进一步二值化信号。可惜我这边坏了,管子都没输出的,我直接烙铁拆除,飞线。降低了一些信号稳定性,少花点钱
- 最后输出的效果,没有示波器我什么都做不到.jpg
- 贴一下还算正常一些的异常(幅值只有几百mv),有些时候就直接趴地上了十分的诡异,导致单片机计数器乱跳
3.3.3 电气连接
- 这个的输出只有5V,所以直接找两个单片机的GPIO脚就行
- 由于没有光耦(工业方案,自己玩玩别炸了就行),所以记得将单片机的GND接开关电源的负级
3.3.4 简易测试代码
- 没啥好说的大神文章直接秒了 https://blog.csdn.net/caoleiwe/article/details/106126237?spm=1001.2014.3001.5506
from machine import Pin
import time
# def global var
# encode now value
encode_value = 0
last_a = 0
last_b = 0
# def pin number for each encode AB line
encode_a = 15
encode_b = 14
p_a = Pin(encode_a, Pin.IN)
p_b = Pin(encode_b, Pin.IN)
# def encode count function
def read_encode(a_v, b_v):
global encode_value
global last_a
global last_b
if a_v != last_a or b_v != last_b:
if last_a == 0 and last_b == 0:
if a_v == 0 and b_v == 1:
encode_value -= 1
if a_v == 1 and b_v == 0:
encode_value += 1
elif last_a == 0 and last_b == 1:
if a_v == 1 and b_v == 1:
encode_value -= 1
if a_v == 0 and b_v == 0:
encode_value += 1
elif last_a == 1 and last_b == 1:
if a_v == 1 and b_v == 0:
encode_value -= 1
if a_v == 0 and b_v == 1:
encode_value += 1
elif last_a == 1 and last_b == 0:
if a_v == 0 and b_v == 0:
encode_value -= 1
if a_v == 1 and b_v == 1:
encode_value += 1
last_a = a_v
last_b = b_v
p_a.irq(lambda pin: read_encode(p_a.value(), p_b.value()))
p_b.irq(lambda pin: read_encode(p_a.value(), p_b.value()))
# main loop
while True:
print(encode_value)
time.sleep(0.1)
3.4 驱动器以及电机
3.4.1 42步进电机
- 这里我将继续站在巨人的肩膀上,推荐文章https://blog.csdn.net/qq_41708115/article/details/130540431
- 42步进电机,大致意思就是长宽都是42mm(?)的电机,他有四根线,分别代表一组线圈的+,-。当电流在线中改变方向的时候,产生的磁力会吸引内部的转子运动
3.4.2 步进驱动器
- 步进驱动器,就是将单片机的信号,转换成具体的电流方向的硬件。这里推荐买一点好的,(直接使用A4988燃烧的心里阴影),买大铁盒封装的通常会好接线而且耐造一些
- 他会承担更高级的控制功能,比如控制外部输出时间等实现让步进电机运转更小的距离(更高的距离精度)
- 由于知识是一样的,所以贴另一篇博客: https://blog.csdn.net/weixin_44543463/article/details/125624679
3.4.3 电气连接
- 没啥特别的,单片机就两根线发信号就好了,本来觉得单片机3.3控制一个可以24v输入的东西,可能不够用,需要找mos管,没想到也能用
- 不考虑急停(enable)的信号的话,按照指定口子接线就好了
- 这里说一下如何确定电机的AB相,最好的方法是问卖家,当然我这个就只能自己量了,选择万用表蜂鸣器档,哪两根会响说明是同一组的。同一组的±可以随意插上(本身就是一根线,±只是两个头),AB也同理
3.4.4 简易测试代码
from machine import Pin, PWM
import time
# need pid alg
servo_dir_pin = 2
servo_pwm_pin = 3
# def same const
servo_running_duty = int(50 * 655.36)
servo_basic_pwm = 1600
# define pin
servo_dir = Pin(servo_dir_pin, Pin.OUT, Pin.PULL_DOWN)
servo_pwm = PWM(Pin(servo_pwm_pin))
# define help tools
def set_servo_stop():
servo_pwm.duty_u16(0)
def set_servo_running(freq):
global servo_basic_pwm
global servo_running_duty
if freq != -1:
servo_pwm.freq(freq)
else:
servo_pwm.freq(servo_basic_pwm)
servo_pwm.duty_u16(servo_running_duty)
set_servo_running(1600)
loop = 1
while True:
time.sleep(2)
servo_dir.toggle()
loop += 1
if loop > 5:
set_servo_stop()
print(loop)
3.5 树莓派Pico
别问为啥不用32,问就是只有一些基础知识,写不来。而且pico就10块钱
- Pico中文站点: https://pico.nxez.com/getting-started/
- 价格便宜的树莓派系列微型控制器,他有两个开发模式
- MicroPython: 用户烧录官方的固件,然后就可以将一些简化的python代码用Thonny发送过去(第一次感受到了硬件的友好)
- C++: 跟32一样了,下载官方的基础库,补好main系列代码,使用arm编译好,用官方的方式烧录进去
- 总的来说可以用python快速验证想法
(直接下载手册抄袭示例就好了)
,然后慢慢写回c++不断的优化执行效率和稳定性,比上来就去碰32大脑懵逼中接着懵逼要好一些
3.6 USB隔离器
后面开始更多可以当作一个通识来看,非必须品,但推荐
- 单片机和笔记本接口没有PLC那么耐造(比如宽幅的电压输入)
- 如果遇到一些意外的接线错误、浪涌电压啥的很容易损坏(单片机烧了就算了,别笔记本也送回去返修了)
- 同时好的隔离器还能对信号的质量有一定的优化作用,但从整体的安全角度来说,还是优先推荐的硬件
3.7 万用表和示波器
- 没啥好说的,没有他们调试起来跟瞎子一样
3.8 逻辑分析仪
- 这里只是提一嘴,毕竟随缘更新,不一定去适配G-Code以及写Qt上位机
- 简单来说就是通信类的代码通常都要在几个ns(?反正得很小)的范围内进行高低信号的读取以及回复
- 手动Debug的话就别想了,做不到的。写点代码看波形对不对吧
3.9 树莓派调试器
多多少少有点成本爆炸了已经,看着买吧
- https://pidoc.cn/docs/microcontrollers/debug-probe/,虽然说嵌入式开发更多还是以瞪眼调试为主,但逻辑类的开发可能还是无法缺少相关的debug流程
- 在尝试开发的时候我们发现Debug按钮是灰色的,实际上就是缺少了这种调试器
四、总结
- 自己想当然的觉得做出一个控制算法很简单,实际上搭建这个平台都废了我半条命,纯软工 😦
- 传感器损坏、编码器异常、电压差点干爆笔记本、接线冒火花。。。。。。
- 但总的来说还是学到了不少东西,最终也是实现了一个环控制,但是非常的垃圾。总归来说还是学有所成,不虚此行
- 后面随缘放出整体的驱动代码(实际上你把上面的示例组装组装,添加一些算法,也能实现,上位机的部分由于工作原因也是随缘写吧
- 希望对于整体看完文章的小白们有所帮助,未来是一个对于技能要求不断综合的时代,我们仍要不断的self-teach
文章里面出现的代码: - https://download.csdn.net/download/qq_34524246/91653921