### Description
The commander module contains the state machine for mode switching and failsafe behavior.
commander <command> [arguments...]
Commands:
start
[-h] Enable HIL mode
calibrate Run sensor calibration
mag|baro|accel|gyro|level|esc|airspeed Calibration type
quick Quick calibration (accel only, not recommended)
check Run preflight checks
arm
[-f] Force arming (do not run preflight checks)
disarm
[-f] Force disarming (disarm in air)
takeoff
land
transition VTOL transition
mode Change flight mode
manual|acro|offboard|stabilized|altctl|posctl|auto:mission|auto:loiter|auto
:rtl|auto:takeoff|auto:land|auto:precland Flight mode
pair
lockdown
on|off Turn lockdown on or off
set_ekf_origin
lat, lon, alt Origin Latitude, Longitude, Altitude
lat|lon|alt Origin latitude longitude altitude
poweroff Power off board (if supported)
stop
status print status info
注:print_usage函数是具体对应实现。
class Commander : public ModuleBase<Commander>, public ModuleParams
注:Commander模块采用了ModuleBase, 但没有使用WorkQueue设计,因此在实现上需要实现instantiate方法。
同样继承了ModuleBase,由ModuleBase的main来完成模块入口。
commander_main
└──> return Commander::main(argc, argv)
模块除支持start/stop/status命令,自定义命令支持以下子命令:
Commander::custom_command
├──> <!is_running()>
│ ├──> print_usage("not running")
│ └──> return 1
├──> <!strcmp(argv[0], "calibrate")>
│ ├──> <!strcmp(argv[1], "gyro")> // gyro calibration: param1 = 1
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 1.f, 0.f, 0.f, 0.f, 0.0, 0.0, 0.f)
│ ├──> <!strcmp(argv[1], "mag")>
│ │ ├──> <argc > 2 && (strcmp(argv[2], "quick") == 0)> // magnetometer quick calibration: VEHICLE_CMD_FIXED_MAG_CAL_YAW
│ │ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_FIXED_MAG_CAL_YAW)
│ │ └──> <else> // magnetometer calibration: param2 = 1
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 0.f, 1.f, 0.f, 0.f, 0.0, 0.0, 0.f)
│ ├──> <!strcmp(argv[1], "baro")> // baro calibration: param3 = 1
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 0.f, 0.f, 1.f, 0.f, 0.0, 0.0, 0.f)
│ ├──> <!strcmp(argv[1], "accel")>
│ │ ├──> <argc > 2 && (strcmp(argv[2], "quick") == 0)> // accelerometer quick calibration: param5 = 3
│ │ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 0.f, 0.f, 0.f, 0.f, 4.0, 0.0, 0.f)
│ │ └──> <else> // accelerometer calibration: param5 = 1
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 0.f, 0.f, 0.f, 0.f, 1.0, 0.0, 0.f)
│ ├──> <!strcmp(argv[1], "level")> // board level calibration: param5 = 2
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 0.f, 0.f, 0.f, 0.f, 2.0, 0.0, 0.f)
│ ├──> <!strcmp(argv[1], "airspeed")> // airspeed calibration: param6 = 2
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 0.f, 0.f, 0.f, 0.f, 0.0, 2.0, 0.f)
│ ├──> <!strcmp(argv[1], "esc")> // ESC calibration: param7 = 1
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_CALIBRATION, 0.f, 0.f, 0.f, 0.f, 0.0, 0.0, 1.f)
│ └──> <else>
│ └──> PX4_ERR("missing argument")
├──> <!strcmp(argv[0], "check")>
│ ├──> uORB::Subscription vehicle_status_sub{ORB_ID(vehicle_status)}
│ ├──> vehicle_status_sub.copy(&vehicle_status)
│ ├──> uORB::Subscription vehicle_status_flags_sub{ORB_ID(vehicle_status_flags)}
│ ├──> vehicle_status_flags_sub.copy(&vehicle_status_flags)
│ ├──> uORB::Subscription vehicle_control_mode_sub{ORB_ID(vehicle_control_mode)}
│ ├──> vehicle_control_mode_sub.copy(&vehicle_control_mode)
│ ├──> bool preflight_check_res = PreFlightCheck::preflightCheck(nullptr, vehicle_status, vehicle_status_flags,
│ │ vehicle_control_mode,
│ │ true, // report_failures
│ │ 30_s,
│ │ false, // safety_buttton_available not known
│ │ false) // safety_off not known
│ ├──> PX4_INFO("Preflight check: %s", preflight_check_res ? "OK" : "FAILED")
│ ├──> print_health_flags(vehicle_status)
│ └──> return 0
├──> <!strcmp(argv[0], "arm")>
│ ├──> float param2 = 0.f
│ ├──> <argc > 1 && !strcmp(argv[1], "-f")> // 21196: force arming/disarming (e.g. allow arming to override preflight checks and disarming in flight)
│ │ └──> param2 = 21196.f
│ ├──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_COMPONENT_ARM_DISARM,
│ │ static_cast<float>(vehicle_command_s::ARMING_ACTION_ARM),
│ │ param2)
│ └──> return 0
├──> <!strcmp(argv[0], "disarm")>
│ ├──> float param2 = 0.f
│ ├──> <argc > 1 && !strcmp(argv[1], "-f")> // 21196: force arming/disarming (e.g. allow arming to override preflight checks and disarming in flight)
│ │ └──> param2 = 21196.f
│ ├──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_COMPONENT_ARM_DISARM,
│ │ static_cast<float>(vehicle_command_s::ARMING_ACTION_DISARM),
│ │ param2)
│ └──> return 0
├──> <!strcmp(argv[0], "takeoff")> // switch to takeoff mode and arm
│ ├──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_NAV_TAKEOFF)
│ ├──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_COMPONENT_ARM_DISARM,
│ │ static_cast<float>(vehicle_command_s::ARMING_ACTION_ARM),
│ │ 0.f)
│ └──> return 0
├──> <!strcmp(argv[0], "land")>
│ ├──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_NAV_LAND)
│ └──> return 0
├──> <!strcmp(argv[0], "transition")>
│ ├──> uORB::Subscription vehicle_status_sub{ORB_ID(vehicle_status)}
│ ├──> vehicle_status_sub.copy(&vehicle_status)
│ ├──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_VTOL_TRANSITION,
│ │ (float)(vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING ?
│ │ vtol_vehicle_status_s::VEHICLE_VTOL_STATE_FW :
│ │ vtol_vehicle_status_s::VEHICLE_VTOL_STATE_MC), 0.0f)
│ └──> return 0
├──> <!strcmp(argv[0], "mode")>
│ ├──> <!strcmp(argv[1], "manual")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_MANUAL)
│ ├──> <!strcmp(argv[1], "altctl")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_ALTCTL)
│ ├──> <!strcmp(argv[1], "posctl")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_POSCTL)
│ ├──> <!strcmp(argv[1], "auto:mission")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,PX4_CUSTOM_SUB_MODE_AUTO_MISSION)
│ ├──> <!strcmp(argv[1], "auto:loiter")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,PX4_CUSTOM_SUB_MODE_AUTO_LOITER)
│ ├──> <!strcmp(argv[1], "auto:rtl")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,PX4_CUSTOM_SUB_MODE_AUTO_RTL)
│ ├──> <!strcmp(argv[1], "acro")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_ACRO)
│ ├──> <!strcmp(argv[1], "offboard")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_OFFBOARD)
│ ├──> <!strcmp(argv[1], "stabilized")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_STABILIZED)
│ ├──> <!strcmp(argv[1], "auto:takeoff")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,PX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF)
│ ├──> <!strcmp(argv[1], "auto:land")) {
│ │ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,PX4_CUSTOM_SUB_MODE_AUTO_LAND)
│ └──> <!strcmp(argv[1], "auto:precland")) {
│ └──> send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,PX4_CUSTOM_SUB_MODE_AUTO_PRECLAND)
├──> <!strcmp(argv[0], "lockdown")>
│ ├──> bool ret = send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_FLIGHTTERMINATION, strcmp(argv[1], "off") ? 2.0f : 0.0f /* lockdown */, 0.0f)
│ └──> return (ret ? 0 : 1)
├──> <!strcmp(argv[0], "pair")> // GCS pairing request handled by a companion
│ ├──> bool ret = broadcast_vehicle_command(vehicle_command_s::VEHICLE_CMD_START_RX_PAIR, 10.f)
│ └──> return (ret ? 0 : 1)
├──> <!strcmp(argv[0], "set_ekf_origin")> // Set the ekf NED origin global coordinates.
│ ├──> double latitude = atof(argv[1])
│ ├──> double longitude = atof(argv[2])
│ ├──> float altitude = atof(argv[3])
│ ├──> bool ret = send_vehicle_command(vehicle_command_s::VEHICLE_CMD_SET_GPS_GLOBAL_ORIGIN, 0.f, 0.f, 0.0, 0.0, latitude, longitude, altitude)
│ └──> return (ret ? 0 : 1)
├──> <!strcmp(argv[0], "poweroff")>
│ ├──> bool ret = send_vehicle_command(vehicle_command_s::VEHICLE_CMD_PREFLIGHT_REBOOT_SHUTDOWN,2.0f)
│ └──> return (ret ? 0 : 1)
└──> return print_usage("unknown command")
这里直接使用px4_task_spawn_cmd创建任务。
Commander::task_spawn
├──> _task_id = px4_task_spawn_cmd("commander",SCHED_DEFAULT,SCHED_PRIORITY_DEFAULT + 40,3250,(px4_main_t)&run_trampoline,(char *const *)argv);
├──> <_task_id < 0>
│ ├──> _task_id = -1
│ └──> return -errno
├──> <wait_until_running() < 0> // wait until task is up & running
│ ├──> _task_id = -1
│ └──> return -1
└──> return 0
注:鉴于ModuleBase::run_trampoline会直接调用instantiate初始化任务,Commander模块就必须对Commander::instantiate方法进行重载实现。
初始化Commander类实例。
Commander::instantiate
├──> Commander *instance = new Commander()
├──> <instance><argc >= 2 && !strcmp(argv[1], "-h")>
│ └──> instance->enable_hil()
└──> return instance;
注:鉴于该模块采用任务,而之前大量的模块使用了WorkQueue的设计方法,在task_spawn里会调用init这个方法,为了对比,保留这个方法。
根据输入参数及业务逻辑计算输出量,并发布消息。
Commander::Run
├──> [LED 初始化]
├──> [Buzzer 初始化]
├──> [PowerButton 初始化]
├──> arm_auth_init(&_mavlink_log_pub, &_vehicle_status.system_id);
├──> <while(!should_exit())>
│ ├──> [update parameters ]
│ ├──> [Update OA parameter ]
│ ├──> [handle power button state ]
│ ├──> [Update land detector ]
│ ├──> [safety button ]
│ ├──> [update vtol vehicle status]
│ ├──> [Auto disarm when landed or kill switch engaged]
│ ├──> [_geofence_warning_action_off when MAIN_STATE_AUTO_RTL/MAIN_STATE_AUTO_LOITER/MAIN_STATE_AUTO_LAND
│ ├──> [battery_status_check]
│ ├──> [If in INIT state, try to proceed to STANDBY state ]
│ ├──> [start mission result check ]
│ ├──> [start geofence result check & action ]
│ ├──> [Check for mission flight termination ]
│ ├──> [data link checks which update the status]
│ ├──> [avoidance_check]
│ ├──> [handle commands last, as the system needs to be updated to handle them ]
│ ├──> [Check for failure detector status ]
│ ├──> [Check wind speed actions if either high wind warning or high wind RTL is enabled]
│ ├──> [Trigger RTL if flight time is larger than max flight time specified in COM_FLT_TIME_MAX.The user is not able to override once above threshold, except for triggering Land.]
│ ├──> [automatically set or update home position]
│ ├──> [check for arming state changes]
│ ├──> [now set navigation state according to failsafe and main state ]
│ ├──> [publish states (armed, control_mode, vehicle_status, commander_state, vehicle_status_flags, failure_detector_status) at 2 Hz or immediately when changed
│ ├──> [play arming and battery warning tunes ]
│ ├──> [reset arm_tune_played when disarmed ]
│ ├──> [check if the worker has finished]
│ ├──> [store last position lock state ]
│ └──> [sleep if there are no vehicle_commands or action_requests to process]
├──> led_deinit();
└──> buzzer_deinit();
具体逻辑业务后续再做深入,从模块代码角度:
uORB::Subscription _action_request_sub {ORB_ID(action_request)};
uORB::Subscription _cpuload_sub{ORB_ID(cpuload)};
uORB::Subscription _esc_status_sub{ORB_ID(esc_status)};
uORB::Subscription _estimator_selector_status_sub{ORB_ID(estimator_selector_status)};
uORB::Subscription _estimator_status_sub{ORB_ID(estimator_status)};
uORB::Subscription _geofence_result_sub{ORB_ID(geofence_result)};
uORB::Subscription _iridiumsbd_status_sub{ORB_ID(iridiumsbd_status)};
uORB::Subscription _vehicle_land_detected_sub{ORB_ID(vehicle_land_detected)};
uORB::Subscription _manual_control_setpoint_sub{ORB_ID(manual_control_setpoint)};
uORB::Subscription _rtl_time_estimate_sub{ORB_ID(rtl_time_estimate)};
uORB::Subscription _system_power_sub{ORB_ID(system_power)};
uORB::Subscription _vehicle_angular_velocity_sub{ORB_ID(vehicle_angular_velocity)};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)};
uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)};
uORB::Subscription _vehicle_gps_position_sub{ORB_ID(vehicle_gps_position)};
uORB::Subscription _vtol_vehicle_status_sub{ORB_ID(vtol_vehicle_status)};
uORB::Subscription _wind_sub{ORB_ID(wind)};
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::SubscriptionMultiArray<battery_status_s, battery_status_s::MAX_INSTANCES> _battery_status_subs{ORB_ID::battery_status};
uORB::SubscriptionMultiArray<distance_sensor_s> _distance_sensor_subs{ORB_ID::distance_sensor};
uORB::SubscriptionMultiArray<telemetry_status_s> _telemetry_status_subs{ORB_ID::telemetry_status};
#if defined(BOARD_HAS_POWER_CONTROL)
uORB::Subscription _power_button_state_sub {ORB_ID(power_button_state)};
#endif // BOARD_HAS_POWER_CONTROL
uORB::SubscriptionData<estimator_status_flags_s> _estimator_status_flags_sub{ORB_ID(estimator_status_flags)};
uORB::SubscriptionData<mission_result_s> _mission_result_sub{ORB_ID(mission_result)};
uORB::SubscriptionData<offboard_control_mode_s> _offboard_control_mode_sub{ORB_ID(offboard_control_mode)};
uORB::SubscriptionData<vehicle_global_position_s> _global_position_sub{ORB_ID(vehicle_global_position)};
uORB::SubscriptionData<vehicle_local_position_s> _local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Publication<actuator_armed_s> _actuator_armed_pub{ORB_ID(actuator_armed)};
uORB::Publication<commander_state_s> _commander_state_pub{ORB_ID(commander_state)};
uORB::Publication<failure_detector_status_s> _failure_detector_status_pub{ORB_ID(failure_detector_status)};
uORB::Publication<test_motor_s> _test_motor_pub{ORB_ID(test_motor)};
uORB::Publication<actuator_test_s> _actuator_test_pub{ORB_ID(actuator_test)};
uORB::Publication<vehicle_control_mode_s> _vehicle_control_mode_pub{ORB_ID(vehicle_control_mode)};
uORB::Publication<vehicle_status_flags_s> _vehicle_status_flags_pub{ORB_ID(vehicle_status_flags)};
uORB::Publication<vehicle_status_s> _vehicle_status_pub{ORB_ID(vehicle_status)};
uORB::PublicationData<home_position_s> _home_position_pub{ORB_ID(home_position)};
uORB::Publication<vehicle_command_ack_s> _vehicle_command_ack_pub{ORB_ID(vehicle_command_ack)};
【1】PX4开源软件框架简明简介
【2】PX4模块设计之十一:Built-In框架
【3】PX4模块设计之十二:High Resolution Timer设计
【4】PX4模块设计之十三:WorkQueue设计
【5】PX4模块设计之十七:ModuleBase模块
【6】PX4模块设计之三十:Hysteresis类
【7】PX4 modules_main