Skip to content

Commit a8597f4

Browse files
committed
Reverted some integrator changes.
1 parent 1d0e336 commit a8597f4

File tree

2 files changed

+20
-44
lines changed

2 files changed

+20
-44
lines changed

rotorpy/vehicles/multirotor.py

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class Multirotor(object):
8181
'cmd_vel': the controller commands a velocity vector in the world frame.
8282
'cmd_acc': the controller commands a mass normalized thrust vector (acceleration) in the world frame.
8383
aero: boolean, determines whether or not aerodynamic drag forces are computed.
84+
enable_ground: boolean, determines whether or not ground contact is enabled.
85+
integrator_kwargs: dictionary of keyword arguments passed to scipy.integrate.solve_ivp
8486
"""
8587
def __init__(self, quad_params, initial_state = {'x': np.array([0,0,0]),
8688
'v': np.zeros(3,),
@@ -90,7 +92,8 @@ def __init__(self, quad_params, initial_state = {'x': np.array([0,0,0]),
9092
'rotor_speeds': np.array([1788.53, 1788.53, 1788.53, 1788.53])},
9193
control_abstraction='cmd_motor_speeds',
9294
aero = True,
93-
enable_ground = False
95+
enable_ground = False,
96+
integrator_kwargs = None,
9497
):
9598
"""
9699
Initialize quadrotor physical parameters.
@@ -173,6 +176,12 @@ def __init__(self, quad_params, initial_state = {'x': np.array([0,0,0]),
173176

174177
self.aero = aero
175178

179+
# Integrator settings.
180+
if integrator_kwargs is None:
181+
self.integrator_kwargs = {'method':'RK45'}
182+
else:
183+
self.integrator_kwargs = integrator_kwargs
184+
176185
def extract_geometry(self):
177186
"""
178187
Extracts the geometry in self.rotors for efficient use later on in the computation of
@@ -222,35 +231,20 @@ def step(self, state, control, t_step):
222231
cmd_rotor_speeds = np.clip(cmd_rotor_speeds, self.rotor_speed_min, self.rotor_speed_max)
223232

224233
# Form autonomous ODE for constant inputs and integrate one time step.
234+
def s_dot_fn(t, s):
235+
return self._s_dot_fn(t, s, cmd_rotor_speeds)
225236
s = Multirotor._pack_state(state)
226237

227-
# Use analytic motor dynamics during the integration window to avoid stiffness
228-
w0 = s[16:].copy()
229-
tau = self.tau_m
230-
231-
def s_dot_fn_analytic(t, s_vec):
232-
# Analytic motor update: w(t) = w_cmd + (w0 - w_cmd) * exp(-t/tau)
233-
w_t = cmd_rotor_speeds + (w0 - cmd_rotor_speeds) * np.exp(-t / tau)
234-
s_eff = s_vec.copy()
235-
s_eff[16:] = w_t
236-
s_dot = self._s_dot_fn(t, s_eff, cmd_rotor_speeds)
237-
# Zero motor derivatives since handled analytically
238-
s_dot[16:] = 0.0
239-
return s_dot
240-
238+
# Integrate
241239
sol = scipy.integrate.solve_ivp(
242-
s_dot_fn_analytic,
240+
s_dot_fn,
243241
(0.0, t_step),
244242
s,
245-
method='Radau',
246-
max_step=t_step,
247-
rtol=1e-3,
248-
atol=1e-6,
243+
**self.integrator_kwargs
249244
)
250245
s = sol['y'][:, -1]
251-
# Set final rotor speeds to analytic value at t_step
252-
s[16:] = cmd_rotor_speeds + (w0 - cmd_rotor_speeds) * np.exp(-t_step / tau)
253246

247+
# Unpack the state vector.
254248
state = Multirotor._unpack_state(s)
255249

256250
# Re-normalize unit quaternion.
@@ -298,30 +292,9 @@ def _s_dot_fn(self, t, s, cmd_rotor_speeds):
298292
# Rotate the force from the body frame to the inertial frame
299293
Ftot = R@FtotB
300294

301-
# Ground contact handling
302-
if self._on_ground(state) and self._enable_ground:
303-
# Calculate the total force without ground contact
304-
total_force_no_ground = self.weight + Ftot
305-
306-
# If the vehicle is on the ground and the total downward force would cause
307-
# further descent, apply a normal force to counteract it
308-
if total_force_no_ground[2] < 0: # Downward force (negative z)
309-
# Apply normal force to prevent going through ground
310-
ground_normal_force = np.array([0, 0, -total_force_no_ground[2]])
311-
Ftot += ground_normal_force
312-
313295
# Velocity derivative.
314296
v_dot = (self.weight + Ftot) / self.mass
315297

316-
# Additional ground contact constraints
317-
if self._on_ground(state) and self._enable_ground:
318-
# Prevent downward velocity when on ground
319-
if v_dot[2] < 0: # Downward acceleration
320-
v_dot[2] = 0.0
321-
# Also damp any existing downward velocity when on ground
322-
if state['v'][2] < 0: # Currently moving downward
323-
v_dot[2] = max(v_dot[2], -10.0 * state['v'][2]) # Damping term
324-
325298
# Angular velocity derivative.
326299
w = state['w']
327300
w_hat = Multirotor.hat_map(w)

rotorpy/vehicles/px4_multirotor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ def __init__(
5454
enable_ground=True,
5555
mavlink_url="tcpin:localhost:4560",
5656
autopilot_controller=True,
57-
lockstep=True
57+
lockstep=True,
58+
integrator_kwargs=None
5859
):
60+
integrator_kwargs = integrator_kwargs if integrator_kwargs is not None else {'method':'Radau', 'rtol':1e-3, 'atol':1e-6, 'max_step':0.01}
5961
# If no initial state passed, initialize to hover at origin
6062
if initial_state is None:
6163
initial_state = {
@@ -72,6 +74,7 @@ def __init__(
7274
control_abstraction=control_abstraction,
7375
aero=aero,
7476
enable_ground=enable_ground,
77+
integrator_kwargs=integrator_kwargs
7578
)
7679
# Simulated IMU (with noise)
7780
self.imu = Imu()

0 commit comments

Comments
 (0)