Copter 4.7.0 — Simple / Super Simple Mode appears to have no effect on roll/pitch (update_simple_mode writes control_in, mode.cpp now reads norm_input_dz)

I think Simple Mode and Super Simple Mode are non-functional for roll/pitch input in Copter 4.7.0. The mode engages fine — GCS message, EV 26, D32 Id=9 arm-bearing capture all present and correct — but the rotated stick values go into a variable that the 4.7 input path no longer reads.

To pre-empt the obvious first question: this was engaged by aux switch (RC8_OPTION = 3), with the SIMPLE and SUPER_SIMPLE bitmask params both at 0.

The code

Copter::update_simple_mode() in ArduCopter/Copter.cpp writes its result via set_control_in():

channel_roll->set_control_in(rollx*ahrs.cos_yaw() + pitchx*ahrs.sin_yaw());
channel_pitch->set_control_in(-rollx*ahrs.sin_yaw() + pitchx*ahrs.cos_yaw());

Mode::get_pilot_desired_lean_angles_rad() in ArduCopter/mode.cpp changed accessor between 4.6.0 and 4.7.0:

// 4.6.0 — reads control_in
rc_input_to_roll_pitch(channel_roll->get_control_in()*(1.0/ROLL_PITCH_YAW_INPUT_MAX), ...

// 4.7.0 — reads radio_in
rc_input_to_roll_pitch_rad(channel_roll->norm_input_dz(), channel_pitch->norm_input_dz(), ...

norm_input_dz() is const and computed only from radio_in / radio_trim / radio_min / radio_max / dead_zone. It never looks at control_in, so the write above cannot influence it. For normal pilot input the two expressions are numerically equivalent, deadzone included, which is presumably why the SI-units migration looked like a safe swap — Simple Mode is the one consumer that relied on control_in being writable.

get_pilot_desired_velocity() changed the same way, so LAND repositioning is in scope too. Both the SIMPLE and SUPERSIMPLE branches of update_simple_mode() end at the same two set_control_in() calls, so Super Simple behaves identically.

Things I checked and ruled out

Call site is present (ModeLoiter::run() and init() both call it). ap.new_radio_frame is still set by read_radio(), so the function does run. Bitmask reapplication in mode_switch_changed() is guarded behind find_channel_for_option(AUX_FUNC::SIMPLE_MODE), so it isn’t clobbering the aux-switch state. No MODE event or SET_SIMPLE_OFF in any analysed window. Nothing is #if’d out. The write happens; the read doesn’t.

Flight data, briefly

Two logs, V4.7.0 (1511f271), Pixhawk 2.4.8, QUAD/X, Loiter, aux switch toggled four times in one session. In Loiter the body→NE rotation uses ahrs.yaw unconditionally and Simple Mode sits upstream, so comparing the direction of the position controller’s desired NE acceleration against the body-frame stick direction separates the two hypotheses cleanly: if the transform is applied the offset should equal the constant arm bearing, if not it should track instantaneous yaw. Regressing offset on yaw across four Simple-ON windows gives slope 0.987, 1.008, 1.004, 0.996 — i.e. it tracks yaw, transform not applied — with β = 0 rejected at 28σ or better after autocorrelation correction.

Sanity checks: feeding the pipeline synthetic data built as if the transform had been applied returns slope 0.00, so the method isn’t rigged. An independent test using only RCIN, ATT.Yaw and EKF NE velocity gives 4.2° mean error for “stick + instantaneous yaw” versus 115.0° for “stick + arm bearing”. D32 Id=9 matches ATT.Yaw at 11/11 ARM events to 0.01°, so the arm bearing itself is captured correctly.

Caveats, stated honestly: the desired-acceleration signal also carries the Loiter braking term and, with LOIT_OPTIONS = 1, the coordinated-turn centripetal term — both bias against my conclusion, so the numbers are conservative. My flight evidence is Loiter only; paths that call get_control_in() directly still see the rotation, so the precise scope is “not applied in the get_pilot_desired_lean_angles_rad() and get_pilot_desired_velocity() paths”. By inspection that covers Stabilize, AltHold, Loiter, PosHold, FlowHold and LAND repositioning. And the firmware reports git hash 1511f271 — I haven’t byte-verified that this is the Copter-4.7.0 tag commit.

SITL check (I haven’t run this)

Should take five minutes on 4.7.0: RC8_OPTION=3, arm, takeoff 20, mode LOITER, rc 8 2000, hold rc 2 1400 and note the ground track on the map, then rc 4 1800 to yaw about 180°, settle, and hold the same rc 2 1400 again. Working Simple Mode drives the vehicle in the same map direction both times (north, from the default SITL arm heading). The bug shows as the second track reversing — and as identical ground tracks whether you run the sequence with rc 8 2000 or rc 8 1000.

I’ll run it myself and follow up with the result. Posting now rather than waiting, in case this is already known or someone gets there first.

Fix direction

Reverting the four lines to get_control_in()*(1.0/ROLL_PITCH_YAW_INPUT_MAX) restores correct behaviour and is what I’m using locally as a stopgap. But control_in looks deliberately on the way out in the 4.7 SI refactor, so that will probably conflict again. Cleaner, I think, is for update_simple_mode() to publish its rotated result as normalised values that get_pilot_desired_lean_angles_rad() and get_pilot_desired_velocity() consume — move the transform into the normalised domain rather than reattaching it to the legacy centidegree cache.

So: can anyone reproduce this in SITL on 4.7.0, is it already known or tracked, and which of the two fix directions would the dev team prefer? I’m happy to write the PR once someone tells me which shape is wanted.

Filed as a GitHub issue with both logs attached: Copter 4.7.0: Simple and Super Simple Mode have no effect on roll/pitch — update_simple_mode() writes control_in, get_pilot_desired_lean_angles_rad() now reads norm_input_dz() · Issue #33936 · ArduPilot/ardupilot · GitHub

Hi @Hasan_Tahsin_Yuksek,

Thanks very much for the report! It’s a shame that despite 4 months of beta testing this was never noticed but in any case we will investigate and hopefully get a fix out with 4.7.1-beta1 very soon!

Hi @rmackay9,

Thanks for the quick response.

I noticed it in flight - simple mode just was not doing anything, the drone kept going where the nose was pointing. Then I confirmed it from the logs.

Good to hear a fix is coming with 4.7.1-beta1, that will be a nice improvement for anyone who relies on simple mode.