@@ -24,17 +24,28 @@ class Control(str, Enum):
24
24
"""Control type of the simulated onboard controller."""
25
25
26
26
state = "state"
27
+ """State control takes [x, y, z, vx, vy, vz, ax, ay, az, yaw, roll_rate, pitch_rate, yaw_rate].
28
+
29
+ Note:
30
+ Recommended frequency is >=20 Hz.
31
+
32
+ Warning:
33
+ Currently, we only use positions, velocities, and yaw. The rest of the state is ignored.
34
+ This is subject to change in the future.
35
+ """
27
36
attitude = "attitude"
28
- thrust = "thrust"
29
- default = attitude
37
+ """Attitude control takes [collective thrust, roll, pitch, yaw].
30
38
39
+ Note:
40
+ Recommended frequency is >=100 Hz.
41
+ """
42
+ thrust = "thrust"
43
+ """Thrust control takes [thrust1, thrust2, thrust3, thrust4] for each drone motor.
31
44
32
- class Controller (str , Enum ):
33
- """Controller type of the simulated onboard controller."""
34
-
35
- pycffirmware = "pycffirmware"
36
- emulatefirmware = "emulatefirmware"
37
- default = emulatefirmware
45
+ Note:
46
+ Recommended frequency is >=500 Hz.
47
+ """
48
+ default = attitude
38
49
39
50
40
51
KF : float = 3.16e-10
@@ -90,27 +101,28 @@ def state2attitude(
90
101
91
102
@partial (jnp .vectorize , signature = "(4),(4),(3),(3)->(4),(3)" , excluded = [4 ])
92
103
def attitude2rpm (
93
- cmd : Array , quat : Array , last_rpy : Array , rpy_err_i : Array , dt : float
104
+ controls : Array , quat : Array , last_rpy : Array , rpy_err_i : Array , dt : float
94
105
) -> tuple [Array , Array ]:
95
- """Convert the desired attitude and quaternion into motor RPMs."""
106
+ """Convert the desired collective thrust and attitude into motor RPMs."""
96
107
rot = R .from_quat (quat )
97
- target_rot = R .from_euler ("xyz" , cmd [..., 1 :])
108
+ target_rot = R .from_euler ("xyz" , controls [ 1 :])
98
109
drot = (target_rot .inv () * rot ).as_matrix ()
99
-
100
110
# Extract the anti-symmetric part of the relative rotation matrix.
101
111
rot_e = jnp .array ([drot [2 , 1 ] - drot [1 , 2 ], drot [0 , 2 ] - drot [2 , 0 ], drot [1 , 0 ] - drot [0 , 1 ]])
102
- rpy_rates_e = - (rot .as_euler ("xyz" ) - last_rpy ) / dt # Assuming zero rpy_rates target
112
+ # TODO: Assumes zero rpy_rates targets for now, use the actual target instead.
113
+ rpy_rates_e = - (rot .as_euler ("xyz" ) - last_rpy ) / dt
103
114
rpy_err_i = rpy_err_i - rot_e * dt
104
115
rpy_err_i = jnp .clip (rpy_err_i , - 1500.0 , 1500.0 )
105
116
rpy_err_i = rpy_err_i .at [:2 ].set (jnp .clip (rpy_err_i [:2 ], - 1.0 , 1.0 ))
106
117
# PID target torques.
107
118
target_torques = - P_T * rot_e + D_T * rpy_rates_e + I_T * rpy_err_i
108
119
target_torques = jnp .clip (target_torques , - 3200 , 3200 )
109
- thrust_per_motor = cmd [0 ] / 4
120
+ thrust_per_motor = jnp . atleast_1d ( controls [0 ]) / 4
110
121
pwm = jnp .clip (thrust2pwm (thrust_per_motor ) + MIX_MATRIX @ target_torques , MIN_PWM , MAX_PWM )
111
122
return pwm2rpm (pwm ), rpy_err_i
112
123
113
124
125
+ @partial (jnp .vectorize , signature = "(4)->(4)" )
114
126
def thrust2pwm (thrust : Array ) -> Array :
115
127
"""Convert the desired thrust into motor PWM.
116
128
@@ -124,6 +136,7 @@ def thrust2pwm(thrust: Array) -> Array:
124
136
return jnp .clip ((jnp .sqrt (thrust / KF ) - PWM2RPM_CONST ) / PWM2RPM_SCALE , MIN_PWM , MAX_PWM )
125
137
126
138
139
+ @partial (jnp .vectorize , signature = "(4)->(4)" )
127
140
def pwm2rpm (pwm : Array ) -> Array :
128
141
"""Convert the motors' PWMs into RPMs."""
129
142
return PWM2RPM_CONST + PWM2RPM_SCALE * pwm
0 commit comments