px4ctrl里calculateControl的代码解析

px4ctrl的代码里将目标加速度转成飞控的rpy的calculateControl代码

/* 
  compute u.thrust and u.q, controller gains and other parameters are in param_ 
*/
quadrotor_msgs::Px4ctrlDebug
LinearControl::calculateControl(const Desired_State_t &des,
    const Odom_Data_t &odom,
    const Imu_Data_t &imu, 
    Controller_Output_t &u)
{
  /* WRITE YOUR CODE HERE */
      //compute disired acceleration
      Eigen::Vector3d des_acc(0.0, 0.0, 0.0);
      Eigen::Vector3d Kp,Kv;
      Kp << param_.gain.Kp0, param_.gain.Kp1, param_.gain.Kp2;
      Kv << param_.gain.Kv0, param_.gain.Kv1, param_.gain.Kv2;
      des_acc = des.a + Kv.asDiagonal() * (des.v - odom.v) + Kp.asDiagonal() * (des.p - odom.p);
      des_acc += Eigen::Vector3d(0,0,param_.gra);

      u.thrust = computeDesiredCollectiveThrustSignal(des_acc);
      double roll,pitch,yaw,yaw_imu;
      double yaw_odom = fromQuaternion2yaw(odom.q);
      double sin = std::sin(yaw_odom);
      double cos = std::cos(yaw_odom);
      roll = (des_acc(0) * sin - des_acc(1) * cos )/ param_.gra;
      pitch = (des_acc(0) * cos + des_acc(1) * sin )/ param_.gra;
      // yaw = fromQuaternion2yaw(des.q);
      yaw_imu = fromQuaternion2yaw(imu.q);
      // Eigen::Quaterniond q = Eigen::AngleAxisd(yaw,Eigen::Vector3d::UnitZ())
      //   * Eigen::AngleAxisd(roll,Eigen::Vector3d::UnitX())
      //   * Eigen::AngleAxisd(pitch,Eigen::Vector3d::UnitY());
      Eigen::Quaterniond q = Eigen::AngleAxisd(des.yaw,Eigen::Vector3d::UnitZ())
        * Eigen::AngleAxisd(pitch,Eigen::Vector3d::UnitY())
        * Eigen::AngleAxisd(roll,Eigen::Vector3d::UnitX());
      u.q = imu.q * odom.q.inverse() * q;


  /* WRITE YOUR CODE HERE */

  //used for debug
  // debug_msg_.des_p_x = des.p(0);
  // debug_msg_.des_p_y = des.p(1);
  // debug_msg_.des_p_z = des.p(2);
  
  debug_msg_.des_v_x = des.v(0);
  debug_msg_.des_v_y = des.v(1);
  debug_msg_.des_v_z = des.v(2);
  
  debug_msg_.des_a_x = des_acc(0);
  debug_msg_.des_a_y = des_acc(1);
  debug_msg_.des_a_z = des_acc(2);
  
  debug_msg_.des_q_x = u.q.x();
  debug_msg_.des_q_y = u.q.y();
  debug_msg_.des_q_z = u.q.z();
  debug_msg_.des_q_w = u.q.w();
  
  debug_msg_.des_thr = u.thrust;
  
  // Used for thrust-accel mapping estimation
  timed_thrust_.push(std::pair<ros::Time, double>(ros::Time::now(), u.thrust));
  while (timed_thrust_.size() > 100)
  {
    timed_thrust_.pop();
  }
  return debug_msg_;
}

第一部分的代码:

    Eigen::Vector3d des_acc(0.0, 0.0, 0.0);
    Eigen::Vector3d Kp,Kv;
    
//    读取参数
    Kp << param_.gain.Kp0, param_.gain.Kp1, param_.gain.Kp2;
    Kv << param_.gain.Kv0, param_.gain.Kv1, param_.gain.Kv2;
    
    // 依照目标位置和速度,对比当前位置和速度,调整目标直线加速度(距离远则加速度增加)
    des_acc = des.a + Kv.asDiagonal() * (des.v - odom.v) + Kp.asDiagonal() * (des.p - odom.p);
    des_acc += Eigen::Vector3d(0,0,param_.gra);
    // 油门的具体算法这里就不讨论了
    u.thrust = computeDesiredCollectiveThrustSignal(des_acc);
    

computeDesiredCollectiveThrustSignal 的解析请看

px4ctrl里estimateThrustModel代码详解-CSDN博客

第二部分的代码:本文重点讨论的部分

    double roll,pitch,yaw,yaw_imu;
    double yaw_odom = fromQuaternion2yaw(odom.q);//calculateControl
    double sin = std::sin(yaw_odom);
    double cos = std::cos(yaw_odom);
    roll = (des_acc(0) * sin - des_acc(1) * cos )/ param_.gra;
    pitch = (des_acc(0) * cos + des_acc(1) * sin )/ param_.gra;
    // yaw = fromQuaternion2yaw(des.q);
    yaw_imu = fromQuaternion2yaw(imu.q);//calculateControl
    // Eigen::Quaterniond q = Eigen::AngleAxisd(yaw,Eigen::Vector3d::UnitZ())
    //   * Eigen::AngleAxisd(roll,Eigen::Vector3d::UnitX())
    //   * Eigen::AngleAxisd(pitch,Eigen::Vector3d::UnitY());
    Eigen::Quaterniond q = Eigen::AngleAxisd(des.yaw,Eigen::Vector3d::UnitZ())
                           * Eigen::AngleAxisd(pitch,Eigen::Vector3d::UnitY())
                           * Eigen::AngleAxisd(roll,Eigen::Vector3d::UnitX());
    u.q = imu.q * odom.q.inverse() * q;

这里要先介绍下三个坐标系:

ROS坐标系(也就是odom坐标系):x向前,y向左,z向上(roll增加向左,pitch向上,yaw向左)

机体坐标系:x向前,y向右,z向下(roll增加向右,pitch向下,yaw向右)。

Eigen坐标系:

这个不太清楚,使用程序里的代码

Eigen::Quaterniond q = Eigen::AngleAxisd(yaw,Eigen::Vector3d::UnitZ())
                       * Eigen::AngleAxisd(pitch,Eigen::Vector3d::UnitY())
                       * Eigen::AngleAxisd(roll,Eigen::Vector3d::UnitX());

Quaternions - Visualisation 对照来看

是(roll增加向右,pitch向下,yaw向左),也就是说yaw和ROS的相同,roll和pitch的和机体坐标系的相同。

------------------------------------------------------------------------------------------------------

有了坐标系的知识后,以下要做的步骤是:

1. 将ROS坐标系的X,Y轴的加速度acc(0)和acc(1)转换成机体坐标系的roll,pitch

2. 将ROS坐标系的yaw和机体坐标系的roll,pitch带入

Eigen::Quaterniond q = Eigen::AngleAxisd(des.yaw,Eigen::Vector3d::UnitZ())
                       * Eigen::AngleAxisd(pitch,Eigen::Vector3d::UnitY())
                       * Eigen::AngleAxisd(roll,Eigen::Vector3d::UnitX());

计算在ROS(odom)坐标系下的目标姿态

3. 使用 u.q = imu.q * odom.q.inverse() * q;的公式,计算将imu的姿态(飞控当前的姿态)转换成目标姿态。发给飞控。

--------------------------------------------------------------------------------------------

首先我们先看一下如何将ROS坐标系的X,Y轴的加速度acc(0)和acc(1)转换成机体坐标系的roll,pitch.

注:这里的acc和des_acc是相同的。

这里先了解下,对于机体坐标而言,roll增加向右,pitch增加向前,可以参考以下的网址

https://arklab.tw/codrone-lite-pro_arduino_getting-started_-part-3/

以ROS的yaw角在0-90度为例,acc(0) 为正时,提供一个向前和向右的力,

向右:最后roll是正值,此时 sin 是正值,所以 roll = acc(0) * sin

向前:最后pitch是正值,此时cos 是正值,所以 pitch = acc(0) * cos

acc(1) 为正时,提供一个向前和向左的力,所以

向左:最后roll是负值,此时 cos 是正值,所以 roll = - acc(1) * cos

向前:最后pitch是正值,此时sin 是正值,所以 pitch = acc(1) * sin

sin和cos是为了把x方向和y方向的力,分解成机体坐标系里朝前和朝右(机体坐标系y轴朝右)的力

所以合并后的公式就是

roll = (des_acc(0) * sin - des_acc(1) * cos )/ param_.gra;
pitch = (des_acc(0) * cos + des_acc(1) * sin )/ param_.gra;

但这里为什么要除以param_.gra呢?我问了chatgpt得到的公式是

可以看到,最后将直线加速度转成roll和pitch都要除以g

这里画一下图帮助理解

可以试着依照上述步骤验证其它三个象限,都会得到同样的公式,或是尝试解析下chatgpt里说的公式。

----------------------------------------------------------------

2. 将ROS坐标系的yaw和机体坐标系的roll,pitch带入

Eigen::Quaterniond q = Eigen::AngleAxisd(des.yaw,Eigen::Vector3d::UnitZ())
                       * Eigen::AngleAxisd(pitch,Eigen::Vector3d::UnitY())
                       * Eigen::AngleAxisd(roll,Eigen::Vector3d::UnitX());

计算在ROS(odom)坐标系下的目标姿态

由于刚刚说了Eigen的坐标系yaw的方向和ROS相同,roll和pitch和机体坐标系相同,所以我们吧des.yaw和刚刚计算的roll和pitch带入。得到的就是在odom坐标系下的目标位姿态。

可以想象下,首先飞机朝ROS坐标系的前方,依照ROS的坐标系旋转yaw角后,依照机体坐标系的roll和pitch旋转机体,得到的就是在ROS坐标系下的目标姿态。

----------------------------------------------------------------

3. 使用 u.q = imu.q * odom.q.inverse() * q;的公式,计算将imu的姿态(飞控当前的姿态)转换成目标姿态。发给飞控。

这段代码可以这样理解 T_imu_target =  T_imu_world * T_world_odom * T_odom_target

这里的T_imu_world的意思是从世界坐标转到imu坐标的变换,由于这里只有姿态,所以就是imu.q,其余同理。

首先imu和odom开机的原点就是world,也就是世界坐标的原点,imu.q就是当前飞控的位姿对应初始位姿T_imu_world。 同理 odom.q = T_odom_world,而odom.q.inverse() 就是T_imu_world。q是目标姿态和当前odom姿态的变换,就是T_odom_target。

所以最后计算的结果就是目标q到imu(飞控)的姿态变换,把这个q和油门值传给飞控,飞控就能推导出四个操作参数 roll, pitch, yaw,油门的值,和遥杆的四个通道对应。

----------------------------------------------------------------

后面都是debug的代码就不解释了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值