Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
postest.py
1 # python routines to test robot position calculations
2 
3 #
4 # Paths are odometer distances from previous positions
5 #
6 
7 import math as m
8 
9 Tau = 2.0 * m.pi # a better pi
10 Wheelbase = 4.0 # robot wheelbase
11 CoR = Wheelbase / 2.0 # robot left-right center of rotation
12 
13 #
14 # r = radius of curvature
15 # theta = degrees of arc
16 #
17 def botturn(name, r, theta):
18  if theta >= 0.0:
19  odleft = (r-CoR) * theta
20  odright = (r+CoR) * theta
21  else:
22  odleft = (r+CoR) * -theta
23  odright = (r-CoR) * -theta
24  return name, odleft, odright
25 
26 paths = {
27  # right angle to the left
28  'path1': [
29  ("up", 100, 100), botturn("left90", 0, m.pi/2), ("left", 100, 100)],
30 
31  # spin left 360
32  'spinleft': [botturn("spin360_left", 0, Tau)],
33 
34  # spin right 360
35  'spinright': [botturn("spin360_right", 0, -Tau)],
36 
37  # up right at a 45
38  'path2': [botturn("right45", 0, -m.pi/4), ("up_right", 100, 100)],
39 
40  # diamond closed path
41  'diamond': [
42  botturn("right45", 0, -m.pi/4),
43  ("up_right", 100, 100),
44  botturn("right90", 0, -m.pi/2),
45  ("dn_right", 100, 100),
46  botturn("right90", 0, -m.pi/2),
47  ("dn_left", 100, 100),
48  botturn("right90", 0, -m.pi/2),
49  ("up_left", 100, 100)],
50 
51  # 8th of a circle
52  'ccw 8th-circle': [botturn("circle(pi/4)", 100.0 * CoR, m.pi/4)],
53 
54  # quarter of a circle
55  'ccw qtr-circle': [botturn("circle(pi/2)", 100.0 * CoR, m.pi/2)],
56 
57  # half circle
58  'ccw half-circle': [botturn("circle(pi)", 100.0 * CoR, m.pi)],
59 
60  # full circle closed path
61  'ccw full-circle': [botturn("circle(2*pi)", 100.0 * CoR, Tau)],
62 
63  # two full circles closed path
64  'ccw two-circles': [botturn("circle(4*pi)", 100.0 * CoR, 2.0*Tau)],
65 
66  # quarter of a circle
67  'cw qtr-circle': [botturn("circle(pi/2)", 100.0 * CoR, -m.pi/2)],
68 }
69 
70 # current pose+
71 cur_pose = { }
72 prev_pose = { }
73 
74 def resetpose(pose):
75  pose['od_left'] = 0.0
76  pose['od_right'] = 0.0
77  pose['s'] = 0.0
78  pose['r'] = 0.0
79  pose['c'] = 0.0
80  pose['x'] = 0.0
81  pose['y'] = 0.0
82  pose['theta'] = 0.0
83 
84 def printpose(name, pose):
85  print "%s: (x,y,theta)=(%.3f, %.3f, %.3f), " \
86  "s=%.3f, r=%.3f, c=%.3f, " \
87  "od=[%.3f, %.3f]" % \
88  (name, pose['x'], pose['y'], m.degrees(pose['theta']),
89  pose['s'], pose['r'], m.degrees(pose['c']),
90  pose['od_left'], pose['od_right'])
91 
92 def theta(od_left, od_right):
93  theta = (od_right - od_left) / Wheelbase
94  #return m.fmod(theta, Tau)
95  return theta
96 
97 def pose():
98  global cur_pose, prev_pose
99 
100  dleft = cur_pose['od_left'] - prev_pose['od_left']
101  dright = cur_pose['od_right'] - prev_pose['od_right']
102  s = (dright + dleft)/2.0
103 
104  theta_j = cur_pose['theta']
105  theta_i = prev_pose['theta']
106  c = theta_j - theta_i
107 
108  # no movement
109  if m.fabs(s) <= 1e-4:
110  r = 0.0
111  x = prev_pose['x']
112  y = prev_pose['y']
113 
114  # arc path
115  elif m.fabs(c) > 1e-4:
116  r = s / c
117  # let u = pi/2 - theta
118  # -u = theta - pi/2
119  # then:
120  # sin(-u) = -sin(u)
121  # cos(-u) = cos(u)
122  # and
123  # sin(u) = cos(theta)
124  # cos(u) = sin(theta)
125  # so
126  # dx = r * (m.cos(theta_j-m.pi/2) - m.cos(theta_i-m.pi/2))
127  # dy = r * (m.sin(theta_j-m.pi/2) - m.sin(theta_i-m.pi/2))
128  # can be rewritten as:
129  dx = r * (m.sin(theta_j) - m.sin(theta_i))
130  dy = r * (m.cos(theta_i) - m.cos(theta_j))
131 
132  x = prev_pose['x'] + dx
133  y = prev_pose['y'] + dy
134 
135  # degenerate case: staight path
136  else:
137  r = s
138  x = prev_pose['x'] + r * m.cos(theta_j)
139  y = prev_pose['y'] + r * m.sin(theta_j)
140 
141  cur_pose['s'] = s
142  cur_pose['r'] = r
143  cur_pose['c'] = c
144 
145  return x, y
146 
147 def nav(path):
148  global cur_pose, prev_pose
149  resetpose(cur_pose)
150  resetpose(prev_pose)
151  n = 1
152  od = [0.0, 0.0]
153  for desc, odl, odr in path:
154  od[0] += odl
155  od[1] += odr
156  print "%d. %s: [%.2f, %.2f]" % (n, desc, od[0], od[1])
157 
158  prev_pose = cur_pose.copy()
159  printpose(" prev_pose", prev_pose)
160 
161  cur_pose['od_left'] = od[0]
162  cur_pose['od_right'] = od[1]
163 
164  cur_pose['theta'] = theta(od[0], od[1])
165 
166  cur_pose['x'], cur_pose['y'] = pose()
167  cur_pose['theta'] = m.fmod(cur_pose['theta'], Tau)
168  printpose(" cur_pose", cur_pose)
169 
170  n += 1
171 
172 def testall():
173  for key, path in paths.iteritems():
174  print "\n------------------------------------------------------------------"
175  print key
176  print "------------------------------------------------------------------"
177  nav(path)