Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/Tools/autotest/helicopter.py
Views: 1798
'''1Fly Helicopter in SITL23AP_FLAKE8_CLEAN4'''56from __future__ import print_function78from arducopter import AutoTestCopter910import vehicle_test_suite11from vehicle_test_suite import NotAchievedException, AutoTestTimeoutException1213from pymavlink import mavutil14from pysim import vehicleinfo1516import copy17import operator181920class AutoTestHelicopter(AutoTestCopter):2122sitl_start_loc = mavutil.location(40.072842, -105.230575, 1586, 0) # Sparkfun AVC Location2324def vehicleinfo_key(self):25return 'Helicopter'2627def log_name(self):28return "HeliCopter"2930def default_frame(self):31return "heli"3233def sitl_start_location(self):34return self.sitl_start_loc3536def default_speedup(self):37'''Heli seems to be race-free'''38return 1003940def is_heli(self):41return True4243def rc_defaults(self):44ret = super(AutoTestHelicopter, self).rc_defaults()45ret[8] = 100046ret[3] = 1000 # collective47return ret4849@staticmethod50def get_position_armable_modes_list():51'''filter THROW mode out of armable modes list; Heli is special-cased'''52ret = AutoTestCopter.get_position_armable_modes_list()53ret = filter(lambda x : x != "THROW", ret)54return ret5556def loiter_requires_position(self):57self.progress("Skipping loiter-requires-position for heli; rotor runup issues")5859def get_collective_out(self):60servo = self.mav.recv_match(type='SERVO_OUTPUT_RAW', blocking=True)61chan_pwm = (servo.servo1_raw + servo.servo2_raw + servo.servo3_raw)/3.062return chan_pwm6364def RotorRunup(self):65'''Test rotor runip'''66# Takeoff and landing in Loiter67TARGET_RUNUP_TIME = 1068self.zero_throttle()69self.change_mode('LOITER')70self.wait_ready_to_arm()71self.arm_vehicle()72servo = self.mav.recv_match(type='SERVO_OUTPUT_RAW', blocking=True)73coll = servo.servo1_raw74coll = coll + 5075self.set_parameter("H_RSC_RUNUP_TIME", TARGET_RUNUP_TIME)76self.progress("Initiate Runup by putting some throttle")77self.set_rc(8, 2000)78self.set_rc(3, 1700)79self.progress("Collective threshold PWM %u" % coll)80tstart = self.get_sim_time()81self.progress("Wait that collective PWM pass threshold value")82servo = self.mav.recv_match(condition='SERVO_OUTPUT_RAW.servo1_raw>%u' % coll, blocking=True)83runup_time = self.get_sim_time() - tstart84self.progress("Collective is now at PWM %u" % servo.servo1_raw)85self.mav.wait_heartbeat()86if runup_time < TARGET_RUNUP_TIME:87self.zero_throttle()88self.set_rc(8, 1000)89self.disarm_vehicle()90self.mav.wait_heartbeat()91raise NotAchievedException("Takeoff initiated before runup time complete %u" % runup_time)92self.progress("Runup time %u" % runup_time)93self.zero_throttle()94self.land_and_disarm()95self.mav.wait_heartbeat()9697# fly_avc_test - fly AVC mission98def AVCMission(self):99'''fly AVC mission'''100self.change_mode('STABILIZE')101self.wait_ready_to_arm()102103self.arm_vehicle()104self.progress("Raising rotor speed")105self.set_rc(8, 2000)106107# upload mission from file108self.progress("# Load copter_AVC2013_mission")109# load the waypoint count110num_wp = self.load_mission("copter_AVC2013_mission.txt", strict=False)111if not num_wp:112raise NotAchievedException("load copter_AVC2013_mission failed")113114self.progress("Fly AVC mission from 1 to %u" % num_wp)115self.set_current_waypoint(1)116117# wait for motor runup118self.delay_sim_time(20)119120# switch into AUTO mode and raise throttle121self.change_mode('AUTO')122self.set_rc(3, 1500)123124# fly the mission125self.wait_waypoint(0, num_wp-1, timeout=500)126127# set throttle to minimum128self.zero_throttle()129130# wait for disarm131self.wait_disarmed()132self.progress("MOTORS DISARMED OK")133134self.progress("Lowering rotor speed")135self.set_rc(8, 1000)136137self.progress("AVC mission completed: passed!")138139def takeoff(self,140alt_min=30,141takeoff_throttle=1700,142require_absolute=True,143mode="STABILIZE",144timeout=120):145"""Takeoff get to 30m altitude."""146self.progress("TAKEOFF")147self.change_mode(mode)148if not self.armed():149self.wait_ready_to_arm(require_absolute=require_absolute, timeout=timeout)150self.zero_throttle()151self.arm_vehicle()152153self.progress("Raising rotor speed")154self.set_rc(8, 2000)155self.progress("wait for rotor runup to complete")156if self.get_parameter("H_RSC_MODE") == 4:157self.context_collect('STATUSTEXT')158self.wait_statustext("Governor Engaged", check_context=True)159elif self.get_parameter("H_RSC_MODE") == 3:160self.wait_rpm(1, 1300, 1400)161else:162self.wait_servo_channel_value(8, 1659, timeout=10)163164# wait for motor runup165self.delay_sim_time(20)166167if mode == 'GUIDED':168self.user_takeoff(alt_min=alt_min)169else:170self.set_rc(3, takeoff_throttle)171self.wait_altitude(alt_min-1, alt_min+5, relative=True, timeout=timeout)172self.hover()173self.progress("TAKEOFF COMPLETE")174175def FlyEachFrame(self):176'''Fly each supported internal frame'''177vinfo = vehicleinfo.VehicleInfo()178vinfo_options = vinfo.options[self.vehicleinfo_key()]179known_broken_frames = {180}181for frame in sorted(vinfo_options["frames"].keys()):182self.start_subtest("Testing frame (%s)" % str(frame))183if frame in known_broken_frames:184self.progress("Actually, no I'm not - it is known-broken (%s)" %185(known_broken_frames[frame]))186continue187frame_bits = vinfo_options["frames"][frame]188print("frame_bits: %s" % str(frame_bits))189if frame_bits.get("external", False):190self.progress("Actually, no I'm not - it is an external simulation")191continue192model = frame_bits.get("model", frame)193# the model string for Callisto has crap in it.... we194# should really have another entry in the vehicleinfo data195# to carry the path to the JSON.196actual_model = model.split(":")[0]197defaults = self.model_defaults_filepath(actual_model)198if not isinstance(defaults, list):199defaults = [defaults]200self.customise_SITL_commandline(201[],202defaults_filepath=defaults,203model=model,204wipe=True,205)206self.takeoff(10)207self.do_RTL()208209def governortest(self):210'''Test Heli Internal Throttle Curve and Governor'''211self.customise_SITL_commandline(212[],213defaults_filepath=self.model_defaults_filepath('heli-gas'),214model="heli-gas",215wipe=True,216)217self.set_parameter("H_RSC_MODE", 4)218self.takeoff(10)219self.do_RTL()220221def hover(self):222self.progress("Setting hover collective")223self.set_rc(3, 1500)224225def PosHoldTakeOff(self):226"""ensure vehicle stays put until it is ready to fly"""227self.set_parameter("PILOT_TKOFF_ALT", 700)228self.change_mode('POSHOLD')229self.zero_throttle()230self.set_rc(8, 1000)231self.wait_ready_to_arm()232# Arm233self.arm_vehicle()234self.progress("Raising rotor speed")235self.set_rc(8, 2000)236self.progress("wait for rotor runup to complete")237self.wait_servo_channel_value(8, 1659, timeout=10)238self.delay_sim_time(20)239# check we are still on the ground...240max_relalt = 1 # metres241relative_alt = self.get_altitude(relative=True)242if abs(relative_alt) > max_relalt:243raise NotAchievedException("Took off prematurely (abs(%f)>%f)" %244(relative_alt, max_relalt))245246self.progress("Pushing collective past half-way")247self.set_rc(3, 1600)248self.delay_sim_time(0.5)249self.hover()250251# make sure we haven't already reached alt:252relative_alt = self.get_altitude(relative=True)253max_initial_alt = 1.5 # metres254if abs(relative_alt) > max_initial_alt:255raise NotAchievedException("Took off too fast (%f > %f" %256(abs(relative_alt), max_initial_alt))257258self.progress("Monitoring takeoff-to-alt")259self.wait_altitude(6, 8, relative=True, minimum_duration=5)260self.progress("takeoff OK")261262self.land_and_disarm()263264def StabilizeTakeOff(self):265"""Fly stabilize takeoff"""266self.change_mode('STABILIZE')267self.set_rc(3, 1000)268self.set_rc(8, 1000)269self.wait_ready_to_arm()270self.arm_vehicle()271self.set_rc(8, 2000)272self.progress("wait for rotor runup to complete")273self.wait_servo_channel_value(8, 1659, timeout=10)274self.delay_sim_time(20)275# check we are still on the ground...276relative_alt = self.get_altitude(relative=True)277if abs(relative_alt) > 0.1:278raise NotAchievedException("Took off prematurely")279self.progress("Pushing throttle past half-way")280self.set_rc(3, 1650)281282self.progress("Monitoring takeoff")283self.wait_altitude(6.9, 8, relative=True)284285self.progress("takeoff OK")286287self.land_and_disarm()288289def SplineWaypoint(self, timeout=600):290"""ensure basic spline functionality works"""291self.load_mission("copter_spline_mission.txt", strict=False)292self.change_mode("LOITER")293self.wait_ready_to_arm()294self.arm_vehicle()295self.progress("Raising rotor speed")296self.set_rc(8, 2000)297self.delay_sim_time(20)298self.change_mode("AUTO")299self.set_rc(3, 1500)300self.wait_disarmed(timeout=600)301self.progress("Lowering rotor speed")302self.set_rc(8, 1000)303304def Autorotation(self, timeout=600):305"""Check engine-out behaviour"""306self.context_push()307start_alt = 100 # metres308self.set_parameters({309"AROT_ENABLE": 1,310"H_RSC_AROT_ENBL": 1,311})312bail_out_time = self.get_parameter('H_RSC_AROT_RUNUP')313self.change_mode('POSHOLD')314self.set_rc(3, 1000)315self.set_rc(8, 1000)316self.wait_ready_to_arm()317self.arm_vehicle()318self.set_rc(8, 2000)319self.progress("wait for rotor runup to complete")320self.wait_servo_channel_value(8, 1659, timeout=10)321self.delay_sim_time(20)322self.set_rc(3, 2000)323self.wait_altitude(start_alt - 1,324(start_alt + 5),325relative=True,326timeout=timeout)327self.context_collect('STATUSTEXT')328329# Reset collective to enter hover330self.set_rc(3, 1500)331332# Change to the autorotation flight mode333self.progress("Triggering autorotate mode")334self.change_mode('AUTOROTATE')335self.delay_sim_time(2)336337# Disengage the interlock to remove power338self.set_rc(8, 1000)339340# Ensure we have progressed through the mode's state machine341self.wait_statustext("SS Glide Phase", check_context=True)342343self.progress("Testing bailout from autorotation")344self.set_rc(8, 2000)345# See if the output ramps to a value close to expected with the prescribed time346self.wait_servo_channel_value(8, 1659, timeout=bail_out_time+1, comparator=operator.ge)347348# Successfully bailed out, disengage the interlock and allow autorotation to progress349self.set_rc(8, 1000)350self.wait_statustext(r"SIM Hit ground at ([0-9.]+) m/s",351check_context=True,352regex=True)353speed = float(self.re_match.group(1))354if speed > 30:355raise NotAchievedException("Hit too hard")356357# Set throttle low to trip auto disarm358self.set_rc(3, 1000)359360self.wait_disarmed()361self.context_pop()362363def ManAutorotation(self, timeout=600):364"""Check autorotation power recovery behaviour"""365RSC_CHAN = 8366367def check_rsc_output(self, throttle, timeout):368# Check we get a sensible throttle output369expected_pwm = int(throttle * 0.01 * 1000 + 1000)370371# Help out the detection by accepting some margin372margin = 2373374# See if the output ramps to a value close to expected with the prescribed time375self.wait_servo_channel_in_range(RSC_CHAN, expected_pwm-margin, expected_pwm+margin, timeout=timeout)376377def TestAutorotationConfig(self, rsc_idle, arot_ramp_time, arot_idle, cool_down):378RAMP_TIME = 10379RUNUP_TIME = 15380AROT_RUNUP_TIME = arot_ramp_time + 4381RSC_SETPOINT = 66382self.set_parameters({383"H_RSC_AROT_ENBL": 1,384"H_RSC_AROT_RAMP": arot_ramp_time,385"H_RSC_AROT_RUNUP": AROT_RUNUP_TIME,386"H_RSC_AROT_IDLE": arot_idle,387"H_RSC_RAMP_TIME": RAMP_TIME,388"H_RSC_RUNUP_TIME": RUNUP_TIME,389"H_RSC_IDLE": rsc_idle,390"H_RSC_SETPOINT": RSC_SETPOINT,391"H_RSC_CLDWN_TIME": cool_down392})393394# Check the RSC config so we know what to expect on the throttle output395if self.get_parameter("H_RSC_MODE") != 2:396self.set_parameter("H_RSC_MODE", 2)397self.reboot_sitl()398399self.change_mode('POSHOLD')400self.set_rc(3, 1000)401self.set_rc(8, 1000)402self.wait_ready_to_arm()403self.arm_vehicle()404self.set_rc(8, 2000)405self.progress("wait for rotor runup to complete")406check_rsc_output(self, RSC_SETPOINT, RUNUP_TIME+1)407408self.delay_sim_time(20)409self.set_rc(3, 2000)410self.wait_altitude(100,411105,412relative=True,413timeout=timeout)414self.context_collect('STATUSTEXT')415self.change_mode('STABILIZE')416417self.progress("Triggering manual autorotation by disabling interlock")418self.set_rc(3, 1000)419self.set_rc(8, 1000)420421self.wait_statustext(r"RSC: In Autorotation", check_context=True)422423# Check we are using the correct throttle output. This should happen instantly on ramp down.424idle_thr = rsc_idle425if (arot_idle > 0):426idle_thr = arot_idle427428check_rsc_output(self, idle_thr, 1)429430self.progress("RSC is outputting correct idle throttle")431432# Wait to establish autorotation.433self.delay_sim_time(2)434435# Re-engage interlock to start bailout sequence436self.set_rc(8, 2000)437438# Ensure we see the bailout state439self.wait_statustext("RSC: Bailing Out", check_context=True)440441# Check we are back up to flight throttle. Autorotation ramp up time should be used442check_rsc_output(self, RSC_SETPOINT, arot_ramp_time+1)443444# Give time for engine to power up445self.set_rc(3, 1400)446self.delay_sim_time(2)447448self.progress("in-flight power recovery")449self.set_rc(3, 1500)450self.delay_sim_time(5)451452# Initiate autorotation again453self.set_rc(3, 1000)454self.set_rc(8, 1000)455456self.wait_statustext(r"SIM Hit ground at ([0-9.]+) m/s",457check_context=True,458regex=True)459speed = float(self.re_match.group(1))460if speed > 30:461raise NotAchievedException("Hit too hard")462463# Check that cool down is still used correctly if set464# First wait until we are out of the autorotation state465self.wait_statustext("RSC: Autorotation Stopped")466if (cool_down > 0):467check_rsc_output(self, rsc_idle*1.5, cool_down)468469# Verify RSC output resets to RSC_IDLE after land complete470check_rsc_output(self, rsc_idle, 20)471self.wait_disarmed()472473# We test the bailout behavior of two different configs474# First we test config with a regular throttle curve475self.progress("testing autorotation with throttle curve config")476self.context_push()477TestAutorotationConfig(self, rsc_idle=5.0, arot_ramp_time=2.0, arot_idle=0, cool_down=0)478479# Now we test a config that would be used with an ESC with internal governor and an autorotation window480self.progress("testing autorotation with ESC autorotation window config")481TestAutorotationConfig(self, rsc_idle=0.0, arot_ramp_time=0.0, arot_idle=20.0, cool_down=0)482483# Check rsc output behavior when using the cool down feature484self.progress("testing autorotation with cool down enabled and zero autorotation idle")485TestAutorotationConfig(self, rsc_idle=5.0, arot_ramp_time=2.0, arot_idle=0, cool_down=5.0)486487self.progress("testing that H_RSC_AROT_IDLE is used over RSC_IDLE when cool down is enabled")488TestAutorotationConfig(self, rsc_idle=5.0, arot_ramp_time=2.0, arot_idle=10, cool_down=5.0)489490self.context_pop()491492def mission_item_home(self, target_system, target_component):493'''returns a mission_item_int which can be used as home in a mission'''494return self.mav.mav.mission_item_int_encode(495target_system,496target_component,4970, # seq498mavutil.mavlink.MAV_FRAME_GLOBAL_INT,499mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,5000, # current5010, # autocontinue5023, # p15030, # p25040, # p35050, # p4506int(1.0000 * 1e7), # latitude507int(2.0000 * 1e7), # longitude50831.0000, # altitude509mavutil.mavlink.MAV_MISSION_TYPE_MISSION)510511def mission_item_takeoff(self, target_system, target_component):512'''returns a mission_item_int which can be used as takeoff in a mission'''513return self.mav.mav.mission_item_int_encode(514target_system,515target_component,5161, # seq517mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,518mavutil.mavlink.MAV_CMD_NAV_TAKEOFF,5190, # current5200, # autocontinue5210, # p15220, # p25230, # p35240, # p4525int(1.0000 * 1e7), # latitude526int(1.0000 * 1e7), # longitude52731.0000, # altitude528mavutil.mavlink.MAV_MISSION_TYPE_MISSION)529530def mission_item_rtl(self, target_system, target_component):531'''returns a mission_item_int which can be used as takeoff in a mission'''532return self.mav.mav.mission_item_int_encode(533target_system,534target_component,5351, # seq536mavutil.mavlink.MAV_FRAME_GLOBAL,537mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH,5380, # current5390, # autocontinue5400, # p15410, # p25420, # p35430, # p45440, # latitude5450, # longitude5460.0000, # altitude547mavutil.mavlink.MAV_MISSION_TYPE_MISSION)548549def scurve_nasty_mission(self, target_system=1, target_component=1):550'''returns a mission which attempts to give the SCurve library551indigestion. The same destination is given several times.'''552553wp2_loc = self.mav.location()554wp2_offset_n = 20555wp2_offset_e = 30556self.location_offset_ne(wp2_loc, wp2_offset_n, wp2_offset_e)557558wp2_by_three = self.mav.mav.mission_item_int_encode(559target_system,560target_component,5612, # seq562mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,563mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,5640, # current5650, # autocontinue5663, # p15670, # p25680, # p35690, # p4570int(wp2_loc.lat * 1e7), # latitude571int(wp2_loc.lng * 1e7), # longitude57231.0000, # altitude573mavutil.mavlink.MAV_MISSION_TYPE_MISSION)574575wp5_loc = self.mav.location()576wp5_offset_n = -20577wp5_offset_e = 30578self.location_offset_ne(wp5_loc, wp5_offset_n, wp5_offset_e)579580wp5_by_three = self.mav.mav.mission_item_int_encode(581target_system,582target_component,5835, # seq584mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,585mavutil.mavlink.MAV_CMD_NAV_SPLINE_WAYPOINT,5860, # current5870, # autocontinue5883, # p15890, # p25900, # p35910, # p4592int(wp5_loc.lat * 1e7), # latitude593int(wp5_loc.lng * 1e7), # longitude59431.0000, # altitude595mavutil.mavlink.MAV_MISSION_TYPE_MISSION)596597ret = copy.copy([598# slot 0 is home599self.mission_item_home(target_system=target_system, target_component=target_component),600# slot 1 is takeoff601self.mission_item_takeoff(target_system=target_system, target_component=target_component),602# now three spline waypoints right on top of one another:603copy.copy(wp2_by_three),604copy.copy(wp2_by_three),605copy.copy(wp2_by_three),606# now three MORE spline waypoints right on top of one another somewhere else:607copy.copy(wp5_by_three),608copy.copy(wp5_by_three),609copy.copy(wp5_by_three),610self.mission_item_rtl(target_system=target_system, target_component=target_component),611])612self.correct_wp_seq_numbers(ret)613return ret614615def scurve_nasty_up_mission(self, target_system=1, target_component=1):616'''returns a mission which attempts to give the SCurve library617indigestion. The same destination is given several times but with differing altitudes.'''618619wp2_loc = self.mav.location()620wp2_offset_n = 20621wp2_offset_e = 30622self.location_offset_ne(wp2_loc, wp2_offset_n, wp2_offset_e)623624wp2 = self.mav.mav.mission_item_int_encode(625target_system,626target_component,6272, # seq628mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,629mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,6300, # current6310, # autocontinue6323, # p16330, # p26340, # p36350, # p4636int(wp2_loc.lat * 1e7), # latitude637int(wp2_loc.lng * 1e7), # longitude63831.0000, # altitude639mavutil.mavlink.MAV_MISSION_TYPE_MISSION)640wp3 = copy.copy(wp2)641wp3.alt = 40642wp4 = copy.copy(wp2)643wp4.alt = 31644645wp5_loc = self.mav.location()646wp5_offset_n = -20647wp5_offset_e = 30648self.location_offset_ne(wp5_loc, wp5_offset_n, wp5_offset_e)649650wp5 = self.mav.mav.mission_item_int_encode(651target_system,652target_component,6535, # seq654mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,655mavutil.mavlink.MAV_CMD_NAV_SPLINE_WAYPOINT,6560, # current6570, # autocontinue6583, # p16590, # p26600, # p36610, # p4662int(wp5_loc.lat * 1e7), # latitude663int(wp5_loc.lng * 1e7), # longitude66431.0000, # altitude665mavutil.mavlink.MAV_MISSION_TYPE_MISSION)666wp6 = copy.copy(wp5)667wp6.alt = 41668wp7 = copy.copy(wp5)669wp7.alt = 51670671ret = copy.copy([672# slot 0 is home673self.mission_item_home(target_system=target_system, target_component=target_component),674# slot 1 is takeoff675self.mission_item_takeoff(target_system=target_system, target_component=target_component),676wp2,677wp3,678wp4,679# now three MORE spline waypoints right on top of one another somewhere else:680wp5,681wp6,682wp7,683self.mission_item_rtl(target_system=target_system, target_component=target_component),684])685self.correct_wp_seq_numbers(ret)686return ret687688def fly_mission_points(self, points):689'''takes a list of waypoints and flies them, expecting a disarm at end'''690self.check_mission_upload_download(points)691self.set_parameter("AUTO_OPTIONS", 3)692self.change_mode('AUTO')693self.set_rc(8, 1000)694self.wait_ready_to_arm()695self.arm_vehicle()696self.progress("Raising rotor speed")697self.set_rc(8, 2000)698self.wait_waypoint(0, len(points)-1)699self.wait_disarmed()700self.set_rc(8, 1000)701702def NastyMission(self):703'''constructs and runs missions designed to test scurves'''704self.fly_mission_points(self.scurve_nasty_mission())705# hopefully we don't need this step forever:706self.progress("Restting mission state machine by changing into LOITER")707self.change_mode('LOITER')708self.fly_mission_points(self.scurve_nasty_up_mission())709710def MountFailsafeAction(self):711"""Fly Mount Failsafe action"""712self.context_push()713714self.progress("Setting up servo mount")715roll_servo = 12716pitch_servo = 11717yaw_servo = 10718open_servo = 9719roll_limit = 50720self.set_parameters({721"MNT1_TYPE": 1,722"SERVO%u_MIN" % roll_servo: 1000,723"SERVO%u_MAX" % roll_servo: 2000,724"SERVO%u_FUNCTION" % yaw_servo: 6, # yaw725"SERVO%u_FUNCTION" % pitch_servo: 7, # roll726"SERVO%u_FUNCTION" % roll_servo: 8, # pitch727"SERVO%u_FUNCTION" % open_servo: 9, # mount open728"MNT1_OPTIONS": 2, # retract729"MNT1_DEFLT_MODE": 3, # RC targettting730"MNT1_ROLL_MIN": -roll_limit,731"MNT1_ROLL_MAX": roll_limit,732})733734self.reboot_sitl()735736retract_roll = 25.0737self.set_parameter("MNT1_NEUTRAL_X", retract_roll)738self.progress("Killing RC")739self.set_parameter("SIM_RC_FAIL", 2)740self.delay_sim_time(10)741want_servo_channel_value = int(1500 + 500*retract_roll/roll_limit)742self.wait_servo_channel_value(roll_servo, want_servo_channel_value, epsilon=1)743744self.progress("Resurrecting RC")745self.set_parameter("SIM_RC_FAIL", 0)746self.wait_servo_channel_value(roll_servo, 1500)747748self.context_pop()749750self.reboot_sitl()751752def set_rc_default(self):753super(AutoTestHelicopter, self).set_rc_default()754self.progress("Lowering rotor speed")755self.set_rc(8, 1000)756757def fly_mission(self, filename, strict=True):758num_wp = self.load_mission(filename, strict=strict)759self.change_mode("LOITER")760self.wait_ready_to_arm()761self.arm_vehicle()762self.set_rc(8, 2000) # Raise rotor speed763self.delay_sim_time(20)764self.change_mode("AUTO")765self.set_rc(3, 1500)766767self.wait_waypoint(1, num_wp-1)768self.wait_disarmed()769self.set_rc(8, 1000) # Lower rotor speed770771# FIXME move this & plane's version to common772def AirspeedDrivers(self, timeout=600):773'''Test AirSpeed drivers'''774775# Copter's airspeed sensors are off by default776self.set_parameters({777"ARSPD_ENABLE": 1,778"ARSPD_TYPE": 2, # Analog airspeed driver779"ARSPD_PIN": 1, # Analog airspeed driver pin for SITL780})781# set the start location to CMAC to use same test script as other vehicles782783self.sitl_start_loc = mavutil.location(-35.362881, 149.165222, 582.000000, 90.0) # CMAC784self.customise_SITL_commandline(["--home", "%s,%s,%s,%s"785% (-35.362881, 149.165222, 582.000000, 90.0)])786787# insert listener to compare airspeeds:788airspeed = [None, None]789790def check_airspeeds(mav, m):791m_type = m.get_type()792if (m_type == 'NAMED_VALUE_FLOAT' and793m.name == 'AS2'):794airspeed[1] = m.value795elif m_type == 'VFR_HUD':796airspeed[0] = m.airspeed797else:798return799if airspeed[0] is None or airspeed[1] is None:800return801delta = abs(airspeed[0] - airspeed[1])802if delta > 3:803raise NotAchievedException("Airspeed mismatch (as1=%f as2=%f)" % (airspeed[0], airspeed[1]))804805airspeed_sensors = [806("MS5525", 3, 1),807("DLVR", 7, 2),808]809for (name, t, bus) in airspeed_sensors:810self.context_push()811if bus is not None:812self.set_parameter("ARSPD2_BUS", bus)813self.set_parameter("ARSPD2_TYPE", t)814self.reboot_sitl()815self.wait_ready_to_arm()816self.arm_vehicle()817818self.install_message_hook_context(check_airspeeds)819self.fly_mission("ap1.txt", strict=False)820821if airspeed[0] is None:822raise NotAchievedException("Never saw an airspeed1")823if airspeed[1] is None:824raise NotAchievedException("Never saw an airspeed2")825if not self.current_onboard_log_contains_message("ARSP"):826raise NotAchievedException("Expected ARSP log message")827self.disarm_vehicle()828self.context_pop()829830def TurbineCoolDown(self, timeout=200):831"""Check Turbine Cool Down Feature"""832self.context_push()833# set option for Turbine834RAMP_TIME = 4835SETPOINT = 66836IDLE = 15837COOLDOWN_TIME = 5838self.set_parameters({"RC6_OPTION": 161,839"H_RSC_RAMP_TIME": RAMP_TIME,840"H_RSC_SETPOINT": SETPOINT,841"H_RSC_IDLE": IDLE,842"H_RSC_CLDWN_TIME": COOLDOWN_TIME})843self.set_rc(3, 1000)844self.set_rc(8, 1000)845846self.progress("Starting turbine")847self.wait_ready_to_arm()848self.context_collect("STATUSTEXT")849self.arm_vehicle()850851self.set_rc(6, 2000)852self.wait_statustext('Turbine startup', check_context=True)853854# Engage interlock to run up to head speed855self.set_rc(8, 2000)856857# Check throttle gets to setpoint858expected_thr = SETPOINT * 0.01 * 1000 + 1000 - 1 # servo end points are 1000 to 2000859self.wait_servo_channel_value(8, expected_thr, timeout=RAMP_TIME+1, comparator=operator.ge)860861self.progress("Checking cool down behaviour, idle x 1.5")862self.set_rc(8, 1000)863tstart = self.get_sim_time()864expected_thr = IDLE * 1.5 * 0.01 * 1000 + 1000 + 1865self.wait_servo_channel_value(8, expected_thr, timeout=2, comparator=operator.le)866867# Check that the throttle drops to idle after cool down time868expected_thr = IDLE * 0.01 * 1000 + 1000 + 1869self.wait_servo_channel_value(8, expected_thr, timeout=COOLDOWN_TIME+1, comparator=operator.le)870871measured_time = self.get_sim_time() - tstart872if (abs(measured_time - COOLDOWN_TIME) > 1.0):873raise NotAchievedException('Throttle did not reduce to idle within H_RSC_CLDWN_TIME')874875self.set_rc(6, 1000)876self.wait_disarmed(timeout=20)877self.context_pop()878879def TurbineStart(self, timeout=200):880"""Check Turbine Start Feature"""881RAMP_TIME = 4882# set option for Turbine Start883self.set_parameter("RC6_OPTION", 161)884self.set_parameter("H_RSC_RAMP_TIME", RAMP_TIME)885self.set_parameter("H_RSC_SETPOINT", 66)886self.set_parameter("DISARM_DELAY", 0)887self.set_rc(3, 1000)888self.set_rc(8, 1000)889890# check that turbine start doesn't activate while disarmed891self.progress("Checking Turbine Start doesn't activate while disarmed")892self.set_rc(6, 2000)893tstart = self.get_sim_time()894while self.get_sim_time() - tstart < 2:895servo = self.mav.recv_match(type='SERVO_OUTPUT_RAW', blocking=True)896if servo.servo8_raw > 1050:897raise NotAchievedException("Turbine Start activated while disarmed")898self.set_rc(6, 1000)899900# check that turbine start doesn't activate armed with interlock enabled901self.progress("Checking Turbine Start doesn't activate while armed with interlock enabled")902self.wait_ready_to_arm()903self.arm_vehicle()904self.set_rc(8, 2000)905self.set_rc(6, 2000)906tstart = self.get_sim_time()907while self.get_sim_time() - tstart < 5:908servo = self.mav.recv_match(type='SERVO_OUTPUT_RAW', blocking=True)909if servo.servo8_raw > 1660:910raise NotAchievedException("Turbine Start activated with interlock enabled")911912self.set_rc(8, 1000)913self.set_rc(6, 1000)914self.disarm_vehicle()915916# check that turbine start activates as designed (armed with interlock disabled)917self.progress("Checking Turbine Start activates as designed (armed with interlock disabled)")918self.delay_sim_time(2)919self.arm_vehicle()920921self.set_rc(6, 2000)922tstart = self.get_sim_time()923while True:924if self.get_sim_time() - tstart > 5:925raise AutoTestTimeoutException("Turbine Start did not activate")926servo = self.mav.recv_match(type='SERVO_OUTPUT_RAW', blocking=True)927if servo.servo8_raw > 1800:928break929930self.wait_servo_channel_value(8, 1000, timeout=3)931self.set_rc(6, 1000)932933# check that turbine start will not reactivate after interlock enabled934self.progress("Checking Turbine Start doesn't activate once interlock is enabled after start)")935self.set_rc(8, 2000)936self.set_rc(6, 2000)937tstart = self.get_sim_time()938while self.get_sim_time() - tstart < 5:939servo = self.mav.recv_match(type='SERVO_OUTPUT_RAW', blocking=True)940if servo.servo8_raw > 1660:941raise NotAchievedException("Turbine Start activated with interlock enabled")942self.set_rc(6, 1000)943self.set_rc(8, 1000)944self.disarm_vehicle()945946def PIDNotches(self):947"""Use dynamic harmonic notch to control motor noise."""948self.progress("Flying with PID notches")949self.set_parameters({950"FILT1_TYPE": 1,951"FILT2_TYPE": 1,952"AHRS_EKF_TYPE": 10,953"INS_LOG_BAT_MASK": 3,954"INS_LOG_BAT_OPT": 0,955"INS_GYRO_FILTER": 100, # set the gyro filter high so we can observe behaviour956"LOG_BITMASK": 65535,957"LOG_DISARMED": 0,958"SIM_VIB_FREQ_X": 120, # roll959"SIM_VIB_FREQ_Y": 120, # pitch960"SIM_VIB_FREQ_Z": 180, # yaw961"FILT1_NOTCH_FREQ": 120,962"FILT2_NOTCH_FREQ": 180,963"ATC_RAT_RLL_NEF": 1,964"ATC_RAT_PIT_NEF": 1,965"ATC_RAT_YAW_NEF": 2,966"SIM_GYR1_RND": 5,967})968self.reboot_sitl()969970self.hover_and_check_matched_frequency_with_fft(5, 20, 350, reverse=True, takeoff=True)971972def AutoTune(self):973"""Test autotune mode"""974# test roll and pitch FF tuning975self.set_parameters({976"ATC_ANG_RLL_P": 4.5,977"ATC_RAT_RLL_P": 0,978"ATC_RAT_RLL_I": 0.1,979"ATC_RAT_RLL_D": 0,980"ATC_RAT_RLL_FF": 0.15,981"ATC_ANG_PIT_P": 4.5,982"ATC_RAT_PIT_P": 0,983"ATC_RAT_PIT_I": 0.1,984"ATC_RAT_PIT_D": 0,985"ATC_RAT_PIT_FF": 0.15,986"ATC_ANG_YAW_P": 4.5,987"ATC_RAT_YAW_P": 0.18,988"ATC_RAT_YAW_I": 0.024,989"ATC_RAT_YAW_D": 0.003,990"ATC_RAT_YAW_FF": 0.024,991"AUTOTUNE_AXES": 3,992"AUTOTUNE_SEQ": 1,993})994995# Conduct testing from althold996self.takeoff(10, mode="ALT_HOLD")997998# hold position in loiter999self.change_mode('AUTOTUNE')10001001tstart = self.get_sim_time()1002self.wait_statustext('AutoTune: Success', timeout=1000)1003now = self.get_sim_time()1004self.progress("AUTOTUNE OK (%u seconds)" % (now - tstart))1005self.autotune_land_and_save_gains()10061007# test pitch rate P and Rate D tuning1008self.set_parameters({1009"AUTOTUNE_AXES": 2,1010"AUTOTUNE_SEQ": 2,1011"AUTOTUNE_GN_MAX": 1.8,1012})10131014# Conduct testing from althold1015self.takeoff(10, mode="ALT_HOLD")10161017# hold position in loiter1018self.change_mode('AUTOTUNE')10191020tstart = self.get_sim_time()1021self.wait_statustext('AutoTune: Success', timeout=1000)1022now = self.get_sim_time()1023self.progress("AUTOTUNE OK (%u seconds)" % (now - tstart))1024self.autotune_land_and_save_gains()10251026# test Roll rate P and Rate D tuning1027self.set_parameters({1028"AUTOTUNE_AXES": 1,1029"AUTOTUNE_SEQ": 2,1030"AUTOTUNE_GN_MAX": 1.6,1031})10321033# Conduct testing from althold1034self.takeoff(10, mode="ALT_HOLD")10351036# hold position in loiter1037self.change_mode('AUTOTUNE')10381039tstart = self.get_sim_time()1040self.wait_statustext('AutoTune: Success', timeout=1000)1041now = self.get_sim_time()1042self.progress("AUTOTUNE OK (%u seconds)" % (now - tstart))1043self.autotune_land_and_save_gains()10441045# test Roll and pitch angle P tuning1046self.set_parameters({1047"AUTOTUNE_AXES": 3,1048"AUTOTUNE_SEQ": 4,1049"AUTOTUNE_FRQ_MIN": 5,1050"AUTOTUNE_FRQ_MAX": 50,1051"AUTOTUNE_GN_MAX": 1.6,1052})10531054# Conduct testing from althold1055self.takeoff(10, mode="ALT_HOLD")10561057# hold position in loiter1058self.change_mode('AUTOTUNE')10591060tstart = self.get_sim_time()1061self.wait_statustext('AutoTune: Success', timeout=1000)1062now = self.get_sim_time()1063self.progress("AUTOTUNE OK (%u seconds)" % (now - tstart))1064self.autotune_land_and_save_gains()10651066# test yaw FF and rate P and Rate D1067self.set_parameters({1068"AUTOTUNE_AXES": 4,1069"AUTOTUNE_SEQ": 3,1070"AUTOTUNE_FRQ_MIN": 10,1071"AUTOTUNE_FRQ_MAX": 70,1072"AUTOTUNE_GN_MAX": 1.4,1073})10741075# Conduct testing from althold1076self.takeoff(10, mode="ALT_HOLD")10771078# hold position in loiter1079self.change_mode('AUTOTUNE')10801081tstart = self.get_sim_time()1082self.wait_statustext('AutoTune: Success', timeout=1000)1083now = self.get_sim_time()1084self.progress("AUTOTUNE OK (%u seconds)" % (now - tstart))1085self.autotune_land_and_save_gains()10861087# test yaw angle P tuning1088self.set_parameters({1089"AUTOTUNE_AXES": 4,1090"AUTOTUNE_SEQ": 4,1091"AUTOTUNE_FRQ_MIN": 5,1092"AUTOTUNE_FRQ_MAX": 50,1093"AUTOTUNE_GN_MAX": 1.5,1094})10951096# Conduct testing from althold1097self.takeoff(10, mode="ALT_HOLD")10981099# hold position in loiter1100self.change_mode('AUTOTUNE')11011102tstart = self.get_sim_time()1103self.wait_statustext('AutoTune: Success', timeout=1000)1104now = self.get_sim_time()1105self.progress("AUTOTUNE OK (%u seconds)" % (now - tstart))1106self.autotune_land_and_save_gains()11071108# tune check1109self.set_parameters({1110"AUTOTUNE_AXES": 7,1111"AUTOTUNE_SEQ": 16,1112"AUTOTUNE_FRQ_MIN": 10,1113"AUTOTUNE_FRQ_MAX": 80,1114})11151116# Conduct testing from althold1117self.takeoff(10, mode="ALT_HOLD")11181119# hold position in loiter1120self.change_mode('AUTOTUNE')11211122tstart = self.get_sim_time()1123self.wait_statustext('AutoTune: Success', timeout=1000)1124now = self.get_sim_time()1125self.progress("AUTOTUNE OK (%u seconds)" % (now - tstart))1126self.land_and_disarm()11271128def autotune_land_and_save_gains(self):1129self.set_rc(3, 1000)1130self.context_collect('STATUSTEXT')1131self.wait_statustext(r"SIM Hit ground at ([0-9.]+) m/s",1132check_context=True,1133regex=True)1134self.set_rc(8, 1000)1135self.wait_disarmed()11361137def land_and_disarm(self, **kwargs):1138super(AutoTestHelicopter, self).land_and_disarm(**kwargs)1139self.progress("Killing rotor speed")1140self.set_rc(8, 1000)11411142def do_RTL(self, **kwargs):1143super(AutoTestHelicopter, self).do_RTL(**kwargs)1144self.progress("Killing rotor speed")1145self.set_rc(8, 1000)11461147def tests(self):1148'''return list of all tests'''1149ret = vehicle_test_suite.TestSuite.tests(self)1150ret.extend([1151self.AVCMission,1152self.RotorRunup,1153self.PosHoldTakeOff,1154self.StabilizeTakeOff,1155self.SplineWaypoint,1156self.Autorotation,1157self.ManAutorotation,1158self.governortest,1159self.FlyEachFrame,1160self.AirspeedDrivers,1161self.TurbineStart,1162self.TurbineCoolDown,1163self.NastyMission,1164self.PIDNotches,1165self.AutoTune,1166self.MountFailsafeAction,1167])1168return ret11691170def disabled_tests(self):1171return {1172}117311741175