赞
踩
RotorS 是一个MAV gazebo 仿真系统。
提供了几种多旋翼仿真模型,例如
但是仿真系统不限于使用这几种模型
AscTec 是 德国Ascending Technologies公司的缩写。
是很早的无人机了,实物张下面这个样子:
仿真系统中包含很多种仿真传感器,都可以安装在无人机上,例如:
功能包中包含了几种控制器,包含位置控制,游戏手柄控制灯
github的地址为:https://github.com/ethz-asl/rotors_simulator
在之前的博客总分析 rotors_simulator 的位置控制器 是如何使无人机在 gazebo里面悬停的
如果无人机仅能悬停那么也没什么用啊,在同样的launch文件下,发现了mav_with_keyboard.launch 文件
通过文件的名字可以推断是通过键盘来控制无人机飞行
运行一下发现,运行不了,具体的原因会在下面给出
但是通过这个功能,发现了其中的一个控制接口 roll pitch yawrate thrust
本篇文章主要分析这个控制器是如何实现的。
了解清楚这个控制器,在之后的博客中,会通过应用更普遍的PID控制器,利用这个接口实现无人机的位置。
这个启动文件对应的是启动 roll_pitch_yawrate_thrust_controller_node
的 节点。
但是 这个 文件有点问题
<include file="$(find rotors_gazebo)/launch/spawn_mav.launch">
<arg name="mav_name" value="$(arg mav_name)" />
<arg name="model" value="$(find rotors_description)/urdf/mav_generic_odometry_sensor.gazebo" />
<arg name="enable_logging" value="$(arg enable_logging)" />
<arg name="enable_ground_truth" value="$(arg enable_ground_truth)" />
<arg name="log_file" value="$(arg log_file)"/>
</include>
这个启动了gazebo,加载了无人机
<node name="key_joystick" pkg="rotors_joy_interface" type="key_joystick.py" />
启动这个的时候,会提示以下几个问题,都好解决
但是启动了,会自己闪退,这个是launch文件运行不成功的原因之一
<node name="rotors_joy_interface" pkg="rotors_joy_interface" type="rotors_joy_interface" />
这个是启动 rotors_joy_interface 功能包下面 joy.cpp 对应生成的可执行文件
<node name="joy_node" pkg="joy" type="joy_node" />
这个功能包,及对应的执行文件就没有。
这个是launch文件启动不成功的原因之二
<node name="roll_pitch_yawrate_thrust_controller_node" pkg="rotors_control" type="roll_pitch_yawrate_thrust_controller_node" output="screen">
<rosparam
command="load" file="$(find rotors_gazebo)/resource/roll_pitch_yawrate_thrust_controller_$(arg mav_name).yaml" />
<rosparam command="load" file="$(find rotors_gazebo)/resource/$(arg mav_name).yaml" />
<remap from="odometry" to="odometry_sensor1/odometry" />
</node>
启动 rotors_control 功能包的 roll_pitch_yawrate_thrust_controller_node.cpp 对应生产的可执行文件
由于只是分析 其 控制的原理与接口,所以只需要分析其代码就ok了,launch文件的问题就不去解决了。
是一个执行文件的代码,所以从main看起
int main(int argc, char** argv) {
ros::init(argc, argv, "roll_pitch_yawrate_thrust_controller_node");
初始化节点
rotors_control::RollPitchYawrateThrustControllerNode roll_pitch_yawrate_thrust_controller_node;
ros::spin();
return 0;
}
实例化 类
下面来看这个类的内容
RollPitchYawrateThrustControllerNode::RollPitchYawrateThrustControllerNode() {
InitializeParams();
ros::NodeHandle nh;
初始化参数
声明节点
cmd_roll_pitch_yawrate_thrust_sub_ = nh.subscribe(kDefaultCommandRollPitchYawrateThrustTopic, 1,
&RollPitchYawrateThrustControllerNode::RollPitchYawrateThrustCallback, this);
订阅控制指令
odometry_sub_ = nh.subscribe(kDefaultOdometryTopic, 1,
&RollPitchYawrateThrustControllerNode::OdometryCallback, this);
订阅里程计
motor_velocity_reference_pub_ = nh.advertise<mav_msgs::Actuators>(
kDefaultCommandMotorSpeedTopic, 1);
发布电机转速
初始化参数就不看了,就是初始化的各个方向 角度和角速度的增益
来看订阅的两个回调函数
控制指令的回调函数
void RollPitchYawrateThrustControllerNode::RollPitchYawrateThrustCallback(
const mav_msgs::RollPitchYawrateThrustConstPtr& roll_pitch_yawrate_thrust_reference_msg) {
mav_msgs::EigenRollPitchYawrateThrust roll_pitch_yawrate_thrust;
mav_msgs::eigenRollPitchYawrateThrustFromMsg(*roll_pitch_yawrate_thrust_reference_msg, &roll_pitch_yawrate_thrust);
转成eigen的格式
roll_pitch_yawrate_thrust_controller_.SetRollPitchYawrateThrust(roll_pitch_yawrate_thrust);
对应格式的 控制指令 送入控制器中
里程计回调函数
void RollPitchYawrateThrustControllerNode::OdometryCallback(const nav_msgs::OdometryConstPtr& odometry_msg) {
EigenOdometry odometry;
eigenOdometryFromMsg(odometry_msg, &odometry);
转成eigen 的里程计信息格式
roll_pitch_yawrate_thrust_controller_.SetOdometry(odometry);
将eigen的里程计信息格式送入 控制器中
Eigen::VectorXd ref_rotor_velocities;
roll_pitch_yawrate_thrust_controller_.CalculateRotorVelocities(&ref_rotor_velocities);
获得控制器 最终 计算的 电机转速
mav_msgs::ActuatorsPtr actuator_msg(new mav_msgs::Actuators);
构造 电机转速的 变量
actuator_msg->angular_velocities.clear();
for (int i = 0; i < ref_rotor_velocities.size(); i++)
actuator_msg->angular_velocities.push_back(ref_rotor_velocities[i]);
actuator_msg->header.stamp = odometry_msg->header.stamp;
赋值电机转速
motor_velocity_reference_pub_.publish(actuator_msg);
发布电机转速
下面来看在控制器里具体执行的操作,是如何计算得到电机转速的
RollPitchYawrateThrustController::RollPitchYawrateThrustController()
: initialized_params_(false),
controller_active_(false) {
InitializeParameters();
}
构造函数 初始化参数标志位为0 控制器激活标志位为0
初始化参数
void RollPitchYawrateThrustController::SetOdometry(const EigenOdometry& odometry) {
odometry_ = odometry;
}
获得里程计数据
赋值给类的成员变量
获得控制指令
void RollPitchYawrateThrustController::SetRollPitchYawrateThrust(
const mav_msgs::EigenRollPitchYawrateThrust& roll_pitch_yawrate_thrust) {
roll_pitch_yawrate_thrust_ = roll_pitch_yawrate_thrust;
controller_active_ = true;
}
获得控制指令,赋值给类的成员变量
控制器激活
计算电机转速
assert(rotor_velocities);
assert(initialized_params_);
判断是否有这两个参数
rotor_velocities->resize(vehicle_parameters_.rotor_configuration_.rotors.size());
电机转速清零
if (!controller_active_) {
*rotor_velocities = Eigen::VectorXd::Zero(rotor_velocities->rows());
return;
}
如果没有控制指令,那么控制器就没有激活 所有电机的转速为0
Eigen::Vector3d angular_acceleration;
ComputeDesiredAngularAcc(&angular_acceleration);
计算期望的角加速度
Eigen::Vector4d angular_acceleration_thrust;
angular_acceleration_thrust.block<3, 1>(0, 0) = angular_acceleration;
将3个方向的力,加上推力方向的力
angular_acceleration_thrust(3) = roll_pitch_yawrate_thrust_.thrust.z();
最后是直接赋值了推力的大小
*rotor_velocities = angular_acc_to_rotor_velocities_ * angular_acceleration_thrust;
*rotor_velocities = rotor_velocities->cwiseMax(Eigen::VectorXd::Zero(rotor_velocities->rows()));
*rotor_velocities = rotor_velocities->cwiseSqrt();
根据电机混控参数 分配各电机转速
计算期望角加速度
void RollPitchYawrateThrustController::ComputeDesiredAngularAcc(Eigen::Vector3d* angular_acceleration) const {
assert(angular_acceleration);
判断有这个变量
Eigen::Matrix3d R = odometry_.orientation.toRotationMatrix();
获得无人机的姿态旋转矩阵
double yaw = atan2(R(1, 0), R(0, 0));
根据旋转矩阵求得yaw角
Eigen::Matrix3d R_des;
R_des = Eigen::AngleAxisd(yaw, Eigen::Vector3d::UnitZ()) // yaw
* Eigen::AngleAxisd(roll_pitch_yawrate_thrust_.roll, Eigen::Vector3d::UnitX()) // roll
* Eigen::AngleAxisd(roll_pitch_yawrate_thrust_.pitch, Eigen::Vector3d::UnitY()); // pitch
计算得到期望的旋转矩阵
Eigen::Matrix3d angle_error_matrix = 0.5 * (R_des.transpose() * R - R.transpose() * R_des);
Eigen::Vector3d angle_error;
vectorFromSkewMatrix(angle_error_matrix, &angle_error);
通过旋转矩阵 计算角度偏差
Eigen::Vector3d angular_rate_des(Eigen::Vector3d::Zero());
声明并清零期望角速度值
angular_rate_des[2] = roll_pitch_yawrate_thrust_.yaw_rate;
把控制指令里面的期望航向角速度 赋值 到向量中
Eigen::Vector3d angular_rate_error = odometry_.angular_velocity - R_des.transpose() * R * angular_rate_des;
计算得到角速度偏差
*angular_acceleration = -1 * angle_error.cwiseProduct(normalized_attitude_gain_)
- angular_rate_error.cwiseProduct(normalized_angular_rate_gain_)
+ odometry_.angular_velocity.cross(odometry_.angular_velocity);
根据角度偏差、角速度偏差、实际角速度 计算得到 期望的角加速度
其中的整个节点逻辑和之前的位置控制类似
在node cpp中获得控制指令和里程计数据,转成 控制器需要的数据格式
控制器中的关键部分就是 期望角角速度的计算
核心思想是:
通过旋转矩阵计算得到 角度偏差 和角速度偏差
根据 角度偏差、角速度偏差、实际角速度 计算得到 期望角加速度
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。