-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathagent3d.py
125 lines (95 loc) · 3.57 KB
/
agent3d.py
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python
import numpy as np
from mathutils import *
##
# Agent in 3D space on a sphere
##
class Agent3DSphere:
def __init__(self, speed=0.05, A=None, B=None):
"""Initialize an agent that can walk on a 3D sphere.
If A and B are not None, then the agent will tangential to the geodesic
between the two points."""
self.speed = speed
# initialize the agent for unit position
if A is None or B is None:
# R = random_rotmat()
R = np.array([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
self.up = np.array([0, 0, 1])
self.right = np.array([1, 0, 0])
self.fwd = np.cross(self.up, self.right)
self.up = R.dot(self.up)
self.right = R.dot(self.right)
self.fwd = R.dot(self.fwd)
# this is only valid on spherical world
self.X = self.up.copy()
else:
# tangent to the geodesic
self.X = A
self.up = self.X
self.right = -np.cross(A, B)
self.fwd = np.cross(self.up, self.right)
self.fwd /= np.linalg.norm(self.fwd)
self.X_history = [self.X]
# XXX theta not really used.
self.theta = 0.0
def normalize(self):
self.up /= np.linalg.norm(self.up)
self.right /= np.linalg.norm(self.right)
self.fwd /= np.linalg.norm(self.fwd)
@staticmethod
def get_proposal(up, right, fwd):
# get random rotation around up vector via Rodrigues
theta = np.random.laplace(0.0, 0.4)
while theta > np.pi:
theta -= 2*np.pi
while theta < -np.pi:
theta += 2*np.pi
# theta = max(-1.0, min(1, theta))
R = rodrigues(up, theta)
# rotate all vectors
up = R.dot(up)
right = R.dot(right)
# normalize all vectors
up = up / np.linalg.norm(up)
right = right / np.linalg.norm(right)
# compute new forward vector
fwd = np.cross(up, right)
fwd = fwd / np.linalg.norm(fwd)
# return the proposal
return theta, up, right, fwd
def check_proposal(self, up, right, fwd):
# in this scenario, we don't want to go below Z == 0
X_new = self.X + self.speed * fwd
return X_new[2] > 0.001
def move(self, random_walk=True):
"""Move the agent forward and get a new heading direction"""
newX = self.X + self.speed * self.fwd
# new coordinate on unit sphere
self.X = newX / np.linalg.norm(newX)
self.up = self.X.copy()
# store to history
self.X_history.append(self.X)
# get proposal for next movement direction
if random_walk:
theta, p_up, p_right, p_fwd = Agent3DSphere.get_proposal(self.up, self.right, self.fwd)
ok = self.check_proposal(p_up, p_right, p_fwd)
if ok:
# we're good to go
# self.up = p_up
self.fwd = p_fwd
# self.right = p_right
# self.right = np.cross(self.fwd, self.up)
else:
# reflect to disallow movement out of arena
self.fwd = -p_fwd
self.up = p_up
self.right = np.cross(self.fwd, self.up)
self.right = self.right / np.linalg.norm(self.fwd)
self.theta = theta
else:
# keep direction
self.fwd = np.cross(self.up, self.right)
self.fwd = self.fwd / np.linalg.norm(self.fwd)