@@ -12,28 +12,40 @@ class SimpleDronePhysics(DronePhysicsEngine):
12
12
Assume units are in centimeters when translating to irl unit
13
13
'''
14
14
15
- GRAVITY = glm .vec3 (0 , 0 , - 1e-3 )
16
- AIR_RESISTANCE = glm .vec3 ((0.95 ,)* 3 )
15
+ GRAVITY = glm .vec3 (0 , 0 , - 2e-1 )
16
+ AIR_RESISTANCE = glm .vec3 ((0.98 ,)* 3 )
17
17
#Angle is counter-clockwise from 3 o'clock position, so negate the magnitude to turn properly
18
- RC_SCALE = glm .vec4 (0.09 , 0.09 , 0.09 , - 0.05 )
18
+ RC_SCALE = glm .vec4 (0.2 , 0.2 , 0.1 , - 0.05 )
19
+
20
+ TAKEOFF_COMPLETE_ERROR_MAX = 1e-1
21
+ ARMED_MIN_THRUST = 1e-3
22
+ TAKEOFF_COMPLETE_STABLE_TICKS = 30
19
23
20
24
#PID controller parameters
21
- STRAFE_CONTROL_PARAM = {'Kp' : 0.45 , 'Ki' : 0.01 , 'Kd' : 0.0 }
22
- LIFT_CONTROL_PARAM = {'Kp' : 0.002 , 'Ki' : 0.002 , 'Kd' : 0.0 }
23
- TURN_CONTROL_PARAM = {'Kp' : 0.5 , 'Ki' : 0.0 , 'Kd' : 0.0 }
25
+ STRAFE_CONTROL_PARAM = {'Kp' : 0.02 , 'Ki' : 0.0 , 'Kd' : 0.0 }
26
+ LIFT_CONTROL_PARAM = {'Kp' : 0.019 , 'Ki' : 0.012 , 'Kd' : 0.0 }
27
+ TURN_CONTROL_PARAM = {'Kp' : 0.2 , 'Ki' : 0.0 , 'Kd' : 0.0 }
24
28
25
29
def reset (self , state : PhysicsStateType = None ) -> PhysicsStateType :
30
+ #TODO: put all of this in a dict-inheriting class and use that as state
31
+
26
32
#Position of the drone in space
27
33
self .pos = glm .vec3 ((0 ,0 ,0 ))
28
34
self .angle = glm .vec3 ((0 ,0 ,0 ))
29
35
#Instantaneous velocity
30
36
self .pvel = glm .vec4 (0 ,0 ,0 ,1 )
31
37
self .fvel = glm .vec3 ()
32
38
self .avel = glm .vec3 ()
39
+ self .ticks = 0
40
+ self ._lastRC = None
41
+ self ._tickLogs : dict = {}
33
42
34
43
#Thrust to apply
35
44
self .thrust_vec = glm .vec3 ()
36
45
46
+ #Landed, stationary
47
+ self ._operation = DroneState .LANDED
48
+
37
49
#Movement PID control. XY and W is for velocity, Z is for absolute position (altitude)
38
50
self .control = Vec4PID (
39
51
PID (** self .STRAFE_CONTROL_PARAM , sample_time = None ),
@@ -45,9 +57,6 @@ def reset(self, state : PhysicsStateType = None) -> PhysicsStateType:
45
57
self ._state : PhysicsStateType = dict ()
46
58
self ._updateState ()
47
59
48
- #Landed, stationary
49
- self ._operation = DroneState .LANDED
50
-
51
60
return self .state
52
61
53
62
def _updateState (self ):
@@ -76,33 +85,59 @@ def state(self, state):
76
85
self .reset (state )
77
86
78
87
def step (self , action : StepActionType , dt : float = None ) -> PhysicsStateType :
88
+ self .ticks += 1
89
+
79
90
rcvec , op , params = self .decodeAction (action )
91
+
92
+ #State machine
80
93
if op is not None :
81
94
if op == DroneAction .TAKEOFF :
82
- self ._operation = DroneState .TAKING_OFF
83
- self .control .z .setpoint = params .get ("altitude" , 10.0 )
84
-
85
- if rcvec is None :
86
- rcvec = StepRC (0 ,0 ,0 ,0 )
95
+ if self ._operation == DroneState .LANDED :
96
+ self ._operation = DroneState .TAKING_OFF
97
+ self .control .z .setpoint = params .get ("altitude" , 10.0 )
98
+ self ._tickLogs [DroneState .TAKING_OFF ] = 0
99
+
100
+ if self ._operation == DroneState .TAKING_OFF :
101
+ z_error = self .control .z .setpoint - self .pos .z
102
+ abs_speed = glm .length (self .pvel .xyz )
103
+ #print(abs_speed)
104
+ if abs (z_error ) < self .TAKEOFF_COMPLETE_ERROR_MAX :
105
+ self ._tickLogs [DroneState .TAKING_OFF ] += 1
106
+ if self ._tickLogs [DroneState .TAKING_OFF ] > self .TAKEOFF_COMPLETE_STABLE_TICKS :
107
+ self ._operation = DroneState .IN_AIR
108
+
109
+ #Save last RC for later
110
+ if rcvec is not None :
111
+ self ._lastRC = (rcvec , self .ticks )
112
+ #If no RC, reuse previous RC input for some steps
113
+ else :
114
+ if self ._lastRC is not None and self ._lastRC [0 ] is not None and self .ticks < self ._lastRC [1 ] + 30 :
115
+ rcvec = self ._lastRC [0 ]
116
+ else :
117
+ rcvec = StepRC (0 ,0 ,0 ,0 )
87
118
88
119
#Which direction to move using RC
89
120
rc_vec = glm .vec4 (rcvec )
90
121
rc_vec = glm .clamp (rc_vec , - 1 , 1 )
91
122
rc_vec *= self .RC_SCALE
92
123
93
124
is_on_surface = False
94
- is_accept_rc = False
125
+ is_armed = self ._operation != DroneState .LANDED
126
+ is_accept_rc = self ._operation == DroneState .IN_AIR
95
127
96
128
#RC thrust control
97
- self .control .x .setpoint = rc_vec .x # Target velocity
98
- self .control .y .setpoint = rc_vec .y # Target velocity
99
- self .control .w .setpoint = rc_vec .w # Target heading angle
100
- self .control .z .setpoint += rc_vec .z # Absolute height
129
+ if is_accept_rc :
130
+ self .control .x .setpoint = rc_vec .x # Target velocity
131
+ self .control .y .setpoint = rc_vec .y # Target velocity
132
+ self .control .w .setpoint = rc_vec .w # Target heading angle
133
+ self .control .z .setpoint += rc_vec .z # Absolute height
134
+ if self .control .z .setpoint < 0 :
135
+ self .control .z .setpoint = 0.0
101
136
102
137
#Apply thrust based on target position
103
138
self .thrust_vec .x = self .control .x (self .pvel .x , dt )
104
139
self .thrust_vec .y = self .control .y (self .pvel .y , dt )
105
- self .thrust_vec .z = self .control .z (self .pos .z , dt )
140
+ self .thrust_vec .z = glm . max ( self .control .z (self .pos .z , dt ), self . ARMED_MIN_THRUST if is_armed else 0.0 )
106
141
self .avel .z = self .control .w (self .avel .z , dt )
107
142
108
143
#3D coordinate transformation matrix
0 commit comments