11#!/usr/bin/env python3
2- from cyecca .models import fixedwing
2+ from cyecca .models import fixedwing , lookupTableFixedwing
33
4+ # from cyecca.models.lookupTableFixedwing import build_tables
45import casadi as ca
56import numpy as np
67
910from rclpy .node import Node
1011from rclpy .parameter import Parameter
1112
12- from geometry_msgs .msg import PoseWithCovarianceStamped , TransformStamped
13+ from geometry_msgs .msg import PoseWithCovarianceStamped , TransformStamped , Point
1314
1415from rosgraph_msgs .msg import Clock
1516from nav_msgs .msg import Odometry , Path
1617from sensor_msgs .msg import Joy
1718from tf2_ros import TransformBroadcaster
19+ from visualization_msgs .msg import Marker
1820
1921
2022class Simulator (Node ):
@@ -31,6 +33,12 @@ def __init__(self, x0=None, p=None):
3133 self .pub_pose = self .create_publisher (PoseWithCovarianceStamped , "pose" , 1 )
3234 self .pub_clock = self .create_publisher (Clock , "clock" , 1 )
3335 self .pub_odom = self .create_publisher (Odometry , "odom" , 1 )
36+ self .pub_lift = self .create_publisher (Marker , "lift" , 1 )
37+ self .pub_drag = self .create_publisher (Marker , "drag" , 1 )
38+ self .pub_weight = self .create_publisher (Marker , "weight" , 1 )
39+ self .pub_thrust = self .create_publisher (Marker , "thrust" , 1 )
40+ self .pub_side_force = self .create_publisher (Marker , "side_force" , 1 )
41+ self .pub_vel_b = self .create_publisher (Marker , "vel_b" , 1 )
3442
3543 self .tf_broadcaster = TransformBroadcaster (self )
3644
@@ -54,18 +62,34 @@ def __init__(self, x0=None, p=None):
5462 # ----------------------------------------------
5563 self .input_mode = "manual"
5664
65+ # -------------------------------------------------------
66+ # Lookup Table
67+ # -------------------------------------------------------
68+
69+ self .lookup_tab = lookupTableFixedwing .build_tables ()
70+ self .coeff_data = {
71+ "CL" : 0.0 ,
72+ "CD" : 0.0 ,
73+ "Cy" : 0.0 ,
74+ "Cl" : 0.0 ,
75+ "Cm" : 0.0 ,
76+ "Cn" : 0.0 ,
77+ "Cmdr" : 0.0 ,
78+ "Cmda" : 0.0 ,
79+ }
5780 # -------------------------------------------------------
5881 # Dynamics
5982 # ----------------------------------------------
6083 dynamics = fixedwing
61- self .model = dynamics .derive_model ()
84+ self .model = dynamics .derive_model (coeff_data = self . coeff_data )
6285 self .x0_dict = self .model ["x0_defaults" ]
6386 if x0 is not None :
6487 for k in x0 .keys ():
6588 if not k in self .x0_dict .keys ():
6689 raise KeyError (k )
6790 self .x0_dict [k ] = x0 [k ]
6891 self .p_dict = self .model ["p_defaults" ]
92+ # print(self.p_dict)
6993 if p is not None :
7094 for k in p .keys ():
7195 if not k in self .p_dict .keys ():
@@ -74,9 +98,14 @@ def __init__(self, x0=None, p=None):
7498
7599 # init state (x), param(p), and input(u)
76100 self .state = np .array (list (self .x0_dict .values ()), dtype = float )
77- self .p = np .array (list (self .p_dict .values ()), dtype = float )
101+ self .p = np .array (
102+ [
103+ self .p_dict [str (self .model ["p" ][i ])]
104+ for i in range (self .model ["p" ].shape [0 ])
105+ ],
106+ dtype = float ,
107+ )
78108 self .u = np .zeros (4 , dtype = float )
79-
80109 # start main loop on timer
81110 self .system_clock = rclpy .clock .Clock (
82111 clock_type = rclpy .clock .ClockType .SYSTEM_TIME
@@ -86,6 +115,7 @@ def __init__(self, x0=None, p=None):
86115 callback = self .timer_callback ,
87116 clock = self .system_clock ,
88117 )
118+ self .Info = None
89119
90120 # Manual Joy AETR
91121 def joy_callback (self , msg : Joy ):
@@ -114,7 +144,7 @@ def auto_joy_callback(self, msg: Joy):
114144 self .input_auto = ca .vertcat (
115145 msg .axes [0 ], # thrust
116146 msg .axes [1 ], # aileron
117- msg .axes [2 ], # elevator
147+ - msg .axes [2 ], # elevator -- positive = elevator down (pitch up)
118148 msg .axes [3 ], # rudder
119149 ) # TAER
120150
@@ -139,9 +169,9 @@ def update_controller(self):
139169 elif self .input_mode == "auto" :
140170 self .u = ca .vertcat ( # TAER mode
141171 float (self .input_auto [0 ]),
142- float (self .input_auto [1 ]), # scaled roll moment from rudder
172+ float (self .input_auto [1 ]),
143173 float (self .input_auto [2 ]),
144- float (self .input_auto [3 ]), # Rudder directly affects yaw for nvp
174+ float (self .input_auto [3 ]),
145175 )
146176 else :
147177 self .get_logger ().info ("unhandled mode: %s" % self .input_mode )
@@ -151,14 +181,78 @@ def integrate_simulation(self):
151181 """
152182 Integrate the simulation one step and calculate measurements
153183 """
184+ RAD2DEG = 180 / ca .pi
185+
154186 try :
187+ # Grab from Look Up Table
188+ if self .Info == None :
189+ self .Info = {}
190+ self .Info ["alpha" ] = 0.0
191+ self .Info ["beta" ] = 0.0
192+ self .Info ["ail" ] = 0.0
193+ self .Info ["elev" ] = 0.0
194+ self .Info ["rud" ] = 0.0
195+
196+ else :
197+ self .coeff_data ["CD" ] = - 1 * self .lookup_tab ["Cx" ](
198+ self .Info ["alpha" ] * RAD2DEG , self .Info ["elev" ] * RAD2DEG
199+ )
200+ self .coeff_data ["Cmdr" ] = - 1 * self .lookup_tab ["DnDr" ](
201+ self .Info ["alpha" ] * RAD2DEG , self .Info ["beta" ] * RAD2DEG / 2
202+ )
203+ self .coeff_data ["Cmda" ] = - 1 * self .lookup_tab ["DlDa" ](
204+ self .Info ["alpha" ] * RAD2DEG , self .Info ["beta" ] * RAD2DEG / 2
205+ )
206+ self .coeff_data ["CL" ] = self .lookup_tab ["Cz" ](
207+ self .Info ["alpha" ] * RAD2DEG ,
208+ self .Info ["beta" ] * RAD2DEG ,
209+ self .Info ["elev" ] * RAD2DEG ,
210+ )
211+ self .coeff_data ["Cy" ] = self .lookup_tab ["Cy" ](
212+ self .Info ["beta" ] * RAD2DEG ,
213+ self .Info ["ail" ] * RAD2DEG ,
214+ self .Info ["rud" ] * RAD2DEG ,
215+ )
216+ self .coeff_data ["Cl" ] = - 1 * self .lookup_tab ["Cl" ](
217+ self .Info ["alpha" ] * RAD2DEG , self .Info ["beta" ] * RAD2DEG
218+ )
219+ self .coeff_data ["Cm" ] = - 1 * self .lookup_tab ["Cm" ](
220+ self .Info ["alpha" ] * RAD2DEG , self .Info ["elev" ] * RAD2DEG
221+ )
222+ self .coeff_data ["Cn" ] = - 1 * self .lookup_tab ["Cn" ](
223+ self .Info ["alpha" ] * RAD2DEG , self .Info ["beta" ] * RAD2DEG
224+ )
225+
226+ dynamics = fixedwing
227+ # print("Cm", self.coeff_data["Cm"])
228+ self .model = dynamics .derive_model (coeff_data = self .coeff_data )
229+
155230 # opts = {"abstol": 1e-9,"reltol":1e-9,"fsens_err_con": True,"calc_ic":True,"calc_icB":True}
231+ opts = {"abstol" : 1e-2 , "reltol" : 1e-6 , "fsens_err_con" : True }
232+ xdot = self .model ["f" ](self .state , self .u , self .p )
233+
234+ # RK4
235+ # f_int = ca.integrator(
236+ # "test", "rk", self.model["dae"], {"t0": self.t, "tf": self.t + self.dt}
237+ # )
238+ # res = f_int(x0=self.state, z0=ca.DM.zeros(self.model["z"].size1()), p=self.p, u=self.u)
239+
240+ # IDAS
241+ # f_int = ca.integrator(
242+ # "test", "idas", self.model["dae"], {"t0": self.t, "tf": self.t + self.dt, **opts}
243+ # )
244+ # res = f_int(x0=self.state, z0=ca.DM.zeros(self.model["z"].size1()), p=self.p, u=self.u)
245+
246+ # #CVODES
156247 f_int = ca .integrator (
157- "test" , "cvodes" , self .model ["dae" ], self .t , self .t + self .dt
248+ "test" , "cvodes" , self .model ["dae" ], self .t , self .t + self .dt , opts
158249 )
159250 res = f_int (x0 = self .state , z0 = 0.0 , p = self .p , u = self .u )
251+
160252 except RuntimeError as e :
161253 print (e )
254+ xdot = self .model ["f" ](x = self .state , u = self .u , p = self .p )
255+ print (xdot )
162256 raise e
163257
164258 x1 = np .array (res ["xf" ]).reshape (- 1 )
@@ -170,6 +264,7 @@ def integrate_simulation(self):
170264 # store states and measurements
171265 # ---------------------------------------------------------------------
172266 self .state = np .array (res ["xf" ]).reshape (- 1 )
267+ self .Info = self .model ["Info" ](x = self .state , u = self .u , p = self .p )
173268
174269 self .publish_state ()
175270
@@ -222,6 +317,64 @@ def publish_state(self):
222317 tf .transform .rotation .z = qz
223318 self .tf_broadcaster .sendTransform (tf )
224319
320+ # ------------------------------------
321+ # publish tf2 transform vector lift
322+ # ------------------------------------
323+ def vector (v , name , color , scale ):
324+ marker = Marker ()
325+ marker .header .frame_id = "base_link"
326+ marker .ns = name
327+ marker .id = 0
328+ marker .type = Marker .ARROW
329+ marker .action = Marker .ADD
330+ marker .header .stamp = msg_clock .clock
331+ marker .pose .position .x = 0.0
332+ marker .pose .position .y = 0.0
333+ marker .pose .position .z = 0.0
334+ marker .pose .orientation .w = 1.0
335+ marker .pose .orientation .x = 0.0
336+ marker .pose .orientation .y = 0.0
337+ marker .pose .orientation .z = 0.0
338+ marker .scale .x = 0.05
339+ marker .scale .y = 0.1
340+ marker .scale .z = 0.1
341+ marker .color .r = color [0 ]
342+ marker .color .g = color [1 ]
343+ marker .color .b = color [2 ]
344+ marker .color .a = color [3 ]
345+
346+ p1 = Point ()
347+ p1 .x = 0.0
348+ p1 .y = 0.0
349+ p1 .z = 0.0
350+ p2 = Point ()
351+ p2 .x = float (v [0 ] * scale )
352+ p2 .y = float (v [1 ] * scale )
353+ p2 .z = float (v [2 ] * scale )
354+ marker .points .append (p1 )
355+ marker .points .append (p2 )
356+ return marker
357+
358+ self .pub_lift .publish (
359+ vector (self .Info ["L_b" ], "lift" , [1.0 , 0.0 , 0.0 , 1.0 ], 1.0 )
360+ )
361+ self .pub_drag .publish (
362+ vector (self .Info ["D_b" ], "drag" , [0.0 , 0.0 , 1.0 , 1.0 ], 1.0 )
363+ )
364+ self .pub_weight .publish (
365+ vector (self .Info ["W_b" ], "weight" , [0.0 , 1.0 , 0.0 , 1.0 ], 1.0 )
366+ )
367+ self .pub_thrust .publish (
368+ vector (self .Info ["T_b" ], "thrust" , [1.0 , 0.0 , 1.0 , 1.0 ], 1.0 )
369+ )
370+ self .pub_side_force .publish (
371+ vector (self .Info ["S_b" ], "side_force" , [1.0 , 1.0 , 0.0 , 1.0 ], 1.0 )
372+ )
373+
374+ self .pub_vel_b .publish (
375+ vector (self .Info ["v_b" ], "vel_b" , [0.0 , 1.0 , 1.0 , 1.0 ], 0.1 )
376+ )
377+ print ("alpha" , self .Info ["alpha" ], "CL" , self .Info ["CL" ], "CD" , self .Info ["CD" ])
225378 # ------------------------------------
226379 # publish pose with covariance stamped
227380 # ------------------------------------
0 commit comments