4.2. Exercise: Pneumatic Valves and Actuators

Objective: learn the basic elements of a pneumatic actuation system and program a movement sequence.

Deliverable: short 10-20 second video posted to the blog of a single DOF actuated structure applying forces to an object or material.

4.2.1. Background

Pneumatic actuators use compressed air as an energy source to produce movement. We will use linear pneumatic cylinders in which a piston enclosed in a cylinder can be extended or retracted using air pressure directed into either end. The air flow will be controlled by electric valves driven from an Arduino with an ULN2803 driver shield. There are different configurations of valves we might use depending on whether the motion needs to be bidirectional and whether there is a passive retraction force.

Our specific actuator cylinder is the Clippard UDR-17-6. For this exercise, we will use two pairs of Clippard EC-2M-12 valves to drive a single cylinder, one pair at each end. Each pair includes one valve to supply pressure and one valve to empty the chamber to atmosphere. This allows complete control of partly or completely filling each side of the actuator, at the cost of needing four separate control channels. The valves are fast enough to pulse with PWM for rate control, albeit with a very audible chatter. With additional external position sensing, it is possible to apply closed loop control in software to control the actuator to any intermediate position.

The force developed on a piston is the air pressure multiplied by the area. The inside face of our piston has a diameter of 1 1/16 inches (27 mm), for an area of 0.887 square inches (573 mm^2). With a modest supply pressure of 40 psi (2.75 bar), the actuator can supply an extension force of 35 lbs (158 N). The retraction force is somewhat lower because the rod occupies some of the outside face. In the context of our lab resources, this is a relatively large force, and available at a relatively large speed, so these actuators can deliver substantially more power than our servos, DC motors, or stepper motors.

However, he primary reason for us to use pneumatics is the nature of the resulting movement. Air is a compressible fluid and so the pneumatics are intrinsically compliant. These de-emphasizes precise movement and rigidity in favor of a more springy and organic quality. In this context, the hiss and clatter of pneumatic actuation becomes part of the aesthetic.

4.2.2. Resources

The Arduino driving the valves can run the supplied ValveControl Arduino Sketch. This example control program can apply either closed-loop or open-loop valve control. It be configured for the specific actuator, valve, and optional feedback sensor arrangement.

The sketch acts upon command messages received over the USB serial port. The simplest means of testing is to use a terminal program to directly issue commands to the controller. This could simply be the console within the Arduino IDE, or a separate terminal program such as minicom or CoolTerm.

4.2.3. Python Scripting

The more flexible way to drive the system is to use a Python script or Max patch on a separate computer to send commands to the embedded controller. A self-contained Python sample appears below. It does require a pySerial installation to communicate with the Arduino.

This example uses only open-loop ‘speed’ commands to set the fill and empty rate on the two pairs of valves. The primary building block function for the sequence is set_flow().

pneumatics_exercise.valve_sequence.set_flow(port, flow, extend=1, retract=2, pause=None, verbose=False)[source]

Issue an open-loop set of flow commands for a valve pair attached to an actuator. Returns immediately or after an optional delay. No return value.

Parameters:
  • port – serial port stream handle
  • flow – net flow from -100 full retraction rate to 100 full extension rate
  • extend – channel number for extension valve pair
  • retract – channel number for retract valve pair
  • pause – time in seconds to delay after issuing command
  • verbose – flag to enable printed debugging output
Returns:

None

The full script is below. It can also be directly downloaded from the Python source tree on the course site.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
#!/usr/bin/env python
"""\
valve_sequence.py

Play back a sequence of pneumatic valve activations using a serial port
connection to an Arduino running ValveControl.

Copyright (c) 2015-2017, Garth Zeglin.  All rights reserved. Licensed under the
terms of the BSD 3-clause license.

"""
################################################################
# Import standard Python 2.7 modules.
from __future__ import print_function
import os, sys, time, argparse

# This module requires a pySerial installation.
#  Package details: https://pypi.python.org/pypi/pyserial,
#  Documentation: http://pythonhosted.org/pyserial/
import serial

################################################################
def set_flow( port, flow, extend=1, retract=2, pause=None, verbose=False):
    """Issue an open-loop set of flow commands for a valve pair attached to an actuator.
    Returns immediately or after an optional delay.  No return value.

    :param port: serial port stream handle
    :param flow: net flow from -100 full retraction rate to 100 full extension rate
    :param extend: channel number for extension valve pair
    :param retract: channel number for retract valve pair
    :param pause: time in seconds to delay after issuing command
    :param verbose: flag to enable printed debugging output
    :return: None
    """


    if flow > 0:
        ext_command  = "speed %d %d" % (extend, flow)
        ret_command = "speed %d %d" % (retract, -100)
    elif flow == 0:
        ext_command  = "speed %d %d" % (extend, 0)
        ret_command = "speed %d %d" % (retract, 0)
    else:
        ext_command  = "speed %d %d" % (extend, -100)
        ret_command = "speed %d %d" % (retract, -flow)

    if verbose:
        print ("Issuing %s, %s" % (ext_command, ret_command))

    port.write("%s\n%s\n" % (ext_command, ret_command))

    if pause is not None:
        time.sleep(pause)

    # ignore any sensor feedback
    port.flushInput()

    return

################################################################
# The following section is run when this is loaded as a script.
if __name__ == "__main__":

    # Initialize the command parser.
    parser = argparse.ArgumentParser( description = """Scripted sequence of pneumatic valve activations using the ValveControl controller on an Arduino.""")
    parser.add_argument( '-v', '--verbose', action='store_true', help='Enable more detailed output.' )
    parser.add_argument( '--debug', action='store_true', help='Enable debugging output.' )
    parser.add_argument( '-p', '--port', default='/dev/tty.usbmodem1411', help='Specify the name of the Arduino serial port device (default is /dev/tty.usbmodem1411).')

    # Parse the command line, returning a Namespace.
    args = parser.parse_args()

    # Open the serial port, which should also reset the Arduino
    print("Connecting to Arduino.")
    port = serial.Serial( args.port, 115200, timeout=5 )
    if args.verbose:
        print("Opened serial port named", port.name)

    print("Sleeping briefly while Arduino boots...")
    time.sleep(2.0)

    # throw away any extraneous input
    print("Flushing Arduino input...")
    port.flushInput()

    # Begin the motion sequence.  This may be safely interrupted by the user pressing Control-C.
    try:
        print("Beginning movement sequence.")
        set_flow(port,   50, pause=2.0, verbose=args.verbose)
        set_flow(port,    0, pause=2.0, verbose=args.verbose)
        set_flow(port,  -50, pause=2.0, verbose=args.verbose)
        set_flow(port,    0, pause=2.0, verbose=args.verbose)
        set_flow(port,  100, pause=2.0, verbose=args.verbose)
        set_flow(port,    0, pause=2.0, verbose=args.verbose)
        set_flow(port, -100, pause=2.0, verbose=args.verbose)
        print("Movement complete.")

    except KeyboardInterrupt:
        print("User interrupted motion.")

    # Close the serial port connection.
    port.write("stop\n")
    port.close()