使用ego-planner进行无人机控制时的数据流(自用)

本文将详细介绍如何使用ego-planner向无人机发送控制指令,实现自动控制的过程。我们将探讨ego-planner的运行方式以及控制指令的传输过程,涉及到ROS节点、MAVROS协议以及无人机的控制器等方面。

在实现自动控制时,控制指令的传输是至关重要的一环。本文将详细介绍ego-planner如何向无人机发送控制指令,从而实现自动控制的过程。

无人机控制流程:

  1. 运行run_ctrl.launch实现自动起飞和悬停。
  2. 可通过遥控器或机载电脑进行手动或自动控制。

本文主要讲解自动控制中的控制指令传输过程。
控制指令传输过程:
在ego-planner中,控制指令的传输是通过px4ctrl_node.cpp实现的,该节点利用MAVROS将控制指令发送给无人机。(realflight_modules/px4ctrl/src/px4ctrl_node.cpp)

ros::Subscriber cmd_sub = nh.subscribe<quadrotor_msgs::PositionCommand>("cmd",100,boost::bind(&Command_Data_t::feed, &fsm.cmd_data, _1),ros::VoidConstPtr(),ros::TransportHints().tcpNoDelay());
fsm.ctrl_FCU_pub = nh.advertise<mavros_msgs::AttitudeTarget>("/mavros/setpoint_raw/attitude", 10);

在控制有限状态机中,当状态为CMD_CTRL时,通过get_cmd_des()函数获取控制指令的期望状态。

case CMD_CTRL:
{if (!rc_data.is_hover_mode || !odom_is_received(now_time)){state = MANUAL_CTRL;toggle_offboard_mode(false);ROS_WARN("[px4ctrl] From CMD_CTRL(L3) to MANUAL_CTRL(L1)!");}else if (!rc_data.is_command_mode || !cmd_is_received(now_time)){state = AUTO_HOVER;set_hov_with_odom();des = get_hover_des();ROS_INFO("[px4ctrl] From CMD_CTRL(L3) to AUTO_HOVER(L2)!");}else{des = get_cmd_des();}...break;
}

控制指令获取过程:
在get_cmd_des()函数中,获取控制指令的期望状态des,包括位置、速度、加速度、加加速度、偏航角、偏航角速度

Desired_State_t PX4CtrlFSM::get_cmd_des()
{Desired_State_t des;des.p = cmd_data.p;des.v = cmd_data.v;des.a = cmd_data.a;des.j = cmd_data.j;des.yaw = cmd_data.yaw;des.yaw_rate = cmd_data.yaw_rate;return des;
}

设定期望值des后,控制器负责根据获取的控制指令更新无人机的状态,其中包括推力模型的估计、控制指令的计算以及控制命令的发布。

// STEP2: estimate thrust modelif (state == AUTO_HOVER || state == CMD_CTRL){// controller.estimateThrustModel(imu_data.a, bat_data.volt, param);controller.estimateThrustModel(imu_data.a,param);}// STEP3: solve and update new control commandsif (rotor_low_speed_during_land) // used at the start of auto takeoff{motors_idling(imu_data, u);}else{debug_msg = controller.calculateControl(des, odom_data, imu_data, u);debug_msg.header.stamp = now_time;debug_pub.publish(debug_msg);}// STEP4: publish control commands to mavrosif (param.use_bodyrate_ctrl){publish_bodyrate_ctrl(u, now_time);}else{publish_attitude_ctrl(u, now_time);}

cmd_data 通过px4ctrl中subscriber接收得到(见上面subscriber中定义)。

可以看到STEP4有两种情况:bodyrate_ctrl和attitude_ctrl。看一下函数定义:

void PX4CtrlFSM::publish_bodyrate_ctrl(const Controller_Output_t &u, const ros::Time &stamp)
{mavros_msgs::AttitudeTarget msg;msg.header.stamp = stamp;msg.header.frame_id = std::string("FCU");msg.type_mask = mavros_msgs::AttitudeTarget::IGNORE_ATTITUDE;msg.body_rate.x = u.bodyrates.x();msg.body_rate.y = u.bodyrates.y();msg.body_rate.z = u.bodyrates.z();msg.thrust = u.thrust;ctrl_FCU_pub.publish(msg);
}
void PX4CtrlFSM::publish_attitude_ctrl(const Controller_Output_t &u, const ros::Time &stamp)
{mavros_msgs::AttitudeTarget msg;msg.header.stamp = stamp;msg.header.frame_id = std::string("FCU");msg.type_mask = mavros_msgs::AttitudeTarget::IGNORE_ROLL_RATE |mavros_msgs::AttitudeTarget::IGNORE_PITCH_RATE |mavros_msgs::AttitudeTarget::IGNORE_YAW_RATE;msg.orientation.x = u.q.x();msg.orientation.y = u.q.y();msg.orientation.z = u.q.z();msg.orientation.w = u.q.w();msg.thrust = u.thrust;ctrl_FCU_pub.publish(msg);
}

这两个函数都是用来发布飞行器的控制指令的,但是它们控制的方式略有不同。
publish_bodyrate_ctrl 函数发布的是基于体轴角速度(Body Rate)的控制指令。这种控制方式直接指定飞行器绕体轴的角速度,也就是说,控制器会直接告诉飞行器应该以多快的速度绕着自身的 X、Y、Z 轴旋转。

publish_attitude_ctrl 函数发布的是基于姿态角(Attitude)的控制指令。这种控制方式指定的是飞行器的期望姿态,也就是说,控制器会告诉飞行器应该朝向哪个方向。该函数忽略了角速度,只指定了期望的姿态。飞行器会根据姿态控制算法自行调整引擎输出以达到期望的姿态。这种控制方式适用于需要控制飞行器朝向的情况,比如指定飞行器俯仰、横滚和偏航的角度。

查找参数设置,可以在realflight_modules/px4ctrl/config/strl_param_fpv.yaml中看到

use_bodyrate_ctrl: false

这说明在该控制器中实际采用的是姿态控制。

realflight_modules/px4ctrl/launch/run_ctrl.launch中,进行了话题重映射

remap from="~cmd" to="/position_cmd" />

这说明控制器实际接收话题名称为/position_cmd 接下来我们寻找ego-planner中发布该指令的位置。

/src/planner/plan_manage/src/traj_server.cpp中可以看到控制指令(位置控制)发布者的定义

pos_cmd_pub = nh.advertise<quadrotor_msgs::PositionCommand>("/position_cmd", 50);

顺藤摸瓜找到其发布消息的函数

void cmdCallback(const ros::TimerEvent &e)
{/* no publishing before receive traj_ */if (!receive_traj_)return;ros::Time time_now = ros::Time::now();double t_cur = (time_now - start_time_).toSec();Eigen::Vector3d pos(Eigen::Vector3d::Zero()), vel(Eigen::Vector3d::Zero()), acc(Eigen::Vector3d::Zero()), pos_f;std::pair<double, double> yaw_yawdot(0, 0);static ros::Time time_last = ros::Time::now();if (t_cur < traj_duration_ && t_cur >= 0.0){pos = traj_[0].evaluateDeBoorT(t_cur);vel = traj_[1].evaluateDeBoorT(t_cur);acc = traj_[2].evaluateDeBoorT(t_cur);/*** calculate yaw ***/yaw_yawdot = calculate_yaw(t_cur, pos, time_now, time_last);/*** calculate yaw ***/double tf = min(traj_duration_, t_cur + 2.0);pos_f = traj_[0].evaluateDeBoorT(tf);}else if (t_cur >= traj_duration_){/* hover when finish traj_ */pos = traj_[0].evaluateDeBoorT(traj_duration_);vel.setZero();acc.setZero();yaw_yawdot.first = last_yaw_;yaw_yawdot.second = 0;pos_f = pos;return;}else{cout << "[Traj server]: invalid time." << endl;}time_last = time_now;cmd.header.stamp = time_now;cmd.header.frame_id = "world";cmd.trajectory_flag = quadrotor_msgs::PositionCommand::TRAJECTORY_STATUS_READY;cmd.trajectory_id = traj_id_;cmd.position.x = pos(0);cmd.position.y = pos(1);cmd.position.z = pos(2);cmd.velocity.x = vel(0);cmd.velocity.y = vel(1);cmd.velocity.z = vel(2);cmd.acceleration.x = acc(0);cmd.acceleration.y = acc(1);cmd.acceleration.z = acc(2);cmd.yaw = yaw_yawdot.first;cmd.yaw_dot = yaw_yawdot.second;last_yaw_ = cmd.yaw;pos_cmd_pub.publish(cmd);
}

该函数为控制频率100Hz执行(traj_server.cpp, line 243)

ros::Timer cmd_timer = nh.createTimer(ros::Duration(0.01), cmdCallback);

查看该函数内容,可以发现cmd所需的位置、速度、加速度均通过轨迹直接计算,计算函数为evaluateDeBoorT。偏航角yaw、其变化率yawdot通过另一个函数calculate_yaw计算。

calculate_yaw函数定义(traj_server.cpp, line 71)为:

std::pair<double, double> calculate_yaw(double t_cur, Eigen::Vector3d &pos, ros::Time &time_now, ros::Time &time_last)
{constexpr double PI = 3.1415926;constexpr double YAW_DOT_MAX_PER_SEC = PI;// constexpr double YAW_DOT_DOT_MAX_PER_SEC = PI;std::pair<double, double> yaw_yawdot(0, 0);double yaw = 0;double yawdot = 0;Eigen::Vector3d dir = t_cur + time_forward_ <= traj_duration_ ? traj_[0].evaluateDeBoorT(t_cur + time_forward_) - pos : traj_[0].evaluateDeBoorT(traj_duration_) - pos;double yaw_temp = dir.norm() > 0.1 ? atan2(dir(1), dir(0)) : last_yaw_;double max_yaw_change = YAW_DOT_MAX_PER_SEC * (time_now - time_last).toSec();if (yaw_temp - last_yaw_ > PI){if (yaw_temp - last_yaw_ - 2 * PI < -max_yaw_change){yaw = last_yaw_ - max_yaw_change;if (yaw < -PI)yaw += 2 * PI;yawdot = -YAW_DOT_MAX_PER_SEC;}else{yaw = yaw_temp;if (yaw - last_yaw_ > PI)yawdot = -YAW_DOT_MAX_PER_SEC;elseyawdot = (yaw_temp - last_yaw_) / (time_now - time_last).toSec();}}else if (yaw_temp - last_yaw_ < -PI){if (yaw_temp - last_yaw_ + 2 * PI > max_yaw_change){yaw = last_yaw_ + max_yaw_change;if (yaw > PI)yaw -= 2 * PI;yawdot = YAW_DOT_MAX_PER_SEC;}else{yaw = yaw_temp;if (yaw - last_yaw_ < -PI)yawdot = YAW_DOT_MAX_PER_SEC;elseyawdot = (yaw_temp - last_yaw_) / (time_now - time_last).toSec();}}else{if (yaw_temp - last_yaw_ < -max_yaw_change){yaw = last_yaw_ - max_yaw_change;if (yaw < -PI)yaw += 2 * PI;yawdot = -YAW_DOT_MAX_PER_SEC;}else if (yaw_temp - last_yaw_ > max_yaw_change){yaw = last_yaw_ + max_yaw_change;if (yaw > PI)yaw -= 2 * PI;yawdot = YAW_DOT_MAX_PER_SEC;}else{yaw = yaw_temp;if (yaw - last_yaw_ > PI)yawdot = -YAW_DOT_MAX_PER_SEC;else if (yaw - last_yaw_ < -PI)yawdot = YAW_DOT_MAX_PER_SEC;elseyawdot = (yaw_temp - last_yaw_) / (time_now - time_last).toSec();}}if (fabs(yaw - last_yaw_) <= max_yaw_change)yaw = 0.5 * last_yaw_ + 0.5 * yaw; // nieve LPFyawdot = 0.5 * last_yaw_dot_ + 0.5 * yawdot;last_yaw_ = yaw;last_yaw_dot_ = yawdot;yaw_yawdot.first = yaw;yaw_yawdot.second = yawdot;return yaw_yawdot;
}

该段代码是一个用于计算航向角(yaw)及其变化率(yaw rate, 即yaw的一阶导数)的函数。函数接收当前时间t_cur、当前位置pos、当前时间time_now及上一次计算时间time_last作为输入,返回一对双精度浮点数,分别代表当前的航向角和航向角变化率。

函数逻辑解析如下:

1. 初始化常量与变量:

YAW_DOT_MAX_PER_SEC:航向角变化率的最大值,限制航向角的变化速度。
yaw_yawdot:存储计算结果的pair,初始为(0, 0)。
yawyawdot:分别用于存储计算出的航向角和航向角变化率。

2. 计算目标方向:

使用De Boor算法(用于计算B样条形式的样条曲线)计算给定时间点的目标位置,然后计算从当前位置pos到目标位置的方向向量dir
d i r = DeBoor ( t _ c u r ​ + t _ f o r w a r d ​ ) − p o s ​ , if  t _ c u r ​ + t _ f o r w a r d ​ ≤ t _ d u r a t i o n ​ d i r = DeBoor ( t _ d u r a t i o n ​ ) − p o s , otherwise ​​ dir = \text{DeBoor}(t\_cur ​ +t\_forward ​ )− pos ​, \text{ if }t\_cur ​ +t\_forward ​ ≤t\_duration ​ \\ dir = \text{DeBoor}(t\_duration ​ )− pos, \text{ otherwise ​} ​ dir=DeBoor(t_cur+t_forward)pos, if t_cur+t_forwardt_durationdir=DeBoor(t_duration)pos, otherwise ​

3. 计算临时航向角:

yaw_temp:基于dir向量,使用atan2函数计算得到的临时航向角,当dir的模长大于0.1时有效,否则使用上一次的航向角last_yaw_

4. 计算航向角变化限制:

max_yaw_change:根据YAW_DOT_MAX_PER_SEC和时间差(time_now - time_last)计算的最大航向角变化量。
Δ y a w m a x = Y A W _ D O T _ M A X _ P E R _ S E C × Δ t Δyaw max =YAW\_DOT\_MAX\_PER\_SEC×Δt Δyawmax=YAW_DOT_MAX_PER_SEC×Δt

5. 调整航向角:

通过比较yaw_templast_yaw_的差值,考虑边界情况(如航向角跨越±π),并限制航向角的变化量不超过max_yaw_change,从而计算出新的yawyawdot

6. 平滑处理:

对计算出的yawyawdot进行简单的低通滤波处理,以平滑航向角及其变化率。
y a w = 0.5 × l a s t _ y a w _ + 0.5 × y a w y a w d o t = 0.5 × l a s t _ y a w _ d o t _ + 0.5 × y a w d o t y a w d o t = 0.5 × l a s t _ y a w _ d o t _ + 0.5 × y a w d o t yaw=0.5×last\_yaw\_+0.5×yaw\\ yawdot=0.5×last\_yaw\_dot\_+0.5×yawdot\\ yawdot=0.5×last\_yaw\_dot\_+0.5×yawdot yaw=0.5×last_yaw_+0.5×yawyawdot=0.5×last_yaw_dot_+0.5×yawdotyawdot=0.5×last_yaw_dot_+0.5×yawdot

7. 更新并返回结果:

更新last_yaw_last_yaw_dot_为当前计算结果。将计算结果存入yaw_yawdot并返回。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/812907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R语言 多组堆砌图

目录 数据格式 普通绘图 添加比例 R语言 堆砌图_r语言堆砌图-CSDN博客 关键点在于数据转换步骤和数据比例计算步骤&#xff0c;然后个性化调整图。 ①data <- melt(dat, id.vars c("ID"))##根据分组变为长数据 ②#计算百分比## data2 <- ddply(data, …

大学生毕业答辩要点

不要太老实 暗号①:论文的创新点体现在哪里? 你就答:本篇毕业论文的创新之处在于讨论XXX的问题时不仅在xxx方面列出xxx,也从实际的角度进行了举例论证。一是在方法上,本文将xxx方法与xxx方法相结合,能多有效消除单方法带来的误差有效提高了数据的精度(结果的有效性)二…

Go 使用ObjectID

ObjectID介绍 MongoDB中的ObjectId是一种特殊的12字节 BSON 类型数据&#xff0c;用于为主文档提供唯一的标识符&#xff0c;默认情况下作为 _id 字段的默认值出现在每一个MongoDB集合中的文档中。以下是ObjectId的具体组成&#xff1a; 1. 时间戳&#xff08;Timestamp&…

Ollama、FastGPT大模型RAG结合使用案例

参考: https://ollama.com/download/linux https://doc.fastai.site/docs/intro/ https://blog.csdn.net/m0_71142057/article/details/136738997 https://doc.fastgpt.run/docs/development/custom-models/m3e/ Ollama作为后端大模型加载运行 FastGPT作为前端页面聊天集成RA…

Redis中的集群(八)

集群 设置从节点 向一个节点发送命令: CLUSTER REPLICATE <node_id>可以让接收命令的节点成为node_id所指定节点的从节点&#xff0c;并开始对主节点进行复制: 1.接收到该命令的节点会首先会在自己的clusterState.nodes字典中找到node_id所对应节点的clusterNode结构,…

根据后端获取到的文档流,下载打开显示“无法打开文件”

原代码&#xff1a; download(item) {this.axios.get(api.download/item.name).then(res > {// console.log(res)let bob new Blob([res.data],{type: application/vnd.ms-excel})const link document.createElement(a);let url window.URL.createObjectURL(bob);link.d…

flutter中鼠标检测事件的应用---主要在于网页端使用

flutter中鼠标检测事件的应用—主要在于网页端使用 鼠标放上去 主要代码 import package:flutter/material.dart;class CustomStack extends StatefulWidget {override_CustomStack createState() > _CustomStack(); }class _CustomStack extends State<CustomStack>…

高质量ChatGPT Prompts 精选

通用超级 Prompt GPT4实用。通用超级 prompt &#xff0c;根据你想要的输出和你的反馈&#xff0c;自动使用相应的专家角色帮你解决问题。如果需要升级ChatGPT Plus&#xff0c;可以参考教程 升级 GPT4.0 保姆教程 您是一位具有多领域专长的专家级ChatGPT提示工程师。在我们…

Nginx 访问日志配置

Nginx 的访问日志主要记录用户客户端的请求信息(见下表)。用户的每次请求都会记录在访问日志中,access_log 指令可以设置日志的输出方式及引用的日志格式。 名称访问日志指令指令access_log作用域http、stream、server、location、if in location、limit except指令值格式lo…

前端面试题收集整理

1. 浏览器地址输入url之后发生了什么 解析url (协议&#xff0c;域名IP端口&#xff0c;地址路径&#xff0c;hash值&#xff0c;参数) DNS对域名进行解析&#xff08;先去本地host查看&#xff09;&#xff1b; 建立TCP连接&#xff08;三次握手&#xff09;&#xff1b; 发送…

2023年全国青少年信息素养大赛(Python)海南赛区复赛真题,包含答案

2023 年全国青少年信息素养大赛 (Python) 海南赛区复赛真题 第 1 题,整数加 8 题目描述: 输入一个整数,输出这个整数加 8 的结果。 输入描述: 输入一行一个正整数。 输出描述: 输出求和的结果。 样例

YOLO算法改进Backbone系列之:Fcaformer

目前&#xff0c;设计更高效视觉Transformer的一个主要研究方向是通过采用稀疏注意力或使用局部注意力窗口来降低自我注意力模块的计算成本。相比之下&#xff0c;我们提出了一种不同的方法&#xff0c;旨在通过密集注意力模式来提高基于变换器的架构的性能。具体来说&#xff…

Fastjson报autotype is not support

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 打开AutoType功能 …

FANUC机器人通过ROBOGUIDE实现与实际的机器人进行程序导入导出的具体方法示例

FANUC机器人通过ROBOGUIDE实现与实际的机器人进行程序导入导出的具体方法示例 如下图所示,在电脑的开始菜单中找到”Robot Neiborhood”,点击进入, 如下图所示,设置要连接的机器人名称和主机IP地址(要确保自己的电脑和机器人IP地址在同一网段内),点击Add添加, 添加在线…

接招吧! selenium环境+元素定位大法

selenium 与 webdriver Selenium 是一个用于 Web 测试的工具&#xff0c;测试运行在浏览器中&#xff0c;就像真正的用户在手工操作一样。支持所有主流浏览器 WebDriver 就是对浏览器提供的原生API进行封装&#xff0c;使其成为一套更加面向对象的Selenium WebDriver API。 …

创新科技:FlexLua助力LoRa无线一氧化碳传感器轻松开发

随着智能科技的不断进步&#xff0c;无线传感器技术在环境监测领域的应用越来越广泛。其中&#xff0c;LoRa无线一氧化碳传感器以其高效的通信原理和精准的传感器原理&#xff0c;在各种应用场景中大显身手。而借助FlexLua低代码技术&#xff0c;开发这类传感器变得更加轻松快捷…

FFmpeg: 自实现ijkplayer播放器--05ijkplayer–连接UI界面和ffplay.c

文章目录 ijkplayer时序图消息循环--回调函数实现播放器播放时状态转换播放停止 ijkmediaPlay成员变量成员函数 ijkplayer时序图 stream_open: frame_queue_init packet_queue_init init_clock 创建read_thread线程 创建video_refresh_thread线程 消息循环–回调函数实现 ui …

中级物流师、高级物流师资格认证考试大纲《物流管理实务》

物流管理实务 第一章 物流市场调查 一、市场调查基本知识 二、物流市场调研 三、物流市场预测 四、物流市场调研报告 第二章 仓库规划与设计 一、仓储规划概述 二、仓库规模和数量规划 三、仓库选址规划 四、仓库的结构与布局 五、自动化立体仓库的规划与设计 第…

创新联合体与新质生产力

发展新质生产力的核心要素是科技创新&#xff0c;主要路径是统筹生产力与生产关系&#xff0c;根本落脚点在产业高质量发展。在当前大国战略博弈全面加剧、新一轮科技革命和产业变革加速演化的新形势下&#xff0c;亟待以体系化思维加强主体力量协同与资源要素整合&#xff0c;…

场景:如何设计一个秒杀系统

来自hollis八股文 设计一个秒杀系统需要考虑以下问题 秒杀系统存在的问题 1. 高并发流量 2. 热点数据 3. 库存正常扣减 4. 重复下单 5. 对普通交易的影响 6. 业务手段 7. 黄牛 高并发流量 将请求链路变短&#xff0c;把一些流量挡在外面 1. 使用CDN服务存储静态资源…