Code Examples
A repository of 155 code examples for BeepBeep
DetectSkidding.java
1 /*
2  BeepBeep, an event stream processor
3  Copyright (C) 2008-2023 Sylvain HallĂ©
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published
7  by the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 package robot;
19 
20 import static ca.uqac.lif.cep.Connector.connect;
21 
22 import java.util.Vector;
23 
24 import ca.uqac.lif.cep.GroupProcessor;
25 import ca.uqac.lif.cep.Processor;
26 import ca.uqac.lif.cep.Pushable;
27 import ca.uqac.lif.cep.complex.RangeCep;
28 import ca.uqac.lif.cep.functions.ApplyFunction;
29 import ca.uqac.lif.cep.functions.Constant;
30 import ca.uqac.lif.cep.functions.Cumulate;
31 import ca.uqac.lif.cep.functions.CumulativeFunction;
32 import ca.uqac.lif.cep.functions.FunctionTree;
33 import ca.uqac.lif.cep.functions.IfThenElse;
34 import ca.uqac.lif.cep.functions.StreamVariable;
35 import ca.uqac.lif.cep.functions.TurnInto;
36 import ca.uqac.lif.cep.io.Print.Println;
37 import ca.uqac.lif.cep.tmf.Fork;
38 import ca.uqac.lif.cep.tmf.Freeze;
39 import ca.uqac.lif.cep.tmf.Window;
40 import ca.uqac.lif.cep.util.Bags;
41 import ca.uqac.lif.cep.util.Booleans;
42 import ca.uqac.lif.cep.util.Numbers;
43 
44 /**
45  * Detects situations where a moving robot loses traction and starts skidding.
46  * For the purpose of this example, skidding is is identified as a prolonged
47  * interval during which the orientation of the robot differs significantly
48  * from the orientation of its movement (i.e. the robot is pointing in one
49  * direction but moving in another one).
50  * <p>
51  * The example does not merely detect a skidding interval (which would be a
52  * relatively classical case of runtime verification). Rather, it summarizes
53  * a skidding situation by outputting a complex event called a "skidding
54  * incident", which is only emitted once the skidding ends. This event is a
55  * triplet containing:
56  * <ul>
57  * <li>The modulus of the robot's speed vector at the start of the skidding
58  * interval (i.e. the initial speed when the skidding started)</li>
59  * <li>The number of consecutive events the skidding incident is spanning</li>
60  * <li>The maximum deviation observed between the robot's orientation and
61  * its direction of motion during the skidding incident</li>
62  * </ul>
63  * This example makes use of the {@link RangeCep} processor, which can create
64  * "complex" events summarizing a stream of lower-level events.
65  * <p>
66  * To make the example more realistic, the detection of skidding allows a
67  * few "normal" events to occur interspersed with skidding events. The
68  * condition is that <i>m</i> out of <i>n</i> successive events should reveal
69  * a skidding state for the overall sequence be considered as an occurrence of
70  * skidding.
71  *
72  * @author Sylvain HallĂ©
73  */
74 public class DetectSkidding
75 {
76 
77  @SuppressWarnings("unused")
78  public static void main(String[] args)
79  {
80 
81  /* The angle (in radians) between the orientation and velocity over which
82  * the robot is considered as skidding. We arbitrarily use 30 degrees. */
83  float skid_angle = rad(30);
84 
85  /* The width of the window (n) and the minimum number of skidding events
86  * in the window (m) required to recognize a skidding situation. */
87  int m = 2, n = 3;
88 
89  /* Create a function that measures the angular deviation between the
90  * robot's orientation and the velocity vector. */
91  FunctionTree angular_dev = new FunctionTree(Numbers.absoluteValue,
92  new FunctionTree(Numbers.subtraction,
93  new FunctionTree(RobotEvent.getAngle, StreamVariable.X),
94  new FunctionTree(ArcTanVector.instance, new FunctionTree(RobotEvent.getVel, StreamVariable.X))));
95 
96  /* Create a function that checks if a robot event represents a skidding
97  * state. This is the case when the difference between the robot's
98  * orientation and the angle of its velocity vector exceed the skid angle
99  * defined above. */
100  FunctionTree skidding = new FunctionTree(Numbers.isGreaterOrEqual,
101  angular_dev.duplicate(), new Constant(skid_angle));
102 
103  /* Create a processor that checks if a sequence of events represents the
104  * start of a skidding situation. This happens when the robot is in a
105  * skidding state for m out of n successive events.
106  */
107  GroupProcessor skid_start = new GroupProcessor(1, 1);
108  {
109  Window win = new Window(new GroupProcessor(1, 1) {{
110  Fork f = new Fork(3);
111  ApplyFunction eq = new ApplyFunction(skidding);
112  connect(f, 0, eq, 0);
113  ApplyFunction ite = new ApplyFunction(IfThenElse.instance);
114  connect(eq, 0, ite, 0);
115  TurnInto one = new TurnInto(1);
116  connect(f, 1, one, 0);
117  connect(one, 0, ite, 1);
118  TurnInto zero = new TurnInto(0);
119  connect(f, 2, zero, 0);
120  connect(zero, 0, ite, 2);
121  Cumulate sum = new Cumulate(new CumulativeFunction<Number>(Numbers.addition));
122  connect(ite, sum);
123  addProcessors(f, eq, ite, one, zero, sum).associateInput(f).associateOutput(sum);
124  }}, n);
125  ApplyFunction gt = new ApplyFunction(new FunctionTree(Numbers.isGreaterOrEqual, StreamVariable.X, new Constant(m)));
126  connect(win, gt);
127  skid_start.addProcessors(win, gt).associateInput(win).associateOutput(gt);
128  }
129 
130  /* Create a processor that checks if a sequence of events represents the
131  * end of a skidding situation. For simplicity we take it as the negation
132  * of the start condition. */
133  GroupProcessor skid_end = new GroupProcessor(1, 1) {{
134  Processor cond = skid_start.duplicate();
135  ApplyFunction not = new ApplyFunction(Booleans.not);
136  connect(cond, not);
137  addProcessors(not, cond).associateInput(cond).associateOutput(not);
138  }};
139 
140  /* Create a processor that gets the module of the velocity vector at the
141  * start of a skidding incident. */
142  GroupProcessor start_vel = new GroupProcessor(1, 1) {{
143  ApplyFunction get_vel = new ApplyFunction(RobotEvent.getVel);
144  Freeze f = new Freeze();
145  connect(get_vel, f);
146  addProcessors(get_vel, f).associateInput(get_vel).associateOutput(f);
147  }};
148 
149  /* Create a processor that counts events in the skidding incident. */
150  GroupProcessor ev_count = new GroupProcessor(1, 1) {{
151  TurnInto one = new TurnInto(1);
152  Cumulate sum = new Cumulate(new CumulativeFunction<Number>(Numbers.addition));
153  connect(one, sum);
154  addProcessors(one, sum).associateInput(one).associateOutput(sum);
155  }};
156 
157  /* Create a processor that calculates the maximum angular deviation. */
158  GroupProcessor max_angle = new GroupProcessor(1, 1) {{
159  ApplyFunction dev = new ApplyFunction(angular_dev.duplicate());
160  Cumulate max = new Cumulate(new CumulativeFunction<Number>(Numbers.maximum));
161  connect(dev, max);
162  addProcessors(dev, max).associateInput(dev).associateOutput(max);
163  }};
164 
165  /* Create a CEP processor that creates sliding events out of robot
166  * events. */
167  RangeCep cep = new RangeCep(skid_start,
168  new Processor[] {start_vel, ev_count, max_angle},
169  new Bags.ToList(Number.class, Number.class, Number.class));
170  connect(cep, new Println());
171 
172  /* Push a few robot events to the pipeline to witness the detection of a
173  * skidding incident. Normally the pipeline would be connected to a source
174  * of robot events (such as a simulator) instead of manually pushing events
175  * like what is done here. */
176  RobotEvent e_s = new RobotEvent(vecp(0, 0), vecp(1, rad(45)), rad(15)); // skidding
177  RobotEvent e_n = new RobotEvent(vecp(0, 0), vecp(1, rad(45)), rad(45)); // normal
178  Pushable p = cep.getPushableInput();
179  p.push(e_s);
180  p.push(e_n);
181  p.push(e_n);
182  p.push(e_s);
183  p.push(e_s);
184  p.push(e_n);
185  p.push(e_n);
186  }
187 
188  /**
189  * Utility method to create a 2D vector from Cartesian coordinates.
190  * @param x The first coordinate
191  * @param y The second coordinate
192  * @return The vector
193  */
194  /*@ non_null @*/ public static Vector<Float> vecc(Number x, Number y)
195  {
196  Vector<Float> v = new Vector<Float>(2);
197  v.add(x.floatValue());
198  v.add(y.floatValue());
199  return v;
200  }
201 
202  /**
203  * Utility method to create a 2D vector from polar coordinates.
204  * @param r The modulus
205  * @param theta The angle (in radians)
206  * @return The vector
207  */
208  /*@ non_null @*/ public static Vector<Float> vecp(Number r, Number theta)
209  {
210  Vector<Float> v = new Vector<Float>(2);
211  v.add(r.floatValue() * (float) Math.cos(theta.floatValue()));
212  v.add(r.floatValue() * (float) Math.sin(theta.floatValue()));
213  return v;
214  }
215 
216  /**
217  * Converts degrees to radians.
218  * @param deg The angle in degrees
219  * @return The angle in radians
220  */
221  public static float rad(float deg)
222  {
223  return (float) (2 * Math.PI * deg / 360f);
224  }
225 }
static final ArcTanVector instance
A publicly visible instance of the function.
static float rad(float deg)
Converts degrees to radians.
static final GetAngle getAngle
Static reference to the function GetAngle.
Definition: RobotEvent.java:44
Detects situations where a moving robot loses traction and starts skidding.
static final GetVelocity getVel
Static reference to the function GetVelocity.
Definition: RobotEvent.java:39
Function that calculates the angle of a two-dimensional vector.
Event representing the status of a fictional robot that moves in a two-dimensional space...
Definition: RobotEvent.java:29
static Vector< Float > vecc(Number x, Number y)
Utility method to create a 2D vector from Cartesian coordinates.
static Vector< Float > vecp(Number r, Number theta)
Utility method to create a 2D vector from polar coordinates.