1
1
#!/usr/bin/env python
2
2
3
+ import os
3
4
import torch
4
5
import numpy as np
5
6
import rospy
@@ -21,11 +22,13 @@ class DiffPhysics:
21
22
hm_topic = '/height_map' ,
22
23
hm_frame = 'base_link' ,
23
24
linear_vels = [1. ],
24
- angular_vels = [0. ]):
25
+ angular_vels = [0. ],
26
+ max_stale_msg_delay = 0.5 ):
25
27
self .cfg = cfg
26
28
self .hm_frame = hm_frame
27
29
self .linear_vels = linear_vels
28
30
self .angular_vels = angular_vels
31
+ self .max_stale_msg_delay = max_stale_msg_delay
29
32
30
33
# paths publisher
31
34
self .paths_pub = rospy .Publisher ('/sampled_paths' , MarkerArray , queue_size = 1 )
@@ -39,12 +42,12 @@ class DiffPhysics:
39
42
assert isinstance (msg , PointCloud2 )
40
43
# if message is stale do not process it
41
44
dt = rospy .Time .now () - msg .header .stamp
42
- if dt > rospy . Duration ( 2. ) :
43
- rospy .logdebug (f'Stale height map message received ({ dt .to_sec ():.1f} [sec]), skipping' )
45
+ if dt . to_sec () > self . max_stale_msg_delay :
46
+ rospy .logwarn (f'Stale height map message received ({ dt .to_sec ():.1f} > { self . max_stale_msg_delay } [sec]), skipping' )
44
47
return
45
48
46
49
height = numpify (msg )['z' ]
47
- h = w = int (self .cfg .d_max / self .cfg .grid_res )
50
+ h = w = int (2 * self .cfg .d_max / self .cfg .grid_res )
48
51
height = height .reshape ((h , w ))
49
52
height = np .asarray (height , dtype = np .float64 )
50
53
height = height .T
@@ -62,13 +65,11 @@ class DiffPhysics:
62
65
assert 'linear_v' in controls .keys ()
63
66
assert 'angular_w' in controls .keys ()
64
67
65
- h , w = np .asarray (height .shape ) * self .cfg .grid_res
66
68
state = State (xyz = torch .tensor ([0 , 0. , 0. ], device = self .cfg .device ).view (3 , 1 ),
67
- rot = torch .eye (3 , device = self .cfg .device ),
68
- vel = torch .tensor ([0. , 0. , 0. ], device = self .cfg .device ).view (3 , 1 ),
69
- omega = torch .tensor ([0. , 0. , 0. ], device = self .cfg .device ).view (3 , 1 ),
70
- device = self .cfg .device )
71
- state [0 ][0 ] = - h / 2. # move robot to the edge of the height map
69
+ rot = torch .eye (3 , device = self .cfg .device ),
70
+ vel = torch .tensor ([0. , 0. , 0. ], device = self .cfg .device ).view (3 , 1 ),
71
+ omega = torch .tensor ([0. , 0. , 0. ], device = self .cfg .device ).view (3 , 1 ),
72
+ device = self .cfg .device )
72
73
73
74
""" Create robot-terrain interaction models """
74
75
system = RigidBodySoftTerrain (height = height ,
@@ -104,8 +105,7 @@ class DiffPhysics:
104
105
angular_w .append (state [3 ])
105
106
forces .append (state [4 ])
106
107
107
- # height map origin is at the edge of the map
108
- xyz = torch .stack (xyz ) + torch .tensor ([h / 2. , 0. , 0. ], device = self .cfg .device ).view (3 , 1 )
108
+ xyz = torch .stack (xyz )
109
109
Rs = torch .stack (Rs )
110
110
linear_v = torch .stack (linear_v )
111
111
angular_w = torch .stack (angular_w )
@@ -133,6 +133,10 @@ class DiffPhysics:
133
133
if linear_vels is None :
134
134
linear_vels = [1. ]
135
135
assert isinstance (height , np .ndarray )
136
+ assert height .shape [0 ] == height .shape [1 ]
137
+ assert isinstance (linear_vels , list )
138
+ assert isinstance (angular_vels , list )
139
+ assert len (linear_vels ) == len (angular_vels )
136
140
137
141
tt = torch .linspace (0. , self .cfg .total_sim_time , self .cfg .n_samples )
138
142
# paths marker array
@@ -141,51 +145,50 @@ class DiffPhysics:
141
145
lower_cost_poses = None
142
146
max_path_cost = torch .tensor (- np .inf , device = self .cfg .device )
143
147
min_path_cost = torch .tensor (np .inf , device = self .cfg .device )
144
- for v in linear_vels :
145
- for w in angular_vels :
146
- # controls
147
- controls = {
148
- 'stamps' : tt ,
149
- 'linear_v' : v * torch .ones (self .cfg .n_samples ),
150
- 'angular_w' : w * torch .ones (self .cfg .n_samples )
151
- }
152
-
153
- # predict states
154
- t0 = time ()
155
- states = self .sim (height , controls )
156
- t1 = time ()
157
- rospy .logdebug ('Path of %d samples simulation took %.3f' % (self .cfg .n_samples , t1 - t0 ))
158
-
159
- # create path message (Marker)
160
- xyz = states [0 ].cpu ().numpy ()[::100 ]
161
- Rs = states [1 ].cpu ().numpy ()[::100 ]
162
- Ts = np .zeros ((len (xyz ), 4 , 4 ))
163
- Ts [:, :3 , :3 ] = Rs
164
- Ts [:, :3 , 3 :4 ] = xyz
165
- Ts [:, 3 , 3 ] = 1.
166
-
167
- # compute path cost
168
- path_cost = self .path_cost (states )
169
- # rospy.logdebug('Path cost: %.3f' % path_cost.item())
170
- if path_cost > max_path_cost :
171
- max_path_cost = path_cost .clone ()
172
- if path_cost < min_path_cost :
173
- min_path_cost = path_cost .clone ()
174
- lower_cost_poses = Ts
175
- # normalize path cost
176
- path_cost = (path_cost - min_path_cost ) / (max_path_cost - min_path_cost ) if max_path_cost > min_path_cost else path_cost
177
- # rospy.logdebug('Path cost normalized: %.3f' % path_cost.item())
178
-
179
- # map path cost to color (lower cost -> greener, higher cost -> redder)
180
- color = np .array ([0. , 1. , 0. ]) + (np .array ([1. , 0. , 0. ]) - np .array ([0. , 1. , 0. ])) * path_cost .item ()
181
- marker_msg = to_marker (Ts , color = color )
182
- marker_msg .header .stamp = rospy .Time .now ()
183
- marker_msg .header .frame_id = self .hm_frame
184
- marker_msg .ns = 'paths'
185
- marker_msg .id = path_id
186
- path_id += 1
187
- marker_array .markers .append (marker_msg )
188
- rospy .logdebug ('Path to marker array conversion took %.3f' % (time () - t1 ))
148
+ for v , w in zip (linear_vels , angular_vels ):
149
+ # controls
150
+ controls = {
151
+ 'stamps' : tt ,
152
+ 'linear_v' : v * torch .ones (self .cfg .n_samples ),
153
+ 'angular_w' : w * torch .ones (self .cfg .n_samples )
154
+ }
155
+
156
+ # predict states
157
+ t0 = time ()
158
+ states = self .sim (height , controls )
159
+ t1 = time ()
160
+ rospy .logdebug ('Path of %d samples simulation took %.3f' % (self .cfg .n_samples , t1 - t0 ))
161
+
162
+ # create path message (Marker)
163
+ xyz = states [0 ].cpu ().numpy ()[::50 ]
164
+ Rs = states [1 ].cpu ().numpy ()[::50 ]
165
+ Ts = np .zeros ((len (xyz ), 4 , 4 ))
166
+ Ts [:, :3 , :3 ] = Rs
167
+ Ts [:, :3 , 3 :4 ] = xyz
168
+ Ts [:, 3 , 3 ] = 1.
169
+
170
+ # compute path cost
171
+ path_cost = self .path_cost (states )
172
+ # rospy.logdebug('Path cost: %.3f' % path_cost.item())
173
+ if path_cost > max_path_cost :
174
+ max_path_cost = path_cost .clone ()
175
+ if path_cost < min_path_cost :
176
+ min_path_cost = path_cost .clone ()
177
+ lower_cost_poses = Ts
178
+ # normalize path cost
179
+ path_cost = (path_cost - min_path_cost ) / (max_path_cost - min_path_cost ) if max_path_cost > min_path_cost else path_cost
180
+ # rospy.logdebug('Path cost normalized: %.3f' % path_cost.item())
181
+
182
+ # map path cost to color (lower cost -> greener, higher cost -> redder)
183
+ color = np .array ([0. , 1. , 0. ]) + (np .array ([1. , 0. , 0. ]) - np .array ([0. , 1. , 0. ])) * path_cost .item ()
184
+ marker_msg = to_marker (Ts , color = color )
185
+ marker_msg .header .stamp = rospy .Time .now ()
186
+ marker_msg .header .frame_id = self .hm_frame
187
+ marker_msg .ns = 'paths'
188
+ marker_msg .id = path_id
189
+ path_id += 1
190
+ marker_array .markers .append (marker_msg )
191
+ rospy .logdebug ('Path to marker array conversion took %.3f' % (time () - t1 ))
189
192
190
193
# publish all sampled paths
191
194
self .paths_pub .publish (marker_array )
@@ -199,12 +202,12 @@ class DiffPhysics:
199
202
200
203
def main ():
201
204
rospy .init_node ('diff_physics' , anonymous = True , log_level = rospy .DEBUG )
205
+ pkg_path = rospkg .RosPack ().get_path ('monoforce' )
202
206
203
207
cfg = Config ()
204
- cfg .grid_res = 0.1
205
- cfg .device = 'cuda'
206
- cfg .d_max = 12.8
207
- cfg .d_min = 1.
208
+ config_path = rospy .get_param ('~config_path' , os .path .join (pkg_path , 'config/cfg.yaml' ))
209
+ assert os .path .isfile (config_path ), 'Config file %s does not exist' % config_path
210
+ cfg .from_yaml (config_path )
208
211
cfg .total_sim_time = rospy .get_param ('~total_sim_time' , 5. )
209
212
cfg .n_samples = 100 * int (cfg .total_sim_time )
210
213
@@ -213,9 +216,12 @@ def main():
213
216
# control parameters
214
217
linear_vels = rospy .get_param ('~linear_vels' , [1. ])
215
218
angular_vels = rospy .get_param ('~angular_vels' , [0. ])
219
+ # max time to wait for a message before it is considered stale
220
+ max_stale_msg_delay = rospy .get_param ('~max_stale_msg_delay' , 0.5 )
216
221
try :
217
222
node = DiffPhysics (cfg = cfg , hm_topic = hm_topic , hm_frame = hm_frame ,
218
- linear_vels = linear_vels , angular_vels = angular_vels )
223
+ linear_vels = linear_vels , angular_vels = angular_vels ,
224
+ max_stale_msg_delay = max_stale_msg_delay )
219
225
rospy .spin ()
220
226
except rospy .ROSInterruptException :
221
227
pass
0 commit comments