[树莓派] 使用pigpio库(3) 如何发送指定数量的脉冲信号

编程入门 行业动态 更新时间:2024-10-15 10:13:22

[树莓派] 使用pigpio库(3) 如何发送指定数量的<a href=https://www.elefans.com/category/jswz/34/1771345.html style=脉冲信号"/>

[树莓派] 使用pigpio库(3) 如何发送指定数量的脉冲信号

如何发送指定数量的脉冲信号

文章目录

  • 如何发送指定数量的脉冲信号
    • 1 需求分析
    • 2 救世主-pigpio
    • 3 用pigpio发送指定数量的脉冲
      • 3.1 wave_add_generic(pulses)函数介绍
      • 3.2 wave_create()函数介绍
      • 3.3 wave_chain()函数介绍
    • 4 步进电机精确控制代码

1 需求分析

当我们在控制步进电机的时候,发送一定数量的脉冲信号是非常需要的,因为步进电机会根据收到的脉冲移动一定的角度。

图中所示的此类步进电机,往往需要一些驱动设备来控制,而使用这些驱动可以大大简化我们的控制。比如说,这些驱动可以设置分辨率,此处的分辨率指的是旋转360°所需要的脉冲波个数。例如分辨率为2000,那么意思就是说你给步进电机发送2000个脉冲信号它就可以旋转360°。很显然,分辨率越大,控制的精度就越高,因为你可以控制的最小移动角度就越小。但是,这同样是一把双刃剑,因为你需要产生更高频率的脉冲信号来驱动它,如果频率不够高的话,那么转速会很慢。除了分辨率之外,我们最需要的就是驱动提供了两个口,其中一个用于控制顺时针旋转,而另一个用于控制逆时针旋转,这样我们只需要给这两个口发送对应的脉冲信号,就可以达到控制方向的效果了。

那么根据原理,我们可以简单的写出下面代码。

from time import sleep
import RPi.GPIO as GPIOCW = 26     # 控制顺时针旋转的GPIO口
CCW = 21    # 控制逆时针旋转的GPIO口
RESOLUTION = 2000  # 设置的分辨率,2000个脉冲信号旋转360°GPIO.setmode(GPIO.BCM)
GPIO.setup(CW, GPIO.OUT)
GPIO.setup(CCW, GPIO.OUT)delay = 0.005
step_count = 2000
print('开始顺时针旋转一圈')
for x in range(step_count):GPIO.output(CW, GPIO.HIGH)sleep(delay)GPIO.output(CW, GPIO.LOW)sleep(delay)sleep(0.5)
print('开始逆时针旋转一圈')
for x in range(step_count):GPIO.output(CCW, GPIO.HIGH)sleep(delay)GPIO.output(CCW, GPIO.LOW)sleep(delay)GPIO.cleanup()

当然了,我们还可以封装成函数,比如函数提供旋转的方向和角度这两个参数,以下提供一个简单的示例。

def rotate(direction, angle):
'''direction: 旋转的方向,可以是CW或者CCWangle: 旋转的角度,范围是0-任意
'''if angle < 0:print('无效的旋转角度')return Falsenum_of_pulse_per_cycle = 2000 / 360 # 以2000为例,计算出旋转1°所需的脉冲个数num_of_pulse_need = angle * num_of_pulse_per_cycle # 计算出所需的脉冲个数delay = 0.005def send_pulse(port, num_of_pulse): # 发送指定个数的脉冲for x in range(step_count):GPIO.output(port, GPIO.HIGH)sleep(delay)GPIO.output(port, GPIO.LOW)sleep(delay)port = CW if direction == 'CW' else CCWsend_pulse(num_of_pulse_need)return True

至此,看起来生活很美好,是吗?我们已经可以控制步进电机了,并且能让他旋转任意角度。那这样有没有问题呢?显然是有的。

缺陷-转速过慢
可以看到,上面的实现代码中,使用的是软件方法来产生脉冲波形,这很显然会被中断,多线程等问题影响,如果暂且不谈论这个的影响,那么还有一个问题就是所产生的脉冲波形频率太低了,就像我们开头所述的那样,这样就会转速过慢,无法发挥步进电机全部的转速。上面的实现代码中,一个周期是 0.005 ∗ 2 = 0.01 0.005*2=0.01 0.005∗2=0.01秒,那么频率就是 1 0.01 = 100 H z \frac{1}{0.01}=100Hz 0.011​=100Hz,如果按照我们设定的参数,旋转一圈需要发送2000个脉冲信号,那也就需要 2000 ÷ 100 = 20 2000\div100=20 2000÷100=20秒,这样的速度显然是我们所不能接受的。
缩短延迟时间,可以吗?NO
有同学可能会直接减少delay的时间,以此来提高频率,可以说这也是我们的第一直觉,然而生活不会这么美好,当你不断减少delay的数值时,你会发现当你减到很小的时候,它不工作了,这是因为软件产生脉冲波形的频率是有上限的,而且这个上限很小,不能满足我们的需要,有兴趣的朋友可以试试最快能到多少。

2 救世主-pigpio

上面一顿分析,我们现在的目标是要更高频率的波形,而这可以由pigpio来帮助你,我在第一篇文章中也提到过,这个库可以产生0-20kHz的波形无压力,甚至最高能到达1MHz,只需调用pi.hardware_PWM(GPIO_port, frequency, duty_cycle),当然这对于GPIO口也有一些要求,这个不在这里详述。
然而这样就可以了吗?没有
尽管我们现在的确可以产生更高频率的波形了,但是我们却不能控制其数量,这样的话我们很难达到精准的控制。有的同学表示,我们可以产生高频率的PWM波形,然后通过计时的方式来达到发送一定数量的脉冲信号。想法是非常美好的,但是现实很残酷,也许你只控制步进电机,什么也不干的时候,可以达到,当你写了一个稍微有点规模的程序,它同时还在做其他工作(测量,记录等),你会发现产生的PWM信号频率和你设定的有一定误差,比如设定产生20kHz的波形,实际可能是18.6kHz。在这种情况下,你很难估计发送的时间,也就很难达到精准控制。

3 用pigpio发送指定数量的脉冲

大家无需感到沮丧,既然有这篇文章,那肯定有解决的办法!先简单介绍以下pigpio库当中的几个函数,它可以帮助我们实现目标哦。

3.1 wave_add_generic(pulses)函数介绍

根据官方描述,这个函数可以帮助我们制造我们想要的波形,注意其所需的参数为pulses,那么我们如何给这个函数传递参数呢?以我们的目标为例,假设我们想制造一个脉冲信号,那也就是输出高电平,过一会之后输出低电平,那我们就可以做如下的操作。

import pigpio
pi = pigpio.pi()
GPIO = 24
pi.set_mode(GPIO, pigpio.OUTPUT);
delay_time = 100  # 延迟100us
my_pulse = []
my_pulse.append(pigpio.pulse(1<<GPIO, 0, delay_time))
my_pulse.append(pigpio.pulse(0, 1<<GPIO, delay_time))
pi.wave_add_generic(my_pulse) # 添加波形
wid = pi.wave_create() # 获得波形的id用于后面调用

可以看到,我们延迟的时间是100微秒,那么频率也就是5000Hz,还是不错的,相较于软件产生的波形,提高了50倍。但是,延时也不是可以无限小的,由于pigpio默认的扫描频率是5微秒,所以我建议delay的时间要大于等于5微秒,那这样我们最多能够产生100kHz的波形,已经相当不错了!

3.2 wave_create()函数介绍

由上面的例程,我们可以在wave_add_generic后面紧跟wave_create函数来获得我们创建波形的id,这将用于后面我们运行时找到我们对应的波形。当我们创建多个波形的时候,这将会非常有用。

3.3 wave_chain()函数介绍

这个函数可以发送一系列的波形,当然也可以只发一个波形啦。但值得注意的是,运行此函数之后,任何的硬件PWM都会停止运行。这个函数可以接受一系列的指令和数据,由以下表格来表示。

名称命令和数据作用
Loop Start255 0开始一个波形
Loop Repeat255 1 x y循环x+y*256次数
Loop Delay255 2 x y延迟 x + y*256 微秒
Loop Forever255 3永久循环

聪明的同学可能已经发现了,我们所需要的不正是第二行吗,没错,利用该命令我们就可以循环我们想要的次数了,这样不就可以发送指定数量的脉冲了吗!

4 步进电机精确控制代码

以下代码是我写的步进电机类,可以精确控制旋转的角度和方向,有需要的朋友可以根据自己手头电机的参数进行配置和使用。值得注意的是,由于我使用的系统中涉及到了齿轮,因此还设置了gear_ratio参数,使用时需要注意。

'''Driver for the motor (stepper motor).@author: Jingkai Zhang@version: 1.2version 1.1: add stepper motor (sofware PWM)version 1.2: add stepper motor (hardware PWM)@since: 2022@update: 2022-4-6edit in 2022-3-29: Add new stepper motor driver, based on pigpio, which is fasteredit in 2022-4-3: Add more information if initialization is failededit in 2022-4-6: fix some bugs
'''
import pigpio
import time, sys
class stepper_motor(object):'''Test script for step motor HW23-601 (/?q=HW23-601) with driver STR2 ()'''num_of_steps_for_360 = 2000                 # this could be changed, please see the totorial of STR2/4resolution = num_of_steps_for_360 / 360     # resolution, how many steps would trigger 1 degree rotationCW_control_port = 21                        # GPIO 21 for clockwise (physically connected to dir+ port in the driver)CCW_control_port = 18                       # GPIO 18 for counter clockwise (physically connected to step+ port in the driver)gear_ratio = 4                              # radius of the outer gear is 4 times than the radius of the inner gearcur_position = 0pwm_frequency = 6250                        # PWM frequency, the larger the faster rotation'''TODO: decide how much frequency is suitableHere are some frequency that I recommend:5000 Hz, 6250 Hz, 10000 Hz, 12500Hz, 15625 Hz, 20000 HzBetter choose something that can be divided by 500000.'''inner_speed = 60 * pwm_frequency / num_of_steps_for_360outer_speed = inner_speed / gear_ratiodelay_micro = int(500000 / pwm_frequency)CCW_wave = [pigpio.pulse(1 << CCW_control_port, 0, delay_micro), pigpio.pulse(0, 1 << CCW_control_port, delay_micro)]CW_wave = [pigpio.pulse(1 << CW_control_port, 0, delay_micro), pigpio.pulse(0, 1 << CW_control_port, delay_micro)]waveform_id = {}name = 'stepper_motor'def __init__(self, log=None, ip=None):self.log = logif ip is not None:self.pi = pigpio.pi(ip)if not self.pi.connected:self.log.critical("cannot connect to tower pi's gpio, please check the ethernet connection or pigpio daemon!")return False         # motor initialization failedelse:    self.pi = pigpio.pi('localhost')if not self.pi.connected:self.log.critical("cannot connect to localhost, check if pigpio daemon is running")return Falseself.pi.wave_clear()self.pi.set_mode(self.CW_control_port, pigpio.OUTPUT)self.pi.set_mode(self.CCW_control_port, pigpio.OUTPUT)self.pi.wave_add_generic(self.CW_wave)self.waveform_id[self.CW_control_port] = self.pi.wave_create()self.pi.wave_add_generic(self.CCW_wave)self.waveform_id[self.CCW_control_port] = self.pi.wave_create()self.log.info('waveform created %s' % self.waveform_id)time.sleep(0.5) self.log.info("stepper motor initialization accomplished, outer speed: %d rpm, resolution: %d steps/degree" % (self.outer_speed, self.resolution))def __del__(self):self.pi = pigpio.pi()    self.pi.wave_tx_stop()  # stop waveformself.pi.wave_clear()self.pi.stop()def send_pulse(self, num_of_pulse, control_port):'''If you want to know why, please visit the following link:.html#wave_chain'''   x = int(num_of_pulse) & 255y = int(num_of_pulse) >> 8chain = [255, 0, self.waveform_id[control_port], 255, 1, x, y]  # send x + 256 * y times# chain = [255, 0, self.waveform_id[control_port], 255, 3]  # infinity loop, just for testself.pi.wave_chain(chain)while self.pi.wave_tx_busy():time.sleep(0.1)self.pi.wave_tx_stop() def move(self, angle, direction):num_of_steps = self.resolution * angle * self.gear_ratio  # gear ratio is 4, so the outer speed is 1/4 of inner gear, need more pulseangle = -angle if direction == 'CCW' else angle           # decrease the angle if direction is CCWself.cur_position += angleif self.cur_position > 360 or self.cur_position < -360:self.log.error("rotation angle will exceeded 360 degree, wire might get twisted, current movement cancelled")self.log.info("movement: <moved %d degree, direction:%s> has been cancelled" % (angle, direction))self.cur_position -= anglereturn Falseelse:if direction == 'CW':self.send_pulse(num_of_steps, self.CW_control_port)else:self.send_pulse(num_of_steps, self.CCW_control_port)self.log.info("moved %d degree, direction:%s, current position: %d" % (abs(angle), direction, self.cur_position))return Truedef get_motor_type(self):return self.name

由于运行时需要传入log,大家在使用的时候可以删除这部分的内容,以防止报错。

更多推荐

[树莓派] 使用pigpio库(3) 如何发送指定数量的脉冲信号

本文发布于:2024-02-16 18:41:37,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1691091.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:脉冲   信号   数量   树莓派   pigpio

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!