Skip to content

Commit c6fab68

Browse files
Some more minor cleanup, also moved the TrajectoryGenerator to the example.
1 parent 023e65f commit c6fab68

File tree

4 files changed

+260
-265
lines changed

4 files changed

+260
-265
lines changed

example-simulations/src/main/java/us/ihmc/exampleSimulations/yoFilteredDouble/emptyRobotSCS/YoFilteredDoubleController.java

Lines changed: 235 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package us.ihmc.exampleSimulations.yoFilteredDouble.emptyRobotSCS;
22

3+
import us.ihmc.exampleSimulations.yoFilteredDouble.emptyRobotSCS.YoFilteredDoubleController.TrajectoryGenerator.ChirpType;
4+
import us.ihmc.exampleSimulations.yoFilteredDouble.emptyRobotSCS.YoFilteredDoubleController.TrajectoryGenerator.Trajectory;
35
import us.ihmc.robotics.math.filters.ContinuousTransferFunction;
46
import us.ihmc.robotics.math.filters.TransferFunctionDiscretizer;
57
import us.ihmc.robotics.math.filters.YoFilteredDouble;
6-
import us.ihmc.robotics.math.trajectories.generators.TrajectoryGenerator;
7-
import us.ihmc.robotics.math.trajectories.generators.TrajectoryGenerator.ChirpType;
8-
import us.ihmc.robotics.math.trajectories.generators.TrajectoryGenerator.Trajectory;
98
import us.ihmc.simulationconstructionset.util.RobotController;
109
import us.ihmc.yoVariables.registry.YoRegistry;
1110
import us.ihmc.yoVariables.variable.YoDouble;
1211

12+
import java.util.ArrayList;
13+
1314
public class YoFilteredDoubleController implements RobotController
1415
{
1516
private YoRegistry registry;
@@ -195,4 +196,235 @@ public void doControl()
195196
RefTraj2_nonjump_Filtered_Var.set(sinusoid_input.getDoubleValue());
196197
chirpFreqHz.set(chirp.getFreqCheckRad() / (2 * Math.PI));
197198
}
199+
200+
/**
201+
* Class is designed for quickly generating several different trajectory types. A majority of the classes are self-explanatory.
202+
* The Triangle Waveform comes from https://en.wikipedia.org/wiki/Triangle_wave, denoting an alternative equation because the mod
203+
* operator is not the remainder operator.
204+
* The Multi-Square Waveform will generate a series of square waves in order repeating forever. It can be initialized using:
205+
* DesiredTrajectory = new TrajectoryGenerator("OpenLoop", registry, Trajectory.MULTI_SQUARE, 2000.0, 0.2, new ArrayList<Double>(Arrays.asList(1000.0, 0.0,
206+
* 2000.0, 0.0, 3000.0, 0.0)));
207+
* The Chirp signal is a sinusoid with a consistent amplitude, but is increasing in frequency. It is commonly used for System ID,
208+
* and was used for identifying the transfer function of the actuator. The equation was given by Dr. Southward from VT in the ME Dept.
209+
* There are two options for chirp signals: linear or exponential. This refers to the rate at which the frequency is increasing and
210+
* this choice depends on which frequencies you care about.
211+
*
212+
* @author Connor Herron
213+
*/
214+
215+
public static class TrajectoryGenerator
216+
{
217+
private String name;
218+
private double totalTime;
219+
private double minFreqRad;
220+
private double maxFreqRad;
221+
private double prevCos;
222+
private double prevSin;
223+
private double chirpSlope;
224+
private double chirpAmplitude;
225+
private double curFreqRad;
226+
private double expoCoeff;
227+
private YoDouble freqCheckRad;
228+
private YoRegistry registry;
229+
private ArrayList<Double> multiSquareList = new ArrayList<>();
230+
231+
private double waveAmp;
232+
private double waveFreq;
233+
private double rampSlope;
234+
private double phaseOffset;
235+
private double amplitudeOffset;
236+
private double wavePeriod;
237+
238+
private int initFlag = 0;
239+
240+
public enum Trajectory
241+
{
242+
STEP, RAMP, SINE, TRIANGLE, SAWTOOTH, SQUARE, MULTI_SQUARE, CHIRP
243+
}
244+
245+
public enum ChirpType
246+
{
247+
LINEAR, EXPONENTIAL
248+
}
249+
250+
private Trajectory myTrajectory;
251+
private ChirpType myChirpType;
252+
253+
public TrajectoryGenerator(String name, YoRegistry registry, Trajectory desiredWaveform, double waveAmp)
254+
{
255+
this(name, registry, desiredWaveform, waveAmp, 0.0);
256+
}
257+
258+
public TrajectoryGenerator(String name, YoRegistry registry, Trajectory desiredWaveform, double waveAmp, double waveFreq)
259+
{
260+
this(name, registry, desiredWaveform, waveAmp, waveFreq, 0.0);
261+
}
262+
263+
public TrajectoryGenerator(String name, YoRegistry registry, Trajectory desiredWaveform, double waveAmp, double waveFreq, double rampSlope)
264+
{
265+
this(name, registry, desiredWaveform, waveAmp, waveFreq, rampSlope, 0.0, 0.0);
266+
}
267+
268+
public TrajectoryGenerator(String name,
269+
YoRegistry registry,
270+
Trajectory desiredWaveform,
271+
double waveAmp,
272+
double waveFreq,
273+
double rampSlope,
274+
double phaseOffset,
275+
double amplitudeOffset)
276+
{
277+
this.name = name;
278+
this.registry = registry;
279+
this.myTrajectory = desiredWaveform;
280+
this.waveAmp = waveAmp;
281+
this.waveFreq = waveFreq;
282+
this.wavePeriod = 1 / waveFreq;
283+
this.rampSlope = rampSlope;
284+
this.phaseOffset = phaseOffset;
285+
this.amplitudeOffset = amplitudeOffset;
286+
}
287+
288+
public TrajectoryGenerator(String name, YoRegistry registry, Trajectory desiredWaveform, double waveAmp, double waveFreq, ArrayList<Double> multiSquare)
289+
{
290+
this.name = name;
291+
this.registry = registry;
292+
this.myTrajectory = desiredWaveform;
293+
this.waveAmp = waveAmp;
294+
this.waveFreq = waveFreq;
295+
this.wavePeriod = wavePeriod;
296+
this.multiSquareList = multiSquare;
297+
}
298+
299+
// For running chirp signals
300+
public TrajectoryGenerator(String name,
301+
YoRegistry registry,
302+
Trajectory desiredWaveform,
303+
ChirpType desiredChirpType,
304+
double totalTime,
305+
double amplitude,
306+
double minFreqHz,
307+
double maxFreqHz)
308+
{
309+
this.name = name;
310+
this.registry = registry;
311+
this.myTrajectory = desiredWaveform;
312+
this.myChirpType = desiredChirpType;
313+
314+
freqCheckRad = new YoDouble(name + "freqCheckRad", registry);
315+
316+
initializeChirpSignal(totalTime, amplitude, minFreqHz, maxFreqHz);
317+
}
318+
319+
public void initializeChirpSignal(double totalTime, double amplitude, double minFreqHz, double maxFreqHz)
320+
{
321+
this.totalTime = totalTime;
322+
this.minFreqRad = 2 * Math.PI * minFreqHz;
323+
this.maxFreqRad = 2 * Math.PI * maxFreqHz;
324+
this.chirpAmplitude = amplitude;
325+
326+
// Calculate the slope of the chirp signal.
327+
this.chirpSlope = (this.maxFreqRad - this.minFreqRad) / (totalTime);
328+
329+
// Initialize coefficient for the exponential function
330+
this.expoCoeff = Math.log10(maxFreqRad / minFreqRad) / totalTime;
331+
332+
// initialize prevCos and prevSin
333+
this.prevCos = 1.0;
334+
this.prevSin = 0.0;
335+
}
336+
337+
private double alpha;
338+
private double beta;
339+
private double cosNew;
340+
private double sinNew;
341+
342+
/**
343+
* Method calculates the open loop current command (mA) to send for a linear sinusoidal chirp signal. This method is initialized
344+
* with the initializeOLChirpSignal method above.
345+
*
346+
* @param t_sec real time seconds.
347+
* @param dT real time control dT
348+
* @return commanded motor current (mA)
349+
*/
350+
public double runChirpSignal(double t_sec, double dT)
351+
{
352+
// Calculate current Freq (Rad)
353+
switch (myChirpType)
354+
{
355+
case LINEAR:
356+
this.curFreqRad = (this.chirpSlope * t_sec + this.minFreqRad);
357+
break;
358+
case EXPONENTIAL:
359+
this.curFreqRad = this.minFreqRad * Math.pow(10, this.expoCoeff * t_sec);
360+
break;
361+
}
362+
363+
// Calculate new sinusoid value using double angle identity formula.
364+
alpha = Math.cos(this.curFreqRad * dT);
365+
beta = Math.sin(this.curFreqRad * dT);
366+
367+
cosNew = alpha * this.prevCos - beta * this.prevSin;
368+
sinNew = beta * this.prevCos + alpha * this.prevSin;
369+
370+
this.prevCos = cosNew;
371+
this.prevSin = sinNew;
372+
373+
if (t_sec > this.totalTime)
374+
{
375+
freqCheckRad.set(0.0);
376+
return 0.0;
377+
}
378+
else
379+
{
380+
freqCheckRad.set(this.curFreqRad);
381+
return this.chirpAmplitude * sinNew;
382+
}
383+
}
384+
385+
// Run method during loop.
386+
public double updateTrajectory(double curSecTime, double dT)
387+
{
388+
switch (myTrajectory)
389+
{
390+
case STEP:
391+
return waveAmp;
392+
case RAMP:
393+
return rampSlope * curSecTime;
394+
case SINE:
395+
return waveAmp * Math.sin(2 * Math.PI * waveFreq * curSecTime + phaseOffset) + amplitudeOffset;
396+
case TRIANGLE:
397+
return (4 * waveAmp / wavePeriod) * Math.abs((((curSecTime - (wavePeriod / 4)) % wavePeriod) + wavePeriod) % wavePeriod - wavePeriod / 2)
398+
- waveAmp;
399+
case SAWTOOTH:
400+
return waveAmp * waveFreq * Math.abs(curSecTime % (1 / waveFreq));
401+
case SQUARE:
402+
return waveAmp * Math.signum(Math.sin(2 * Math.PI * waveFreq * curSecTime + phaseOffset)) + amplitudeOffset;
403+
case MULTI_SQUARE:
404+
return waveAmp = multiSquareList.get(Math.floorMod((int) (curSecTime * waveFreq), multiSquareList.size()));
405+
case CHIRP:
406+
return runChirpSignal(curSecTime, dT);
407+
default:
408+
return 0.0;
409+
}
410+
}
411+
412+
// Wave Frequency is defined as [rad/s]
413+
public double updateTrajectoryDot(Trajectory Desired_Trajectory, double curSecTime, double waveAmp, double waveFreq, double phaseOffset)
414+
{
415+
if (Desired_Trajectory == Trajectory.SINE)
416+
{
417+
return 2 * Math.PI * waveFreq * waveAmp * Math.cos(2 * Math.PI * waveFreq * curSecTime + phaseOffset);
418+
}
419+
else
420+
{
421+
return 0;
422+
}
423+
}
424+
425+
public double getFreqCheckRad()
426+
{
427+
return freqCheckRad.getDoubleValue();
428+
}
429+
}
198430
}

ihmc-robotics-toolkit/src/main/java/us/ihmc/robotics/math/filters/ContinuousTransferFunction.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ public ContinuousTransferFunction()
3434
* @param numerator - numerator coefficients in power order (see above).
3535
* @param denominator - denominator coefficients in power order (see above).
3636
*/
37-
public ContinuousTransferFunction(double k, double numerator[], double denominator[])
37+
public ContinuousTransferFunction(double k, double[] numerator, double[] denominator)
3838
{
3939
this("", k, numerator, denominator);
4040
}
4141

42-
public ContinuousTransferFunction(String name, double k, double numerator[], double denominator[])
42+
public ContinuousTransferFunction(String name, double k, double[] numerator, double[] denominator)
4343
{
4444
this.name = name;
4545
this.k = k;
@@ -56,13 +56,13 @@ public ContinuousTransferFunction(String name, ContinuousTransferFunction[] seve
5656
{
5757
this.name = name;
5858

59-
int numOfTranferFunctionsToCascade = severalContinuousTransferFunctions.length;
59+
int numOfTransferFunctionsToCascade = severalContinuousTransferFunctions.length;
6060
ContinuousTransferFunction tf_total;
6161

6262
// Initialize total transfer function with first transfer function.
6363
tf_total = severalContinuousTransferFunctions[0];
6464

65-
for (int i = 1; i < numOfTranferFunctionsToCascade; i++)
65+
for (int i = 1; i < numOfTransferFunctionsToCascade; i++)
6666
{
6767
tf_total = CascadeTwoTransferFunctions(tf_total, severalContinuousTransferFunctions[i]);
6868
}
@@ -75,10 +75,11 @@ public ContinuousTransferFunction(String name, ContinuousTransferFunction[] seve
7575

7676
/**
7777
* Method cascades two transfer functions, H1(s) and H2(s) and returns a new transfer function H3(s).
78-
*
79-
* N1(s) N2(s) N3(s)
78+
* <pre>
79+
* N1(s) N2(s) N3(s)
8080
* H3(s) = H1(s)*H2(s) = k1 * ----- k2 * ----- = k3 * -----
81-
* D1(s) D2(s) D3(s)
81+
* D1(s) D2(s) D3(s)
82+
* </pre>
8283
*
8384
* @param H1 - Transfer Function 1
8485
* @param H2 - Transfer Function 2
@@ -89,8 +90,8 @@ private ContinuousTransferFunction CascadeTwoTransferFunctions(ContinuousTransfe
8990
ContinuousTransferFunction H3 = new ContinuousTransferFunction();
9091

9192
H3.setGain(H1.getGain() * H2.getGain());
92-
H3.setNumerator(CascadePolynomials(H1.getNumerator(), H2.getNumerator()));
93-
H3.setDenominator(CascadePolynomials(H1.getDenominator(), H2.getDenominator()));
93+
H3.setNumerator(cascadePolynomials(H1.getNumerator(), H2.getNumerator()));
94+
H3.setDenominator(cascadePolynomials(H1.getDenominator(), H2.getDenominator()));
9495

9596
return H3;
9697
}
@@ -102,7 +103,7 @@ private ContinuousTransferFunction CascadeTwoTransferFunctions(ContinuousTransfe
102103
* @param poly2
103104
* @return poly3 = poly1*poly2
104105
*/
105-
private double[] CascadePolynomials(double[] poly1, double[] poly2)
106+
private static double[] cascadePolynomials(double[] poly1, double[] poly2)
106107
{
107108
// Initialize new polynomial to new size.
108109
double[] poly3 = new double[poly1.length + poly2.length - 1];

ihmc-robotics-toolkit/src/main/java/us/ihmc/robotics/math/filters/YoFilteredDouble.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,20 @@
2222
public class YoFilteredDouble
2323
{
2424

25-
private TransferFunctionDiscretizer filter;
25+
private final TransferFunctionDiscretizer filter;
2626

27-
private DMatrixRMaj inputCoefficients;
28-
private DMatrixRMaj outputCoefficients;
27+
private final DMatrixRMaj inputCoefficients;
28+
private final DMatrixRMaj outputCoefficients;
2929

30-
private DMatrixRMaj inputHistory;
31-
private DMatrixRMaj outputHistory;
30+
private final DMatrixRMaj inputHistory;
31+
private final DMatrixRMaj outputHistory;
3232

3333
private final YoDouble filteredYoDouble;
3434
private final YoDouble rawYoDouble;
3535
private boolean firstTick = true;
36-
private double[] inTemp;
37-
private double[] outTemp;
38-
private double newFilteredValue;
39-
private boolean safeStartup;
36+
private final double[] inTemp;
37+
private final double[] outTemp;
38+
private final boolean safeStartup;
4039

4140
public YoFilteredDouble(YoRegistry registry, TransferFunctionDiscretizer filter)
4241
{
@@ -49,11 +48,11 @@ public YoFilteredDouble(String name, YoRegistry registry, TransferFunctionDiscre
4948
}
5049

5150
/**
52-
* @param name - class name
53-
* @param registry - YoRegistry to add YoVariables to.
54-
* @param filter - Filter object from trec-simulation-tools.YoVariables.
55-
* @param safeStartup - Recommended to set true. Boolean refers to whether you would like to set the
56-
* to avoid input/output histories of zero. Without this enabled, you will start the first ticks with high outputs.
51+
* @param name class name
52+
* @param registry YoRegistry to add YoVariables to.
53+
* @param filter Filter object from trec-simulation-tools.YoVariables.
54+
* @param safeStartup Recommended to set true. Boolean refers to whether you would like to set the to avoid input/output histories of zero.
55+
* Without this enabled, you will start the first ticks with high outputs.
5756
*/
5857
public YoFilteredDouble(String name, YoRegistry registry, TransferFunctionDiscretizer filter, boolean safeStartup)
5958
{
@@ -127,7 +126,7 @@ public void set(double newRawValue)
127126
inputHistory.set(1, inTemp.length, false, inTemp);
128127

129128
// Solve for new filtered value.
130-
newFilteredValue = CommonOps_DDRM.dot(inputCoefficients, inputHistory) + CommonOps_DDRM.dot(outputCoefficients, outputHistory);
129+
double newFilteredValue = CommonOps_DDRM.dot(inputCoefficients, inputHistory) + CommonOps_DDRM.dot(outputCoefficients, outputHistory);
131130

132131
// Set new filtered value.
133132
filteredYoDouble.set(newFilteredValue);

0 commit comments

Comments
 (0)