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