Maybe it's also worth considering why people find gimbal lock "wrong" even when they do not know what gimbal lock is. This suggests some intuitive expectation that doesn't align with what Euler angles do (i.e. they actually do not want to work with gimbals). It begs the question how this natural expectation may be fulfilled instead.
Assume you are given a joystick to control an airplane's orientation (you are in the airplane), but make it special so that the stick can also be rotated around its axis to achieve a yaw rotation. Store the set of orthonormal vectors describing the orientation of the plane $e_1,e_2,e_3$ and identify the stick's current direction with $e_2$. Now let the airplane's computer record the airplane's basis $e_1, e_2, e_3$, and the stick's direction $d$, every $x$ seconds and keep its last two states: previous $p$ and current $c$. Assume you have tilted the stick from $e_2$ in $p$ to $e'_2$ in $c$. Intuitively one wants to align the plane so that $e'_2$ is the plane's "up" again. This can be done by rotating the previous basis $e_1, e_2, e_3$ along the shortest path as to align $e_2$ to $e'_2$. This can be achieved using the following formula:
$$e'_i = e_i - \frac{e_i \cdot e'_2}{1 + (e_2 \cdot e'_2)}(e_2 + e'_2), \, i \in \{1,3\},$$
and the vectors have to be further normalized. This yields a new basis $e'_1, e'_2, e'_3$ that you align your airplane to. This has a singularity if you allow your joystick a range of motion of 180 degrees, since if in $c$ you recorded $e'_2 = -e_2$ it is not clear along which rotation you reached it (all rotations are equal then: 180 degrees around any axis perpendicular to $e_2$, and there are infinitely many such axes). A solution is to either disallow 180 degrees tilts (which holds for actual joysticks), poll your joystick state more often than every $x$ seconds so it would be physically impossible to flip it by 180 degrees between polling in $p$ and $c$, or set a "default" flip rotation, e.g. $e'_1 = e_1, e'_2 = -e_2, e'_3 = -e_3$ which rotates around $e_1$ whenever $e'_2 = -e_2$ in $c$ (your airplane becomes reversed, but any other axis perpendicular to $e_2$ would work). Now what should be done about the yaw rotation induced by the rotation of the stick around its axis? One option is to rotate around $e'_2$. Note that the above combination of operations do not lead to gimbal lock ever, since it's not a gimbal configuration of rotations in the first place. In all states you get the exact same "intuitive" behaviour.
Note that one could have chosen a different vector $d$ instead of $e_2$ to correspond to the joystick's direction and the idea remains the same. The point is that the two rotations - one induced from the tilting of $d$, and one induced from the rotation of $d$ around its axis, are different in behaviour, but at least for flight sims/joystick that seems to be the intuitive expected behaviour. You can achieve the same thing by using a keyboard too, e.g. up/down corresponds to tilting the stick forward/backward, left/right = tilting left/right, some other two keys for the clockwise and counter-clockwise rotation around its axis.
Now forget the airplane and joystick setting, and assume you were given an actual physical 3-gimbal as a controller. In that scenario it is likely that you would not find the joystick behaviour intuitive but the 3-gimbal one, in which case it would make sense to use Euler angles. The point is that the gimbal lock by itself isn't the issue, it's the mismatch between expectations and how a system operates. That is, if I give you a joystick controller and it acts like a 3-gimbal virtually (i.e. I use Euler angles), or if I give you a 3-gimbal controller and it acts like a joystick (i.e. I use the shortest rotation described for the joystick), you will likely find both confusing. All in all, I would say this issue arises simply because people apply the wrong mathematical model to what they envision, i.e. it doesn't model their expectations but something else instead. Speaking of which, I haven't seen any 3-gimbal PC controllers. Using those, Euler angles ought to make perfect sense.
TLDR:
In order to achieve incremental rotations while avoiding gimbal lock don't do this:
pitch += incr_pitch; yaw += incr_yaw; roll += incr_yaw;
R = Rz(roll) * Ry(yaw) * Rx(pitch);
Some alternatives are to store the orientation matrix $R$ and modify it by the desired incremental change:
R = Rz(incr_roll) * Ry(incr_yaw) * Rx(incr_pitch) * R;
Another very convoluted and inefficient option, which nevertheless doesn't exhibit gimbal lock and thus illustrates that the issue is the incrementing of pitch, yaw, roll
and not the storage of Euler angles, is the following:
R = Rz(incr_roll) * Ry(incr_yaw) * Rx(incr_pitch) * Rz(roll) * Ry(yaw) * Rx(pitch);
(pitch,yaw,roll) = extract_Euler(R)
A different alternative is to use quaternions in the exact same way as the second snippet (q
is assumed to be stored):
q = quat(incr_roll/2,(0,0,1)) * quat(incr_yaw/2,(0,1,0)) * quat(incr_pitch/2,(1,0,0)) * q;
This also provides a counterexample to a common misconception that you cannot get gimbal lock by using quaterions. The below snippet will exhibit gimbal lock:
pitch+=incr_pitch; yaw+=inct_yaw; roll+=incr_roll;
q = quat(incr_roll/2,(0,0,1)) * quat(incr_yaw/2,(0,1,0)) * quat(incr_pitch/2,(1,0,0));
The point I am trying to make is that the issue arises when incrementing Euler angles and then producing a rotation from those. The increment results in a behaviour consistent with turning a specific gimbal from a 3-axis gimbal, and not with the controls of an airplane for example. For the latter one has to produce the increment through matrix/quaternion composition with the orientation, or the trick that I suggested:
$$e'_i = e_i - \frac{e_i \cdot e'_2}{1 + (e_2 \cdot e'_2)}(e_2 + e'_2), \, i \in \{1,3\},$$