PX4飞控手动位置控制POSCTL模式的实现流程

第一步,POSCTL模式的进入

    1、从遥控器模式开关进入。

    首先在PX4IO的主循环中调用了io_publish_raw_rc(),从IO芯片获取遥控器的各通道输入,发布input_rc消息;

    然后,在Sensors的主循环中调用rc_poll()函数,获取input_rc消息的内容,按照通道映射和校准信息将遥控器输入值重新计算并写入manual_control_setpoint消息中进行发布,发布内容包括;

			manual.y = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_ROLL, -1.0, 1.0);
			manual.x = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_PITCH, -1.0, 1.0);
			manual.r = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_YAW, -1.0, 1.0);
			manual.z = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_THROTTLE, 0.0, 1.0);
			manual.flaps = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_FLAPS, -1.0, 1.0);
			manual.aux1 = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_AUX_1, -1.0, 1.0);
			manual.aux2 = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_AUX_2, -1.0, 1.0);
			manual.aux3 = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_AUX_3, -1.0, 1.0);
			manual.aux4 = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_AUX_4, -1.0, 1.0);
			manual.aux5 = get_rc_value(rc_channels_s::RC_CHANNELS_FUNCTION_AUX_5, -1.0, 1.0);

			/* filter controls */
			manual.y = math::constrain(_filter_roll.apply(manual.y), -1.f, 1.f);
			manual.x = math::constrain(_filter_pitch.apply(manual.x), -1.f, 1.f);
			manual.r = math::constrain(_filter_yaw.apply(manual.r), -1.f, 1.f);
			manual.z = math::constrain(_filter_throttle.apply(manual.z), 0.f, 1.f);

			if (_parameters.rc_map_flightmode > 0) {

				/* the number of valid slots equals the index of the max marker minus one */
				const int num_slots = manual_control_setpoint_s::MODE_SLOT_MAX;

				/* the half width of the range of a slot is the total range
				 * divided by the number of slots, again divided by two
				 */
				const float slot_width_half = 2.0f / num_slots / 2.0f;

				/* min is -1, max is +1, range is 2. We offset below min and max */
				const float slot_min = -1.0f - 0.05f;
				const float slot_max = 1.0f + 0.05f;

				/* the slot gets mapped by first normalizing into a 0..1 interval using min
				 * and max. Then the right slot is obtained by multiplying with the number of
				 * slots. And finally we add half a slot width to ensure that integer rounding
				 * will take us to the correct final index.
				 */
				manual.mode_slot = (((((_rc.channels[_parameters.rc_map_flightmode - 1] - slot_min) * num_slots) + slot_width_half) /
						     (slot_max - slot_min)) + (1.0f / num_slots));

				if (manual.mode_slot >= num_slots) {
					manual.mode_slot = num_slots - 1;
				}
			}

    最后,在commander的主循环中调用set_main_state_rc()函数,其中利用sp_man.mode_slot的值来进行模式切换,当开关切换到POSCTL对应的位置时,执行main_state_transition()函数将新模式写入到internal_state.main_state中,并记录之前的模式和模式切换时间。

    2、从mavlink协议进入。

    同样在commander的主循环中,当接收到vehicle_command消息时运行handle_command()函数。如果消息的内容是VEHICLE_CMD_DO_SET_MODE,就是根据参数的内容进行模式设定。当参数中的base_mode为CUMTOM且custom_main_mode是PX4_CUSTOM_MAIN_MODE_POSCTL,同样是调用了main_state_transition()函数将新模式写入。

	case vehicle_command_s::VEHICLE_CMD_DO_SET_MODE: {
			uint8_t base_mode = (uint8_t)cmd->param1;
			uint8_t custom_main_mode = (uint8_t)cmd->param2;
			uint8_t custom_sub_mode = (uint8_t)cmd->param3;
.......
			if (base_mode & VEHICLE_MODE_FLAG_CUSTOM_MODE_ENABLED) {
				/* use autopilot-specific mode */
				if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_MANUAL) {
					/* MANUAL */
					main_ret = main_state_transition(status_local, commander_state_s::MAIN_STATE_MANUAL, main_state_prev, &status_flags, &internal_state);

				} else if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_ALTCTL) {
					/* ALTCTL */
					main_ret = main_state_transition(status_local, commander_state_s::MAIN_STATE_ALTCTL, main_state_prev, &status_flags, &internal_state);

				} else if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_POSCTL) {
					/* POSCTL */
					main_ret = main_state_transition(status_local, commander_state_s::MAIN_STATE_POSCTL, main_state_prev, &status_flags, &internal_state);

......
		}
		break;

第二步,导航模式和控制模式的选择。

    上一步,在commander中除了切换模式之外,还进行了导航模式和控制模式的设定。

    在set_nav_state()函数中设定status->nav_state = vehicle_status_s::NAVIGATION_STATE_POSCTL;

    在set_control_mode()函数中根据nva_state的值设定了控制相关的flag,包括:

	case vehicle_status_s::NAVIGATION_STATE_POSCTL:
		control_mode.flag_control_manual_enabled = true;
		control_mode.flag_control_auto_enabled = false;
		control_mode.flag_control_rates_enabled = true;
		control_mode.flag_control_attitude_enabled = true;
		control_mode.flag_control_rattitude_enabled = false;
		control_mode.flag_control_altitude_enabled = true;
		control_mode.flag_control_climb_rate_enabled = true;
		control_mode.flag_control_position_enabled = !status.in_transition_mode;
		control_mode.flag_control_velocity_enabled = !status.in_transition_mode;
		control_mode.flag_control_acceleration_enabled = false;
		control_mode.flag_control_termination_enabled = false;
		break;

    并按照一定周期发布了vehicle_control_mode和vehicle_status两个消息。

    然后,在navigator的主循环中,根据在commander中设定的导航模式,选择了对应模式的实例。

		case vehicle_status_s::NAVIGATION_STATE_MANUAL:
		case vehicle_status_s::NAVIGATION_STATE_ACRO:
		case vehicle_status_s::NAVIGATION_STATE_ALTCTL:
		case vehicle_status_s::NAVIGATION_STATE_POSCTL:
		case vehicle_status_s::NAVIGATION_STATE_TERMINATION:
		case vehicle_status_s::NAVIGATION_STATE_OFFBOARD:
		case vehicle_status_s::NAVIGATION_STATE_STAB:
		default:
			_pos_sp_triplet_published_invalid_once = false;
			_navigation_mode = nullptr;
			_can_loiter_at_sp = false;
			break;

    由于这些都是手控模式,因此_navigation_mode = nullptr。也就是不会调用任何导航实例。而是调用了如下代码

		/* if nothing is running, set position setpoint triplet invalid once */
		if (_navigation_mode == nullptr && !_pos_sp_triplet_published_invalid_once) {
			_pos_sp_triplet_published_invalid_once = true;
			_pos_sp_triplet.previous.valid = false;
			_pos_sp_triplet.current.valid = false;
			_pos_sp_triplet.next.valid = false;
			_pos_sp_triplet_updated = true;
		}

		if (_pos_sp_triplet_updated) {
			_pos_sp_triplet.timestamp = hrt_absolute_time();
			publish_position_setpoint_triplet();
			_pos_sp_triplet_updated = false;
		}

      实际上由于_pos_sp_triplet.current.valid = false; 函数 publish_position_setpoint_triplet(); 会立即返回,并不会发布提供位置控制期望的position_setpoint_triplet消息出去。

第三步,位置控制的实现。

  在mc_pos_control的主循环中首先调用do_control()函数;由于在commander中设定了control_mode.flag_control_manual_enabled = true,进而进入control_manual()函数。对于非手控的模式则进入control_non_manual()函数。

    在control_manual()函数中从manual_control_setpoint消息中获取纵横垂向的杆量,并根据参数进行限幅之后给中间变量。然后转换到NED坐标系中。

	math::Vector<2> req_vel_sp_xy;
	req_vel_sp_xy.zero();
	float req_vel_sp_z = 0.0f;

	if (_control_mode.flag_control_altitude_enabled) {
		/* set vertical velocity setpoint with throttle stick */
		req_vel_sp_z = -scale_control(_manual.z - 0.5f, 0.5f, _params.alt_ctl_dz, _params.alt_ctl_dy); // D

		/* reset alt setpoint to current altitude if needed */
		reset_alt_sp();
	}

	if (_control_mode.flag_control_position_enabled) {
		/* set horizontal velocity setpoint with roll/pitch stick */
		req_vel_sp_xy(0) = math::expo_deadzone(_manual.x, _params.xy_vel_man_expo, _params.hold_xy_dz);
		req_vel_sp_xy(1) = math::expo_deadzone(_manual.y, _params.xy_vel_man_expo, _params.hold_xy_dz);

		/* reset position setpoint to current position if needed */
		reset_pos_sp();
	}

	/* scale requested velocity setpoint to cruisespeed and rotate around yaw */
	math::Vector<3> vel_cruise(_params.vel_cruise(0),
				   _params.vel_cruise(1),
				   (req_vel_sp_z > 0.0f) ? _params.vel_max_down : _params.vel_max_up);
	math::Vector<3> req_vel_sp_scaled(req_vel_sp_xy(0), req_vel_sp_xy(1), req_vel_sp_z);

	/* scale velocity setpoint to cruise speed (m/s) and rotate around yaw to NED frame */
	math::Matrix<3, 3> R_input_fame;

	if (_control_mode.flag_control_fixed_hdg_enabled) {
		R_input_fame.from_euler(0.0f, 0.0f, _yaw_takeoff);

	} else {
		R_input_fame.from_euler(0.0f, 0.0f, _att_sp.yaw_body);

	}

	req_vel_sp_scaled = R_input_fame * req_vel_sp_scaled.emult(
				    vel_cruise); // in NED and scaled to actual velocity;

    根据control_mode设定的flag_control_altitude_enabled和flag_control_position_enabled确定是否在赶回中后保持高度和平面位置(也就是ALTCTL模式和POSCTL模式的作用了)。最后判断是否已经着陆,如果没有着陆就执行control_position()函数,从位置偏差计算速度期望值_vel_sp,并通过消息vehicle_global_velocity_setpoint发布出去,当然还有很多边边角角的复杂计算,需要自己看代码来理解。

    回到主循环中,结束了do_control()函数之后,将位置期望和速度期望赋值给_local_pos_sp,并发布消息vehicle_local_position_setpoint。接下来调用generate_attitude_setpoint()函数将姿态角期望写入_att_sp结构体中,并发布vehicle_attitude_setpoint消息。

第四步,姿态控制的实现。

    目前还没看完这部分代码,等看完了再更新。

第五步,控制信号的输出。

    在PX4IO的主循环中,执行io_set_control_groups()将对应控制组的控制量输出给IO芯片来产生控制电调或者舵机的PWM信号。

猜你喜欢

转载自blog.csdn.net/SIR_wkp/article/details/80681148