Are you using the MPU9250's internal digital low pass filters for the gyroscope and accelerometer? For a quadcopter the 20Hz setting is good for both sensors.
Also, to estimate angle and reduce noise it's necessary fuse the gyroscope and accelerometer. The accelerometer is noisy over the short term, but stable over the long term. The gyroscope by contrast is stable over the short term, but drifts over time. Using a sensor fusion algorithm such as a complementary filter allows you simultaneously take advantage of the gyroscope's short term and accelerometer's long term stablility. The MPU9250 also contains a Digital Motion Processor (DMP) that can calculate the roll and pitch angles for you.
An easier route to getting your quadcopter airborne, is to use angular rate in degrees per second or radians per second rather than angles, since the x, y and z axes can be handled separately. (Changes to the roll, pitch and yaw angles by contrast affect each other). In any case, if you think about it, rate control is how most aircraft respond to pilot input, the further you pull the stick the faster the aircraft turns.
There's no one solution here, what I did was to take the pilot's throttle, roll, pitch and yaw commands. I then mapped the roll, pitch and yaw values to degrees per second, around ±250°/s for stick minimum and maximum, these became the set points to three rate PID controllers. The raw throttle value remained unchanged.
The gyrocope's raw output was also be converted to degrees per second for roll, pitch and yaw rates, these became the input for the previously mentioned rate PIDs.
The quadcopter can fly on just the control loops' proportional setting alone, (other terms can be added later). Each P controller simply subtracts the gyroscope from the pilot input and multiplies this by a gain factor:
P control loop = (pilot set point - gyrocope input) * gain
The pilot setpoint is specifying where we want to be in terms of the aircraft's angular rotation and the gyro input is where we currently are. In effect the closed loop is driving the motors to allow the gyroscope to shadow the pilots commands.
It's important to note that the output from the control loops don't have any units as such. These outputs were then mixed and used offset the raw throttle for each motor:
motor1 = rawThrottle + rollRateOut + pitchRateOut + yawRateOut;
motor2 = rawThrottle - rollRateOut + pitchRateOut - yawRateOut;
motor3 = rawThrottle - rollRateOut - pitchRateOut + yawRateOut;
motor4 = rawThrottle + rollRateOut - pitchRateOut - yawRateOut;
Following that the motor values were mapped and contrained using the Arduino map and constrain functions, before sending them out as PWM signals to the ESC using the Arduino analogWrite() function at 490Hz.
Auto-level where the airelon and elevator sticks represent a roll and pitch angle, can be appended as a later addition to this existing system.