Mit PDF
Mit PDF
Mit PDF
..iASSAC1HUSETTS INSTITUTE
OF TECHNOLOGY
AUG 31 1995
I iaaARIES Didrar rnr
Sensor Design and Feedback Motor Control For Two
Dimensional Linear Motors
by
Douglas Stewart Crawford
Abstract
A high speed flexible automation system based on a two dimensional Sawyer linear
motor is being developed at MIT. These motors are commercially available as stepper
motors with no means of directly controlling the commutation current. In this thesis,
I designed a two axis position and velocity sensor which measures the translational
and rotational movements of the motor. Using this sensor, a digital closed loop
controller was also implemented using a high speed digital signal processor. Under
servo control, the motor's dynamic performance can be optimized to produce greater
force and increased damping at high velocities.
1 Introduction 12
1.1 Industrial Application .......................... 12
1.2 Linear Motor Operation ......................... 14
1.3 Advantages of Feedback Motor Control . ................ 20
1.4 Thesis Content .............................. 21
4 Position Detection 46
4.1 Signal Processing Techniques ...... 46
4.1.1 Demodulation and Calibration. 46
4.1.2 Digital ATAN Processing . . . . 47
4.1.3 Inductosyn to Digital Converter 48
4.1.4 DSP Based Tracking Converter 50
4.2 Relative Position Sensing Performance 54
4.2.1 Experimental Results ...... 54
4.3 Summary ................ 56
6 Conclusion 78
A Modeling Parameters 80
A.1 Calculation of Sensor Core Reluctance . . . . . . . . . . . . . . . . . 80
A.2 Air Gap Reluctance ............................ 82
A.3 Estimation of Coil Inductance . .................. ... 84
B Sensor Fabrication 85
C System Hardware 89
C.1 Connection Diagrams ........................... 91
D Simulation Codes 98
Introduction
Currently, there are no two dimensional linear motors in production which include
an integral feedback device. Without feedback, the linear motor runs in stepper mode,
or open loop mode. My project is concerned with designing a feedback sensor which
measures the position and velocity in both the X and Y directions. Rather than
redesign an existing motor, my thesis will focus on sensor development as a method
of improving motor performance.
The sensing system is mounted directly to the motor itself, which consists of a
separate sensor for the X axis the Y axis. Under this setup, the current state of
the motor can accurately be measured. This feedback information can then be used
to improve the dynamic characteristics of the existing motor. In order to better
understand the dynamics of both the forcer and the sensor, it is important to have
some knowledge of how linear motors operate.
this respect, the linear motor is very similar to a rotary motor. The rotor is analogous
to the linear motor core and the stator is analogous to the platen. Also, position in
a linear motor can be substituted for angle in a rotary motor. Likewise, force can be
substituted for torque.
By supplying the appropriate currents to phase A and phase B of the motor,
linear motion in either direction can be produced. Figure 1-2 also explains how the
motor travels when it is driven using cardinal steps, or 1 pitch increments. In the
first step, the current in phase B is applied producing enough magnetic flux to cancel
the flux generated by the permanent magnet in poles 5 and 7. Under this magnetic
field, the motor will align itself such that the magnetic reluctance is minimized, which
corresponds to the first step in figure 1-2. Next, the current in phase B is switched
off and the current in phase A is switched on. This causes the motor to move a1
pitch increment to the left. For another 1 pitch increment, phase A is switched off
and phase B is switched on in the opposite direction. Finally, to travel one complete
pitch phase B is switched off and phase A is switched on in the opposite direction.
In practice, however, the currents are not switched on and off. Instead, phase A
is supplied with a sine current and phase B is supplied with a cosine current. With
this type of control, the motor can be micro stepped along the platen. Theoretically,
the resolution of the sine input will determine the resolution of the motor within
one pitch. It is important to note that motor operation is cyclical for each pitch.
Therefore the average motor velocity is proportional to the frequency of the sine and
cosine driver currents.
Up to now, no mention of how much force the motor can generate has been made.
Figure 1-3 illustrates a simple model for one motor tooth.
WE pitch
Using this model, a simple equation for the energy stored in the air gap can be
written as the following:
E, = 2 I2 (I.
where (Dis the flux through the motor tooth, and R, is the varying air gap reluc-
tance. A simple approximation of the air gap reluctance shows that it varies almost
sinusoidally with the platen pitch. A function for IR, therefore is:
where R, is the average air gap reluctance, and kg is the air gap reluctance amplitude.
The tangential force which is produced by displacement is
F= -
ME _-
1 2 BVR9 (1.3)
Ox 2 Ox
The sawyer linear motor is designed such that the flux path through the motor core is
premagnetized with a permanent magnet. One of the reasons for using a permanent
magnet is to reduce hysteresis losses which occur when the flux path changes from
forward to reverse. Since, the maximum flux generated by the electric coil is designed
to be equal to the flux generated by the permanent magnet, the resulting flux path
through the motor core flows in one direction. Assuming the Reluctance through the
magnet is negligible, the resulting flux through the tooth is
Smagnet coil
mg= + (1.4)
2 2
where
Substituting (1.4) into equation (1.3) results in the force output for one tooth which
can be written as
F = -(4magnet + ()coi) 7r kg
s2 (1.5)
4p p
By summing equation (1.5) for each tooth, the total force per axis can be calculated.
Rather than re-derive the equations, the reader is referred to previous thesis' con-
taining detailed derivations [13, 21]. After summing the separate equations, the force
relation simplifies into
p p
where 'magnet is the flux caused by the permanent magnet, and ýcoil is the flux
generated by the sinusoidal control currents with frequency w. An important quantity
is ?P or the lead angle which is expressed as:
t -W=
- (1.7)
The lead angle can be thought of as the phase advance of the magnetic field relative to
the instantaneous motor position. It is important to note that the force is maximized
when this quantity is equal to 900.
The influence of the lead angle is better illustrated in the force vs. displacement
curve shown in figure 1-4. As the graph points out, the maximum force is reached
when the motor is displaced 1 pitch or 90 degrees. Beyond that, the force decreases
and the motor will seek a new equilibrium position. This brings up the important
point of stalling. When the motor is run, the driving currents will attempt to produce
a magnetic field which is 90 degrees ahead of the instantaneous motor position. If
this angle is less than 90 degrees the motor won't be producing it's maximum force.
However, if the lead angle is greater than 90 degrees, then the motor will seek a
different equilibrium position and loose synchronism with the controller. This loss of
synchronism leads to stalling and causes the motor to stop abruptly. It should be
noted, that the optimal lead angle of 90 degrees is only true when the motor is static
or traveling at low speeds. When the motor is moving at high speeds, other effects
Linear Region
such as saturation become significant and can change the force vs. displacement
curve.
All of the force generation principles and dynamic equations also carry over to the
two dimensional case. A two dimensional forcer is basically made of one dimensional
cores arranged along the X and Y axes. Figure 1-5 illustrates a cut away view of a
two dimensional linear motor and platen. As mentioned earlier, the force from the
air bearing opposes the force from the hold down magnet in order to produce the
necessary air gap. The main difference between the single dimension case is that now
the platen is a grid of cubes in a waffle pattern, rather than an array of ridges. In
all other aspects, the two dimensional forcer operates in the same manner as a one
dimensional motor.
Once the basic theory of operation is understood, the next step is to investigate
how motor performance can be improved. One approach is to optimize the physical
geometry of the motor cores. On the other hand, significant gains in performance can
also be realized by optimizing the control currents which are used to drive the motor.
Although there are open loop optimizations which can be used to achieve small gains,
the optimal motor performance can only be achieved through closed loop, feedback
control.
Figure 1-5: Layout of a two dimensional linear motor
This chapter presents several different types of linear displacement transducers and
the design methodology used to develop the inductive sensor. Finally, the modified
dual core sensor design is described along with it's improvements in performance and
robustness.
This type of system could be applied to two dimensional linear motors and in
fact, an attempt to do so has already been made. Reference [16] describes how to use
the linear motor cores themselves as the capacitive sensing elements. The problem
with capacitive sensors is their extreme sensitivity to environmental changes. One
might believe that the . in equation 2.1 is always constant. However, this is not the
case. Small changes in humidity or temperature can significantly affect this value.
Sensor
fiberoptic bundl
Platen
d~B
V = -N (2.2)
dt
One particular sensor which can be used with one dimensional linear motors is
the Inductosyn made by Farrand Controls Inc. The Inductosyn consists of a fixed
scale and a moving slider. Figure 2-3 illustrates the arrangement of the slider [3]. A
primary AC voltage is applied to the scale and two secondary coils are wound around
poles on the slider. The reluctance between the poles on the slider and the scale varies
sinusoidally, and each pole is offset from one another by 1/4pitch. This produces two
.11,iul
Scale
I I
Vout
Secondary coil
rr
K l
U Li
Platen
In order to obtain the maximum reluctance change, the secondary cores are me-
chanically displaced 1 pitch from each other. However, this design can not distinguish
which direction the sensor is moving. By using an offset of 1 pitch, or 900, it is pos-
sible to measure both displacement and direction. An inductive sensing core which
uses this arrangement is described in figure 2-5 [5]. In this modified core, the central
legs (2 and 5) are mechanically displaced by an integral number of pitches plus 1
pitch. In other words, legs 2 and 5 have a 900 phase offset. Also, each of the side
Primary Coil
legs (1 and 3)(4 and 6) are physically offset from the corresponding central legs by
an integral number of pitches plus 1 pitch. Unlike the first version, the new core is
fabricated from laminated magnetic steel sheets. This reduces eddy current losses,
or circulating magnetic flux lines into and out of the page. Eddy currents can cause
significant losses in the overall flux path and diminish the sensor's sensitivity.
The sensor acts like a linear motor in reverse. Rather than suppling a signal
to generate motion, the movement of the sensor will create an induced voltage in
the secondary coils. The underlying operating principle is the same. Position is
determined by measuring the reluctance change between the sensor teeth and the
platen teeth. A close examination of the sensor reveals that legs 1 and 2 are in phase
with each other, but are 1800 apart from leg 2. Likewise, legs 4 and 6 are also 1800
apart from leg 5. When one of the central legs, for example leg 2 is perfectly aligned
with the platen teeth, the induced voltage in the corresponding secondary coil will
increase. If leg 2 leg is 1800 out of alignment, more flux will travel through legs 1
and 3, and the the secondary voltage will decrease. The total flux path is always
conserved.
0.4
S0.2
0
-0.2 :
-0.4
0 0.002 0.004 0.006 0.008 0.01 0.012 0.014 0.016 0.018 0.02
time (sec)
wc is the frequency of the carrier or primary, p is the length of one pitch, and x is the
sensor position within one pitch.
The first step in recovering the position, is to synchronously rectify and filter the
secondary output signals. Synchronous rectification, or demodulation, is performed
mathematically by multiplying the output signal with the primary input, and then
filtering the result as shown in figure 2-7. If, for example, one of the secondary outputs
is combined with the reference, the resulting signal takes the form of:
A simple low pass filter will eliminate the high frequency term and allow only the
envelope of the input signal to pass.
Input
Signal Output
Low Pass
IhReference
Signal
Filter
Synchronous demodulation works well as long as the phase shift between the input
signal and the reference is small and constant. In practice, however, the secondary
output signals have a phase shift consisting of a constant term, 0,, and a variable
term, 0,, which depends on the angular position within one pitch, 1. p The variable
part of the phase shift, 0,, is also cyclic with each pitch. In other words, the secondary
signal has a variable amplitude and phase shift, both of which are repetitive with each
pitch. As mentioned earlier, due to the physical 1 pitch offset between the legs on the
sensor core, the output envelope of one secondary coil follows a sine while the other
follows a cosine. The same generalization can be applied to the variable phase shift
term, 0,, by including a f offset in the function for one of the secondary coils. It is
not necessary to know exactly how 0, varies with position. The only key concerns
are that the function varies cyclically with each pitch. Taking into account the phase
shift, the sensor signals can be more accurately described as:
The constant part of the phase shift is on the order of 150 to 300 and is caused by
the inductance of the primary coil. The constant term, however, can be compensated
with phase shifting circuitry. The variable component is much smaller, on the order
of 1' to 30, yet it can still produce errors in the recovered position signal and is not
easily corrected using simple circuits.
To overcome this, an alternate method of demodulating the input signal is used
which compares the input signal to itself rather than with the reference. In practice,
the multiplier block shown in figure 2-7 is usually implemented in an analog com-
parator. The comparator does not actually multiply the signals, instead it uses the
reference signal to trigger either positive or negative amplification, thus producing
a rectified output. The problem with using the input signal as the trigger is that
the envelope of the input may go to zero in which case there would be no signal to
trigger the comparator. This is not a problem with our sensor signals, since the en-
velope always has an offset, and never goes to zero. Therefore, by using the sensor's
secondary output as both the input and the trigger, the signal can be demodulated
without phase shift errors.
Once the output has been demodulated and filtered, the resulting signal for each
of the secondary coils is an offset sine wave described by the equations below.
It is important to understand why the offset terms are apparent in the output. To
answer this question, we should take a closer look at the underlying operating principle
of the sensor. The envelope of the secondary output is proportional to the reluctance
of the flux path which travels through the secondary coil. The predominant factor
which influences this reluctance is the air gap. As previously mentioned in chapter
one, equation (1.2) the air gap reluctance contains a constant term which is related
to the nominal air gap distance, and a variable sinusoid term which is related to the
moving sensor teeth.
described. The important difference, however, is that now the physical offset between
the central teeth of one core corresponds to an integral number of pitches plus 1 pitch,
rather than ¼pitch which was previously used. The second core is also constructed
in the same manner. Each separate core is then positioned within the sensor housing
such that the relative shift between each of them is an integral number of pitches
plus ¼pitch. Therefore, the resulting signals from the secondary cores once they have
been demodulated can be written as:
V2 =-o+ ap+
=
.i
V2o+Vap+V2asin x )--
r)
V3 = V3o+vap + 3. cos
The sensor should be designed such that V10 is equal to V2o and Via is equal to V2a.
Also, V3o should be equal to V40 and V3 a should be equal to V4 a. This is usually
accomplished by matching the number of turns and winding impedances for each
secondary coil. Assuming this relation is true, the signals V2 and V1 can be subtracted
to produce a new signal which does not contain the offset voltage. A similar algorithm
can be performed for signals V3 and V4 producing:
1r = (2Vla sin -)
One important assumption is that the air gap fluctuations affect all four sensor
outputs in the same manner. This assumption is based on the fact that all of the cores
are permanently mounted in a rigid housing and each sensor tooth, initially, should
experience the same nominal air gap. It has been demonstrated through experiments
that slight variations in the air gap do in fact produce similar responses from each
of the secondary coils. Nevertheless, it is possible that the sensor could tilt in such
a way that part of the sensor would have a smaller air gap, and thus, ,gapwould
be present in the output. Even in this case, however, the amplitude of Vgap will be
partially reduced.
2.3.1 Signal Processing and Calibration
The dual core sensor design is similar to a previous design by Sawyer [17]. One of
the main differences between the two is that, in the Sawyer design, the outputs of Vi
and V2 are connected in series to form one signal rather than having each output pass
through a separate demodulator. One problem with Sawyer's method is the phase
shift in the carrier portion of the secondary signal, (2.7). Since the carrier phase
shift, 4,, will not be exactly the same for Vi and V2 , errors will arise if the signals are
combined directly. A better approach is to demodulate each signal separately and
subtract the results after demodulation.
Another problem with Sawyer's design is that the signal parameters, V1 o and
V2 o are assumed to be exactly equal, and likewise, Via and V2a are assumed to be the
same. In practice, it is extremely difficult to have these parameters be perfectly equal.
Many factors can cause this imbalance, including mismatched winding impedances,
or errors in fabrication due to manufacturing tolerances. Using equations (2.12) and
considering the parameter mismatches, the combined signals can be calculated. After
demodulation, the signals corresponding to V1 and V2 , and V3 and V4 are subtracted
producing:
V1 = Vo - Vo + (a + V2a)sin -
27rx
V2 = V3 o - V4 o + (V3 a + V4a) cos - (2.14)
p
Any remaining offsets and amplitude imbalances can be removed online by adding
a fixed constant, a, and multiplying by a fixed factor, P. The values for these adjust-
ment parameters can be determined off line from a calibration run in which the motor
travels at a very low speed so as not to produce a large attractive force between the
motor and platen. During calibration, the sensor signals are stored in a data buffer
in the computer. Next, the offsets and amplitudes are measured in order to calculate
the adjustment factors: a and 0. For example, assuming the signals from V1 and V2
are being calibrated, the value of a would be V2 , - V1o, and the value for f would be
(V
+a)A diagram of the signal conditioning circuitry for one sensor is shown in
figure 2-9.
Figure 2-9: Signal Conditioning Circuitry for the Dual Core Sensor
After calibrating the other two signals, V3 and V4 , in a similar manner and sub-
tracting the results, we are left with:
27rx
V1 = sin-
27rx
V2= cos (2.15)
p
Once the signals are in this ideal format, the position and velocity are readily obtained
using one of the signal processing techniques presented in chapter 4.
V2 = V 2 +V 2aE knsin
n=l
(n- P
--r
V3 = 3o + V3aEkn cos n-
After subtracting V2 from V1 and V4 from V3 , we are left with two new signals which
contain only the odd harmonics. For simplicity, all of the offset and amplitude terms
are considered to be equal.
00 27rx
Vinew = E knsin n
n=ln=odd P
00 27rx
V2new = E k cos n (2.17)
n=1 n=odd p
In practice the elimination of the even harmonics is not precise, however, this does
illustrate that even further refinement of the output signal is possible by using two
sensor cores rather than just one.
2.4 Summary
In general, inductive sensors are robust to environmental changes and provide very
high resolutions. Also, since the inductive sensing design is non contacting and not
dependent on a fixed scale, it is particularly well suited for two dimensional linear mo-
tors. By using a pair of inductive cores, the sensor can be made relatively insensitive
to vertical deflections, there by improving it's robustness and performance.
Chapter 3
This chapter presents the mathematical model of the sensor, and describes the indi-
vidual sensor cores in more detail . Also, some simulation results are included and a
design methodology for selecting certain design parameters is also presented.
Rsl
Rpb
all modeled as a reluctance. lcr and Rpb are the reluctances for the core body and
platen base respectively. R,1 includes the reluctance for one of the central legs and
the corresponding sensor teeth. Since each pair of the side legs are in phase with each
other, the reluctances for each of them are lumped together into R,1. This circuit
representation assumes that all of the magnetic flux lines pass within the steel core
and the platen. In reality, however, this is not the case. The flux path will leak
outside of the core boundaries and follow the path of least reluctance. However, the
core is designed such that these losses are minimized. All fringing and leakage effects,
therefore, are neglected in the model and simulations.
By far, the majority of the reluctance is produced by the air gap. As noted earlier,
the air gap reluctance varies sinusoidally with position, and R, and R3 are 900 out
of phase. Since reluctances R2a, 7 7
and R4a,
2b IZ4b are in phase with each other, they
are lumped together in 7 2 and R4. All of the reluctance calculations are performed
in the appendix. By analyzing the circuit, the following relations can be made:
cl = cl 1 sl p rim ary
(3.3)
whe + re + + R2
sre
where
Mprimary = magneto motive force due to current in primary coil
(primary = total magnetic flux induced by primary coil
IcD = magnetic flux in central leg
Once the current is known, the magneto motive force can be calculated and is directly
proportional to the current times the number of turns, Nprimary.
Now, using the relations in equation (3.3) the flux through each of the central legs can
be calculated. It is important to note, that the complete reluctance path, equation
(3.2), remains relatively constant with changes in displacement. This suggests that
the rate of change of flux in the primary, Aprimary also remains relatively constant.
The total reluctance in the central and side legs, however, changes dramatically with
displacement, and the voltage induced in each secondary coil is proportional to the
rate of change of magnetic flux through the central legs.
One may think that the current in the secondary coil would also produce a magneto
motive force in the central leg. However, the outputs of the secondary are connected
to a high impedance voltage amplifier, which results in extremely small currents.
Because the currents are so small, energy losses due to the resistance in the secondary
coil and the coil impedance are neglected. A comparison of the model with the actual
sensor will show that these assumptions are justified.
All energy losses such as eddy currents and hysteresis have been neglected, since
including these effects is beyond the scope of this thesis. The laminated construction
of the sensor core, however, dramatically reduces eddy current losses. Saturation
losses are also not included into the model since the peak flux densities produced by
the primary coil are relatively small. Therefore, the relationship between flux density
and magnetic field strength in the core can be assumed to be linear.
-_
--00 Simulated Data
... Experimental Data
-YY
: : :: Frequency (Hz):
102 le 10l
Frequency (Hz)
o00
50
.......... ...
102 102 O4 10
l
s
Frequency (Hz)
coil inductances are dictated by the number of turns on each winding. The coils are
wrapped such that the maximum number of turns can fit within the small confines of
the sensor geometry. For the prototype sensor, the primary and secondary coils have
100 and 70 turns respectively. The values for the resulting inductances are estimated
in the appendix.
For each test sample in the frequency response, the sensor is static and remains
fixed with respect to the platen. All other parameters are held constant. As the graph
shows, both the experimental and simulated cases exhibit similar frequency responses.
This would suggest that the model is an accurate description of the physical system
at least over the selected frequency range.
One method of characterizing how sensitive the output is to position changes is
to define a parameter, y, which can be expressed as:
where V,a and Vin are the maximum and minimum values of the envelope as shown
in figure 2-6. One of the goals of this design is to maximize the sensitivity for a given
carrier frequency and sensor thickness.
The first major design parameter is the carrier frequency, or the frequency of the
primary driving voltage. For an AM signal, the carrier frequency should be about
10 times the highest frequency in the envelope [7]. As a result of this, the frequency
content in the envelope will be well below the cutoff frequency in the synchronous
demodulator. The estimated maximum speed of the motor is 1000 pitch/s, which
corresponds to a 1000 Hz frequency in the output waveform from the sensor. Thus,
a carrier frequency of 10lkHz was selected as a first choice. The selection of carrier
frequency can also affect the sensitivity as defined by equation (3.7). A plot of
sensitivity vs carrier frequency is provided in Figure 3-3.
0.45
0.4 .. .entaD
.............. l ata.....
....................
...
...........
Experim SSimuled Data .....
0.35 -- ·
0.3
. .. .. .. ..
90.25
...
..
..
..
....
....
..
...
-..
..
........: ..:
....
....
........
..
................
:
c 0.2
.:
..:
....
.:.:.:
...
.i .: i ".
0.15 ....
..
..
..
..
..
..
..
..
....
.......
....
...!
.} ..
..
...
.:
-..
:..
..
..
..
.......
... ...... "
0.1 .. ... ...
.. ... ..... .....
...
.
...... ......
:~. . . . . .. ....--.....i ..
"..............
.•
. .
. ...
. . .
Ii i
-- · o ·
0.05
n
0s
4
10 10
Carrier Frequency (Hz)
In general, the simulation results exhibited much higher sensitivities. This may
be due to the fact that magnetic losses were neglected in the model. The effects of
fringing become increasingly important especially around the area between the platen
teeth and the sensor teeth. If flux fringes around the edges of the central legs directly
to the platen, the overall sensitivity will be reduced.
Another generality obtained from the graph is the decrease in sensitivity with
increased carrier frequency. At 30 kHz and 50 kHz there is a sharp decline in the
sensitivity, especially in the experimental data. Again this may be due to neglected
energy loss terms. In general, the power dissipated due to eddy currents is a function
of the material properties, the core volume, the peak flux density, and the excita-
tion frequency. Therefore, it is not unusual to expect higher energy losses at higher
43
frequencies.
Based on the previous experiment, 10 kHz remains a good choice for the primary
driving frequency. In practice, however, the position sensing bandwidth depends also
on the cutoff frequency of the demodulator and the speed of the position detection
algorithm which will be discussed in chapter 4.
Up to now, sensor performance along only one axis has been considered. In order
to function on a two dimensional platen, the sensor must be independent to motion
orthogonal to it's sensing core. Any motion normal to the sensor, for example along
the Y axis, should not produce a detectable change in the output. This imposes
an absolutely critical requirement that the thickness of the sensor, t, be equal to an
integral number of pitches. Under this constraint, the overlap area between the sensor
teeth and the platen teeth remains constant and thus the reluctance does not change
when the sensor moves sideways.
A simulation of sensor thickness vs sensitivity is presented in figure 3-4. One
simple method of modifying the core thickness is to split apart the laminations of a
larger core. Therefore, the simulation only uses sample thickness that are an integral
number of pitches and an integral number of laminations. The speed at which each
sample is measured is 100 pitch/s. All other parameters are kept constant. As
expected, the sensitivity increases with thickness. However, the sensitivity is only
weakly dependent upon the thickness when compared with the dependency on the
carrier frequency. Since reducing the sensor weight is also a design goal, it does not
make sense to use a large sensor core just to increase sensitivity.
Due to the difficulty in fabricating different laminated cores the simulation could
not be compared with real data. However, based on the model, a core thickness of
0.28in., or 20 laminations was chosen as a good compromise between sensitivity and
size.
__n_
C
4)
U,
5 10 15 20 25 30 35 40
Sensor Thickness (laminations)
3.3 Summary
In general, inductive sensors are robust to environmental changes and are capable
of providing very high resolutions. Also, since the sensor is non contacting and
not dependent on a fixed scale, it is particularly well suited for two dimensional
applications. A basic model was implemented to simulate the sensor's dynamics and
to optimize various design parameters.
Chapter 4
Position Detection
The purpose of this chapter is to present and select a robust and accurate algorithm
to convert the output from the secondary coils into useful position and velocity in-
formation. Three methods are described: digital arctangent processing, Inductosyn
to digital conversion, and a software based digital tracking converter. Also, sensor
performance and accuracy are compared for each algorithm.
U,
U,
a-
.2
12
time(sec)
V2 = V sin - sin(wt)
I
V
V2
the true sensor position within one pitch, x. Basically, the converter uses a feedback
loop to force i equal to x. The first step in the algorithm is to multiply V1 by cos 2
and V2 by sin 2__p and then subtract the two signals producing:
This can be simplified into the following equation which is the error signal for the
servo loop.
E = sin(wt) sin (2r 2p (4.4)
This error signal is then synchronously demodulated and the servo loop drives the
signal to zero. Since the error signal is also integrated over time, all steady state
errors between the actual position and the digital position will go to zero. Thus, after
demodulating, the error signal is:
sin 2- x -2 =0 (4.5)
x=& (4.6)
Therefore the digital output word will track the actual position of the sensor.
Figure 4-3 shows the layout of the related circuitry used in conjunction with
the Inductosyn to converter chip. As illustrated, analog op amps handle all of the
calibration and filter off high frequency noise on the input signals.
The Inductosyn to Digital Converter Chip has several limitations. First of all, The
maximum speed is limited by the maximum tracking rate for the particular converter,
which for our chip was 100pitch/s. A higher speed converter was available but with
a lower resolution. Another problem was that calibrating the signals before demodu-
lation proved to be less effective than calibrating them after they were demodulated
which was done in the arctangent algorithm. Also, since the converter only provides
information for one axis, two separate chips are needed for a two dimensional system.
I@ I1 II
MoI as U
U is
U
U0
isi I
sensor signals are demodulated before being multiplied by the cosine and sine of the
position tracking variable, I. Therefore, equations (4.3) to (4.6) are still valid except
that the high frequency carrier component is filtered off.
At this point, it is helpful to explain the similarity between linear position, I, and
the angular position, 0. Since the sensor only measures the position within one pitch
it is equivalent to measuring an angle within 21r radians. Therefore, 0 = . Often,
expressing the position as an angle simplifies the notation.
A close examination of the feedback path in an Inductosyn chip reveals the fol-
lowing open loop transfer function between the error signal (equation 4.4) and the
angular position, 9 [3]:
G(s) (s) (1 + Ti) (4.7)
E(s) s2 (1+ T2 s)
This system employs a double integrator which forces both the position and velocity
error to zero when the sensor is traveling at constant acceleration. The phase lead el-
ement, (1 + Tis), is used to compensate for the 1800 lag of the double integrator. The
(1 + T2s) element is a low pass filter and is used to remove the high frequency carrier
term. Since the low pass filter is implemented separately in the analog demodulator,
the new open loop transfer function is:
This transfer function can be further broken down to produce the velocity output, 9.
The gains, K, T1, should be selected such that the bandwidth of the transfer
function is 10 to 20 times the bandwidth of the closed loop system. However, the
gains should not be so high as to amplify noise in the input. For the initial design,
a bandwidth of 400 Hz and a damping ratio of 0.707 was selected. The bandwidth
in rad/s is approximately equal to the damping ratio times the natural frequency
for a second order system. Thus, the gains can be calculated by comparing the
characteristic equations of the closed loop transfer function with the general second
order system.
Characteristic Equation s2 + KTls + K
Second Order System s2 + 2(w, + w, 2
where
C = 0.707
w, = 4 4 4 3 rad
K = 44432
T, = 1.591 x 10- 4
(z) = Z- (4.11)
EO(z)
(T) Zz- 1
( T) (4.12)
9(z) z-1
where, K = KT, + KT,
KT,
KT, + KT,
T,= sampling period
From this, the digital algorithm which is implemented in software can be derived.
This routine involves only three sets of multiplication and addition instructions.
Also, the evaluation of the sine and cosine functions (equation 4.3) can be imple-
mented in a high speed look up table. Another advantage of this algorithm is that
the output is continuous and does not require an extra pitch counting step as in the
previous two approaches.
The next section will discuss the relative performance of each algorithm.
To measure the performance and accuracy of the sensor in one dimension, the output
can be compared to a known reference sensor. Figure 4-5 describes the setup used to
compare the inductive sensor output with a Linear Variable Differential Transformer
(LVDT). In each test, the linear motor was commanded to follow a fixed, linear
velocity profile in open loop mode.
The position signal generated using the arctangent algorithm is plotted against
the reference LVDT output in figure 4-6. Likewise, the output from the software
based tracking converter is also plotted against the LVDT signal in figure 4-7. Both
Figure 4-5: Experimental Setup to Test Inductive Sensor Along One Axis
4.3 Summary
Three different approaches to position conversion were investigated and experimen-
tally tested. The advantage of using a software implementation is increased system
flexibility, and the ability to calibrate the sensor signals online within the computer.
In the final version of the digital controller, the tracking conversion algorithm was
implemented to improve computational efficiency. Although the experiments using
the LVDT may be inconclusive, the sensor does show good accuracy, and is definitely
suitable for the task of electronic commutation when the motor is operated under
closed loop control.
0
(n
0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1
time (sec)
.2
C
0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1
time (sec)
b I I I I I I 1 I .
0.4 E
0.3 E
time (sec)
0
0
Time (sec)
v = fdaPAi (5.1)
r
where, vc = commanded velocity
This algorithm has a number of limitations. For example, the smallest increment
for Ai is 1. Therefore, assuming that the D/A frequency is constant, the minimum
velocity that the motor can achieve is, Vmin = Ld. If the D/A frequency is reduced,
lower speeds are possible, but, the output waveform becomes more discretized at
higher speeds which can lead to increased velocity ripple.
Saving only the velocity data, however, does lead to much lower storage require-
ments compared to saving a table of specified motor positions, especially when the
motor is traveling at a constant velocity. Velocity storage is one of the best algo-
rithms when using an integer based DSP processor. However, since the TIC40 chip
is a floating point processor the trajectory generation module was revised to take
advantage of this.
Rather than storing each motor position for every time step, the software calcu-
lates the necessary parameters for a function which is evaluated at every sampling
period. Once the updated position is obtained, the sine and cosine functions can still
be evaluated in a high speed look up table. Since the commanded trajectory is the
actual position rather than velocity, this method does not suffer from the velocity
discretization problem previously described. Another advantage is further reduction
in storage requirements. Since only a few parameters describing the piecewise con-
struction of the position are required, much longer trajectories can be down loaded to
the DSP. The only real disadvantage is that the trajectory function must be evaluated
online at each time step, which increases the number of overall computations. Fortu-
nately, the speed of the floating point processor makes online trajectory generation a
practical alternative.
Currently the software supports constant acceleration and linearly variable accel-
eration profiles. Figure 5-2 plots the commanded position and velocity for a constant
acceleration profile. It is possible to include more complex trajectories, however, the
increase in computational load would have to be considered.
Because of the time critical nature of these tasks, all functions are implemented
completely within the Digital Signal Processor. One of the goals of a good digital
controller is to minimize the time between the input sample and the output command.
This is accomplished by sharing the computation load between both DSP processors.
Figure 5-3 provides an approximate timing diagram and describes which functions
are performed by each processor.
In general, processor A is responsible for operations in the X axis, and processor
B handles all operations in the Y axis. The analog input/output card is connected
directly to processor A, so all information must first be routed through processor A
and then to processor B.
The controlling software also has the capability of reading information for a third
sensor mounted in the X axis as shown in figure 5-4. Using a third sensor, the rotation
of the motor can be calculated along with the nominal X and Y displacements. This
information can then be used to compensate for torque disturbances around the Z
axis of the motor. Unfortunately, this controller could not be tested experimentally
because the required number of amplifiers needed to control rotation were unavailable.
5.3.1 Commutation
Recalling from chapter one, the current supplied to phase A and phase B of the motor
uniquely defines an equilibrium position, x,, within one pitch. These commands are:
2
7rxe 2rxZe
iA = I sin -- , iB =I cos 2 (5.2)
p p
Card
Analow InmarKlnermt
E
D
CPU CY
U
r- r1n
PPU
. B
-1r- r1
C~PUA
- -I
,Opcbmd • ! ..-
- ............... ...
l .. .........
•p ! \"'""'""
:~~~ I "'-..
--
Yaxis X axis
h
admnandI fnr VaIt
Createnew trajectroy for next
DR -ivye nnmnds fnr Y axis
Evaluate sin look up table for
I
imtstep phase A and phase B
Output command signals to D/A Card
Save position variables in buffer
It is important to note that in the above equation, Xe defines the new equilibrium
position that the motor will attempt to reach. Xe does not command the exact motor
position, and should not be confused with x from equation (2.15) which is the actual
sensed motor position.
In open loop mode, the amplitude of the current in, I,, is set to the maximum
rated value, which for the Normag motor is 4 amps, and the argument to the sine and
cosine functions is simply the desired trajectory. Therefore, the updated value of the
equilibrium position, xe, is simply equal to the desired trajectory. Without feedback,
however, the motor may in fact never reach the new equilibrium position and loose
synchronism with the platen.
Under closed loop control, the controller must continuously advance the equilib-
rium position ahead of the actual position in order to produce the required force.
I~,,,,r 'Ll,,,r
I
I
This idea of generating the output command based on the input position summarizes
the principle of electronic commutation. As mentioned in chapter one, the lead angle
is defined as the difference between the equilibrium position, which is defined by the
current through phase A and B, and the actual position of the motor. Therefore, the
lead angle should be added to or subtracted from the sensed position, x, in order to
produce a positive or negative force. It is important to note that the lead angle is
only one of the variables which influences force generation. The current amplitude,
I,, can also be regarded as a control output.
00
No. of Samples (fs = 5KHz)
Figure 5-5: Position Response to a Step Input with a Constant lead Angle
Two key characteristics can be obtained from figures 5-5 and 5-6. The first, and
most obvious, is that higher lead angles result in higher maximum velocities. This
would suggest that the motor can produce more force at high speed using a larger
lead angle. The second important observation is that at low speeds high lead angles
w
.,3
0.
Figure 5-6: Filtered Velocity Response to a Step Input, Constant lead Angle
result in a slower initial response suggesting that less force is produced. A strange
anomaly appeared when driving the motor in the reverse direction. The experimental
results suggest that higher lead angles are required in the negative direction in order
to achieve comparable performance as in the positive direction. One explanation
for this may be misalignment of the motor cores, but, further investigation of this
behavior is required.
Clearly, the lead angle should be treated as a control variable in order to optimize
performance. One of the obstacles at this point, however, is that previous attempts
at modeling motor dynamics [13, 2, 9], assume that the ideal force equation (1.6) is
always true. Recalling from chapter one, this equation assumes that the optimal lead
angle is 900 and can be written as:
This relationship, however, is only true for the static case, and is not accurate under
dynamic conditions.
5.3.3 Variable Lead Angle Control
The goal of controlling the lead angle is to maximize or minimize the force output at
any given speed or loading condition without loosing synchronism with the platen.
In a rotary motor, this can be accomplished by generating torque-speed curves for
different lead angles. Then, an optimum lead angle can be selected which produces
maximum or minimum torque for a given speed. A technique for accomplishing this
is presented in Kuo [14], in which he mentions that the near optimum lead angle
depends on the angular speed, w, and if the motor is accelerating or decelerating. In
a linear motor, however, it is much more difficult to measure the force as a function
of velocity compared to measuring the torque speed curve for a rotary motor.
Another important inference from the step responses in figure 5-5 is the extremely
oscillatory response at high lead angles. In fact, much higher speeds could be achieved
than those plotted in figure 5-6, but, the motor would loose synchronism and stall as
soon as it began to decelerate. This behavior is caused by using a constant positive
lead angle throughout acceleration and then suddenly switching to a constant negative
lead angle for deceleration. The switch between an extremely high positive lead angle
to a large negative lead angle produced a substantial jerk on the motor, often times
causing it to stall. Thus, in order to decelerate the motor more effectively, the lead
angle should be decreased from a high positive value to a smaller positive value and
finally to a negative value.
For initial testing, a variable lead angle controller was designed which increased
the lead angle linearly with velocity for acceleration, and likewise linearly decreased
the lead angle with velocity for deceleration. These new functions for acceleration
and deceleration are provided in figure 5-7. The functions graphed in figure 5-7
apply to the case when the motor is traveling in the positive direction. If the motor is
moving in the negative direction, the two curves should be reflected about the velocity
axis. As a general trend, increasing the slope of the acceleration curve improved the
acceleration up to a point. Likewise, decreasing the slope of the deceleration curve
resulting in higher deceleration up to a certain point after which stalling occurred.
Optimized lead Angles for Motion inthe Positive Direction
9!
T
r
00
Velocity (pitch/s)
The above functions are only a first step. In order to obtain better performance,
the influence of the lead angle on force generation must be further researched. Several
authors outline strategies for determining these optimum functions [14, 6]. Danbury
presents a method of calculating the near optimum lead angle at each time instant
based on the slopes of the state plane trajectories [6]. Using this type of adaptive
control the motor force could be optimized for different loading conditions as well as
for different velocities.
U
0..
C.
E
ICL
0
Time (sec)
a,
Time (sec)
motor is commanded at a constant velocity of 100 pitch/s along one axis. Note that
the velocity response is the actual output from the sensor, not the filtered output as
shown in figure 5-9.
Both figures have very large ripple components, however, under closed loop control
the ripple is slightly reduced. Increasing the gain for the velocity error improves
performance up to a certain point, after which, higher gains only lead to increased
ripple. This is due to the fact that at high control gains, relatively small levels of
noise in the sensor can be amplified greatly in the commanded output.
If both phases are driven by pure sine waves, the commanded position and the
actual position may differ by some error which repeats with each pitch. This is
referred to as cyclic error and is a major cause of velocity ripple in two-phase motors.
According to Nordquist, the predominant harmonics present in the cyclic error are
the fundamental and the fourth harmonic [15]. Therefore, in the previous experiment,
we should see the major components of the velocity ripple to be at 100 Hz and at
400 Hz. Figure 5-11 plots the frequency spectrum of the steady state velocity output
when the motor is commanded to move at 100 pitch/s. The spectrum was obtained
using a Fast Fourier Transform performed in MatlabTM. As expected, there are two
Constant Velocity Command-100 p/s, Open Loop Control
peaks in the spectrum at 100 Hz, and at 400 Hz. The relatively large peak at 300 Hz
may be due to additional error harmonics in the cyclic error, or to other unmodeled
dynamics of the motor.
Nordquist presents a way of compensating for cyclic error by including extra har-
monics in the driving signals to phases A and B [15]. For example, rather than
supplying simply a sine to phase A and a cosine to phase B, a new controller could
be developed in which non sinusoidal currents are supplied to the motor. This type
of control, however, is not implemented in the current version of the software, but,
would be an interesting topic for further research.
Pownr Snadcral Density for Onen Loon Resnonse
Figure 5-11: Frequency Spectrum of Velocity Signal (Nominal Motor Speed-100 p/s)
'
0.06
:; :: :: :: :: :· :: :· :: :· ,
·· · ·
· ··
0.02
·· ·
· ·· ·
~-· f: :f :: :: I: :: :i I: i
"' r
-0.04
-0.06
-0. .,U
nA I I I I
... I I I I E
0 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5
Time (sec)
Figure 5-12: Motion along Y axis, sensor reading from X axis (open loop)
0.06 L
j
0.04
:·
t
:i
'''
·' :
- --
-0.02 ::
E
-0.04
-0.06 -
-0. vv
NR , i I I I I
.... I I I
0 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5
Time (sec)
Figure 5-13: Motion along Y axis, sensor reading from X axis (closed loop)
75
X Axis Control-Closed Loop, Y Axis Control-Open loop
Figure 5-14: Motion along X and Y axis, sensor reading from X and Y axis
.15
10
x
0
a. Xaxis-closed looc ..
- Desired trajectory
VII · · · -- ·-- · · ·
Figure 5-15: Motion along X and Y axis, sensor reading from X and Y axis
very high. In servo mode, the currents drop to near zero when there are no external
forces. Using only two sensors, the motor has high stiffness along either the X or
Y axis, but not in the rotational degree of freedom. The solution is to use three
sensors as described in figure 5-4. At the time this thesis was written, three sensors
were fabricated, and the software implementation of the rotational controller was
ready. However, the required number of actuators were not available. Thus even if
the sensors could accurately detect motor twist, the control system would have no
means of actively compensating for it.
5.5 Summary
A digital controller utilizing parallel processing techniques was implemented and suc-
cessfully used to control the linear motor both in open loop and closed loop mode.
Under closed loop control, the motor exhibited improvements in top speed and ac-
celeration, and even larger gains in terms of disturbance rejection when, the motor
was constrained to one axis. Effective control of the lead angle has proven to increase
force generation throughout the speed range resulting in increased acceleration and
top speed. When operating in two dimensions, however, the rotational degree of free-
dom can not be neglected and proved to be a serious problem when controlling the
motor using only two sensors. Never the less, the sensors still worked reliably even
while the motor was executing combined trajectories along the X and Y axis.
Chapter 6
Conclusion
In this thesis, a position and velocity sensing system for two dimensional linear motors
has been implemented and experimentally tested. Before designing the sensor, several
different sensing technologies were evaluated in order to meet the requirements and
operating conditions for our motor. Once a sensing mechanism was selected, several
prototype sensors were designed and tested. The final version of the inductive sensor
offers high accuracy along with strong robustness against temperature changes and
air gap fluctuations.
Once the basic design of the sensor was complete, a mathematical model and
computer simulation were developed in order to optimize sensitivity and bandwidth.
The results from the simulation and experimental tests were very similar suggesting
that the model accurately describes the characteristics of the sensor, at least over the
frequency ranges which were tested. Using the model, many sensor parameters could
be adjusted in order to improve the resolution and accuracy of the position output.
Finally, a digital controller based on a dual processor DSP was implemented.
Along with controlling the driving currents to the motor, the software also has the
responsibility of converting the sensor inputs into useful position and velocity infor-
mation. Different position conversion algorithms were investigated and implemented
in software in order to improve the accuracy of the position data as well as reduce
the computational load within the digital signal processor.
The final version of the controller utilized both current magnitude and lead angle
as control variables. Of these two, the lead angle proved to be the most influential
parameter affecting force generation. Using a look up table, the lead angle was varied
as a function of velocity depending if the motor was accelerating or decelerating. This
variable lead angle controller resulted in significant improvements in motor response
especially at high speeds.
One of the most serious problems, however, with closed loop control is the ex-
tremely low rotational stiffness when using only two sensors. Future research should
definitely focus on improving the control loop about the rotational axis in order to
maintain correct alignment between the motor and the platen.
Other areas for further research include low level time optimal control and adap-
tive control. Several strategies have been outlined by previous authors to optimize
performance in rotary motors by using an adaptive controller on the lead angle [6].
The concepts used in rotary motors could easily be applied to the linear motion
case. Another interesting area of research is optimizing constant speed performance
by using non-sinusoidal output waveforms. Two dimensional linear motors have the
capability of serving a variety of applications besides palletizing. For example, other
robotic applications might include computer controlled machining in which case, re-
ducing the velocity ripple would have the up most importance.
Another area of further research might include collision avoidance and high level
motor control. It is conceivable that using the current DSP, more processors could
be added such that multiple motors could be controlled simultaneously. As far as low
level control is concerned, the two most important areas for further development are:
understanding and optimizing the affects of the lead angle throughout the motor's
speed range, and improving the rotational control in order to reduce the motor's sen-
sitivity to torque disturbances. Even now, the closed loop controller offers significant
improvements in performance, especially high speed performance which is critical for
almost all automated robotic applications.
Appendix A
Modeling Parameters
The following equations calculate the reluctance for each section of the sensor
core. All measurements are in meters and o,is the permeability of free space, which
is equal to 47r x 10-'H/m.
Core body:
Rc
ilr
porAcr
- (A.1)
le = 21.84 x 10- 3 m
Ac = (2.413 x 10-3) (t) m2
Central leg:
R =Ac
yoprAct (A.2)
ld = 12.46 x 10- 3 m
Side legs:
si = olorAsI (A.3)
1,1 = 12.46 x 10- 3 m
A,1 = (2) (2.667 x 10-3)(t) m 2
Central and side leg teeth:
The platen material is low carbon silicon steel, and in general has a lower perme-
ability than the sensor core. Mpfor the platen is approximately 1200.
Platen base:
lpb = pb (A.5)
IoIrApb
3
1pb = 21.84 x 10- m
Platen teeth:
pt = t (A.6)
Po r Apt
4 m
1pt = 5.08 x 10-
Ap = (24) (5.08 x 10-4) (t) m 2
= Io i (A.7)
It should be noted that the platen is a an array of cubes, not ridges, so the
effective area between the platen teeth and sensor teeth is reduced by half. In a
single dimensional case, the overlapping area between the platen and a sensor tooth
is simply toothroidth X toOththickness. However, in the two dimensional case the platen is
an array of cubes so the overlapping area is reduced by half. The combined minimum
and maximum reluctances can be found by summing the overlapping areas for every
tooth on both the side legs and the central legs. The subscripts used in defining each
reluctance follow the same notation as was used to describe the reluctances in the
sensor core in figure 3-1 . The minimum air gap reluctance can be calculated when
the sensor teeth and platen teeth are aligned.
1 = A0o 6
2 +6
( 0.020.02 +5 (-
0.02t 1]0.0254 (A.8)
Rlmin d d + 0.02 d + 0.04)
The maximum reluctances are found when the teeth are 1800 out of alignment.
1 0.02 ) +4 .2 0.02t
= A0o 4 +4•2]6 0 0 + 0.0254 (A.11)
R 2max d + 0.04 d + 0.02 / d+ 0.02
iamplitude = ma Emin
2
9i corresponds to the offset of each central leg. therefore for one sensor core, 01 is 00,
and 02 is 1800.
A.3 Estimation of Coil Inductance
The Inductance for a coil wrapped around a steel bar can be estimated using:
L = Po•,n2Al (A.13)
n is the number of turns per unit length, A is the cross sectional area of the bar,
and 1 is the length of the winding. The primary coil has 10000 windings/m and the
secondary has 7000 windings/m. The length and area of both coils is approximately
0.01m, and 6.5 x 10- 4 m 2 respectively. The inductances are found to be .816mH for
the primary and .401mH for the secondary.
Appendix B
Sensor Fabrication
A critical requirement for good sensor accuracy is to maintain the correct phase
relationship between each sensor tooth. In practice, however, the ideal spacing and
positioning is difficult to achieve. For example, in the final version of the sensor each
core has a 1800 offset between the central legs. However, the actual offset is only
accurate to within certain tolerances. Of the six sensor cores produced, the worst
error was 150, in other words the actual offset was 1650 rather than 1800. This error
proved to be unacceptable in terms of position accuracy, and the core had to be
replaced.
One of the main sources of error in the sensor cores in the fabrication process.
initially, the cores are manufactured in a long stack of laminated sheets which are
glued together. This laminated construction dramatically reduces losses due to eddy
currents. In order to create sensors of a certain thickness, each core is split off of
the main stack at the correct number of laminations. This splitting process often
produces slight bends in the resulting cores. For example, the relatively large error
of 150 degrees was produced by only a 0.0016 in. error in the linear distance between
each central leg on one of the sensor cores. In the future, in would be wise to inves-
tigate different manufacturing processes which minimize handling the cores in order
to guarantee the required tolerances.
Even if each individual core is within tolerances, a larger source of error still exists:
the relative positioning between each of the separate cores both to each other and with
respect to the motor cores on the linear motor. Several different sensor housings were
constructed and experimentally evaluated. The goal of each design was to maintain
rigid alignment between the cores, while at the same time minimize the overall weight.
Also, provisions were required to allow adjustment of at least one the cores in order
to produce the 900 phase offset once the sensors were inside the housing. The final
version of the housing is presented in the following pictures. Figure, B-1 is a CAD
drawing of the housing, and figure B-2 is a picture of one completed sensor along with
two sensor cores. Once each core is aligned in the correct position, they are held in
place with set screws. This is to provide a temporary fixture. Once the sensor has
been experimentally tested and is within tolerances, it can be potted with epoxy or
polyester resin. After potting, the alignment between the cores is fixed, and there is
less of a chance of either the primary or secondary coils shorting through the sensor
core. Unfortunately, due to a limited number of cores the final versions of the sensor
was never potted.
In future versions, it might be worthwhile to look at methods of fabricating the
core out of one solid piece an then cutting it into separate pieces once it is aligned
and potted. In fact, this is a common method used in the construction of both single
dimensional and two dimensional linear motors.
.20"
CIO
C)
C,,
System Hardware
The two dimensional linear motor is produced by Northren Magnetics inc., model
4XY2504-2-0. Some general specifications for this motor are:
Number of axes = 2
Number of phases = 2
Number of sets/axis = 2
Static Force = 26-30 lbs
Force @ 40 in/sec = 19-22 lbs
Resistance/phase/set = 1.9 Q
Inductance/phase/set = 2.3 mh
Amps/phase/set = 4.0
Airgap = 0.0008 in
Maximum forcer temp = 110 C
Weight = 4.5 lbs
Air pressure = 80.0 psi
Airflow = 60.0 scfh
The digital signal processing board used to control the two dimensional linear
motor is the QPC40b quad processor carrier board produced by Spectrum Signal
Processing Inc. This carrier board can accept up to four Texas Instruments C40
processor modules. Currently, the carrier has two MDC40S1 processor modules each
containing 3 banks of 32 kbyte sram memory. Data transfer between the DSP and
the PC is performed through a direct memory access controller, or DMA, allowing
the cpu to perform computations in parallel with data transfer.
The analog input output board is the PC/16IO08 which is also produced by spec-
trum. The input section consists of sixteen analog to digital converters. Each A/D
has 12 bit resolution and the board is capable of sampling all 16 channels simulta-
neously at 25 kHz. The sampling frequency can be increased to 48 kHz if only 4
channels are sampled. The output section consists of eight, 12 bit D/A converters.
The maximum update rate for the D/A converters is 100 KHz. All data transfer
between the DSP and the analog board is performed over a high speed proprietary
bus called the DSPLinkTM. This bus can sustain a data throughput rate which is
much higher than is capable with the PC bus. Also, if additional analog boards are
required they can be connected in series along the DSPLinkTM , providing even further
flexibility.
In closed loop mode using rotation control, a single motor requires six A/D inputs
(2 per sensor) and eight D/A outputs (phase A and phase B for each axis and each
half of the motor). In the future, if multiple motors are to be operated simultaneously,
the number of pro cessors on the QPC40b carrier board could be increa
the number of analog boards along the DSPLinkTM.
The driving amplifiers are the UD12 servo drives produced by parker compumotor.
The major properties of each UD12 are:
Peak motor current iA,peak, ±iB,peak = 25 A (5 sec)
Max continuous motor current fiA,maz, iiB,max = 12 A
Motor supply voltage VA,maz, VB,max = 150 V
Peak power dump current = 12 A @ 150 V
Maximum continuous dump power = 40 W
Internal resistance Rd = 6.80
Bandwidth = 2500 Hz
PWM switching frequency = 20000 Hz
Form factor = 1.01
Gain = 10... 3000 A/V
C.1 Connection Diagrams
The remaining pages contain further information for all of the major hardware com-
ponents. Figures (C-1, C-2, C-3, C-4, C-5, C-6) containing wiring diagrams and other
information pertaining to the sensor, current amplifiers, signal conditioning circuitry,
DSP, and the motor wiring harness.
Inductive Sensor Connection Diagram
93
UD12 PWM Amplifier Connection Diagram
UD2 AMPO UD12AMP2UD12 AMP3 UDI2 AMP4 UD12 Power Spply
1•.4
.mL. cnuemUmi.c4cWut LA4 ar Li•4
Vedohay
OO- Vdoc•y udO VelaVhd
o~eO Vdoby O•im
- m - - -
Phase Set 2
Right Half
Motor 3 Motor4
This wiring harness is used to control
four motors as asingle unit. The controller
can control both the left and right half of the
unit. Motor I has an inverted 25 pin
connector to identify it as the master motor
i
/ ..D
Y Axis
Control Diagram
D/A output Motor which is
channel No. controlled
0 motor 1& motor 4 (Phse A, X axis)
1 motor 1& motor 4 (Phase B, X axis)
2 motor 4 & motor 3 (Phase A, Y axis)
3 motor 4&motor 3(phas , Yaxis)
4 motor 2 & motor 3 (Phase A, X axis)
5 motor 2 & motor 3 (Phase B, X axis)
6 motor 1 & motor 2(PaseA, Y axis)
7 motor 1 & motor 2 (Phase B, Y axis)
PWM AMPO
Simulation Codes
All of the simulation for the sensor model was performed in Matlab. The flowing
code listing was used to find the sensitivity for different parameters. The frequency
response test, was performed by repeating this section of code multiple times at
discrete frequencies up to the sensor's bandwidth.
%%e%%%%%%%m^M%%%%%%
%%set up parameters
clear;
clg;^M
Ep=l; %amplitude of primary voltage
Np=100; %number of turns on primary
Lp=2.94E-4*i; %Inductaneof primary in heneries
Rp=l; %resistanceof primary in ohms
Ns=70; %number of turns on secondary
fc=10000; %carrierfrequency in Hz
fx=10; %frequency of position cycles
Ax=.5; %Amplitude of position cycle in pitches
res=10; %how many sample points for one carrierperiod
boderes=10; %determines resolution of bode plot
w=.007112; %thickness of sensor
d=.0002032; %width of airgap
p=.000508; %halfof pitch length
Imc=.02184; %length of main core
Amc=.00241; &Area of main core
lcl=.01246; %length of one central leg
Acl=.00559*w; %Area of one central leg
lct=.000508; %length of one core tooth
Act=.000508*w*6; %overlapping area of all teeth on one central leg
lsl=.01246; %length of one side leg
Asl=.005334*w; %Area of one side leg
lst=.000508; %length of one side tooth
Ast=.000508*w*6; %Area of all teeth on one side leg
lpb=.02113; %length of platen base 30
Apb=.001524*w/2; %Area of platen base
lpt=.000508; %length of one platen tooth
Apt=.000508*12*w; %overlappingarea of all platen teeth
VOLcore=Amc*lmc+2*(Acl*lcl)+2*(Asl*lsl); %Volume of main core
VOLplaten=Apb*lpb; %Average volume of platen in flux path of core
Uo=12.5663E-7; %permeabilityof free space
Ucr=4000; %average permeability of core
Upb=1000; %average permeability of platen
RI=inv(Uo*Ucr*Amc/lmc);
RII=inv(Uo*Ucr*(Acl/lcl+Act/lct)); 40
RIII=inv(Uo*Ucr*(Asl/lsl+Ast/lst));
RIV=inv(Uo*Upb*(Apb/lpb+Apt/lpt));
Rlmin=inv(Uo*(6*p*w/2/d + 6*p*w/2/(d+p) + 5*p*w/(d+2*p)));
R2min=inv(Uo*(6*p*w/2/d + 6*p*w/2/(d+p) + 4*p*w/(d+2*p)));
Rlmax=inv(Uo*(6*p*w/(d+p) + 5*p*w/2/(d+p) + 5*p*w/2/(d+2*p)));
R2max=inv(Uo*(6*p*w/(d+p) + 4*p*w/2/(d+p) + 4*p*w/2/(d+2*p)));
Rlo=RII+(R1max+R1min)/2;
Rla= (R1max-R1min)/2;
R2o=RIII+(R2max+R2min)/2;
R2a= (R2max-R2min)/2; 50
%%begin simulation
omega=logspace(1,5,boderes);
for k=l:boderes %resolutionfo bode plot
fx=500
Vout(1)=0;
PHI2old=0;
PHIin=0; %Average total flux through one secondary coil 60
PHIinold=0;
[B,A]=butter(4,2*(.2*fc)/(fc*res));
Zp=Rp+2*pi*fc*Lp;
Zpmag=abs(Zp);
Zpangle=angle(Zp);
Pcdisp=146.3*VOLcore*(fc/60) ^ 2;
Ppdisp=15680*VOLplaten*(fc/60) ^2.5;
Rdisp=Ep ^2*(Pcdisp+Ppdisp)/2/Zpmag;
Zpmag=abs(Rdisp+Zpmag);
Ttr=10/fc; %transienttime is 10 times the carrierperiod 70
Tss=l/fx; %staedy state time is 1 times the position period
Ttot=Tss+Ttr;
Tstep=l/fc/res;
t=0:Tstep:Ttot;
jmax=Ttot/Tstep;
for j=2:jmax+l
Vp=Ep*sin(2*pi*fc*t(j));
MFin=Np/Zpmag*(Vp-Np*(PHIin-PHIinold));
PHIinold=PHIin; so
pos=.5*sin(2*pi*fx*t(j));
%pos=fx*t(j);
R1=Rlo+Rla*sin(2*pi*pos+pi/2);
R2=R2o+R2a*sin(2*pi*pos-pi/2);
R3=Rlo+Rla*sin(2*pi*pos+3*pi/2);
R4=R2o+R2a*sin(2*pi*pos-3*pi/2);
PHIin=MFin/(RI+RIV+R1*R2/(R1+R2) +R3*R4/(R3+R4));
PHIS(j)=PHIin;
PHI2=PHIin*R1/(R1+R2);
Vout(j)=Ns* (PHI2-PHI2old)/(t(j)-t(j- 1)); 90
PHI2old=PHI2;
end
env=filter(B,A,abs(Vout));
Vmax=max(env(jmax/4:3*jmax/4));
Vmin=min(env(jmax/2:jmax));
%tl=floor(jmax/2-.5*res);
%t2=floor(jmax/2+. 5*res);
% Vmax=max(Vout(tl:t2));
%tl=floor(3*jmax/4- .5 *res);
%t2=floor(3*jmax/4+. 5 *res); 100
% Vmin=max(Vout(tl:t2));
sen(k)=(Vmax-Vmin); %sensorsensitivity
end
100
Appendix E
This chapter includes the source code listings for the host C program and the DSP
C programs for the A and B CPU's. All files ending with .cpp and all header files
ending with .h belong to the host program. Each DSP program consists of the main
code, "lmd2a.c" and "lmd2b.c", and the header files , "lmda.sys" and "lmdb.sys".
Many comments describing program flow, communication, and handshaking are in-
cluded in the code.
C++ Chapter:
Header File
lmd2.h
10
Written by Henning Schulze-Lauen
04/13/1993
Rewritten by Doug Crawford
02/19/1995
This file is included into every module of the software and provides for
the global variables.
#include <stdlib.h>
#include <stdio.h> 20
#include <conio.h> */
101
/ *Defines*/
#define CppExeName "lmd2. exe" /* Executable C++ program file (i.e .*/
/ * result of compiling this file). */
#define SysParFilename "sy spar. lmd" / * System Parametersettings file. */
#define DfMotorFilename "m otor. lmd" /* Default machine constant database.*/
#define Version "1.0"
// Declarations
typedef struct { / *This structure defines the*/
unsigned int pathtype; /*trajectory in both the X and*/
float time,ax,ay, / *Y ais*/
vxold,vyold,xxold,xyold,
vxfin,vyfin,xxfin,xyfin;
} pathList;
// System Parameters * /
#ifdef Main /* Define Globals only in Main module. */
unsigned int
driverMode, /*Defines the stste of the motor, ie XYstep*/
calReady, /*flag set to 1 if calibrationhas been perfromed*/
maxInstrCount / *maximum number of different piecewise trajectories*/
numMotors; / * number of motors in current configuration, 1 or 4*/
float
maxAmplitude, / *maximum voltage output from DIA this voltage will
/ *result in the maximu currentfrom the PWM amps*/
filterBWData, / *Defines cut off frequency for digital filter*/
filterBWCal, / *filterBWData is the cutoff used for all incoming AID*1
filterBWVel; / *samples, filterBWcal has a lower cut off and is used */
/ *in place of filterBWdata during calibration.*/
/ *filterBWVel is used to filter the velocity signal*/
double
*/
/ *=3.14...
char
motorName[40]= "<none>",
/ * Description of current motor configuration.*/
motorFilename[80]=DfMotorFilename;
/ * Filename of machine data base. */
unsigned long
sampleFreq, /*controls the sampling rate on the multi channel I/0*/
/*card, the main interrupt is controlled by this timing*/
minSampleFreq, / *Minimum and maximum sampling freqeuncy [Hz], */
maxSampleFreq; / *determined by the by the limits of I/O board. */
float
leadMaxAccel, / *maximum lead angle which defines the saturationlevel, */
102
leadMaxDecel, /* ie when the velocity is maximuml.*/
leadMinAccel, / *minimum lead angle, ie, the value when velocity is zero*/
leadMinDecel,
IsAccel, /*speed at which lead angle saturates to it's maximum*/
lsDecel, /*defined in leadMaxAccel and leadMaxDecel*/
P, / *Proportionalconst for current control*/
D, / *Derivativeconstant for current control*/
I, / *Integralconst for current control(currently not used) */
Prot, / *Proportionalconst for rotation control(currentlynot used)*/
Drot, /*Derivative const for rotation control(currently not used)*/
leadMax, /*maximum value of the lead angle which can be set user*/
kmax; / *maximum value of the control parameters set by user*/
#else // In all non-Main modules declare globals extern
extern unsigned int maxInstrCount, driverMode, calReady, numMotors;
extern float maxAmplitude, filterBWData, filterBWCal, filterBWVel;
extern double pi;
extern char motorName[40], motorFilename[80];
extern unsigned long sampleFreq, minSampleFreq, maxSampleFreq;
extern float leadMaxAccel, leadMaxDecel, leadMinAccel, leadMinDecel,
IsAccel, IsDecel, P, D, I, Prot, Drot, leadMax, kmax;
#endif
/*///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratory for Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Main Program
// lmd2.cpp
//
// Written by Henning Schulze-Lauen
// 04/08/1993
// Rewritten by Doug Crawford
// 03/15/1995
//
// This program drives a 2-axis linear motor, 2 phases per axis, using the
// Texas Instruments QPC-40 digital signal processing board. While this C++
// Chapter provides for user interface and calculation of trajectoriesto
// run, the TMS32OC40 C program is responsible for generating the necessary
// waveforms.
// For communication between this C++ program and the TMS320C40 System Board
// we use the NETAPI High Level Language Interface Library provided by
// Spectrum.
/-
// All functions ending with ..DSPO are defined in the interface module,
// Imd2_ifr.cpp */
103
#define Main
30
#include "lmd2.h"
#include "lmd2_rou.h"
#include "lmd2_ifr.h"
/ *////////////////////////////////////////////////////////////////////////////
// Declaration of Modules
/*/
int mainMenu(pathList *);
void initialization(void);
void createTrajectory (pathList *); 40
void go(pathList *);
void resetMotor(void);
void dumpADCInput(void);
void setup(void);
void termination(void);
void main()
// 50
{
initialization();
static pathList *trajectoryl=
(pathList *) calloc (maxInstrCount, sizeof(pathList));
/ * From here the current trajectory is distributed to the modules*/
while (mainMenu(trajectoryl));
/ * Repeat mainMenu until user requests termination. */
termination();
}
// end main() 60
104
case 2: go(trajectory); return 1;
case 3: resetMotor(); return 1;
case 4: dumpADCInputo; return 1;
case 5: setup(); return 1;
case 6: calibrateSensorDSPo; return 1;
case 0: return !waitYN("\nTerminate session - sure? (Y/N; <Enter>=Y) ", 'Y');
return 0; 90
//end mainMenu()
/*///////////////////////////////////////////////////////////////////////////
//Linear Motor Driver
//
// High Speed Flexible Automation Project
//Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Initialization Mo dule
// lmd2_inm.cpp 10
//
// Written by Henning Schulze-Lauen
//04/13/1993
// 07/30/1993
//09/12/1993
// Rewritten by Doug Crawford
// 03/15/1995
/*/
#define Initialization
#include "lmd2. h" 20
#include "lmd2.rou. h"
#include "lmd2_dsr. h"
#include "lmd2_ifr.h"
#include "math.h"
void initialization()
pageInit("Welcome! ");
105
systemDefaults();
loadSysPar(); Load system parameters including filename of*/
loadMotorData(); current motor database, which is loaded next. */
If file(s) not available, default values as*/
loaded from DSP board will be used. */
startUpDSP();
sendGlobalsDSP();
calReady=0; This flag is set to one once sensor calibration*/
has been performed*/
}
// end initialization()
void systemDefaults(void)
pi=4*atan(1);
minSampleFreq=0;
maxSampleFreq=30000;
maxInstrCount=15;
leadMaxAccel=400; //lead angle values are in degrees
leadMaxDecel=170;
leadMinAccel=80;
leadMinDecel=-80;
lsAccel=700; //used in lead angle control units are pitch/s
lsDecel=700; //used in lead angle control units are pitch/s
P=25;
D=0.1;
I=1;
Prot=1;
Drot=1;
leadMax=720;
kmax= 1000000;
maxAmplitude=2.6; /*this will cause a sinusoid with maximum amplitude*/
/ *of +/- 2.6 volts from the D/A which corresponds*/
/ *to about +/- 8 amps from the UD12 amplifiers*/
/ *The gain of the amplifiers, however, may be adjusted*/
/ *through a potentiometer, so make sure the amps*/
/ *are supplying the correct amps before connecting them*/
/ *to the motor*/
/*///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Online Trajectory Generation Module
// Imd2_tgm.cpp
106
//
// Written by Doug Crawford
// 02/19/1995
/*/
#define TrajectoryMenu
#include "lmd2. h"
#include "lmd2_rou.h"
// Declaration of Sub-Modules 20
//
int createTrajectoryMenu(pathList *);
void standardTrajectory(const int, pathList *);
void dumpInstr(pathList *); /*dumps the trajectory information to the screen*/
// end createTrajectory()
40
int createTrajectoryMenu(pathList trajectory[)
if (trajectory[1].pathtype) {
printf(" Note: Selection 1 through 2 will delete\n"
" the currently programmed trajectory!\n\n"
"Show current trajectory's instruction list .... 3\n\n");
maxSelect=3; 60
}
else
maxSelect=2;
printf("Return to Main Menu ........................... 0\n\n");
107
switch (userSelection(O,maxSelect,(trajectory[1].pathtype?0:1))) {
case 1: standardTrajectory(1, trajectory); return 1;
case 2: standardTrajectory(2, trajectory); return 1;
case 3: dumpInstr(trajectory); return 1;
} 70
return 0;
// end createTrajectoryMenu
108
runTime=0; / * Time running at const vX, vY [s].*/
static float vX=200, /* Target velocities [pitch/s].*/
vY=0; /* Note that initialization is static*/
static float xxmax,xymax,totTime;
int trajStep;
switch (shape) {
case 1:
trajStep=0;
trajectory[trajStep].xxold=O; trajectory[trajStep].xyold=0;
trajectory[trajStep].vxold=O; trajectory[trajStep].vyold=O;
constantAccel(vX,vY,accelTime,1,trajectory);
constantVel(vX,vY,runTime,2,trajectory);
constantAccel(0,0,decelTime,3,trajectory);
xxmax=trajectory[3].xxfin; xymax=trajectory[3].xyfin;
totTime=accelTime+runTime+decelTime;
if (waitYN("\nlnclude return trip? (Y/N <enter>=Y) ", ,'Y)){
constantAccel(-vX,-vY,accelTime,4,trajectory); 80so
constantVel(-vX,-vY,runTime,5,trajectory);
constantAccel(0,0,decelTime,6,trajectory);
break;
case 2:
trajStep=0;
trajectory[trajStep].xxold=O; trajectory[trajStep].xyold=O;
trajectory[trajStep].vxold=O; trajectory[trajStep].vyold=O;
linearAccel(vX,vY,accelTime,l,trajectory);
constantVel(vX,vY,runTime,2,trajectory); 90
linearAccel(0,0,decelTime,3,trajectory);
xxmax=trajectory[3].xxfin; xymax=trajectory[3].xyfin;
totTime=accelTime+runTime+decelTime;
if (waitYN("\nInclude return trip? (Y/N <enter>=Y) ,, ,y )){
linearAccel(-vX,-vY,accelTime,4,trajectory);
109
constantVel(-vX,-vY,runTime,5,trajectory);
linearAccel(0,0,decelTime,6,trajectory);
}
break;
} // end switch(shape) 100
printf("\nYour trajectory has been compiled.\n"
"Required platen space = (%.lf,%.lf)..(I. lf,%.1f) pitch\n"
"Total run time = %.3f s\n\n",
0, 0, xxmax, xymax, totTime);
// end standardTrajectory()
{
trajectory[ts].pathtype = 2; //constant acceleration 120
trajectory[ts].time = duration;
trajectory[ts].ax = (vxfin-trajectory[ts-1].vxfin)/duration;
trajectory[ts].ay = (vyfin-trajectory[ts- 1].vyfin)/duration;
trajectory[ts].vxold = trajectory[ts- 1].vxfin;
trajectory[ts].vyold = trajectory[ts- 1].vyfin;
trajectory[ts].xxold = trajectory[ts- 1].xxfin;
trajectory[ts].xyold = trajectory[ts-1].xyfin;
trajectory[ts].vxfin=vxfin;
trajectory [ts].vyfin=vyfin;
trajectory[ts].xxfin= (trajectory[ts].ax*duration*duration)/2 130
+trajectory[ts].vxold*duration+trajectory[ts].xxold;
trajectory[ts].xyfin= (trajectory[ts].ay*duration*duration)/2
+trajectory [ts].vyold*duration+trajectory [ts].xyold;
I
// end constantAccel
void constantVel(const float vxfin, const float vyfin,
const float duration, const int ts, pathList trajectoryf)
trajectory[ts].pathtype = 1; /*constantvelocity*/
trajectory[ts].time = duration;
trajectory[ts].vxold = trajectory[ts- 1].vxfin;
110
trajectory[ts].vyold = trajectory[ts-1].vyfin; 150
trajectory[ts].xxold = trajectory[ts-1].xxfin;
trajectory[ts].xyold = trajectory[ts-1].xyfin;
trajectory [ts].vxfin=vxfin;
trajectory[ts].vyfin=vyfin;
trajectory[ts].xxfin=(trajectory[ts].vxfin*duration)
+trajectory[ts].xxold;
trajectory[ts].xyfin= (trajectory[ts].vyfin*duration)
+trajectory[ts].xyold;
}
// end constantVel 160
111
pagelnit(" Instruction List");
printf("Hit any key to stop and resume the listing, <Esc> to abort.\n"
"Listing will show:\n\n"
"instr # pathtype velx vely accelx accely velxi velyi posxi posyi time \n");
waitEnterO;
unsigned int pc; 210
pc=1;
while (trajectory[pc].pathtype != 0) {
printf("\n%6i> %4i %3.2f %3.2f %3.2f %3.2f %3.2f %3.2f %3.2f %3.2f ,3.1
pc, trajectory[pc].pathtype, trajectory[pc].vxfin, trajectory[pc].vyfin,
trajectory[pc].ax, trajectory[pc].ay, trajectory[pc].vxold,
trajectory[pc].vyold, trajectory[pc].xxold, trajectory[pc].xyold,
trajectory[pc].time);
pc++;
if (kbhit())
if(getcho==Esc) 220
break;
else
if(getcho==Esc)
break;
}
printf("\n\n");
waitEnter();
}
// end dumpInstr()
230
// end reverseTrajectory()
/ *///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Trajectory Execution Module
// lmd2 gom.cpp 10
112
// June, 1995
#define Go
#include "lmd2. h"
#include "lmd2_rou. h"
#include "Imd2_ifr.h" 20
}
// end go()
113
/*IIIIIIIIIIIIIIIII//////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratory for Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// ADC Input Dump Module
// Imd2_mdm.cpp l1
//
// Written by Henning Schulze-Lauen
// 09/10/1993
// Rewritten by Doug Cratwford
// June 1995
*/
#deflne ADCDumpMenu
#include "lmd2. h"
#include "lmd2_rou. h"
20
/*///////////////////////////////////////////////////I//////////////////
// Declaration of Sub-Modules
*/ // File Names
int dumpADCInputMenu(void);
void dumpADCInputASCII(int); // Ilmdc_mds.cpp
void dumpADCInput(void)
//
{
while (dumpADCInputMenuo); // Repeat menu until user requests
// termination.
// end dumpADCInputo
int dumpADCInputMenu(void)
///////////////////////////////////////40
// Print menu, wait for input, and execute selection
//
// Return: 0 = user requested main menu;
// 1 = else.
//
int maxSelect;
114
"during the last execution of a trajectory. What kind of data\n"
"these readings represent depends on the current hardware and\n"
"software configuration.\n\n"
"Dump sample buffer from processor A .................. i\n"
"Return to Main Menu .................................. O\n\n\n");
switch (userSelection(0,2,2)) {
case 1: dumpADCInputASCII(1); return 1;
)
return 0; 60
}
// end dumpADCInputMenu()
/ *///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// ADC Input Dump Module
// Submodule ADCDumpASCII to
// lmd2_mds.cpp
//
// Written by Doug Crawford
// 07/20/1995
*/
#define ADCDumpASCII
#include "lmd2. h"
#include "lmd2_rou. h"
#include "lmd2_ifr. h"
#include "math.h" 20
115
if (readFilename("A new file will be created for dumping the last measurements.\n"
"Please enter filename -", filename, "w"))
return;
printf("\nWriting... ");
ADBuffer=getADBufferDSP(&ADBufferSize,&ADBufferCol,procNo);
//returns a pointer to an array containing DSP buffer
bufferWriteLength=floor(ADBufferSize/ADBufferCol)*ADBufferCol;
dumpfile=fopen(filename,"w"); // Open ADCII output file. 50
if(dumpfile==NULL) {
printf("unable to open file %s\n",dumpfile);
waitEnter();}
for (i=0; i< bufferWriteLength; i=i+ADBufferCol) {
for(j=0 ; j< (ADBufferCol-1) ; j++)
fprintf(dumpfile, "%f\t", *ADBuffer++);
fprintf(dumpfile, "%f\n", *ADBuffer++);
//format for matlab must be in double precision format
}
/*free((void *)ADBuffer);*/ 60
printf(" Done. \n\n");
fclose(dumpfile);
return;
}
// end dumpADCInputASCII()
/*///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Reset Motor Module
// lmd2_rmmacpp 10
//
// Written by Henning Schulze-Lauen
// 04/13/1993
// Rewritten by Doug Crawoford
// June 1995
*/
#define ResetMotor
#include "lmd2. h"
#include "lmd2_rou. h"
#include "lmd2_if r. h" 20
116
void resetMotoro
{
pageInit("Reset Motor"); 30
/ *///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Setup Module
// lmdc sum.cpp 10
// This module controls all user input for the global variables
//
// Written by Henning Schulze-Lauen
//04/13/1993
// 07/30/1993
// Rewritten by Doug Crawford
//June 1995
*/
117
#define Setup
#include "lmd2. h" 20
#include "imd2.rou. h"
#include "lmd2_ifr. h"
#include "lmd2_dsr.h"
void setup()
printf("\n\n");
sampleFreq=readNumPrompt("Sampling Frequency", ". Olf Hz",
" determines the frequency at which the sensors are sampled and the\n"
" output of motor current is updated.\n\n",
minSampleFreq, maxSampleFreq, sampleFreq);
printf("\n\n");
leadMaxAccel=readNumPrompt("Maximum lead Angle for Acceleration", "%.11f deg",
" provides the maximum value of the lead angle for acceleration\s"
" values should be in degrees for the positive direction, typical\n"
" values are 360 to 400 degrees.\n\n",
-720, 720, leadMaxAccel);
printf("\n\n");
leadMinAccel=readNumPrompt("Minimum lead Angle for Acceleration", "%.llf deg",
" provides the minimum value of the lead angle for acceleration\n"
" values should be in degrees for the positive direction. This is\n"
" the value of the lead angle which is used at zero velocity. \n"
" Typical values are 40 to 90 degrees.\n\n", 60
-720, 720, leadMinAccel);
printf("\n\n");
leadMaxDecel=readNumPrompt("Maximum lead Angle for Deceleration", "%.llf deg",
" provides the maximum value of the lead angle for acceleration\n"
" values should be in degrees for the positive direction, typical\n"
" values are 120 to 180 degrees.\n\n",
-720, 720, leadMaxDecel);
printf("\n\n"); 70
leadMinDecel=readNumPrompt("Minimum lead Angle for Deceleration", ".%.
11f deg",
" provides the minimum value of the lead angle for acceleration\n"
118
" values should be in degrees for the positive direction. This is\n"
the value of the lead angle which is used at zero velocity.\n"
" Typical values are -40 to -90 degrees.\n\n",
-720, 720, leadMinDecel);
printf("\n\n");
IsAccel=readNumPrompt("Lead Angle Saturation Speed (Acceleration)", "%.lf p/s",
" The speed at which the lead angle function saturates to it's\ngb
" maximum value for acceleration. Typically 400 to 700 pitch/s.\n\n",
0, 2000, IsAccel);
printf("\n\n");
IsDecel=readNumPrompt("Lead Angle Saturation Speed (Deceleration) ","%.llf p/s",
" The speed at which the lead angle function saturates to it's\n"
" maximum value for deceleration. Typically 400 to 700 pitch/s.\n\n",
0,2000, lsDecel);
printf("\n\n"); so
P=readNumPrompt("Proportional Controller Constant", "%-71G",
" Proportional controller constant for the current amplitude.\n\n",
-kmax, kmax,P);
printf("\n\n");
D=readNumPrompt("Derivative Controller Constant", "%-71G",
" Derivative controller constant for current amplitude\n\n",
-kmax, kmax,D);
printf("\n\n"); 100
I=readNumPrompt("Integral Controller Constant", "%-71G",
" Integral controller constant for current amplitude.\n\n",
-kmax, kmax,I);
printf("\n\n");
Prot=readNumPrompt("Proportional Rotation Controller Constant", "%-71G",
" Proportional controller for rotation control.\n\n",
-kmax, kmax, Prot);
printf("\n\n");
filterBWData=readNumPrompt("Input Data Stream filter Cut Off Frequency", "%.1Of Hz",
" determines the cut off frequency for the data stream filter.\n\n",
0, sampleFreq/2,filterBWData);
printf("\n\n"); 120
filterBWCal=readNumPrompt("Calibration filter Cut Off Frequency", "%.Olf Hz",
" determines the cut off frequency for the calibration filter.\n\n",
0,sampleFreq/2,filterBWCal);
printf(" \n\n");
filterBWVel=readNumPrompt("Velocity filter Cut Off Frequency", "%.01f Hz",
119
" determines the cut off frequency for the calibration filter.\n\n",
0, sampleFreq/2,filterBWVel);
130
printf("\n\n");
printf("\n\n"); 140
oldvalue=numMotors;
numMotors=readNumPrompt("Number of Motors", "%,.1lf",
" is the number of motors in simultaneous operation. \n\n",
1, 4, numMotors);
if (numMotors!=oldvalue) newdatal=1;
printf("\n\n");
oldvalue=maxAmplitude;
maxAmplitude=readNumPrompt("Max Amplitude", "%.11f Volts",
" is the maximum amplitude of the D/A signal used during operatinm. \n"
" Make Sure the amps are no greater than 4 amps for the single motor. \n"
" and no greater than 8 amps for the quad motor configuration. \n"
please note, 2.6 volts on the DAC will produce 8 amps of current. \n\n'
0, 8, maxAmplitude);
if (maxAmplitude!=oldvalue) newdatal=1;
printf("\n\n");
newdatal =readStringPrompt("Conf igurat ion Name",
" Description of configuration entered above.\n"
" Max 35 characters.\n\n", 160
motorName, 35);
if ((newdata & 1)
&& waitYN("\n\nMachine data has been changed -
Save? (Y/N, <Enter>=Y) ", 'Y')) {
if (!readFilename("\n Please enter database filename -",
motorFilename, "w"))
if (!saveMotorDatao) { 170
printf("\nData saved. \n");
newdata&=OxFFFE;
}
printf(" \n");
saveSysPar(); /* Save all system parameters, including new */
/* current motor database filename.*/
sendGlobalsDSPO; /* update data on DSP board*/
180
120
// end setup()
*///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratory for Manufacturing and Productivity
// Massachusetts Institute of Technology
/-
// C++ Chapter:
// General I/O Routines
// Header File 10
// lmdcrou.h
//
// Written by Henning Schulze-Lauen
//02/04/1993
*/
// Defines
//
// Macros 20
#define LoByte(w) ((unsigned char)(w))
#define HiByte(w) ((unsigned char)((unsigned int)(w) >> 8))
#define Round(w) ((long) ((w)>=0.0 ? floor((w)+0.5) : ceil((w)-0.5)))
#define Max(a,b) (((a) > (b)) ? (a): (b))
#define Min(a,b) (((a) < (b)) ? (a): (b))
// ASCII Characters
#define CR '\xOD'
#define EoF '\xA'
#define Esc '\xlB' 30
// Declarations
//
void pageInit(char *);
int userSelection(int, int, int);
void waitEnter(void);
int waitEnterEsc(void);
int waitYN(char *, char);
double readNum(double); 40
double readNumMinMax(char *, double, double, double);
double readNumPrompt(char *, char *, char *, double, double, double);
int readString(char *, int);
int readStringPrompt(char *, char *, char *, int);
int readFilename(char *, char *, char *);
void checkInstrCount(unsigned int);
/ *//////////////////////////////////////////////////////////////////////1111
121
//Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and PYroductivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// General I/O Routines
// lmd2 rou.cpp
// Written by Henning Schulze-Lauen
//02/04/1993
*/
#define Routines
#include "lmd2. h"
#include "imd2_rou.h"
#include <string.h>
#include <ctype.h>
#include <math.h>
122
} while ((selection<min) 11(selection>max));
printf("%i", selection);
return selection;
}
// end userSelectiono 60
void waitEnter()
/ *///////////////////////////////////////////////////////////////////////////
// Wait for user to press <Enter>
/*/
{
printf("Press <Enter> to continue ");
while (getchO!=CR);
printf("\n"); 70
}
// end waitEnter()
int waitEnterEsc()
/ *///////////////////////////////////////////////////////////////////////////
// Wait for user to press <Enter> or to break with <Esc>
//
// Return: 0 = break,
// 1 = continue. so
*/
{
char key;
123
printf("%s", prompt); 110
do {
key=toupper(getch();
switch (key) {
case CR: key=defaultValue; break;
case 'O': key=' N'; break; // Alternative for Insiders...
case '1': key=' Y'; break;
}
} while (!((key=='Y')I (key=='N')));
printf("%c\n", key);
if (key==' Y') 120
return 1;
return 0;
}
// end waitYN()
gets(inString);
if (!strlen(inString)) 140
return defaultValue;
sscanf(inString, "%lf", &inNum);
return inNum;
}
// end readNumo
124
strcat(textLine, " max ");
strcat(textLine, format);
sprintf(output, textLine, min, max);
while (strlen(output)<45)
strcat(output, " ");
strcat(output, "New value = ");
170
do {
printf("%s", output);
inNum=readNum(defaultValue);
} while ((inNum<min) I (inNum>max));
return inNum;
}
//end readNumMinMax
strcat(output, title);
while (strlen(output)<45)
strcat(output, " ");
strcat(output, "Old value = ");
strcat(output, format); 200
strcat(output, "\n\n");
printf(output, old);
printf(".s", description);
return readNumMinMax(format, min, max, old);
}
//end readNumPrompt
125
double inNum;
char inString[80];
gets(inString);
if (!strlen(inString))
return 0;
else {
strncpy (input, inString, len);
return 1;
}
// end readString()
int readStringPrompt (char *title, char *description, char* input, int len)
/I*//////////////////////////////////////////////////////////////////////////
// Output prompt and read string from keyboard
//
// Parameters: title = name of value to be read.
// iade.
description = explanation printed before input is m7
// input = pointer to input, also used as default.
//0 = default strin used,
// Return: 0 = default string used,
1 = new string entered.
end readStringPrompt
FILE *fp;
printf("%s\n\n"
" Default = %s\n",title, filename); 270
do {
126
printf(" New = ");
if (!readString(filename, 80) && (mode[1]=='n'))
return 1: / *Only CR entered, so return error if default not allowed. */
if (!isalpha(filename[0]))
printf(" The first character must be a letter. Please try again.\n\n");
} while (!isalpha(filename[0]));
fp=fopen(filename, "r");
switch (mode[0]) {
case 'w'
if (fp!=NULL) {
fclose(fp);
printf("\nExisting file '%s' will be replaced if you proceed.\n",
filename);
if (!waitEnterEsc())
return 1;
}
return 0;
case 'r' :
if(fp==NULL) {
printf("\nFile I's' not found - read skipped.\n", filename);
waitEnter();
return 1;
fclose(fp);
r eturn 0;
case 'a':
if (fp!=NULL) {
fclose(fp);
printf("\nFile '%s' exists.\n", filename);
if (waitYN("Do you want to delete this file and replace
it with a new file\n"
"of the same name? (Y/N/<Enter>=N) ", 'N')) {
fp=fopen(filename, "w");
fclose(fp);
return 0;
}
if (waitYN("\nDo you want to use the existing file and "
"append new data? <Y/N/<Enter>=Y) ", 'Y')) 310
return 0;
return 1;
return 0;
} // end switch
return 1;
}
// end readFilename0;
127
// Returns: exit if overflow
*/
{
if (pc >= maxInstrCount) {
printf("\n\nFatal Error: Out of memory.\n\n" 330
"The program attempted to create a trajectory too large for storage\n"
"in DSP board's memory. After restarting please arrange for a less\n"
"complex trajectory (possibly not including a return trip) or ask your\n"
"system programmer to change the buffer size [lmda.dsp] or the time\n"
"step during trajectory generation.\n\n"
"Sorry! - Good Bye.\n");
exit(l);
}
// end checkInstrCount() 340
/I*/////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Disc I/O Routines
// Header File 1o
// lmdc-dsr.h
//
// Written by Henning Schulze-Lauen
// 07/30/1993
// Rewritten by Doug Crawford
// June, 1995
*/
void systemDefaults(void);
int loadSysPar(void);
int saveSysPar(void); 20
int loadMotorData(void);
int saveMotorData(void);
/ *///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
// Chapter:
// C++ Chapter:
128
// Disc I/O Routines
// lmd2 dsr.cpp to
//
// Written by Henning Schulze-Lauen
// 07/30/1993
*/
#define DiscIO
#include "lmd2. h"
#include "lmd2 _ rou. h"
#include "imd2_dsr. h"
#include <string.h>
#include <math.h> 20
int loadSysPar(void)
/*////////////////////////////////////////////////////////////////////////////
// Restore last-used system parametersfrom disk
//
// Returns: 0 = read successful,
// 1 = error, no parameters changed.
//
{ 30
FILE *fp;
char inString[80], cmpString[20];
/ * Read data*/
fread(&leadMaxAccel, sizeof(leadMaxAccel), 1, fp);
fread(&leadMaxDecel, sizeof(leadMaxDecel), 1, fp);
fread(&leadMinAccel, sizeof(leadMinAccel), 1, fp);
fread(&leadMinDecel, sizeof(leadMinDecel), 1, fp);
fread(&lsAccel, sizeof(lsAccel), 1, fp); 60
fread(&lsDecel, sizeof(lsDecel), 1, fp);
fread(&sampleFreq, sizeof(sampleFreq), 1, fp);
129
fread(&P, sizeof(P), 1, fp);
fread(&D, sizeof(D), 1, fp);
fread(&I, sizeof(I), 1, fp);
fread(&Prot, sizeof(Prot), 1, fp);
fread(&Drot, sizeof(Drot), 1, fp);
fread(&filterBWData, sizeof(filterBWData),1,fp);
fread(&filterBWCal, sizeof(filterBWCal),1,fp);
fread(&filterBWVel, sizeof(filterBWVel),1,fp); 70
return 0;
}
//end loadSysPar()
int saveSysPar(void) so
/I*///////////////////////////////////////////////////////////////111111111
//Save current system parameters in file
//
// Returns: 0 = write successful,
// 1 = error.
*/
{
FILE *fp;
char outString[80];
90
if ((fp=fopen(SysParFilename,"wb"))==NULL) {
printf("Error writing file '%s' -\n"
"system parameters not saved. Sorry.\n",
SysParFilename);
waitEnter();
printf("\n");
return 1;
}
sprintf(outString, "LMD %s System Parameter File\r\n%c", Version, EoF);
fwrite(outString, sizeof(char), sizeof(outString), fp); 100
130
fwrite(motorFilename, sizeof(char), sizeof(motorFilename), fp);
fclose(fp);
return 0;
I
// end saveSysParo
int loadMotorData(void)
/ *///////////////////////////////////////////////////////////////////////////
// Load current machine database from disk
//
// Returns: 0 = read successful,
// 1 = error, no parameters changed.
*/
FILE *fp;
char inString[80], cmpString[20];
/ * Read data */
fread(motorName, sizeof(char), sizeof(motorName), fp);
fread(&maxAmplitude, sizeof(maxAmplitude), 1, fp);
fread(&numMotors, sizeof(numMotors), 1, fp);
fclose(fp);
return 0;
end loadMotorDataO
end loadMotorData()
int saveMotorData(void)
/*//////////////////////////////////////
// Load current machine databasefrom disk
// Returns: 0 = read successful,
131
// 1 = error, no parameters changed.
*/
{
FILE *fp;
char outString[80];
if ((fp=fopen(motorFilename,"wb"))==NULL) {
printf("Error writing file '%s' -\n"
"Machine data unsaved.\n",
motorFilename); 1so
waitEnter();
printf(" \n");
return 1;
}
sprintf(outString, "LMD %s Machine Database\r\n%.s\r\n%c",
Version, motorName, EoF);
fwrite(outString, sizeof(char), sizeof(outString), fp);
fwrite(motorName, sizeof(char), sizeof(motorName), fp);
fwrite(&maxAmplitude, sizeof(maxAmplitude), 1, fp);
fwrite(&numMotors, sizeof(numMotors), 1, fp); 190
fclose(fp);
return 0;
}
// end saveMotorDatao
200
/*///////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratory for Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// Termination Module
// lmd2_trm.cpp 10
132
void termination()
/*//////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratory for Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// DSP Interface Routines
// Header File 10
// Imdc_dsr.h
//
// Written by Doug Crawford
// 03/03/1995
// Rewritten by Doug Crawford
// June, 1995
*/
void startUpDSP(void);
void setAmpZeroDSP(void); 20
void executeTrajectoryDSP(pathList *);
void resetMotorDSP(void);
void sendGlobalsDSP(void);
float* getADBufferDSP(unsigned long*, unsigned long*, int);
void shutDownDSP(void);
void calibrateSensorDSP(void);
/ *//////////////////////////////////////////////////////////////////////////
// Linear Motor Driver
//
// High Speed Flexible Automation Project
// Laboratoryfor Manufacturing and Productivity
// Massachusetts Institute of Technology
//
// C++ Chapter:
// TMSS320C40 DSP Module
133
// tic40.cpp to
//
// Written by Doug Crawford
//02/19/1995
//
// This module contains all of the interface routines to the DSP
// this includes data transfer and handshaking routines from the DSP to the PC
//this is also the only module that calls library functions in TIC40.cpp
*/
#define interface 20
#include "lmd2. h"
#include "lmd2_rou.h"
#include "time.h"
#include "1md2ifr. .h"
#include <windows.h>
extern "C"{
#include "c4xwin.h" / *netapiapplications library for the TIC40*/
}
#include "chkerror. c" / *netapierror checker printouts*/
30
/ *defines*/
#define dspFileA "lmd2_a.out"
#define dspFileB "lmd2_b.out"
#define procNameA "CPUA"
#define procNameB "CPUB"
extern unsigned _stklen = Ox4000U; /*necessary when using borland c*/
/ *localfunction declarations */
void callDSPA(int);
void createDSPList(float *,long *,float *,pathList *,char *); 50
void sendTrajData(float *,long *,float *,long *);
void waitForClock(float);
void startUpDSP(void)
//*/////////////////////////////////////// 6o
//performs a global reset and loads the program, prints error messages if there
//are any
*/
134
printf("\n\nRebooting the C40 network ... ");
ret=Global Network Reboot();
checkReturnCode(ret);
printf("OK");
}
//end startUpDSP 90
float totTimeList=0;
createDSPList(instrsX, pathsX, &totTimeList, trajectory,"X");
createDSPList(instrsY, pathsY, &totTimeList, trajectory,"Y");
sendTrajData(instrsX,pathsX,instrsY,pathsY);
callDSPA(changeModeC);
135
callDSPA(driverMode); / *this variable is declared in lmd2.h*/
/ *and it defines the state of the motor, ie*/
/ *XYoff, Xystep, etc..*/ 120
//end executeTrajectory
void resetMotorDSP(void)
/*//////////////////////////////////////////////////////////////////////
//turns off motor currents, and leaves them in step mode when finished
*/
{ 130
callDSPA(resetC);
driverMode=XYstep;
}
//end resetStep
void sendGlobalsDSP(void)
/*/////////////////////////////////////////////////////////////////////
//send Global Parametersto DSP board CPU A
//The functions write_liafoats_32 and writeliawords_32 both accept 3
// arguments: the pointer to the processor which receives the data, the
f/increment for each element in an array, and the pointer to the word or
// array of data to be written
*/A
//create dummy variables of type ULong and float, because the function
//write lia floats 32 corrupts the data
ULONG
oneRec= 1, 150
maxInstrCountTemp=maxInstrCount;
float
sampPeriodTemp= (float) 1/sampleFreq,
maxAmplitudeTemp=maxAmplitude,
leadMaxAccelTemp=leadMaxAccel,
leadMaxDecelTemp=leadMaxDecel,
leadMinAccelTemp=leadMinAccel,
leadMinDecelTemp=leadMinDecel,
lsAccelTemp=lsAccel,
IsDecelTemp=lsDecel, 160
filterBWDataTemp=filterBWData,
filterBWCalTemp=filterBWCal,
filterBWVelTemp=filterBWVel,
PTemp=P,
DTemp=D,
ITemp=I,
ProtTemp=Prot,
DrotTemp=Drot;
/ *the order of these writes, must match exactly with the order of the reads*/
/ *in the DSP program*/ 170
136
/ *processorA writes*/
callDSPA(GlobalsC);
ret=WriteLIA Words 32(handleA,1,&oneRec);
ret= Write LIA Words 32(handleA,1,&maxInstrCountTemp);
ret=WriteLIAWords 32(handleA,1,&oneRec);
ret=Write LIAFloats_32(handleA,1,&sampPeriodTemp);
ret=WriteLIAWords_32(handleA,1,&oneRec);
ret=Write LIA Floats 32(handleA,1,&maxAmplitudeTemp);
ret=Write LIA Words_32(handleA,1,&oneRec); 180
ret=WriteLIAFloats 32(handleA,1,&filterBWDataTemp);
ret=Write LIA Words 32(handleA,1,&oneRec);
ret=WriteLIA Floats 32(handleA,1,&filterBWCalTemp);
ret=Write LIA Words_32(handleA,1,&oneRec);
ret=Write LIA Floats 32(handleA,1,&filterBWVelTemp);
ret=Write LIA Words 32(handleA,1,&oneRec);
ret=Write_LIAFloats 32(handleA,1,&leadMaxAccelTemp);
ret=Write LIA Words 32(handleA,1,&oneRec);
ret=Write LIA Floats_32(handleA,1,&leadMaxDecelTemp);
ret=Write LIA Words 32(handleA,1,&oneRec); 19o
ret=WriteLIAFloats 32(handleA,1,&leadMinAccelTemp);
ret=Write LIA Words 32(handleA,1,&oneRec);
ret=WriteLIA Floats_32(handleA,1,&leadMinDecelTemp);
ret=Write LIA Words 32(handleA,1,&oneRec);
ret=WriteLIAFloats 32(handleA,1,&lsAccelTemp);
ret=WriteLIA Words_32(handleA,1,&oneRec);
ret=WriteLIA Floats 32(handleA,1,&lsDecelTemp);
ret=Write LIAWords 32(handleA,1,&oneRec);
ret=Write LIA_Floats_32(handleA,1,&PTemp);
ret=Write LIA Words 32(handleA,1,&oneRec); 200
ret=Write LIA Floats_32(handleA,1,&DTemp);
ret=Write LIA Words 32(handleA,1,&oneRec);
ret=Write LIA Floats_32(handleA,1,&ITemp);
ret=Write LIA Words 32(handleA,1,&oneRec);
ret=WriteLIA Floats_32(handleA,1,&ProtTemp);
ret=WriteLIA Words 32(handleA,1,&oneRec);
ret=Write LIA Floats 32(handleA,1,&DrotTemp);
}
//end sendGlobals
210
137
*ADBufferCol=bcol;
ret=Read LIA Words_32(handleA,1,&numToRec);
*ADBufferSize=numToRec;
ADBuffer=(float *)calloc(*ADBufferSize, sizeof(float));
ret=ReadLIA Floats 32(handleA,*ADBufferSize,ADBuffer); 230
)
return(ADBuffer);
//end getADBufferDSP
void calibrateSensorDSP(void)
/*////////////////////////////////////////
l/sets amplifier currents to zero
*/ 240
{
callDSPA(calibrateC);
calReady=l;
I
//end setAmpZero
void shutDownDSP(void)
/*/////////////////////////////////////////////////////////////////////
//performs a global reset and clears all memory and closes processor ID
//prints error messages if there are any
*/
{
ret=Close Processor ID(handleB);
checkReturnCode(ret); 270
ret=CloseyProcessor ID(handleA);
checkReturnCode(ret);
ClearAllLib Memory();
//end dspShutDown
/ *///////////////////////////////////////////////////////////////////
138
// LOCAL FUNCTION DEFINITIONS 280
*1
void callDSPA(int mode)
/*//////////////////////////////////////////////////////////////////////
l/switches the mode on the dsp program for processor A
*/
{
ULONG modeCopy=mode;
ret=Write_LIA_Words_32(handleA,1,&modeCopy); 290
checkReturnCode(ret);
}
//end callDSPA()
330
if (axis[0]=='Y'){
139
if(trajectory[i].pathtype== 1){
instrs j]=trajectory [i].time;
instrsbj+ 1]=trajectory[i].vyfin;
paths[i-1]=1;
j=j+2;
if(trajectory[i].pathtype==2){ 340
instrs[j]=trajectory[i].time;
instrs[j+l]=trajectory[i].ay;
instrs[j+2]=(trajectory[i].ay)/2;
paths[i-1]=2;
j=j+3;
}
if(trajectory[i].pathtype==3) {
instrs[j]= trajectory[i].time;
instrsj+11]= (trajectory[i].ay)/2;
instrs[j+2]= (trajectory[i].ay)/6; 350
paths[i-1]=3;
j=j+3;
}
}
*totTimeList=*totTimeList+trajectory[i] .time;
i++;
140
ret=Write_LIA_Words_32(handleA,1,&dspPathTempX[k]);
}
for(k=0;k<(maxInstrCount*3);k++) { 390
dspInstrTempX[k]=dspInstrX[k];
ret=WriteLIAWords_32(handleA,1,&oneRec);
ret=Write LIAFloats 32(handleA,1,&dspInstrTempX[k]);
dspInstrTempY[k]=dspInstrY[k];
ret=WriteLIAWords 32(handleA,1,&oneRec);
ret=WriteLIAFloats 32(handleA,1,&dspInstrTempY[k]);
}
/ *free((void *)dspPathTempX);
free((void *)dspPathTempY); 400
free((void *)dspInstrTempX);
free((void *)dspInstrTempY);*/
}
//end sendTrajList
141
/************************************************************************
FILE: lmd2_a.sys
This file defines all constants and variables for lmd a.c
Header Files
****************/
142
#define CR ((unsigned long*) (dsplink+0x18)) /*control registers used*/
#define SR ((unsigned long*)(dsplink+0x18)) /*by the multi channel I/O card*/
#define TIMER16 ((unsigned long*) (dsplink+0x19))
#define TIMER2 ((unsigned long*)(dsplink+OxlA))
#define PGR ((unsigned long*)(dsplink+0xlB))
#define DOR ((unsigned long*) (dsplink+0xlC))
#define DIR ((unsigned long*) (dsplink+0xlC))
#define ADCASYNC ((unsigned long*) (dsplink+0xlE)) 60
/********************
Global Variables
********************/
100
unsigned int
motorMode=XYoff, /*this variable directs the interrupts*/
calMode=0, /*checks if calibrating sensors*/
motorMoving=0, /*determins if trajPieceX should be executed*/
handshakeAB=normalAB, /*controls communication between A and B*/
intCounter=0, /*flag to determine how many interrupts have passed*/
143
maxInstrCount=0, /*max size of pathType and trajList*/
ADBufferCount=0, /*current index into ADBuffer*/
ADBufferCol=4, /*number of collumns data buffer is split into*/
*pathTypeX, /*type of trajectory piece in the x axis*/
*pathTypeY;
float
*ADBuffer, /*pointer to the buffer used to save a history of*/
*trajListX, /*motor parameters*/
*trajListY, /*explained in trajPieceX 0 see'lmd2 b.c*/
sampPeriod=0.0, /*global variables, see lmd2.h for further definitions*/
maxAmplitude=0.0,
filterBWData=O0.0,
filterBWCal=0.0,
filterBWVel=0.0,
leadMaxAccel=0.0,
leadMaxDecel=0.0,
leadMinAccel=0.0,
leadMinDecel=0.0,
IsAccel=0.0,
IsDecel=0.0,
P=0.0,
D=0.0,
I=0.0,
Prot=0.0,
Drot=0.0;
/******************************************************************
Other global variables which are not updated by the host program
volatile long hostIn=0; /*This is a handshke variable between the host and */
/*processor A, this is the first word read from the host*/
/*during any mode changes.*/
144
/*sinSig and cosSig are the sine and cosine signals from a sensor "after"
they have been adjusted using the calibration parameters*/
/*sensorOffset is the physicall offset of the sensor from the beginning edge
of one pitch. It is calculated in the reset function*/
float posX=0,posXold=0,posXR=0,posXRold=0,
velX=0,velXold=0,velXR=O,velXRold=O,
errorX=0,errorXold=0,errorXR=O,errorXRold=0,
sensorOffsetX=0, sensorOffsetXR=0,
sinSig=0, cosSig=0, Ktrack=O, Atrack=0;
170
Function Prototypes
*************************/
145
void c int04(void); /* ISR for interrupt */
void getTrajData(void);
void reset(void);
void calibrate(void);
void changeMode(void);
void saveADBuffer(void);
void dumpADBuffer(void);
void setGlobalVars(void);
void setFilterCoef(void);
float filter(float, float *, float *);
void ADin(void);
void sensorconvertX(void);
void sensorConvertXR(void);
void controlLawX(void);
void controlLawRot(void);
void posCmd(void);
void setAmpZero(void);
void DAout(void);
void getGlobals(void);
void initAnalog(void);
void setADtimer(void);
void setInterruptEOC(void);
void globalsToB(void);
void calToB();
void trajToB();
/
FILE: Imd2_a.CPP (C source code for QPC/C40B)
DESCRIPTION:
This C40 program Controls all of the DSP operations using the
TI C40 processor
MAIN
usanwassassessessenessay$,~
void main(void){
motorMode=XYoff; /*initial state of motor currents off*/
hostIn=inword(liaChanNo); / *getfirst word form host program*/
getGlobals(); / *update parametersfrom host*/
setGlobalVarsO; /*set global varibales for the new*/
/ *samplingfrequency*/
146
setFilterCoefO; / *recalculatefilter coeficients*/
*for the new sampling frequency*/
globalsToB(); / *send global parameters to procB*/
ADBuffer=calloc(ADBufferSize,sizeof(float));
pathTypeX=calloc(maxInstrCount,sizeof(unsigned long));
trajListX=calloc(maxInstrCount*3,sizeof(float));
pathTypeY=calloc(maxInstrCount,sizeof(unsigned long));
trajListY=calloc(maxInstrCount*3,sizeof(float));
initAnalogo; /*initialize analog board*/
setADtimer(; /*set timer on analog noard to desired sampling rate*/
setInterruptEOC(); /*initialize interrupts*/
/ *this is the main polling loop. It will loop forever untill one of two things
happen user input, or AID end of conversion interrupt. Either it will detect
data coming in from the host PC, in which case the
unsigned long variable, hostln, reads the first word of data and then switches
control depending on what that integer is. Also, at any time during this loop,
the interrupt routine c_int04 may be called in which case, when c int04 is finished,
control is returned exactly to the point in the loop when the interruptfunction
was originally called*/
while(l) { 50
if(cpjin_level(liaChanNo)){ /*is there data in the input comm port?*/
hostIn=inword(liaChanNo); / *readhandshake parameterfrom PC*/
switch(hostIn){
case changeModeC: motorMode=inword(liaChanNo);
if(motorMode!=XYoff) {
ADBufferCount=O; motorMoving=l;}
/ *now begin executing trajPieceX() */
break;
case GlobalsC: *CR=0x30000000; /*disable interrupts*/
getGlobalso; 60
setGlobalVars();
setFilterCoef();
*CR=0x30010000; /*enable interrupts*/
dumVar=*ADCO;
handshakeAB=globalsAB;
intCounter=0;
while (intCounter<1);
handshakeAB=normalAB;
break;
case resetC: reset(); 70
break;
case calibrateC: calibrate();
reset();
break;
case TrajDataC: getTrajData();
break;
case dumpADBufferC: *CR=0x30000000; /*disable interrupts*/
dumpADBuffer();
147
*CR=0x30010000; / *enable interrupts*/
dumVar=*ADCO; 80
break;
}
/ * end of main() */
/ ****************************** 90
Interrupt Service Routines
void c.int04(void)
Throughout the program, two functions are used to send data back and forth between
processors and between the PC and the DSP. These are outmsgo, and inmsgo .
inmsgo has three arguments: comm port number to receive data from, pointer to
memory location to store incoming data, and increment value when receiving arrays.
outmsg0 has four arguments: comm port number to send data, pointer to maemory
where data to be sent resides, size in words(elements) of the data to be sent, and
increment using when sending arrays. The first word out_msg sends is the number of
words to be sent. the first word in_msg receives is the number of words to receice,
so there is no problem when using in_msg to receive data from out msg. However, 130
when communicating with the PC, the first word of data that the PC either sends or
receives must correspond with the total number of words being sent. */
148
/ *all units for the position and velocity are 2*pi*pitch. This convention is used
position, velocity, lead angle, and all other parameters used by teh functions
controlLawX and sensorConvertX() 0 *
{ 140
ADin(); /*read in data from the AID converter*/
intCounter++;
if(intCounter>5) intCounter=4; / *this simply limits the maximum size of intCounter*/
/ *so that the variable does not become too large*/
/*in handshaking, we are only concerned when */
/ *intCountergoes above 1 */
out_msg(procB, &handshakeAB,1,1); /*send handshake message to B*/
switch(handshakeAB) {
case globalsAB: globalsToB(); /*sends global parameters to B*/
break; 150
case trajAB: trajToB(); /*sends trajectory information to B*/
break;
case calAB: calToB(); / *sends calibraton and sensor offsets to B*/
break;
case initAB: break; / *initializessensor vars on processor B*/
case normalAB: / *sends Y axis data from AID board to B*/
outmsg(procB, &motorMode, 1,1); / *state of the motor*/
out.msg(procB, &motorMoving,l,1);
/ *flag set to 1 if motor is moving, no new trajectories can be recieved until motor*/
/ *has stopped moving */ 160
outmsg(procB, &sinInY,1,1); / *signalsfrom sensor*/
out_msg(procB, &cosInY,1,1); / *signalsfrom sensor*/
inmsg(procB, &pi2trajPosX,1); / *(desired x pos)(2)(pi)*/
inmsg(procB, &pi2trajVelX,1); / *(desired y pos)(2)(pi)*/
in.msg(procB, &pi2trajPosY,1); /*(desired x vel)(2)(pi)*/
in_msg(procB, &pi2trajVelY,1); / *(desired y vel)(2)(pi)*/
sensorConvertX(; / *converts sine and cosinse sensor
signals into position and velocity for X axis *1
if(motorMode==XYRotservo) sensorConvertXR();
if((motorMode==XYstep) II (motorMode==XstepYsexva)){
cmdX=pi2trajPosX, amplitudeX=maxOutput;}
f((motorMode==XYservo) I1(motorMode==XservoYstep))
controlLawX();
in_msg(procB,&motorMoving,1);
in_msg(procB,&cmdY,1);
inmsg(procB,&litudeY,1);
inmsg(procB,&posY,1);
in_msg(procB,&velY,1);
in_msg(procB,&filtVelY,1);
if(motorMode==XYRotservo); controlLawRoto; 180
else rotation=0;
posCmdo; / *generate sine and cosine waveforms for
all phases on the motor (X and Y axis) */
if(motorMode==XYoff) setAmpZero();
DAouto; /*send commanded signals to D/A converter*/
saveADBuffer();
149
break;
}
}
/* end c_int02() */
function definitions
********************1
void globalsToB()
out_msg(procB, &sensorOffsetY,1,1);
out_msg(procB, &sinInYoffset,1,1); / *calibrationAdjustment(alpha)*/
out_msg(procB, &sinInYfactor,1,1); / *calibrationAdjustment(beta) */
out msg(procB, &cosInYoffset,1,1);
outmsg(procB, &cosInYfactor,1,1);
}/ *end calToB*/
void trajToBO
/************************************************************
sends both X and Y axis trajectory information to processor B*/
outmsg(procB, pathTypeX,maxInstrCount,1);
outmsg(procB, trajListX,maxInstrCount*3,1);
outmsg(procB, pathTypeY,maxInstrCount,1);
outmsg(procB, trajListY,maxInstrCount*3,1);
150
}
/ *end trajToB(;*/
void getTrajData(void)
/ **************************************************************/
/ *load in the trajectory data for X axis*/
/*the function in_msg expects the first value sent to be an integer*/
/ *equal to the number of items*/ 250
{
int i=0;
while(motorMoving== 1); / *wait here until previous trajectory
is finished*/
/ *startloading new trajectory, but wait until
mode change to begin running*/
*CR=0x30000000; / *disable interrupts*/
for(i=0;i<maxInstrCount;i++){ / *receive data from B*1
in_msg(liaChanNo,&pathTypeX[i],1);
in_msg(liaChanNo,&pathTypeY[i],l); 260
}
for(i=O;i< (maxInstrCount*3);i++){
in msg(liaChanNo,&trajListX[i],l);
inmsg(liaChanNo,&trajListY[i],1);
}
*CR=0x30010000; / *enable interrupts*/
dumVar=*ADCO;
handshakeAB=trajAB; /*change handshake mode*/
intCounter=0;
while(intCounter<1); / *wait for at least one 270
time step for data to be sent to b*/
handshakeAB=normalAB;
}
/ *end getTrajDataO*/
void reset(void)
/ **********************************************************/ 280
/ *This function resets all trajectory history, and sensor variables*/
/ *It also recalculatesthe sensor offset*/
/ *If accurate sensor information is desired, the required sensors need
/ *to be calibrated before this function is called*/
{
int duration;
motorMoving=0; /*puts trajectory in hold mode*/
handshakeAB=initAB;
intCounter=0;
while(intCounter<1); 290
handshakeAB=normalAB;
velXold=0; velXRold=O;
posXold=0; posXRold=O;
errorXold=0; errorXRold=O;
151
motorMode=XYstep; / *activatestepMode set currents to maximum and hold*/
timestart(l);
duration=time_read(1);
while(duration < 10E6) / *hold the motor for 0.5 see to let transients die*/
duration=time read(1);
duration=timestop(1); 300
sensorOffsetX= -posX;
sensorOffsetY= -posY;
sensorOffsetXR= -posXR;
handshakeAB=calAB;
intCounter=0;
while(intCounter<1);
handshakeAB =normalAB;
}
/ *end reset() */
310
void calibrate(void)
/ *finds calibration offsets and adjustment factors for sensor's X and XR*/
/ *the trajectory which is executed corresponds to a constant accel path with*/
/ *accel time .25 sec, cruising time 1 see, decel time .25 see, max speed 5 p/s*1
{
int i; 320
float max=O, min=0, duration;
handshakeAB=initAB; /*initialize trajectory vars on proc B*/
intCounter=0;
while(intCounter<1);
handshakeAB=normalAB;
motorMoving=0; /*puts motor into a hold mode*/
calMode=1; / *set this up to store the buffer properly*/
/ *if calMode==1 then the raw sensor signals are stored in the buffer*/
/ *if it is set to zero, then the user defined variables are saved in */
/ *the buffer*/ 330
motorMode=XYstep;
timestart(l);
duration=time_read(1);
while(duration < 10E6) / *hold the motor for 1 sec to let transients die*/
duration=time_read(1);
duration=time_stop(1);
for(i=0;i<7;i++) pathTypeX[i]=pathCal[i];
for(i=0;i<16;i++) trajListX[i]=listCal[i];
for(i=O;i<7;i++) pathTypeY[i]=pathCal[i];
for(i=0;i<16;i++) trajListY[i]=IistCal[i]; 340
handshakeAB=trajAB; /*send trajectory information to B*/
intCounter=0;
while(intCounter<1);
handshakeAB=normalAB;
ADBufferCount=O; /*clear input sample buffer*/
motorMoving=l; /*start the motor moving on the calibration trajectory*/
152
is finished */
350
/ *calibrateeach signalfrom all three sensors*/
max=O; min=O;
for(i= 1;i<(int) (ADBufferSize);i+ =6){
if(ADBuffer[i] > max) max=ADBuffer[i];
if(ADBuffer[i] < min) min=ADBuffer[i];
}
sinInXoffset=-(max+min)*0.5;
sinInXfactor=1/(max-min);
max=O; min=O;
for(i=1;i< (int) (ADBufferSize);i+=6){- 360
if(ADBuffer[i] > max) max=ADBuffer[i];
if(ADBuffer[i] < min) min=ADBuffer[i];
}
cosInXoffset=- (max+min)*0.5;
cosInXfactor=1/(max-min);
max=0; min=0;
for(i=2;i<(int)(ADBufferSize);i+=6){
if(ADBuffer[i] > max) max=ADBuffer[i];
if(ADBuffer[i] < min) min=ADBuffer[i];
} 370
sinInYoffset=-(max+min)*0.5;
sinInYfactor=1/(max-min);
max=O; min=O;
for(i=3;i< (int)(ADBufferSize);i+=6){
if(ADBuffer[i] > max) max=ADBuffer[i];
if(ADBuffer[i] < min) min=ADBuffer[i];
}
cosInYoffset=- (max+min)*0.5;
cosInYfactor=1/(max-min);
max=0; min=0; 380
for(i=4;i<(int)(ADBufferSize);i+=4){
if(ADBuffer[i] > max) max=ADBuffer[i];
if(ADBuffer[i] < min) min=ADBuffer[i];
}
sinInXRoffset=-(max+min)*0.5;
sinInXRfactor=1/(max-min);
max=0; min=0;
for(i=5;i< (int) (ADBufferSize);i+=4){
if(ADBuffer[i] > max) max=ADBuffer[i];
if(ADBuffer[i] < min) min=ADBuffer[i]; 390
}
cosInXRoffset=- (max+min)*0.5;
cosInXRfactor=1/(max-min);
handshakeAB=calAB;
intCounter=0;
while(intCounter<1);
handshakeAB=normalAB;
calMode=O; /*exit from calibration mode*/
i
/*end calibrateO*/ 400
153
void saveADBuffer(void)
/*************************************************************/
/ *saves selected data to ADBuffer*/
{
if(ADBufferCount < ADBufferSize){
if(calMode==0){
ADBufferCol=4; 410
ADBuffer[ADBufferCount++]=posX+sensorOffsetX;
ADBuffer[ADBufferCount++]=posY+sensorOffsetY;
ADBuffer[ADBufferCount++]=filtVelX;
ADBuffer[ADBufferCount++]=pi2trajPosX;
}
else{ / *calibrationmode*/
ADBufferCol=6;
ADBuffer[ADBufferCount++]=sinInX;
ADBuffer[ADBufferCount++]=cosInX;
ADBuffer[ADBufferCount++]=sinInY; 420
ADBuffer[ADBufferCount++]=cosInY;
ADBuffer[ADBufferCount++]=sinInXR;
ADBuffer[ADBufferCount++]=cosInXR;
}
}
}
/ *end save AD sample*/
430
void dumpADBuffer(void)
/****************************************************************
/ *sends ADBuffer data to the host program*/
{
while(motorMoving==1); / *wait here until previous trajectory
is finished before dumpin data*/
outmsg(liaChanNo,&ADBufferCol,1,1);
send_msg(liaChanNo,ADBuffer,ADBufferSize,1);
while(chk_dma(liaChanNo)); 440
}
/ *end dumpADBufferO;*/
void setGlobalVars(void)
/************************ ***********************************/
/ *updates control variables once they are received from the Host*/
{
float K,T1; 450
K=1414*1414;
T1=0.001;
/ *Ktrack=1888.0;*/ *used in the digital tracking algorithim*/
/*Atrack=0.665;*/ /*used in the digital tracking algorithim*/
Ktrack=K*T1+K*sampPeriod; / *Kbar*/
Atrack=(K*T1)/(K*T1+K*sampPeriod); / *Tbar*/
154
/ *the above to parameters are described in section
4.1 of my thesis, sensor position conversion*/
maxOutput=0.99*maxAmplitude/10.0*2048; / *maximum output amplitude*/
leadMaxAccel=2*PI*leadMaxAccel/360.0; /*control Law parameters*/ 460
leadMaxDecel=2*PI*leadMaxDecel/360.0;
leadMinAccel=2*PI*leadMinAccel/360.0;
leadMinDecel=2*PI*leadMinDecel/360.0;
IsAccel=(leadMaxAccel-leadMinAccel)/(2*PI*lsAccel);
IsDecel=(leadMaxDecel-leadMinDecel)/(2*PI*lsDecel);
490
float filter(float input, float *coefptr, float *histptr)
/**************************************************************/
/ *This function implements a second order IIR digital filter*/
/ *the first argument is the sample to be filtered, the second
argument is a pointer to the coeficient array. The third
argument contains the filter history for the particularsignal
each signal must have its own filter history allocatedfor it.*/
{
float output=O;
*histptr=input; 500
output += (*histptr++)*(*coefptr++).;
output += (*histptr++)*(*coefptr++);
output += (*histptr++)*(*coefptr++);
output += (*histptr++)*(*coefptr++);
output += (*histptr)*(*coefptr);
*histptr = *(--histptr);
*histptr-- = output;
*histptr = *(--histptr);
*histptr = *(--histptr);
coefptr -=4; 510
155
return(output);
/ *end filter*/
void ADin(void)
/***gets the samples from the 16 input multi****************I0
board***************
/ *gets the samples from the 16 input multi I/O board*/
volatile long input;
float *coefptr;
if(calMode==1) coefptr=coefCal;
else coefptr=coefData3;
coefptr=coefData;
input= *ADCO; input<<=4; input>>=20;
/ *necessaryto right shift the data when readingfrom the A/D*/
sinInX=(float) input;
sinInX=filter(sinInX,coefptr,histData);
input= *ADCO; input<<=4; input>>=20;
cosInX=(float) input;
cosInX=filter(cosInX,coefptr,histData+5);
input= *ADCO; input<<=4; input>>=20;
sinInY=(float) input;
sinInY=filter(sinInY,coefptr,histData+10);
input= *ADCO; input<<=4; input>>=20;
cosInY=(float) input;
cosInY=filter(cosInY,coefptr,histData+ 15);
input= *ADC1; input<<=4; input>>=20;
sinInXR=(float) input;
sinInXR=filter(sinInXR,coefptr,histData+20);
input= *ADC1; input<<=4; input>>=20;
cosInXR=(float) input;
cosInXR=filter(cosInXR,coefptr,histData+25);
)/*end ADinO*/
void sensorConvertX(void)
/*********************************************************************
calculates the X sensor position using the tracking method*/
/ *also filters the velocity signal using IIR digital filter*/
{
sinSig=sinInXfactor*(sinInX+sinInXoffset);
cosSig=cosInXfactor*(cosInX+cosInXoffset);
errorX=sinSig*cos(posXold)-cosSig*sin(posXold);
velX=velXold+Ktrack*(errorX-Atrack*errorXold);
posX=posXold+sampPeriod*velX;
errorXold=errorX;
velXold=velX;
posXold=posX;
filtVelX=filter(velX,coefVel,histVel);
)/ *end
sensorConvertX*/
156
void sensorConvertXR(void)
/*********************************************************************
calculates the XR sensor position using the tracking method*/
{570
sinSig=sinInXRfactor*(sinInXR+sinInXRoffset);
cosSig=cosInXRfactor*(cosInXR+cosInXRoffset);
errorXR=sinSig*cos(posXRold) -cosSig*sin(posXRold);
velXR=velXRold+Ktrack*(errorXR-Atrack*errorXRold);
posXR=posXRold+sampPeriod*velXR;
errorXRold=errorXR;
velXRold=velXR;
posXRold=posXR;
}
/ *end sensorConvertXR*/ 580
void controlLawX(void)
amplitudeX=fabs(P*posErrorX+D*velErrorX);
if(amplitudeX > maxOutput) amplitudeX=maxOutput;
if(fabs(posErrorX) > 450) motorMode=XYoff; /*prevents runaway condition*/
/ *stops motor current if posError> 71 *2*pi pitch, Sin*/
/ * if the current appears to be stopping for no apparentreason*/
/ *try increasing this value*/
cmdX=posX+sensorOffsetX+Klead; 610
}
/ *end controlLawO);*/
void controlLawRot(void)
157
rotError=posX-posXR; /*this may need to be modified in the future*/ 620
rotation=Prot*rotError;
}
/ *end controlLawO;*/
void posCmd(void)
/ ******************************************************************* 630
calculates the outputs to the DA converter*/
{
phaseAXR=(long) amplitudeX*sin(cmdX+rotation);
phaseAXR <<=16; /*necessary to left shift the integerfor proper
communication along teh DSPlink cable*/
phaseBXR=(long) amplitudeX*cos(cmdX+rotation);
phaseBXR <<=16;
phaseAYR=(long) amplitudeY*sin(cmdY+rotation);
phaseAYR <<=16;
phaseBYR=(long) amplitudeY*cos(cmdY+rotation); 640
phaseBYR <<=16;
phaseAXL=(long) amplitudeX*sin(cmdX-rotation);
phaseAXL <<=16;
phaseBXL=(long) amplitudeX*cos(cmdX-rotation);
phaseBXL <<=16;
phaseAYL=(long) amplitudeY*sin(cmdY-rotation);
phaseAYL <<=16;
phaseBYL=(long) amplitudeY*cos(cmdY-rotation);
phaseBYL <<=16;
650
/ *end controlLawO;*/
void setAmpZero(void)
/*********************************************************************
sets the amplifier currents to zero*/
{
motorMode=XYoff; /*set motor mode to rest*/
phaseAXR=O; 660
phaseBXR=O;
phaseAYR=O;
phaseBYR=O;
phaseAXL=O;
phaseBXL=O;
phaseAYL=O;
phaseBYL=O;
/ *outputs on DA 's should now be zero*/
}/ *end setAmpZero);*/ 670
158
void initAnalog(void)
/**************************************************************
/ *Initializesthe multi channel input board*/
{
*CR=0OL; /*board reset*/
*DACO=0xO000000; / *clear DAC channels*/
*DAC1=0x000000000;
*DAC2=0x00000000; 680
*DAC3=0x00000000;
*DAC4=0x00000000;
*DAC5=0x00000000;
*DAC6=0x00000000;
*DAC7=0x00000000;
*DOR=0OL; /*data output register*/
*PGR=0x00000000L; /*Gain =1*/
}
/ *end initAnalog*/
690
void setADtimer(void)
/*************************************/
/*calibrates the analog to digital converter chip on the AD board*/
{
long TVAL16, TVAL2;
TVAL16=0x00010000 * (long) (10000000.0 * sampPeriod);
*TIMER16=TVAL16;
TVAL2=(long) (10000000.0 * sampPeriod);
*TIMER2=TVAL2; 700
}
/ *end setADtimer()*/
void setInterruptEOC(void)
/**************************************************************/
/ *sets up and enables the interruptson the QPC board*/
{
/* Set up the C40 interrupts */
/ * Need to perform an IACK instruction to allow
external interrupts through to the C40 */ 710
159
/ *end setInterrupto*/
730
void DAout(void)
/*********************************************** **************/
/ *sends data out through the DA card*/
{
*DACO=phaseAXR;
*DAC1=phaseBXR;
*DAC2=phaseAYR;
*DAC3=phaseBYR;
*DAC4=phaseAXL; 740
*DAC5=phaseBXL;
*DAC6=phaseAYL;
*DAC7=phaseBYL;
}
/ *end DAoutO*/
750
void getGlobals(void)
/************************************************************/
/ *loads in global variable values from the host program*/
{
/ *the order of these reads must match exactly with the order of the
writes from the host C program*/
inmsg(liaChanNo,&maxInstrCount,1);
inmsg(liaChanNo,&sampPeriod,1); 760
inrsg(liaChanNo,&maxAmplitude,1);
in msg(liaChanNo,&filterBWData, 1);
in msg(liaChanNo,&filterBWCal,1);
inmsg(liaChanNo,&filterBWVel,1);
in msg(liaChanNo,&leadMaxAccel,1);
inmsg(liaChanNo,&leadMaxDecel,1);
in msg(liaChanNo,&leadMinAccel,1);
in msg(liaChanNo,&leadMinDecel,1);
in msg(liaChanNo,&lsAccel,1);
in msg(liaChanNo,&lsDecel,1); 770
in msg(liaChanNo,&P,1);
in.msg(liaChanNo,&D,1);
in msg(liaChanNo,&I,1);
in msg(liaChanNo,&Prot,1);
inmsg(liaChanNo,&Drot,1);
160
/************************************************************************
FILE: lmd2.a.sys
This file defines all constants and variables for lmd a.c
161
#define XYoff 0
#define XYstep 1
#define XservoYstep 2
#define XstepYservo 3
#define XYservo 4
#define XYRotservo 5
50
/*handshaking modes used to talk to processor A*/
#define normalAB 0
#define globalsAB 1
#define calAB 2
#define trajAB 3
#define initAB 4
/******************** 60
Global Variables
unsigned int
motorMode=XYoff, /*this variable directs the interrupts*/
motorMoving=0, /*determins if trajPieceY should be executed*/
handshakeAB=normalAB, /*used to communicate with proc A*/
maxInstrCount=0, /*max size of pathType and trajList*/
*pathTypeX,
*pathTypeY; 70
float
*trajListX,
*trajListY,
sampPeriod=0.0,
leadMaxAccel=0.0,
leadMaxDecel=0.0,
leadMinAccel=0.0,
leadMinDecel=0.0,
IsAccel=0.0, 80
IsDecel=0.0,
P=0.0,
D=0.0,
I=0.0;
Other global variables which are not updated by the host program
162
float posY=0,posYold=0,
velY=0,velYold=0,
errorY=0,errorYold=0,
sensorOffsetY=0,
sinSig=0, cosSig=0, Ktrack=O, Atrack=O; 100
/***********************
Function Prototypes
*************************/
float filter(float, float *, float *);
void sensorconvertY(void);
void controlLawY(void);
void trajPieceX(void);
void trajPieceY(void);
void globalsFromA(void);
void calFromA(void);
void trajFromA(void);
void initializeAB(void);
DESCRIPTION:
This C4O program Controls all of the DSP operations using the
TI C40 processor
163
/*****************************
MAIN
******************************
void main(void){
motorMode=XYoff; 20
globalsFromAo; /*update parameters from host*/
pathTypeX=calloc(maxInstrCount,sizeof(unsigned long));
trajListX=calloc(maxInstrCount*3,sizeQf(float));
pathTypeY=calloc(maxInstrCount,sizeof(unsigned long));
trajListY=calloc(maxInstrCount*3,sizeof(float));
while(1){
in msg(procA, &handshakeAB,1); / * wait for input from proc A: HANDSHAKE SIGNAL*/
/ *this line won't be executed untill new
information from processor A arrives*/
switch(handshakeAB) {
case globalsAB: globalsFromAo; /*reads global parameters from A*/
break; 40
case trajAB: trajFromA(; /*reads trajectory information from A*/
break;
case calAB: calFromAo; / *reads calibraton and sensor offsets from A */
break;
case initAB: initializeAB ();/ *initializes sensor variables*/
break;
case normalAB: / *readsnormal time step info from A */
/ *these lines must correspond exactly with the out_msg
lines in processor A */
in_msg(procA, &motorMode, 1); 5so
in_msg(procA, &motorMoving,1);
in_msg(procA, &sinInY,1);
in_msg(procA, &cosInY,1);
out_msg(procA, &pi2trajPosX,1,1);
out_msg(procA, &pi2trajVelX,1,1);
out_msg(procA, &pi2trajPosY,1,1);
out msg(procA, &pi2trajVelY,1,1);
if(pathTypeX[indexlX]==0) motorMoving=0;
sensorConvertY();
if((motorMode==XYstep) 1I(motorMode==XsewsoYstep)){
cmdY=pi2trajPosY, amplitudeY=maxOutput;}
if((motorMode==XYservo) 1I(motorMode==XstepYservo))
controlLawY();
outmsg(procA,&motorMoving,1 ,1);
outmsg(procA,&cmdY,1,1);
outmsg(procA,&litudeY,1,1);
out.msg(procA,&posY,1,1);
outmsg(procA,&velY,1,1);
164
outmsg(procA,&filtVelY,1,1);
if(motorMoving==1){
trajPieceX();
trajPieceY();}
pi2trajPosX=trajPosX*PI2;
pi2trajVelX=trajVelX*PI2;
pi2trajPosY=trajPosY*PI2;
pi2trajVelY=trajVelY*PI2;
break;
}
}
/ *end main*/
function definitions
void globalsFromA()
************************************************************oeens
/receives all global parameters rom proessor A
receives all global parameters and filter coeficients from processor A */
int i;
in_msg(procA, &maxInstrCount,1);
in_msg(procA, &maxOutput,1);
inmsg(procA, &sampPeriod, 1);
inmsg(procA, &leadMaxAccel,1);
inmsg(procA, &leadMinAccel,1);
in_msg(procA, &leadMaxDecel,1);
in_msg(procA, &lsAccel,1);
inmsg(procA, &lsDecel,1);
inmsg(procA, &P,1);
inmsg(procA, &D,1);
inmsg(procA, &I,1);
in_msg(procA, &Atrack,1);
inmsg(procA, &Ktrack,1);
inmsg(procA, coefVel,1);
for(i=0;i<5;i++) histVel[i]=0.0;
}/ *end globalsFromAO; */
void calFromA()
receives all calibration data and sensor offsets from processor A*/
in_msg(procA, &sensorOffsetY,1);
in_msg(procA, &sinInYoffset,1);
in_msg(procA, &sinInYfactor,1);
in_msg(procA, &cosInYoffset,1);
inmsg(procA, &cosInYfactor,1);
165
/ *end calFromA0);*/
void trajFromA()
/ ************************************************************
receives both X and Y axis trajectory informationfrom processor A */
{ 130
in msg(procA, pathTypeX,1);
in msg(procA, trajListX,1);
in msg(procA, pathTypeY,1);
in msg(procA, trajListY,1);
void initializeAB()
/ *initABO;*/ 160
float output=0;
*histptr=input;
output += (*histptr++)*(*coefptr++);
output += (*histptr++)*(*coefptr++); 170
output += (*histptr++)*(*coefptr++);
output += (*histptr++)*(*coefptr++);
output += (*histptr)*(*coefptr);
*histptr = *(--histptr);
*histptr-- = output;
*histptr = *(--histptr);
166
*histptr = *(--histptr);
/ *histptr +=4;*/
coefptr -=4;
return(output); 180
}
/ *end filter*/
void sensorConvertY(void)
/*********************************************************************
calculates the Y sensor position using the tracking method*/
/ *also filters the velocity signal using IIR digital filter*/
{ 190
sinSig=sinInYfactor*(sinInY+sinInYoffset);
cosSig=cosInYfactor*(cosInY+cosInYoffset);
errorY=sinSig*cos(posYold)-cosSig*sin(posYold);
velY=velYold+Ktrack*(errorY- Atrack*errorYold);
posY=posYold+sampPeriod*velY;
errorYold=errorY;
velYold=velY;
posYold=posY;
filtVelY=filter(velY,coefVel,histVel); 200
}
/ *end sensorConvertY*/
void controlLawY(void)
/**** *****************************************************************
calculates the control law for the Y axis */
{
posErrorY= (pi2trajPosY-posY-sensorOffsetY); 210o
velErrorY=pi2trajVelY-velY;
amplitudeY=fabs(P*posErrorY+D*velErrorY);
if(amplitudeY > maxOutput) amplitudeY=maxOutput;
if(fabs(posErrorY) > 450) motorMode=XYoff; /*prevents runaway condition*/
cmdY=posY+sensorOffsetY+Klead;
230
167
/ *end controlLaw();*/
void trajPieceX(void)
X DIRECTION
calculates the pre-programed trajectory in real time
switch(pathTypeX[indexlX]){
case 0: trajVelX=0; 260
trajPosX=trajPosOldX;
break;
168
trajPosOldX;
trajTimeOldX=trajTimeOldX+sampPeriod;
}
else {
indexlX++;
index2X=index2X+3; 290
trajVelOldX=trajVelX;
trajPosOldX=trajPosX;
trajTimeOldX=0;
break;
break;
}
}
/ *end trajPieceX()*
void trajPieceY(void)
/ ************************************************************************* 320
Y DIRECTION
trajListY is one array of floats
parameters ending with old are for the previous time step
the array pathType[] contains integers describing what "type" of path is underway:
0-no motion
1- constant velocity
2- constant acceleration
3-linear acceleration
the array trajList[] contains trajectory instructions which are dependant on the
current path Type. Each path Type will correspond with some defining parameters 330
in trajList which are:
0 constant position: none
1 constant vel: duration(time), velocity
2 constant accel: duration, a*t, 1/2*a*t 2
3 linear accel: duration,1/2*a*t^2, 1/6*a*t ^3
For example, if path Type[indezl]=1 then trajList[index2]
is equal to duration and trajList[index2+1]=vel.
*/
169
switch(pathTypeY[indexlY]){
case 0: trajVelY=O;
trajPosY=trajPosOldY;
break;
case 1: if (trajTimeOldY < trajListY[index2Y]){
trajVelY=trajListY[index2Y+ 1];
trajPosY=trajListY[index2Y+1]*trajTimeOldY + trajPosOldY;
trajTimeOldY=trajTimeOldY+sampPeriod;
else {
indexlY++;
index2Y=index2Y+2;
trajVelOldY=trajVelY;
trajPosOldY=trajPosY;
trajTimeOldY=0;
break;
else {
indexlY++;
index2Y=index2Y+3;
trajVelOldY=trajVelY;
trajPosOldY=trajPosY;
trajTimeOldY=0;
}
break;
break;
170
/ *end trajPieceY()*/
171
Bibliography
[1] Analog Devices, ADSP-2100 Applications Handbook, Second Edition, May 1987.
[2] Joe Abraham. Modeling the Sawyer Linear Stepper Motor. 2.141 Term Paper,
Massachusetts Institute of Technology, Cambridge, Massachusetts, 1992.
[3] Geoffrey S. Boyes. Synchro and Resolver Conversion. Memory Devices Ltd, Sur-
rey, U.K., 1980
[4] Lee Clark. Fiberoptic Encoder For Linear Motors and the like, United States
Patent 5,324,934, Magamation Incorporated, Lawrenceville, New Jersey, Jun.
28, 1994.
[7] Paul M. Embree, and Bruce Kimble. C Language Algorithms for Digital Signal
Processing, Prentice Hall, New Jersey, 1991.
[8] Gene F. Franklin, J. David Powell, and Michael L. Workman. Digital Control of
Dynamic Systems, Addison-Wesley, April 1992.
[9] Paul D. Gjeltema. Design of a Closed Loop Linear Motor System. M.S. Thesis,
Massachusetts Institute of Technology, Cambridge, Massachusetts, 1993.
172
[10] Dean C. Karnopp, Donald L. Margolis, and Ronald C. Rosenberg. System Dy-
namics: A Unified Approach, John Wiley & Sons, Inc., New York, 1990.
[11] Dr. Duane Hanselman. "Signal Processing Techniques for Improved Resolver-
to-Digital Conversion Accuracy" IEEE Transactions on Industrial Electronics
1990.
[12] Mashide Hirasawa, Mitsunobo Nakamura, and Makoto Kanno. "Optimum Form
of Capacitive Transducer for Displacement Measurement" IEEE Transactionson
Instrumentation and Measurement, Vol 1M-33, No 2. December 1984.
[14] B. C. Kuo and R. H. Brown. "The Step Motor Time-Optimal Control Prob-
lem" ,Proceedings: Fourteenth Annual Symposium on Incremental Motion Con-
trol Systems and Devices, ed. B. C. Kuo, 1985.
[15] Jack Nordquist and Edmond Pelta. "Constant Velocity Systems using Sawyer
Linear Motors", Proceedings, 15th Annual Symposium on Incremental Motion
Control Systems and Devices ed. B. C. Kuo, Champaign, Illinois, 1986.
[17] Bruce A. Sawyer Linear Magnetic Drive System. United States Patent 3735231,
Woodland Hills, California. May 22, 1973.
173
[19] Alexander H. Slocum. Precision Machine Design, Prentice Hall, New Jersey,
1992.
[20] Surahammars Bruk. Non-Oriented Electrical Steels Catalog and Design Guide.
Sweden, 1987.
, 'y
i
174