Raspberry Pi完成直线平台的设计全过程

描述

像 Raspberry Pi 这样的单板计算机 (SBC) 只需数小时就能轻松构建复杂的机器,而不必使用一颗裸微控制器从头开始。 为了展示使用现代 SBC 有多简单,此“Easy Build”课程将引导您完成构建直线平台并进行编码所需的步骤。这里的直线平台是一个能够以直线方向执行来回运动的,由系统工程师或研究人员用来完成重复性任务的平台。

除了引导您完成这些步骤外,我们还将附上用于演示随附视频中直线平台的代码,以及帮助您启动的项目物料清单。

为什么选择直线平台?

我们启动此项目的初衷是,展示设定 Raspberry Pi 微型计算机来执行稍复杂的任务是如此容易。 尽管我们不会满足于几个闪光灯,但我们也不想尝试并计算轨道力学。 最终我们决定控制步进电机致动的直线平台。 毕竟,驱动步进电机正好介于闪烁的 LED 与计算三个天体的多体问题之间。

我们将项目分成三个部分:电子硬件、软件和机械构造。

电子硬件

智能硬件

图 1:显示从 Raspberry Pi Model B 到限位开关的所有硬件元素的完整系统布局(左上角)。 (使用 Digi-Key Scheme-it 绘制的原理图)

目前为止,这些内容还非常简单、直接。 很显然,除了 Raspberry Pi 2 (Rpi) 外,我们还需要步进电机、步进电机驱动器以及上述两个器件的电源。 之后,我们确定了需要的输入。 最后,我们确定了以下项目:

- 左移按钮

- 右移按钮

- 高/低速开关

- 左限位开关

- 右限位开关 

我们将电路布置到 B&K Precision GS-830 试验板上。 顾名思义,它拥有 830 个连接点以及一个总线条。

图 2:如图所示,滑动架挤靠在限位开关上,其中蓝色盒子就是限位开关,且在丝杠下方还有一个柱塞。 (来源:Digi-Key Electronics) 

输入从左,还是从右,完全随意。 我们只需对两个相反方向命名。 请注意,该限位开关使用上拉电阻器接线为“正常高”。 我们这样做是为了给这些输入提供一些抗噪能力。 Raspberry Pi 具有惊人的灵敏度,我们发现,紧凑型荧光灯等外部噪声源和继电器线圈等电感器有时可以在 Pi 上触发输入。

对于步进电机,我们选择了 NMB Technologies Corporation 的 23KM-K2(图 3)。 我们使用的步进驱动器是 Geckodrive 的 G210X。 Geckodrive 比市场上许多廉价的非品牌驱动器要昂贵一些,但它们可以提供流畅、无故障的步进控制。 同样,我们手头已经有了一个这样的步进驱动器。 大多数类似 G210x 这样的廉价步进电机驱动器是通过 DIRECTION 引脚和 STEP 引脚来控制的。 这很适合简单的项目:我们只需将这些引脚直接连接到 Rpi 上未使用的 GPIO 引脚即可。

图 3:对于步进电机,我们选择了 NMB Technologies Corp. 的 23KM-K2 标准混合电机。 (来源:NMB Technologies Corp.)

我们还将 Geckodrive 的 ENABLE 引脚接至 Rpi。 这样做使得驱动器仅在程序运行时才“开启”并向电机线圈供电。 否则,即使驱动器未收到执行任何操作的任何指令,驱动器仍会在我们向整个电路供电后立即将功率转储至电机。

将功率转储至电机线圈造成两个结果。 首先,它会促使电机产生制动效果。 电机会主动抵制任何旋转其轴的尝试。 这在某些情况下是希望达到的效果;比如当您将滑动架移到某个点并且希望将其锁定到位,使其不会被撞离位置。

第二个结果是我们希望避免的,即电机会在原地不断升温。 此电路在我们编辑和测试代码的过程中一直保持通电状态。 如果我们不停用驱动器,它会不断向我们的电机转储功率。 步进电机通常都会变热,但我们最好不要让其在等待指令的过程中自行加热。

图 4:36 VDC XP Power SHP350 系列 36 V 开关式电源用于替换 24 V 电源为系统供电,以增加电机转速。 (来源:XP Power)

最初,我们为电机连接 24 VDC 电源, 之后我们决定提高转速,于是找到了 36 VDC 电源,即 XP Power 的 SHP350 单输出、350 W AC-DC 电源。 这将在 24 V 电源的基础上显著提升电机转速。

Gecko 驱动器会限制通过电机线圈的最大电流。 您可以在端子 11 和 12 之间使用电阻器来设置电流限制,也可以使用驱动器上的 DIP 开关。 顺便提一下,同一组 DIP 开关还用于控制驱动器的微步进模式。 我们将在软件部分对此做进一步讨论。

软件

软件程序显示在本页面的底部。 此时我们决定选择使用两个程序:一个程序用于手动控制平台;另一个程序负责让直线平台滑动架在限位开关之间来回穿梭。 所有代码均使用 IDLE 3.2.1 以 Python 语言编写。 我们之所以使用 Python,是因为它是 Rpi 的默认语言。 IDLE 是随 Raspbian 一起提供的 Python 精简版系统开发环境。

为什么使用 Python? 不要让新语言吓住了您。 Python 与 C 非常相似,即使您不会编写 Python 代码,但只要熟悉 C,就几乎肯定能够阅读和理解程序内容。

我们的手动控制程序要执行的操作几乎一目了然,代码也未作任何说明。 具体而言,电机加速的问题。 步进电机必须逐渐提高转速。 我知道,许多读者会说,他们以前见过或控制过步进电机,电机转速完全取决于向其输入的脉冲频率。 也许是这样,但我敢打赌,这些情况下的电机负载肯定很小,甚至根本没有任何负载。

而我们的直线平台中的电机肯定带有负载。 承载的步进电机必须达到一定的转速,否则电机将会失速。 将脉冲输入到其驱动器时,步进电机只会在原地断续运转。 这也是我们在软件中置入加速程度的原因。 加速度需要足够快,使观察者不容易观察到从静止到最高转速的过程,但不能太快以至于失速。

加速程序一开始很慢地旋转电机(步进电机在低速下具有最高的扭矩),以向系统施加最大扭矩,从而克服惯性和“静摩擦力”。 之后,此程序会快速缩短步进脉冲之间的时间,直至达到最高转速。 扭矩会随着转速的提高而减小,但到此时,如果调节得当,系统中的机械阻力已被克服。

简单地介绍一下“静摩擦力”一词的含义。 此术语的真正解释远远超出了此项目分析的目的。 但我们可以采用类比的方法加深了解。 想象一下握住机器的旋钮或手轮,并尝试旋转。 通常,在旋转时必须克服一个初始阻力。 一旦克服这个阻力,旋转旋钮或手轮就会变得容易多了,现在需要的扭矩也要小得多。 在本文中,该初始阻力便是静摩擦力。 这种现象在利用滑台执行操作的机床(例如车床或铣床)中特别明显。

我们的确注意到,Python 也有一个显著的局限。 我们对为电机驱动器提供输出的步进引脚进行切换的速度存在限制。 到某一点后,我们在 5 µs 步进脉冲之间加入的延迟不论多小都无关紧要,因为 Python 代码中的命令执行之间存在固有的延迟。 我们没有完全弄清这一问题的缘由,也不知道如何解决此问题。 最好的办法是采取变通方案,这也是我们将电机驱动器设为 Full_Step 模式的原因。

如果使用任何类型的微步进,我们需要将最大脉冲率(取决于 Python 的具体情况)除以我们将驱动器切换到的微步数。

因此,对于半步进电机,需要将最高转速除以二。 使用 1/10 微步进时,我们需要将最高转速除以 10! 如果您追求更流畅、波动较小的动作,微步进方式是不错的选择,但对此项目而言则不必如此。

关于微步进的说明

我们在此项目中所用的双极步进电机类型几乎总是设计为每转 200 步。 也就是说,200 个“全”步将会旋转电机轴一周。 现在,比方说,我们将电机驱动器设置成 ¼ 微步进。 我们这样做会将电机的每个满步分成 4 个更小的步。 电机驱动器收到的每个 STEP 脉冲会将电机轴旋转 ¼ 满步。 这意味着,我们现在必须发送 800 个脉冲到驱动器才能将轴旋转一整转。

1/10 微步呢? 现在,您需要发送 2000 个脉冲才能完成一整转。 咋一看,这看起来不错。 这样做增加了电机的位置分辨率。 是的,但不完全如此:您可以采取多小的步幅存在物理限制。 而且,微步越多,您所获得的可用扭矩越小。

手动控制程序分解

首先,我们导入了时间和 GPIO 库。 然后,我们为每个变量设置引脚以及这些引脚的输入/输出。

方向按钮和速度开关设置为使用内部下拉电阻。 限位开关设置为使用内部上拉电阻。

转速和加速变量在开始时设置,并可全局更改,以调节运行程序中的电机转速和加速度。

程序仅运行一个“while”循环作为主循环,扫描“左”和“右”按钮。

按其中一个按钮后,rampUp() 子程序就会立即执行电机,沿所按按钮的方向加速至最高转速。

只要按下按钮,程序就会留在 rampUp() 程序中。 松开按钮后,rampUp() 程序将会跳入 rampDown() 程序,将电机减速至停止。 rampDown() 会持续减速,直至用完减速步数。 然后程序会返回到主循环,以检查方向按钮。 转速开关有两个设置,高和低。 此开关会将速度变量更改为对应的速度变量。

在按下左或右方向按钮以及发出步进脉冲时,步进程序还会检查确认是否已启用两个限位开关之一。 当移动平台触及限位开关时,电机就会停止,改变方向,并执行与原始行进方向相反的 50 步 (decelPulseCount) 移动。 这会将滑动架移至足够远离限位开关的位置,以免再次触及限位开关。

来回穿梭程序

从 Raspberry Pi 运行该程序后,直线平台会立即在一个方向上以固定的速度移动,直至触及其中一个限位开关。 平台随即改变方向,逐步提速,并继续在另一个方向上移动,直至其触及平台行程另一侧的限位开关,周而复始。 移动速度可通过手动控制程序中所用的相同开关,在预设的高速度或低速度之间切换。

机械构造

图 5:直线平台的最终构造使用现货零件组装而成,包括 1.5 英寸铝型材以及一根 8 螺纹/英寸的丝杠(8 头)。 (来源:Digi-Key Electronics)

直线平台完全由现货零件制成:用于框架的 1.5 英寸铝型材、夹具、轴、电机底座,以及 0.5 英寸直径的 8 螺纹/英寸的丝杠(8 头)。 八头表示螺纹具有 8 条平行螺纹,类似于橙汁容器盖。 最终,丝杠每转动一整转,机械系统就会移动 1 英寸。 我们这样做是为了让直线平台操作视频更生动一些。 我们有 10 螺纹/英寸、单螺纹丝杠,但这会导致移动速度非常慢。 步进电机的局限在于其最大转速实际上很慢。

滑动架架在一对 20 mm 直线轴及配对的轴承上。 针对所用的 NEMA 23 双极步进电机,我们选择了一个通用的 NEMA 23 安装支架。 问问图片和视频您可以看到,我们使用齿轮和正时皮带将机械动力从电机传递到轴末端,以便在出现振动或机械静摩擦时能够调节齿轮比。 一些实验表明,电机和轴之间的比率为 1:1 效果很好。

对于位于丝杠从动端的轴台,我们必须做一些设计工作和加工(图 6)。 轴台是支撑轴的一个机械元件,在本例中仅传输旋转运动并阻止直线运动(至少在理论上如此)。 我们能否在没有轴台的情况下完成项目? 存在这种可能。 我们本可以将丝杠直接连接到步进电机的输出轴,但如果我们选择这种做法,我们的系统将很不稳定。 因此,我们在直线平台上设计并制造了一个直接连接到 20 mm 直线轴的轴台。

图 6:为延长设计寿命,使用轴台支撑轴并且仅传输旋转运动,同时阻止直线运动(至少在理论上如此)。 (来源:Digi-Key Electronics)

步进电机内的轴承通常仅设置为处理侧向力,而非轴向力。 随着时间的推移,在滑动架来回穿梭的过程中,这些电机轴承将受到不应有的应力。 最终会损坏电机。 我们不清楚多久会发生这种情况。 但我们不希望电机在项目中途出现磨损,因此我们制造轴台时使用一些推力轴承、一个滚针轴承和一根 1/4 英寸的轴。 滚针轴承(红色)在轴台右侧中间位置固定 1/4 英寸轴,止推轴承(蓝色)负责处理从丝杠传输的任何轴向力。

最后,我们对限位开关进行定位,让滑动架在撞上其机械行程极限之前便能触发该开关。 此时,滑动架的撞击不会导致物理损毁,但肯定会开始磨损正时皮带和齿轮,因此我们会尽力避免这种情况。

以下就是运行中的完整项目:

结论

Raspberry Pi Model B 等 SBC 可以让工程师和研究人员更轻松地设计并实现可行且实用的系统。 此 Easy Build 分步指南通过一个实用的实例,详细指导读者完成直线平台的设计全过程,同时针对过程中的组件选择和设计决策,提供了深入浅出的分析。 除物料清单和相关代码外,您在实现下一个创意时也可借鉴该实例。

物料清单:

  1. Seeed Raspberry Pi 2 Model B

  2. XP Power:36 V 电源

  3. NMB Technologies Corporation:步进电机(零件即将缺货。)

  4. B&K Precision:试验板

  5. 项目电线

    1. Bud Industries

    2. MikroElektronika

  6. Honeywell:限位开关

    1. DTE6-2RQ9

    2. NGCMB10AX01R

  7. Judco Manufacturing:表面贴装开关

  8. C&K Components:JS 系列滑动开关

其它零件:

Gecko 210X 步进电机控制器

½ 英寸 8 头丝杠

Bass 丝杠螺母

轴台轴承

联轴器

滑轮

电机驱动正时皮带

铝型材

直线轴承

精密连杆

两个定制底座、滑动架和轴台

代码:

代码、按钮控制

发送顺时针 (CW) 和逆时针 (CCW) 脉冲至步进驱动器;对限位开关做出反应;按初始行程方向相反的方向转动电机以“退避”限位开关。

"""

"""

导入模块和/或模块分段

"""

import RPi.GPIO as GPIO

import time

"""

设置 I/O 引脚掩码

"""

step = 18       # step signal to driver

directionPin = 23  # direction signal to driver

enable = 24     # enable signal to driver

button1 = 13    # direction pin

button2 = 5

output1 = 19

output2 = 12

output3 = 21

speedHiLo = 6

limitLeft = 12

limitRight = 16

"""

设置通用 IO

"""

GPIO.setmode(GPIO.BCM)          #configure pin layout

GPIO.setwarnings(False)

GPIO.setup(step, GPIO.OUT)     

GPIO.output(step, GPIO.LOW)

GPIO.setup(directionPin, GPIO.OUT)

GPIO.output(directionPin, GPIO.LOW)

GPIO.setup(enable, GPIO.OUT)

GPIO.output(enable, GPIO.HIGH)

GPIO.setup(button1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.setup(button2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.setup(speedHiLo, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.setup(limitLeft, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(limitRight, GPIO.IN, pull_up_down=GPIO.PUD_UP)

"""

一般配置及声明

"""

global lospeed, hispeed, startTime, endTime, limitFlag, timeVar, accelTime, decelTime,decelPulseCoun, flag1, highPulse, direction, limitPulseCount

flag1 = 0               # variable for loop control

timeVar = 0             # variable for loop control

direction = 0           # variable for storing direction pin value

endTime = 0             # variable for storing wait time between pulses whilst slowing to a stop

limitFlag = 0           # signals that limit has been reached and flow must return to main loop

highPulse = 0.0001    # time for pulses to go high to trigger driver

startTime = 0.001       # time between pulses at start of movement

hispeed = 0.0001       # time between pulses at full speed

lospeed = 0.001         # time between pulses

accelTime = 0.000003   # amount of time to decrement between acceleration pulses

decelTime = 0.00005    # amount of time to increment between deceleration pulses

decelPulseCount = 50   # number of pulses sent during deceleration, 1/4 rev for current setup

limitPulseCount = 200  # number of pulses sent to the driver when a limit is tripped, 1/2 rev for current setup

"""

功能定义

"""

def rampUp():

        GPIO.output(enable, 1)  # enable driver for movement

        timeVar = startTime #initialize time variable with starting time between pulses

        global flag1

        global limitFlag

        global direction

        flag1 = 1  # set flag HI

        if(GPIO.input(button1)):

                direction = 1

                GPIO.output(directionPin, direction)

                         # light an LED

        if(GPIO.input(button2)):

                direction = 0

                GPIO.output(directionPin, direction)

                        # light an LED

   

        while((GPIO.input(button1) or GPIO.input(button2) == 1)):

                if(GPIO.input(limitLeft)== 0): # if a limit input goes LOW, call the limit function

                        limit()

                if(GPIO.input(limitRight)== 0): #if a limit input goes LOW, call the limit function

                        limit()

                if(limitFlag == 1):

                        limitFlag = 0

                        break

               

                GPIO.output(step, 1)

                time.sleep(highPulse)

                GPIO.output(step, 0)

                time.sleep(abs(timeVar))                               

                if(timeVar > endTime):

                        timeVar = timeVar - accelTime #decrease time between pulses until they reach endTime

                           

def rampDown():

        global flag1

        flag1 = 0

        global timeVar

        timeVar = endTime #initialize time variable with ending time bewteen pulses

       

        x = decelPulseCount

        while(x > 0):

                x = x - 1

                GPIO.output(step, 1)

                time.sleep(highPulse)

                GPIO.output(step, 0)      

                time.sleep(abs(timeVar))

                       

                if(timeVar < startTime):

                        timeVar = timeVar + decelTime

                     

def limit():  # this routine is like the ramp down routine

        print("Limit Hit")

        time.sleep(0.015) #wait for a bit

        if(GPIO.input(limitLeft) and GPIO.input(limitRight) == 1): # debounce

                return 

       

        GPIO.output(enable, 1) # enable driver for movement

        global direction

        global timeVar

        global limitFlag

        global flag1

       

        flag1 = 0 # disable the flag so u dont call rampDown upon exiting limit()

        timeVar = endTime  #initialize time variable with ending time bewteen pulses

       

        direction = not direction

        GPIO.output(directionPin, direction)

        limitFlag = 1 # set this flag so that the rampUp routine 'breaks' and jumps back to Main()

        x = limitPulseCount

        while(x > 0):               

                x = x - 1

                GPIO.output(step, 1)

                time.sleep(highPulse)

                GPIO.output(step, 0)      

                time.sleep(abs(timeVar))

                       

                if(timeVar < startTime):

                        timeVar = timeVar + decelTime

                           

"""

主循环

"""

while(1): #loop forever, check for button presses, speed changes and limit trips

         #disable driver to keep from overheatings

                       

        if(GPIO.input(button1) or GPIO.input(button2) == 1): # button pressed, call rampUp function

                rampUp()

        if(flag1 == 1):

                rampDown()   

        # movement over, deactivate direction LEDs

       

        if(GPIO.input(speedHiLo) == 0):

                endTime = lospeed       #if lo-speed selected, initialize endTIme with lo-speed wait time

               

        else:

                endTime = hispeed       #if hi-speed selected, initialize endTIme with hi-speed wait time

                 # light up red LED to indicate hi-speed mode

        if(GPIO.input(limitLeft)== 0): # if a limit input goes LOW, call the limit function

                limit()

        if(GPIO.input(limitRight)== 0): #if a limit input goes LOW, call the limit function

                limit()

代码,来回穿梭

import RPi.GPIO as GPIO

import time

"""

设置 I/O 引脚掩码

"""

step = 18       # step signal to driver

directionPin = 23  # direction signal to driver

enable = 24     # enable signal to driver

button1 = 13    # direction pin

button2 = 5

output1 = 19

output2 = 12

output3 = 21

speedHiLo = 6

limitLeft = 12

limitRight = 16

"""

设置通用 IO

"""

GPIO.setmode(GPIO.BCM)          #configure pin layout

GPIO.setwarnings(False)

GPIO.setup(step, GPIO.OUT)     

GPIO.output(step, GPIO.LOW)

GPIO.setup(directionPin, GPIO.OUT)

GPIO.output(directionPin, GPIO.LOW)

GPIO.setup(enable, GPIO.OUT)

GPIO.output(enable, GPIO.HIGH)

GPIO.setup(button1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.setup(button2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.setup(speedHiLo, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.setup(limitLeft, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.setup(limitRight, GPIO.IN, pull_up_down=GPIO.PUD_UP)

"""

一般配置及声明

"""

global lospeed, hispeed, startTime, endTime, limitFlag, timeVar, accelTime, decelTime,decelPulseCoun, flag1, highPulse, direction, limitPulseCount

flag1 = 0               # variable for loop control

timeVar = 0             # variable for loop control

direction = 0           # variable for storing direction pin value

endTime = 0             # variable for storing wait time between pulses whilst slowing to a stop

limitFlag = 0           # signals that limit has been reached and flow must retuen to main loop

highPulse = 0.000005    # time for pulses to go high to trigger driver

startTime = 0.001       # time between pulses at start of movement

hispeed = 0.0005      # time bewtween pulses at full speed

lospeed = 0.0009         # time between pulses

accelTime = 0.000003    # amount of time to decrement between accleration pulses

decelTime = 0.00005    # amount of time to increment between deceleration pulses

decelPulseCount = 50   # number of pulses sent during deceleration, 1/4 rev for current setup

limitPulseCount = 5  # number of pulses sent to the driver when a limit is tripped, 1/2 rev for current setup

timeVar = startTime

def limit():  # this routine is like the ramp down routine

        GPIO.output(enable, 0)

        print("Limit Hit")

        time.sleep(0.015) #wait for a bit

        if(GPIO.input(limitLeft) and GPIO.input(limitRight) == 1): # debounce

                return 

       

        GPIO.output(enable, 1) # enable driver for movement

        global direction

        global timeVar

        global limitFlag

        global flag1

       

        flag1 = 0 # disable the flag so u dont call rampDown upon exiting limit()

        timeVar = endTime  #initialize time variable with ending time bewteen pulses

       

        direction = not direction

        GPIO.output(directionPin, direction)

        limitFlag = 1 # set this flag so that the rampUp routine 'breaks' and jumps back to Main()

        x = limitPulseCount

        while(x > 0):               

                x = x - 1

                GPIO.output(step, 1)

                time.sleep(highPulse)

                GPIO.output(step, 0)      

                time.sleep(abs(timeVar))

                       

                if(timeVar < startTime):

                        timeVar = timeVar + decelTime

"""                       

        global direction

        print("Limit Hit")

        time.sleep(0.015)

        if(GPIO.input(limitLeft) and GPIO.input(limitRight) == 1): # debounce

                return

        timeVar = startTime

        flag1 = 0

        direction = not direction

"""              

"""

主循环

"""

while(1):

        GPIO.output(enable, 1)

        if(GPIO.input(limitLeft)== 0): # if a limit input goes LOW, call the limit function

                        GPIO.output(enable, 0)

                        limit()

        if(GPIO.input(limitRight)== 0): #if a limit input goes LOW, call the limit function

                        GPIO.output(enable, 0)

                        limit()

        if(limitFlag == 1):

                        limitFlag = 0

                        

               

        GPIO.output(directionPin, direction)

        GPIO.output(step, 1)

        time.sleep(highPulse)

        GPIO.output(step, 0)

        time.sleep(abs(timeVar))                               

        if(timeVar > endTime):

                timeVar = timeVar - accelTime

       

        if(GPIO.input(speedHiLo) == 0):

                endTime = lospeed       #if lo-speed selected, initialize endTIme with lo-speed wait time

               

        else:

                endTime = hispeed

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分