diff --git a/src/modules/mc_pos_control/PositionControl.cpp b/src/modules/mc_pos_control/PositionControl.cpp
index 0b5d4e6294c1de998f87ada88247c410caddaa02..92f5114cb531d1951c7437fdfcedcdaa7103d944 100644
--- a/src/modules/mc_pos_control/PositionControl.cpp
+++ b/src/modules/mc_pos_control/PositionControl.cpp
@@ -183,7 +183,8 @@ void PositionControl::_positionController()
 		_vel_sp(1) = _vel_sp(1) * _VelMaxXY / vel_norm_xy;
 	}
 
-	_vel_sp(2) = math::constrain(_vel_sp(2), -_VelMaxZ[0], _VelMaxZ[1]);
+	/* Saturate velocity in D-direction */
+	_vel_sp(2) = math::constrain(_vel_sp(2), -_VelMaxZ.up, _VelMaxZ.down);
 }
 
 void PositionControl::_velocityController(const float &dt)
@@ -193,67 +194,101 @@ void PositionControl::_velocityController(const float &dt)
 	 * u_des = P(vel_err) + D(vel_err_dot) + I(vel_integral)
 	 * Umin <= u_des <= Umax
 	 *
-	 * Saturation for vel_integral;
+	 * Anti-Windup:
 	 * u_des = _thr_sp; r = _vel_sp; y = _vel
 	 * u_des >= Umax and r - y >= 0 => Saturation = true
 	 * u_des >= Umax and r - y <= 0 => Saturation = false
 	 * u_des <= Umin and r - y <= 0 => Saturation = true
 	 * u_des <= Umin and r - y >= 0 => Saturation = false
 	 *
+	 *	Notes:
+	 * - PID implementation is in NED-frame
+	 * - control output in D-direction has priority over NE-direction
+	 * - the equilibrium point for the PID is at hover-thrust
+	 * - the maximum tilt cannot exceed 90 degrees. This means that it is
+	 * 	 not possible to have a desired thrust direction pointing in the positive
+	 * 	 D-direction (= downward)
+	 * - the desired thrust in D-direction is limited by the thrust limits
+	 * - the desired thrust in NE-direction is limited by the thrust excess after
+	 * 	 consideration of the desired thrust in D-direction. In addition, the thrust in
+	 * 	 NE-direction is also limited by the maximum tilt.
 	 */
 
+	/* Get maximum tilt */
+	float tilt_max = M_PI_2_F;
+
+	if (PX4_ISFINITE(_constraints.tilt_max) && _constraints.tilt_max <= tilt_max) {
+		tilt_max = _constraints.tilt_max;
+	}
+
 	Vector3f vel_err = _vel_sp - _vel;
 
-	/* TODO: add offboard acceleration mode
-	 * PID-controller */
-	Vector3f offset(0.0f, 0.0f, _ThrHover);
-	Vector3f thr_sp = Pv.emult(vel_err) + Dv.emult(_vel_dot) + _thr_int - offset;
+	/*
+	 * TODO: add offboard acceleration mode
+	 * */
 
-	/* Get maximum tilt */
-	float tilt_max = PX4_ISFINITE(_constraints.tilt_max) ? _constraints.tilt_max : M_PI_2_F;
-	tilt_max = math::min(tilt_max, M_PI_2_F);
+	/* Consider thrust in D-direction */
+	float thrust_desired_D = Pv(2) * vel_err(2) + Dv(2) * _vel_dot(2) + _thr_int(2) - _ThrHover;
 
-	/* Compute maximum allowed thrust along xy based on thrust in z-direction and
-	 * scale _thrust_sp by that value. This only has an effect for altitude mode where
-	 * _thr_sp(0:1).length() > 0.0f.
-	 */
+	/* The Thrust limits are negated and swapped due to NED-frame */
+	float uMax = -_ThrustLimit.min;
+	float uMin = -_ThrustLimit.max;
+
+	/* Apply Anti-Windup in D-direction */
+	bool stop_integral_D = (thrust_desired_D >= uMax && vel_err(2) >= 0.0f) ||
+			       (thrust_desired_D <= uMin && vel_err(2) <= 0.0f);
+
+	if (!stop_integral_D) {
+		_thr_int(2) += vel_err(2) * Iv(2) * dt;
+
+	}
+
+	/* Satureate thrust setpoint in D-direction */
+	_thr_sp(2) = math::constrain(thrust_desired_D, uMin, uMax);
 
 	if (matrix::Vector2f(&_thr_sp(0)).length() > FLT_EPSILON) {
 
-		float thr_xy_max = fabsf(thr_sp(2)) * tanf(tilt_max);
+		/* Thrust setpoints in NE-direction is already provided. Only
+		 * scaling is required.
+		 */
+
+		float thr_xy_max = fabsf(_thr_sp(2)) * tanf(tilt_max);
 		_thr_sp(0) *= thr_xy_max;
 		_thr_sp(1) *= thr_xy_max;
 
-	}
+	} else {
 
-	_thr_sp += thr_sp;
+		/* PID for NE-direction */
+		Vector2f thrust_desired_NE;
+		thrust_desired_NE(0) = Pv(0) * vel_err(0) + Dv(0) * _vel_dot(0) + _thr_int(0);
+		thrust_desired_NE(1) = Pv(1) * vel_err(1) + Dv(1) * _vel_dot(1) + _thr_int(1);
 
-	/* Limit tilt with priority on z
-	 * For manual controlled mode excluding pure manual and rate control, maximum tilt is 90;
-	 * It is to note that pure manual and rate control will never enter _velocityController method. */
-	_thr_sp = ControlMath::constrainTilt(_thr_sp, tilt_max);
+		/* Get maximum allowed thrust in NE based on tilt and excess thrust */
+		float thrust_max_NE_tilt = fabsf(_thr_sp(2)) * tanf(tilt_max);
+		float thrust_max_NE = sqrtf(_ThrustLimit.max * _ThrustLimit.max - fabsf(_thr_sp(2) * fabsf(_thr_sp(2))));
+		thrust_max_NE = math::min(thrust_max_NE_tilt, thrust_max_NE);
 
+		/* Get the direction of (r-y) in NE-direction */
+		float direction_NE = Vector2f(&vel_err(0)) * Vector2f(&_vel_sp(0));
 
-	/* Constrain thrust set-point and update saturation flag */
-	/* To get (r-y) for horizontal direction, we look at the dot-product
-	 * for vel_err and _vel_sp. The sign of the dot product indicates
-	 * if (r-y) is greater or smaller than 0
-	 */
-	float dot_xy = matrix::Vector2f(&_vel_sp(0)) * matrix::Vector2f(&_vel(0));
-	float direction[2] = {dot_xy, -vel_err(2)}; // negative sign because of N-E-D
-	bool stop_I[2] = {false, false}; // stop integration for xy and z
-	ControlMath::constrainPIDu(_thr_sp, stop_I, _ThrLimit, direction);
-
-	/* Update integrals */
-	if (!stop_I[0]) {
-		_thr_int(0) += vel_err(0) * Iv(0) * dt;
-		_thr_int(1) += vel_err(1) * Iv(1) * dt;
-	}
+		/* Apply Anti-Windup in NE-direction */
+		bool stop_integral_NE = (thrust_desired_NE.length() >= thrust_max_NE && direction_NE >= 0.0f);
 
-	if (!stop_I[1]) {
-		_thr_int(2) += vel_err(2) * Iv(2) * dt;
-	}
+		if (!stop_integral_NE) {
+			_thr_int(0) += vel_err(0) * Iv(0) * dt;
+			_thr_int(1) += vel_err(1) * Iv(1) * dt;
+		}
+
+		/* Saturate thrust in NE-direction */
+		_thr_sp(0) = thrust_desired_NE(0);
+		_thr_sp(1) = thrust_desired_NE(1);
+
+		if (thrust_desired_NE.length() > thrust_max_NE) {
+			_thr_sp(0) = thrust_desired_NE(0) / thrust_desired_NE.length() * thrust_max_NE;
+			_thr_sp(1) = thrust_desired_NE(1) / thrust_desired_NE.length() * thrust_max_NE;
 
+		}
+	}
 
 }
 
@@ -293,10 +328,10 @@ void PositionControl::_setParams()
 	param_get(_Dvz_h, &Dv(2));
 
 	param_get(_VelMaxXY_h, &_VelMaxXY);
-	param_get(_VelMaxZup_h, &_VelMaxZ[0]);
-	param_get(_VelMaxZdown_h, &_VelMaxZ[1]);
+	param_get(_VelMaxZup_h, &_VelMaxZ.up);
+	param_get(_VelMaxZdown_h, &_VelMaxZ.down);
 
 	param_get(_ThrHover_h, &_ThrHover);
-	param_get(_ThrMax_h, &_ThrLimit[0]);
-	param_get(_ThrMin_h, &_ThrLimit[1]);
+	param_get(_ThrMax_h, &_ThrustLimit.max);
+	param_get(_ThrMin_h, &_ThrustLimit.min);
 }
diff --git a/src/modules/mc_pos_control/PositionControl.hpp b/src/modules/mc_pos_control/PositionControl.hpp
index 207b988d91dd6166081a76e6275805e923c6c963..c694d91b3ea1aa5a2344773277a19514e99a24b3 100644
--- a/src/modules/mc_pos_control/PositionControl.hpp
+++ b/src/modules/mc_pos_control/PositionControl.hpp
@@ -116,9 +116,17 @@ private:
 	/* Parameters */
 	matrix::Vector3f Pp, Pv, Iv, Dv = matrix::Vector3f{0.0f, 0.0f, 0.0f};
 	float _VelMaxXY{};
-	float _VelMaxZ[2]; //index 0: index up; 1: down
+	struct DirectionD {
+		float up;
+		float down;
+	};
+	DirectionD _VelMaxZ;
+	struct Limits {
+		float max;
+		float min;
+	};
+	Limits _ThrustLimit;
 	float _ThrHover{0.5f};
-	float _ThrLimit[2]; //index 0: max, index 1: min
 	bool _skipController{false};
 
 	/* Helper methods */