Module simulationEnvironment
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 from math import *
40 import numpy
41 import random
42
43 from messages import PrimitiveMessage
44
46 """
47
48 Based on AIMA's environment:
49 Abstract class representing an Environment.
50 'Real' Environment classes inherit from this.
51 Your Environment will typically need to implement:
52 percept: Define the percept that an agent sees.
53 executeAction: Define the effects of executing an action.
54
55 """
56
57 - def __init__( self, initialState, shipAgents ):
58 """
59 Initialize the simulation environment.
60
61 @type initialState: State
62 @param initialState: The initial state of the sea and ships
63
64 @type shipAgents: array of Agents
65 @param shipAgents: The initial state of the sea and ships
66 """
67 self.state = initialState
68 self.shipAgents = shipAgents
69
70 self.msgCallbacks = {}
71 self.msgCallbacks[PrimitiveMessage.TYPE_VISIT] = self.processMsgVisit.__name__
72 self.msgCallbacks[PrimitiveMessage.TYPE_EDGE_LENGTH] = self.processMsgEdgeLength.__name__
73
74
75
76
77
78
79 self.time = 0
80 self.visitTimes = {}
81 self.edgeLengths = {}
82
83
84
86 """
87 Return the percept that the agent sees at this point.
88 We view the world such that the complete state is sent to
89 a ship, and a ship can only extract from it what it can percept.
90 """
91 return self.state.ships[agentIndex].getPercepts(self.state)
92
94 """Change the world to reflect this action.
95 Here, we only change the ship's internal state.
96 The external state will be changed in exogenousChange()
97
98 @type agentIndex: int
99 @param agentIndex: sort of an agent-id - an agent's index in the array of all agents
100
101 @type action: CompositeAction
102 @param action: An action chosen by the agent, to be executed on the ship
103 """
104 self.state.ships[agentIndex].executeAction(action)
105
107 """
108 Compute a change in the world state, that is external to the agent.
109 This function is called in every simulation step, after computing the
110 effects of the agent's actions.
111
112 Here the change that is computed is:
113 - A change in the ships' external states, which are outside the control
114 of the agent. (Ships' internal states are changed by agent actions.)
115 - A change in the sea conditions
116 """
117
118
119 for i in range(len(self.state.ships)):
120 self.updateShipExternalState(i)
121
122
123 self.computeChangeInSeaConditions(timePassed=1)
124
126 """
127 Run the SimulationEnvironment for one time step (One second).
128
129 Clarification, originally from the AIMA book's code:
130 If the actions and exogenous changes are independent,
131 this method will do.
132 If there are interactions between them, you'll need to
133 override this method.
134 """
135 self.time += 1
136
137 if not self.isDone():
138 actions = [agent.getAction(self.percept(agentIndex)) for agentIndex, agent in enumerate(self.shipAgents)]
139 for (agentIndex, action) in enumerate(actions):
140 self.executeAction(agentIndex, action)
141 self.exogenousChange()
142
143
144
145
146 self.processAgentsMessages()
147
148
149 '''
150 if not self.isDone():
151 actions = [agent.getAction(self.percept(agent)) for agent in self.shipAgents]
152 for (agentIndex, action) in enumerate(actions):
153 self.executeAction(agentIndex, action)
154 self.exogenousChange()
155 '''
156
157
159 "By default, we're done when we can't find a live agent."
160
161
162
163 return False
164
165 - def run(self, steps=250000):
166 """Run the SimulationEnvironment for given number of time steps."""
167 for step in xrange(steps):
168 if self.isDone():
169 return
170 self.step()
171 print 'Done!'
172
174 """
175 Compute ship position change in time, given the current state.
176
177 Computation of the next state is based on the world state,
178 the ship internal and external state and time passed.
179
180 Currently, we approximate ship movement using the following model:
181 - For forward motion, we model forward force that operates on the ship
182 by the engine, and a drag, which is quadratic in the ship's speed.
183 - For turning, there is an rotational torque that is applied by the rudder,
184 and is proportional to the ship's speed, and to sin(rudder-steering-angle),
185 which is the projection of the rudder on the lateral direction.
186 There is also a rotational drag force, that is quadratic in the ship's
187 angular speed, (need to check about the accuracy of this modelling).
188 - Based on the above forces and the ship's mass, we compute the
189 forward and angular accelerations.
190 - Then based on the average forward and angular speed in a given time step,
191 computed using the above accelerations, we infer the turn radius,
192 and based on that, compute the ship position at the end of this time-step.
193
194 Approximations we make:
195
196 - Although the angular acceleration depends on forward speed,
197 which is changing due to forward acceleration, we still
198 assume constant angular acceleration, based on the avg. speed in this step.
199 - Forward acceleration computation does not take into account the affects
200 of turning which (might?) slow it down.
201 - Forward acceleration depends on the drag, which is changing with speed change,
202 but we approximate the drag based on the initial speed of a time step
203
204 """
205
206
207
208
209 ship = self.state.ships[shipIndex]
210 s = self.state.shipExternalStates[shipIndex]
211 shipSpeed = s.speed
212 angularSpeed = s.angularSpeed
213 timeStepLength = 1
214
215
216
217 forwardForce = ship.getForwardForce(shipSpeed)
218 forwardAcceleration = ship.getForwardAccelerationFromForce(forwardForce)
219
220
221 avgSpeed = shipSpeed + forwardAcceleration * timeStepLength * 0.5
222
223
224
225 steeringForce = ship.getSteeringForce(avgSpeed, angularSpeed)
226 angularAcceleration = ship.getAngularAccelerationFromForce(steeringForce)
227
228 avgAngularSpeed = angularSpeed + angularAcceleration * timeStepLength * 0.5
229
230 avgAngularSpeedRad = radians(avgAngularSpeed)
231
232
233
234
235
236
237
238
239 if avgAngularSpeedRad == 0:
240 relXTranslation = avgSpeed * timeStepLength
241 relYTranslation = 0
242 else:
243 radius = float(avgSpeed) / avgAngularSpeedRad
244 relXTranslation = radius * sin(avgAngularSpeedRad * timeStepLength)
245 relYTranslation = radius * (1 - cos(avgAngularSpeedRad * timeStepLength))
246
247 if avgAngularSpeedRad < 0:
248 relYTranslation = -relYTranslation
249
250
251 startAngle = s.orientation
252
253 cos_start_angle = cos(radians(startAngle))
254 sin_start_angle = sin(radians(startAngle))
255 frontDirectionVector = numpy.array( [cos_start_angle , sin_start_angle] )
256
257 sideDirectionVector = numpy.array( [-sin_start_angle, cos_start_angle] )
258
259 shipPos = numpy.array( [s.x, s.y] )
260
261 shipNewPos = shipPos + relXTranslation * frontDirectionVector + relYTranslation * sideDirectionVector
262
263
264
265 sea = self.state.sea
266 shipNewPos += ship.getOffsetByEnvConditions(sea.wind.getSpeedVectorInLocation(s.x, s.y),
267 sea.waterCurrents.getSpeedVectorInLocation(s.x, s.y),
268 startAngle)
269
270 progressVector = shipNewPos - shipPos
271
272
273 s.x = shipNewPos[0]
274 s.y = shipNewPos[1]
275 s.orientation += avgAngularSpeed * timeStepLength
276
277 while( s.orientation > 180 ):
278 s.orientation -= 360
279 while( s.orientation <= -180 ):
280 s.orientation +=360
281 s.speed += forwardAcceleration * timeStepLength
282 s.angularSpeed += angularAcceleration * timeStepLength
283
284
285
286
287 return
288
289
290
291
292
293
294
295
296
297
298
299
301 "Compute sea state change in time"
302
303 pass
304
305
306
307
308
309
311 """
312 The communication module of the simulator.
313 Each round, all agents' messages are being processed.
314 """
315 for agentIndex, agent in enumerate(self.shipAgents):
316 compositeMsg = agent.getOutgoingMessage()
317 self.processCompositeMessage(compositeMsg, agentIndex)
318
320 """
321 Processes a Composite messages by breaking it into primitive messages
322 and using the appropriate callbacks based on the message type.
323
324 @type compositeMsg: compositeMsg
325 @param compositeMsg: Contains 0 or more primitive messages from one agent.
326
327 @type agentIndex: int
328 @param agentIndex: sort of an agent-id - an agent's index in the array of all agents
329 """
330 for msg in compositeMsg.primitiveMsgs:
331 if msg.to == PrimitiveMessage.TO_SIMULATOR:
332
333 getattr(self, self.msgCallbacks[msg.type])(msg, agentIndex)
334
335 elif msg.to == PrimitiveMessage.TO_ALL:
336 for agent in self.shipAgents:
337 agent.receiveMsg(msg)
338
339 else:
340 raise Exception("Unknown message receipient")
341
342
344 """Processes a msg that notifies about an agent visit at a point.
345
346 @type msg: messages.PrimitiveMessage
347 @param msg: a message with a specifically assumed format (see below)
348
349 @type agentIndex: int
350 @param agentIndex: sort of an agent-id - an agent's index in the array of all agents
351 """
352
353 point = msg.content
354
355
356
357
358
359
360 self.visitTimes.setdefault(point, []).append(self.time)
361
362
363
365 """
366 Processes a msg that notifies the length of
367 an edge that an agent just completed.
368
369 @type msg: messages.PrimitiveMessage
370 @param msg: a message with a specifically assumed format (see below)
371
372 @type agentIndex: int
373 @param agentIndex: sort of an agent-id - an agent's index in the array of all agents
374 """
375
376 edge, length = msg.content
377
378
379
380
381
382
383 self.edgeLengths.setdefault(edge, []).append(length)
384
385
386