版本:V2.3
更新日期:2026年2月10日
fcu_core_v2
|- fcu_core.launch 一键启动launch脚本
|- mavlink 第三方消息库用来传输Mavlink数据
|- rviz_cfg rviz配置文件存放目录
└─ src 功能包源文件
|- fcu_mission.cpp 任务规划文件
|- fcu_command.cpp 命令发送文件
|- fcu_bridge_001.cpp 和飞控之间的通信文件
|- fcu_bridge_002.cpp
└─ fcu_bridge_00*.cpp
实验平台:windows11宿主机 VMware17.5Pro虚拟机里Ubuntu-20.04LTS
终端执行下面命令
wget http://fishros.com/install -O fishros && . fishros
按照下面操作运行




完成之后执行下面指令
sudo apt-get install ros-noetic-serial // 串口库
sudo apt-get install libeigen3-dev // Eigen库是一个开源的C++线性代数库,支持线性代数、矩阵和矢量运算,数值分析及其相关的算法
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/vins_estimator/odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.4.1" type="string"/>
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="true" type="bool"/>
<param name="set_goal" value="false" type="bool"/>
<param name="simple_target" value="true" type="bool"/>
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
fcu_mission是fcu_core_v2中进行任务规划的部分,想要规划飞行任务就要先知道自己的定位
所以首先他接收飞控发送过来的odom数据
ros::Subscriber odom001=nh.subscribe<nav_msgs::Odometry>("odom_global_001", 100, odom_global001_handler);
进行坐标变换和四元数到姿态角的转化
void odom_global001_handler(const nav_msgs::Odometry::ConstPtr& odom)
{
pos_odom_001_x=(float)odom->pose.pose.position.x;//位置点改为FRU坐标
pos_odom_001_y=-(float)odom->pose.pose.position.y;
pos_odom_001_z=(float)odom->pose.pose.position.z;
float quaternion_odom[4]={(float)odom->pose.pose.orientation.w,
(float)odom->pose.pose.orientation.x,
(float)odom->pose.pose.orientation.y,
(float)odom->pose.pose.orientation.z};
mavlink_quaternion_to_euler(quaternion_odom, &pos_odom_001_roll, &pos_odom_001_pitch, &pos_odom_001_yaw);
}
接下来就是如何规划我们的轨迹,fcu_mission文件中给出四个例程让我们手动规划轨迹。每一个例程都需要使用键盘启动,所以要有一个接收键盘指令的话题
ros::Subscriber comm=nh.subscribe<std_msgs::Int16>("/fcu_command/command", 100, cmdHandler);
而他的发送方则是fcu_command.cpp,可以看到键盘上每一个按键都对应一个case,我们可以类比着添加自己的任务启动按键
switch(buf[0]){
case 'p':
printf("执行路径\n");
cmd.data=0;
command.publish(cmd);
break;
case 'a':
printf("解锁\n");
cmd.data=1;
command.publish(cmd);
break;
case 'd':
printf("锁定\n");
cmd.data=2;
command.publish(cmd);
break;
case 't':
printf("起飞\n");
cmd.data=3;
command.publish(cmd);
break;
case 'l':
printf("降落\n");
cmd.data=4;
command.publish(cmd);
break;
***
在fcu_mission中,键入指令的处理函数也是需要考虑的一环,同样使用switch case语句来进行处理
void cmdHandler(const std_msgs::Int16::ConstPtr& cmd){
switch(cmd->data){
case 0:
enable_path=true;
break;
***
if(enable_track){
theta+=M_PI/20/200;
px1=1.0*cosf(theta)+2;
py1=1.0*sinf(theta)+2;
pz1=0.6;
px2=1.0*cosf(theta+M_PI*2/6)+2;
py2=1.0*sinf(theta+M_PI*2/6)+2;
pz2=0.6;
px3=1.0*cosf(theta+M_PI*4/6)+2;
py3=1.0*sinf(theta+M_PI*4/6)+2;
pz3=0.6;
px4=1.0*cosf(theta+M_PI*6/6)+2;
py4=1.0*sinf(theta+M_PI*6/6)+2;
pz4=0.6;
px5=1.0*cosf(theta+M_PI*8/6)+2;
py5=1.0*sinf(theta+M_PI*8/6)+2;
pz5=0.6;
px6=1.0*cosf(theta+M_PI*10/6)+2;
py6=1.0*sinf(theta+M_PI*10/6)+2;
pz6=0.6;
}
首先根据theta==0的时候的初始位置摆放好飞机,启动,连接,检查心跳包,解锁"a",起飞"t",绕圆'r',停止's'。
和绕圆任务类似,具体逻辑看代码
switch (path_track_status)
{
case ReadyToGoal:
switch(goal_point)
{
case 0:
SetGoal(1,0.5,0.5,0);
break;
case 1:
SetGoal(1,1.0,1.0,0);
break;
case 2:
SetGoal(1,1.5,1.0,0);
break;
case 3:
SetGoal(1,2.0,1.0,0);
break;
case 4:
SetGoal(1,2.5,1.0,0);
break;
case 5:
SetGoal(1,2.5,2.0,0);
break;
case 6:
SetGoal(1,2.0,2.0,0);
break;
case 7:
SetGoal(1,1.5,2.0,0);
break;
case 8:
SetGoal(1,1.0,2.0,0);
break;
case 9:
SetGoal(1,0.5,2.0,0);
break;
}
path_track_status = ExecutingGoal;
break;
case ExecutingGoal:
if(IsReachGoal(1,0.1))
{
path_track_status = ReadyToGoal;
goal_point++;
break;
}
// std::cout << "Not Finish yet..." << std::endl;
break;
首先根据goal_point==0的时候的初始位置摆放好飞机,启动,连接,检查心跳包,解锁"a",起飞"t",执行路径'p',停止's'。
switch(enable_pos){
case 0:
px1=pos_takeoff_001_x; py1=pos_takeoff_001_y; pz1=0.0f;
px2=pos_takeoff_002_x; py2=pos_takeoff_002_y; pz2=0.0f;
px3=pos_takeoff_003_x; py3=pos_takeoff_003_y; pz3=0.0f;
px4=pos_takeoff_004_x; py4=pos_takeoff_004_y; pz4=0.0f;
px5=pos_takeoff_005_x; py5=pos_takeoff_005_y; pz5=0.0f;
px6=pos_takeoff_006_x; py6=pos_takeoff_006_y; pz6=0.0f;
break;
case 1:
px=1.0;
py=1.0;
pz=1.0;
px1=pos_takeoff_001_x+px; py1=pos_takeoff_001_y+py; pz1=pz;
px2=pos_takeoff_002_x+px; py2=pos_takeoff_002_y+py; pz2=pz;
px3=pos_takeoff_003_x+px; py3=pos_takeoff_003_y+py; pz3=pz;
px4=pos_takeoff_004_x+px; py4=pos_takeoff_004_y+py; pz4=pz;
px5=pos_takeoff_005_x+px; py5=pos_takeoff_005_y+py; pz5=pz;
px6=pos_takeoff_006_x+px; py6=pos_takeoff_006_y+py; pz6=pz;
break;
可以摆放飞机,启动,连接,检查心跳包,解锁"a",起飞"t",执行位置点"1"(1~4)。
根据fcu_command的enable_pos来进行定点飞行,pos_takeoff_001_x和pos_takeoff_001_y是起飞时候的位置,而px和py都是根据起飞点位置的偏移量,进行相加之后就是绝对位置目标点,所以我们可以看到,最终publish的都是绝对目标点。
追踪飞行分别为前视追踪和下视追踪,分别对应带有前视摄像机的无人机和下视摄像头的机载电脑无人机,机载电脑运行着fcu_core_v2的程序和aruco检测程序。
在远程主机中,我们摁下按键触发指令,就会向飞控发送追踪指令。

飞控再将消息转发至机载电脑,从而开启追踪和下发追踪指令。具体流程图如下

在fcu_mission中,我们只需要将规划的目标点位置姿态传出去就可以了,这一步通过定时器实现
ros::Timer timer_mission_001 = nh.createTimer(ros::Duration(0.1),execute_mission_001,false);
我们创建了一个100ms的定时器,以这个周期运行execute_mission_001的函数
void execute_mission_001(const ros::TimerEvent &event){
if(get_pos_cmd){
return;
}
if(set_goal&&!use_goal_001){
return;
}
//发布mission
mission_001.layout.dim.push_back(std_msgs::MultiArrayDimension());
mission_001.layout.dim[0].label = "mission_001";
mission_001.layout.dim[0].size = 11;
mission_001.layout.dim[0].stride = 1;
mission_001.data.resize(11);
mission_001.data[0]=yaw;//rad
mission_001.data[1]=yaw_rate;//rad/s
mission_001.data[2]=px1;//x
mission_001.data[3]=py1;//y
mission_001.data[4]=pz1;//z
mission_001.data[5]=vx;//vx
mission_001.data[6]=vy;//vy
mission_001.data[7]=vz;//vz
mission_001.data[8]=ax;//ax
mission_001.data[9]=ay;//ay
mission_001.data[10]=az;//az
mission_pub_001.publish(mission_001);
use_goal_001=false;
}
这个函数就是填充数据然后发送,最重要的是publish这一步,其实就是将我们规划好的目标点通过话题发送给fcu_bridge再发送给飞控。
除掉直接使用fcu_mission.cpp进行任务规划,我们还可以rviz_plugin进行任务规划,这个插件的源代码放在fcu_core_rviz_swarm_goals_plugin中。通过rviz自动加载我们的自定义插件,然后通过rviz中发布的点进行任务规划,然后点击执行路径按钮,任务就会发送给fcu_mission.cpp,再通过fcu_mission.cpp进行任务规划,所以本质上还是使用了fcu_mission.cpp。

这个部分是RVIZ原生的功能,用于选择在"显示界面"中展示的可视化组件。具体介绍可以参考RVIZ官方文档
这是我们的自定义界面,源代码放在fcu_core_rviz_swarm_goals_plugin
按钮区域

第一行和第二行的按钮作用不再赘述,具体实现就是使用槽函数将对应消息发送到/fcu_command/command话题上面,和fcu_command节点所做的事别无二致。
第三行和第四行是新加的控制按钮,就是以按钮的方式控制飞机的前进后退左转右转。
使用方法:1. 由于该控制仅对指定id的飞机生效,所以首先观察当前目标飞机id,确保控制飞机的id正确。

2. 接着确保该飞机至少有一个目标点

3. 然后点击执行路径按钮,飞机会飞向该目标点,此时前进后退左转右转的功能就是控制该飞机的目标点的移动,飞机跟随目标点移动,即可实现前后左右移动的效果。
循环轨迹
如下图所示就是一个checkbox,如果需要循环执行哪个飞机的目标点就勾选对应飞机的循环checkbox即可

首先映入眼帘的是矩形构成的墙体,这个墙体是可以移动的,我们在控制界面的这一行能够决定这个矩形墙体的两个对角线上的点。


下发目标点


此时已经将该目标点加入到待执行列表中。


点击执行目标点之后,飞机会实时监测飞机是否到达目标点,到达目标点之后将会自动执行下一个目标点,直到"待执行列表"里的最后一个目标点执行完毕。
既然要判断飞机是否抵达目标点,那就需要给定一个允许误差阈值,这个阈值就是我们通过rviz界面设置的,具体如下(单位m)


fcu_bridge_00x和fcu_mission,fcu_command统一规划不同,每一个bridge文件只对应一个飞机,下面代码解析只真对fcu_bridge_001.cpp,对于其他bridge文件也适用。
打开并配置串口/网络
if(mav_chan==MAVLINK_COMM_0){
try{
//设置串口属性,并打开串口
ser.setPort(usb_port.c_str());
ser.setBaudrate(BAUDRATE);
serial::Timeout to = serial::Timeout::simpleTimeout(5000);
ser.setTimeout(to);
ser.open();
}catch (serial::IOException& e)
{
printf("Unable to open port \n");
return -1;
}
//检测串口是否已经打开,并给出提示信息
if(ser.isOpen())
{
printf("Serial Port initialized\n");
}
else
{
return -1;
}
}else{
socket_cli=socket(AF_INET, SOCK_STREAM, 0);
if(socket_cli < 0){
printf("socket error!\n");
return -1;
}
memset(&drone_addr, 0, sizeof(drone_addr));
drone_addr.sin_family = AF_INET;
drone_addr.sin_port = htons(DRONE_PORT);
drone_addr.sin_addr.s_addr = inet_addr(drone_ip.c_str());
printf("fcu_bridge 001 connecting...\n");
get_drone=connect(socket_cli, (struct sockaddr*)&drone_addr, sizeof(drone_addr));
if(get_drone<0){
printf("fcu_bridge 001 connect error!\n");
return -1;
}else{
printf("fcu_bridge 001 connect succeed!\n");
}
}
使用环形缓冲队列进行数据的写入和读取,最终将数据在flush_data()函数中发送出去/在parse_data()函数中读取进来。
由于直接与飞控通信,他还需要将飞控的数据处理之后以话题的方式发布出去。
gnss_global = nh.advertise<sensor_msgs::NavSatFix>("gnss_global_001",100);
imu_global = nh.advertise<sensor_msgs::Imu>("imu_global_001",100);
odom_global = nh.advertise<nav_msgs::Odometry>("odom_global_001",100);
gnss_001=nh.subscribe<sensor_msgs::NavSatFix>("gnss_global_001", 100, gnssHandler, ros::TransportHints().tcpNoDelay());
odom=nh.subscribe<nav_msgs::Odometry>("odometry_001", 100, odomHandler, ros::TransportHints().tcpNoDelay());
cmd=nh.subscribe<std_msgs::Int16>("command", 100, cmdHandler, ros::TransportHints().tcpNoDelay());
mission=nh.subscribe<std_msgs::Float32MultiArray>("mission_001", 100, missionHandler, ros::TransportHints().tcpNoDelay());
path_global = nh.advertise<nav_msgs::Path>("path_global_001", 100);
goal=nh.advertise<geometry_msgs::PoseStamped>("goal_001", 100);
motion=nh.subscribe<geometry_msgs::PoseStamped>("motion_001", 100, motionHandler, ros::TransportHints().tcpNoDelay());
command = nh.advertise<std_msgs::Int16>("command",100);
path_target_pub = nh.advertise<nav_msgs::Path>("path_target_001", 100);
launch文件中的simple_target==true,这就意味着是简单的位置控制,如果要更改为速度控制就需要将simple_target改为false。
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="true" type="bool"/>
<param name="set_goal" value="false" type="bool"/>
<param name="simple_target" value="true" type="bool"/> // 更改为true
实验平台:windows11宿主机 VMware17.5Pro虚拟机里Ubuntu-20.04LTS
创建工作空间
mkdir -p ~/ros_ws/src
拉取fcu_core_v2工程并按照readme.md进行编译
readme
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/vins_estimator/odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.4.1" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="true" type="bool"/>
<param name="set_goal" value="false" type="bool"/>
<param name="simple_target" value="true" type="bool"/>
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
pos_takeoff_001_x和pos_takeoff_001_y是起飞时候的位置,而px和py都是根据起飞点位置的偏移量,进行相加之后就是绝对位置目标点,所以我们可以看到,最终publish的都是绝对目标点。
在UWB套件中,不论是绕圆轨迹、多点飞行还是定点飞行,坐标系都是UWB的全局坐标系,但是定点飞行中,我们的demo程序记住了起飞点,并对起飞点设置了目标偏移量,实现了飞行相对于任意起飞点,飞行一段位移的效果
switch(enable_pos){
case 0:
px1=pos_takeoff_001_x; py1=pos_takeoff_001_y; pz1=0.0f;
px2=pos_takeoff_002_x; py2=pos_takeoff_002_y; pz2=0.0f;
px3=pos_takeoff_003_x; py3=pos_takeoff_003_y; pz3=0.0f;
px4=pos_takeoff_004_x; py4=pos_takeoff_004_y; pz4=0.0f;
px5=pos_takeoff_005_x; py5=pos_takeoff_005_y; pz5=0.0f;
px6=pos_takeoff_006_x; py6=pos_takeoff_006_y; pz6=0.0f;
break;
case 1:
px=1.0;
py=1.0;
pz=1.0;
px1=pos_takeoff_001_x+px; py1=pos_takeoff_001_y+py; pz1=pz;
px2=pos_takeoff_002_x+px; py2=pos_takeoff_002_y+py; pz2=pz;
px3=pos_takeoff_003_x+px; py3=pos_takeoff_003_y+py; pz3=pz;
px4=pos_takeoff_004_x+px; py4=pos_takeoff_004_y+py; pz4=pz;
px5=pos_takeoff_005_x+px; py5=pos_takeoff_005_y+py; pz5=pz;
px6=pos_takeoff_006_x+px; py6=pos_takeoff_006_y+py; pz6=pz;
break;
根据fcu_command的enable_pos来进行定点飞行,pos_takeoff_001_x和pos_takeoff_001_y是起飞时候的位置,而px和py都是根据起飞点位置的偏移量,进行相加之后就是绝对位置目标点,所以我们可以看到,最终publish的都是绝对目标点。
摆放基站:详见前文
设置定位参数:使用手机APP连接飞控,其中标签ID设置为1,对应通信节点001
放置无人机:重启无人机,使用虚拟机连接无人机,此时终端应该有各个飞机的心跳包。将无人机放置在对应位置上,这里我们使用无人机绕圆的例程,那么001号无人机的初始位置就在(3,2)坐标点上。将无人机放置在基站坐标系下的起点坐标处,机尾面朝用户。
将
theta = 0
代入
px1=1.0*cosf(theta)+2;
py1=1.0*sinf(theta)+2;

catkin_make
source ./devel/setup.bash
接着运行ros程序
roslaunch fcu_core fcu_core.launch
出现下面打印即是收到心跳包

与此同时,系统会自动弹出RViz界面(下图坐标点仅供参考,以实际运行效果为准),如下图所示。

RViz是一个三维可视化平台,可用于无人机位置及飞行轨迹的图形化显示。
图中右侧3D视图区中坐标轴图案代表世界坐标系,其中红轴为X轴,绿轴为Y轴,蓝轴为Z轴。
图中的红点代表无人机在世界坐标系中的位置。


我们为了方便用户测试,开发了一套简单指令如下:
"a": 解锁
"d": 上锁
"t": 起飞
"l": 降落
工程运行后,直接在Ubuntu终端中输入:指令字符+回车,即可完成对应指令的下发。
清除RViz界面轨迹:在开始运行新的轨迹之前,必须清除RViz界面上的现有轨迹。请按照以下步骤操作:首先,退出当前的ROS工程;其次,重新运行ROS工程。
开始运行轨迹
按照上述步骤将无人机放置初始坐标。
无人机解锁
(1)对于基础款(无论有无UWB)的飞机,用户需要确保起落架(机架的腿部)成X形,分别位于四个电机的正下方,不可以有偏移
(2)对于没有搭载高精度GNSS模组的机型
特别注意!在FanciSwarm无人机启动提示音结束后,将飞机垂直拿起(拿起一次即可),拿到距离地面超过1m后放下。如果激光测距工作正常,解锁才能顺利进行,否则FanciSwarm 会发出“滴滴滴."的报警音,同时,固件强制锁定,不允许起飞。对于Pro款飞机,如果配置了SLAM定高(如何配置在激光雷达SLAM的pdf教程中详细展开),是不需要拿到距离地面超过1m。
(3)对于搭载高精度GNSS模组的机型
特别注意!首次开机使用时,请耐心等待,当GNSS模组蓝色LED灯长亮时,表明当前环境的GNSS信号良好,解锁才能顺利进行;当GNSS模组上的蓝色LED灯未保持长亮状态时,若尝试进行硬件解锁操作,FanciSwarm系统会发出连续的"滴滴滴.."报警声,同时,固件强制锁定,不允许起飞。
上述检查完成后,直接在终端输入“a”,接着点击“回车键”(无需输入“a”后立即点击),界面会打印“解锁”字样,如下图所示:

此时,Mcontroller 右侧LED绿灯将点亮,并伴随着启动声音。FanciSwarm™ 无人机完成解锁。
一键起飞
直接在终端输入“t”,接着点击“回车键”(无需输入“t”后立即点击),界面会打印“起飞”字样,如下图所示:

此时,FanciSwarm™ 无人机起飞到一定高度(FanciSwarm App中设置的起飞高度)并悬停。
开始运行轨迹
直接在终端输入“r”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“运行”字样,如下图所示:

此时,FanciSwarm™ 无人机开始飞圆轨迹。
停止运行轨迹
直接在终端输入“s”,接着点击“回车键”(无需输入“s”后立即点击),界面会打印“停止”字样,如下图所示:

此时,FanciSwarm™ 无人机停止飞圆轨迹,并保持悬停状态。
一键降落
直接在终端输入“l”,接着点击“回车键”(无需输入“l”后立即点击),界面会打印“降落”字样,如下图所示:

此时,FanciSwarm™ 无人机开始降落,并着陆。
锁定无人机
等待FanciSwarm™ 无人机着陆后,直接在终端输入“d”,接着点击“回车键”(无需输入“d”后立即点击),界面会打印“锁定”字样,如下图所示:

此时,FanciSwarm™ 电调指示灯熄灭,同时Mcontroller 右侧LED绿灯熄灭、LED红灯点亮。FanciSwarm™ 无人机完成锁定。
关闭无人机和基站
FanciSwarm™ 无人机锁定后,如果不继续飞行,请及时关闭无人机和基站,并拔掉电池,以避免电量损耗,节省电池电量。
至此,整个UWB轨迹飞行过程已完成。
实验平台:windows11宿主机 VMware17.5Pro虚拟机里Ubuntu-20.04LTS
创建工作空间
mkdir -p ~/ros_ws/src
拉取fcu_core_v2和quadrotor_msgs
readme
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/vins_estimator/odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.0.201" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="false" type="bool"/> // use_uwb改为false
<param name="set_goal" value="false" type="bool"/>
<param name="simple_target" value="true" type="bool"/>
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
在GNSS套件中,不论是绕圆轨迹、多点飞行还是定点飞行,坐标系都是GNSS的全局坐标系(以飞机GNSS连接到卫星那一刻的坐标点为原点,坐标轴为"北东天"),但是定点飞行中,我们的demo程序记住了起飞点,并对起飞点设置了目标偏移量,实现了飞行相对于任意起飞点,飞行一段位移的效果
switch(enable_pos){
case 0:
px1=pos_takeoff_001_x; py1=pos_takeoff_001_y; pz1=0.0f;
px2=pos_takeoff_002_x; py2=pos_takeoff_002_y; pz2=0.0f;
px3=pos_takeoff_003_x; py3=pos_takeoff_003_y; pz3=0.0f;
px4=pos_takeoff_004_x; py4=pos_takeoff_004_y; pz4=0.0f;
px5=pos_takeoff_005_x; py5=pos_takeoff_005_y; pz5=0.0f;
px6=pos_takeoff_006_x; py6=pos_takeoff_006_y; pz6=0.0f;
break;
case 1:
px=1.0;
py=1.0;
pz=1.0;
px1=pos_takeoff_001_x+px; py1=pos_takeoff_001_y+py; pz1=pz;
px2=pos_takeoff_002_x+px; py2=pos_takeoff_002_y+py; pz2=pz;
px3=pos_takeoff_003_x+px; py3=pos_takeoff_003_y+py; pz3=pz;
px4=pos_takeoff_004_x+px; py4=pos_takeoff_004_y+py; pz4=pz;
px5=pos_takeoff_005_x+px; py5=pos_takeoff_005_y+py; pz5=pz;
px6=pos_takeoff_006_x+px; py6=pos_takeoff_006_y+py; pz6=pz;
break;
pos_takeoff_001_x和pos_takeoff_001_y是起飞时候的位置,而px和py都是根据起飞点位置的偏移量,进行相加之后就是绝对位置目标点,所以我们可以看到,最终publish的都是绝对目标点。
在空旷露天环境下!!!!
摆放RTK基站:rtk基站要求静止摆放在空旷区域,距离地面1.5m左右高度。(没有RTK基站的可以略过)
设置定位参数:使用手机APP连接飞控,其中标签ID设置为1,对应通信节点001
放置无人机:重启无人机,使用虚拟机连接无人机,此时终端应该有各个飞机的心跳包。GNSS套件的坐标系原点是根据飞机第一个接收到GNSS坐标的点,在绕圆例程中仅需要保证起飞位置安全且水平即可。
重启无人机并开启rtk基站(没有RTK基站的可以略过RTK部分)



catkin_make
source ./devel/setup.bash
接着运行ros程序
roslaunch fcu_core fcu_core.launch
出现下面打印即是收到心跳包
可以看到心跳包最后两个参数为sat卫星数量和gnss定位精度
对于带有RTK基站的套件来说gnss定位精度要到达5才可解锁起飞
对于没有带RTK基站的GNSS套件来说gnss定位精度要到达2才可解锁起飞

与此同时,系统会自动弹出RViz界面(下图坐标点仅供参考,以实际运行效果为准),如下图所示。

RViz是一个三维可视化平台,可用于无人机位置及飞行轨迹的图形化显示。
图中右侧3D视图区中坐标轴图案代表世界坐标系,其中红轴为X轴,绿轴为Y轴,蓝轴为Z轴。
图中的红点代表无人机在世界坐标系中的位置。
在空旷露天环境下,启动无人机电源,使用虚拟机连接无人机,此时终端应该有飞机的心跳包,其中包括GNSS搜寻到的卫星数量,静止状态下,卫星数量要保证在20左右,定位状态要到达2,那代表信号良好,可以起飞。
注意:第一次飞行需要在交流群里的工程师指导下进行

我们为了方便用户测试,开发了一套简单指令如下:
"a": 解锁
"d": 上锁
"t": 起飞
"l": 降落
工程运行后,直接在Ubuntu终端中输入:指令字符+回车,即可完成对应指令的下发。
清除RViz界面轨迹:在开始运行新的轨迹之前,必须清除RViz界面上的现有轨迹。请按照以下步骤操作:首先,退出当前的ROS工程;其次,重新运行ROS工程。
开始运行轨迹
无人机解锁
对于搭载高精度GNSS模组的机型
特别注意!首次开机使用时,请耐心等待,当GNSS模组蓝色LED灯长亮时,表明当前环境的GNSS信号良好,解锁才能顺利进行;当GNSS模组上的蓝色LED灯未保持长亮状态时,若尝试进行硬件解锁操作,FanciSwarm系统会发出连续的"滴滴滴.."报警声,同时,固件强制锁定,不允许起飞。
上述检查完成后,直接在终端输入“a”,接着点击“回车键”(无需输入“a”后立即点击),界面会打印“解锁”字样,如下图所示:

此时,Mcontroller 右侧LED绿灯将点亮,并伴随着启动声音。FanciSwarm™ 无人机完成解锁。
一键起飞
直接在终端输入“t”,接着点击“回车键”(无需输入“t”后立即点击),界面会打印“起飞”字样,如下图所示:

此时,FanciSwarm™ 无人机起飞到一定高度(FanciSwarm App中设置的起飞高度)并悬停。
开始运行轨迹
直接在终端输入“r”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“运行”字样,如下图所示:

此时,FanciSwarm™ 无人机开始飞圆轨迹。
停止运行轨迹
直接在终端输入“s”,接着点击“回车键”(无需输入“s”后立即点击),界面会打印“停止”字样,如下图所示:

此时,FanciSwarm™ 无人机停止飞圆轨迹,并保持悬停状态。
一键降落
直接在终端输入“l”,接着点击“回车键”(无需输入“l”后立即点击),界面会打印“降落”字样,如下图所示:

此时,FanciSwarm™ 无人机开始降落,并着陆。
锁定无人机
等待FanciSwarm™ 无人机着陆后,直接在终端输入“d”,接着点击“回车键”(无需输入“d”后立即点击),界面会打印“锁定”字样。
此时,FanciSwarm™ 电调指示灯熄灭,同时Mcontroller 右侧LED绿灯熄灭、LED红灯点亮。FanciSwarm™ 无人机完成锁定。
关闭无人机和基站
FanciSwarm™ 无人机锁定后,如果不继续飞行,请及时关闭无人机和基站,并拔掉电池,以避免电量损耗,节省电池电量。
至此,整个GNSS轨迹飞行过程已完成。
实验平台:windows11宿主机 VMware17.5Pro虚拟机里Ubuntu-20.04LTS
创建工作空间
mkdir -p ~/ros_ws/src
拉取fcu_core_v2和quadrotor_msgs
readme
对于VINS套件来说,set_goal==true意味着用于发送ego避障目标点,而set_goal==false意味着用于发送普通非避障目标点。
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/vins_estimator/odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.0.201" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="false" type="bool"/> // use_uwb改为false
<param name="set_goal" value="true" type="bool"/> // set_goal改为true
<param name="simple_target" value="true" type="bool"/>
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
在VINS套件中,定点飞行的坐标系是ego的全局坐标系(以机载电脑ego程序启动那一刻的坐标点为原点,坐标轴为飞机的"前右上"),在定点飞行中,我们的demo程序记住了起飞点,并对起飞点设置了目标偏移量,实现了飞行相对于任意起飞点,飞行一段位移的效果
switch(enable_pos){
case 0:
px1=pos_takeoff_001_x; py1=pos_takeoff_001_y; pz1=0.0f;
px2=pos_takeoff_002_x; py2=pos_takeoff_002_y; pz2=0.0f;
px3=pos_takeoff_003_x; py3=pos_takeoff_003_y; pz3=0.0f;
px4=pos_takeoff_004_x; py4=pos_takeoff_004_y; pz4=0.0f;
px5=pos_takeoff_005_x; py5=pos_takeoff_005_y; pz5=0.0f;
px6=pos_takeoff_006_x; py6=pos_takeoff_006_y; pz6=0.0f;
break;
case 1:
px=1.0;
py=1.0;
pz=1.0;
px1=pos_takeoff_001_x+px; py1=pos_takeoff_001_y+py; pz1=pz;
px2=pos_takeoff_002_x+px; py2=pos_takeoff_002_y+py; pz2=pz;
px3=pos_takeoff_003_x+px; py3=pos_takeoff_003_y+py; pz3=pz;
px4=pos_takeoff_004_x+px; py4=pos_takeoff_004_y+py; pz4=pz;
px5=pos_takeoff_005_x+px; py5=pos_takeoff_005_y+py; pz5=pz;
px6=pos_takeoff_006_x+px; py6=pos_takeoff_006_y+py; pz6=pz;
break;
pos_takeoff_001_x和pos_takeoff_001_y是起飞时候的位置,而px和py都是根据起飞点位置的偏移量,进行相加之后就是绝对位置目标点,所以我们可以看到,最终publish的都是绝对目标点。
将无人机放到地面上启动,如下图所示代表ego已经成功启动
VNC连接机载电脑的远程画面/连接显示屏的画面:

fcu_core_v2的rviz画面:

将无人机提起移动一段距离再放下,如下图所示轨迹正常

使用2D Nav Goal工具发布一个目标点,如下图所示规划正常


catkin_make
source ./devel/setup.bash
接着运行ros程序
roslaunch fcu_core fcu_core.launch
出现下面打印即是收到心跳包,即成功连接。

保证起飞的起点和终点一米以内不可以有障碍物,确保slam定位是否正常,否则不能解锁
飞行测试注意以下事项:
完成上述检查后可以起飞无人机,如果只连接远程电脑ros,没有连接遥控器,那么默认就在电脑控制模式中,如果既连接了机载电脑又连接了遥控器,应注意,ch7位于中间档位时飞机受遥控器控制,ch7档位位于下方时,飞机受电脑控制

我们为了方便用户测试,开发了一套简单指令如下:
"a": 解锁
"d": 上锁
"t": 起飞
"l": 降落
工程运行后,直接在Ubuntu终端中输入:指令字符+回车,即可完成对应指令的下发。
按照上述步骤将无人机放置初始坐标。
无人机解锁
我们使用"global_gnss_001"这个话题作为SLAM是否正常启动的标志位
我们可以使用下面命令查看话题内容从而判断SLAM是否正常启动rostopic echo /fcu_bridge_001/global_gnss_001
还没启动/不正常启动:

正常启动:

也可以使用手机APP连接飞控查看


上述检查完成后,直接在终端输入“a”,接着点击“回车键”(无需输入“a”后立即点击),界面会打印“解锁”字样,如下图所示:

此时,Mcontroller 右侧LED绿灯将点亮,并伴随着启动声音。FanciSwarm™ 无人机完成解锁。
一键起飞
直接在终端输入“t”,接着点击“回车键”(无需输入“t”后立即点击),界面会打印“起飞”字样,如下图所示:

此时,FanciSwarm™ 无人机起飞到一定高度(FanciSwarm App中设置的起飞高度)并悬停。
开始运行目标点
直接在终端输入“1”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点1”字样,如下图所示:

回到初始目标点
直接在终端输入“4”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点4”字样,如下图所示:

一键降落
直接在终端输入“l”,接着点击“回车键”(无需输入“l”后立即点击),界面会打印“降落”字样,如下图所示:

此时,FanciSwarm™ 无人机开始降落,并着陆。
关闭无人机
FanciSwarm™ 无人机锁定后,如果不继续飞行,请及时关闭无人机,并拔掉电池,以避免电量损耗,节省电池电量。
至此,整个SLAM单机飞行过程已完成。
实验平台:windows11宿主机 VMware17.5Pro虚拟机里Ubuntu-20.04LTS
创建工作空间
mkdir -p ~/ros_ws/src
拉取fcu_core_v2和quadrotor_msgs
readme
对于SLAM套件来说,set_goal==true意味着用于发送ego避障目标点,而set_goal==false意味着用于发送普通非避障目标点。
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/Odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.0.201" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="false" type="bool"/> // use_uwb改为false
<param name="set_goal" value="true" type="bool"/> // set_goal改为true
<param name="simple_target" value="true" type="bool"/>
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
在SLAM套件中,定点飞行的坐标系是ego的全局坐标系(以机载电脑ego程序启动那一刻的坐标点为原点,坐标轴为飞机的"前右上"),在定点飞行中,我们的demo程序记住了起飞点,并对起飞点设置了目标偏移量,实现了飞行相对于任意起飞点,飞行一段位移的效果
switch(enable_pos){
case 0:
px1=pos_takeoff_001_x; py1=pos_takeoff_001_y; pz1=0.0f;
px2=pos_takeoff_002_x; py2=pos_takeoff_002_y; pz2=0.0f;
px3=pos_takeoff_003_x; py3=pos_takeoff_003_y; pz3=0.0f;
px4=pos_takeoff_004_x; py4=pos_takeoff_004_y; pz4=0.0f;
px5=pos_takeoff_005_x; py5=pos_takeoff_005_y; pz5=0.0f;
px6=pos_takeoff_006_x; py6=pos_takeoff_006_y; pz6=0.0f;
break;
case 1:
px=1.0;
py=1.0;
pz=1.0;
px1=pos_takeoff_001_x+px; py1=pos_takeoff_001_y+py; pz1=pz;
px2=pos_takeoff_002_x+px; py2=pos_takeoff_002_y+py; pz2=pz;
px3=pos_takeoff_003_x+px; py3=pos_takeoff_003_y+py; pz3=pz;
px4=pos_takeoff_004_x+px; py4=pos_takeoff_004_y+py; pz4=pz;
px5=pos_takeoff_005_x+px; py5=pos_takeoff_005_y+py; pz5=pz;
px6=pos_takeoff_006_x+px; py6=pos_takeoff_006_y+py; pz6=pz;
break;
pos_takeoff_001_x和pos_takeoff_001_y是起飞时候的位置,而px和py都是根据起飞点位置的偏移量,进行相加之后就是绝对位置目标点,所以我们可以看到,最终publish的都是绝对目标点。
将无人机放到地面上启动,如下图所示代表ego已经成功启动
VNC连接机载电脑的远程画面/连接显示屏的画面:

fcu_core_v2的rviz画面:

将无人机提起移动一段距离再放下,如下图所示轨迹正常

使用2D Nav Goal工具发布一个目标点,如下图所示规划正常


catkin_make
source ./devel/setup.bash
接着运行ros程序
roslaunch fcu_core fcu_core.launch
出现下面打印即是收到心跳包,即成功连接。

保证起飞的起点和终点一米以内不可以有障碍物,确保slam定位是否正常,否则不能解锁
飞行测试注意以下事项:
odom_global_001话题,如果z从 0 变为 -0.1 表示完成了slam启动,此时应核对xy是否在0附近,在0附近表示slam工作正常完成上述检查后可以起飞无人机,如果只连接远程电脑ros,没有连接遥控器,那么默认就在电脑控制模式中,如果既连接了机载电脑又连接了遥控器,应注意,ch7位于中间档位时飞机受遥控器控制,ch7档位位于下方时,飞机受电脑控制

我们为了方便用户测试,开发了一套简单指令如下:
"a": 解锁
"d": 上锁
"t": 起飞
"l": 降落
工程运行后,直接在Ubuntu终端中输入:指令字符+回车,即可完成对应指令的下发。
按照上述步骤将无人机放置初始坐标。
无人机解锁
(1)对于开启了SLAM定高的SLAM套件机型
我们使用"global_gnss_001"这个话题作为SLAM是否正常启动的标志位
我们可以使用下面命令查看话题内容从而判断SLAM是否正常启动rostopic echo /fcu_bridge_001/global_gnss_001
还没启动/不正常启动:

正常启动:

(2)对于没有开启SLAM定高的SLAM套件机型(默认为激光定高)
则需要在FanciSwarm无人机启动提示音结束后,将飞机垂直拿起(拿起一次即可),拿到距离地面超过1m后放下。如果激光测距工作正常,解锁才能顺利进行,否则FanciSwarm 会发出“滴滴滴."的报警音,同时,固件强制锁定,不允许起飞。
上述检查完成后,直接在终端输入“a”,接着点击“回车键”(无需输入“a”后立即点击),界面会打印“解锁”字样,如下图所示:

此时,Mcontroller 右侧LED绿灯将点亮,并伴随着启动声音。FanciSwarm™ 无人机完成解锁。
一键起飞
直接在终端输入“t”,接着点击“回车键”(无需输入“t”后立即点击),界面会打印“起飞”字样,如下图所示:

此时,FanciSwarm™ 无人机起飞到一定高度(FanciSwarm App中设置的起飞高度)并悬停。
开始运行目标点
直接在终端输入“1”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点1”字样,如下图所示:

回到初始目标点
直接在终端输入“4”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点4”字样,如下图所示:

一键降落
直接在终端输入“l”,接着点击“回车键”(无需输入“l”后立即点击),界面会打印“降落”字样,如下图所示:

此时,FanciSwarm™ 无人机开始降落,并着陆。
关闭无人机
FanciSwarm™ 无人机锁定后,如果不继续飞行,请及时关闭无人机,并拔掉电池,以避免电量损耗,节省电池电量。
至此,整个SLAM单机飞行过程已完成。
实验平台:windows11宿主机 VMware17.5Pro虚拟机里Ubuntu-20.04LTS
集群飞行需要所有无人机都连接到同一个局域网。
这时候就需要我们对Mlink-esp Wi-Fi模块进行IP配置。
使用Mlink-esp调试器配置
注: Mlink-esp调试器为新增配件,这将大大简化组网配置步骤。FanciSwarm™ 集群套件用户如果手中缺少该配件,可通过之前的购买渠道向幻思创新申请,申请通过后将免费给您寄送。
该方法需要首先将Mlink-esp Wi-Fi模块与Mlink-esp调试器连接,接着将Mlink-esp调试器通过USB接口与电脑连接,操作步骤共分为以下6步:
将Mlink-esp Wi-Fi模块从FanciSwarm™ 无人机上取下,按照下图所示,将Mlink-esp Wi-Fi模块与Mlink-esp调试器连接。
特别注意,请勿接反,接反必烧!

下载并解压该文件,得到sscom5.13.1.exe文件,在电脑上双击安装即可。
打开上一步下载的串口助手软件,将Mlink-esp调试器与电脑连接。
如下图所示,首先点击端口号处的下拉按键,接着选中第一行“......USB-SERIAL CH340” , 最后在输入框输入AT指令,见下步。

在输入框中依次输入以下AT指令并发送:
(1)重启模块。输入以下指令并发送:
AT+RST\r\n
(2)恢复出厂设置。输入以下指令并发送:
AT+RESTORE\r\n
(3)设置为Station模式(默认是AP模式)。输入以下指令并发送:
AT+CWMODE_DEF=1\r\n
(4)设置要连接的路由器名称和密码。输入以下指令并发送:
特别注意,WiFi名称不能为中文名。
(例:AT+CWJAP_DEF="Fancinnov","Mcontroller"\r\n)
AT+CWJAP_DEF="WiFi名称","WiFi密码"\r\n
若串口助手接收界面显示“WIFI CONNECTED”则说明组网模块成功连接路由器,“WIFI GOT IP”代表模块分配到了IP。否则,请再次尝试,直到连接成功。
(5)查询ip地址。输入以下指令并发送:
AT+CIFSR\r\n
上述指令成功发送后,串口助手接收界面会显示路由器分配到该组网模块的ip地址。
(6)设置固定的IP地址。输入以下指令并发送:
AT+CIPSTA_DEF="ip","网关","子网掩码"\r\n
例:上步查询到的ip地址为“192.168.0.2”,则应发送的指令如下:AT+CIPSTA_DEF="192.168.0.100","192.168.0.254","255.255.255.0"(注:均为英文符号)其中”192.168.0.100" 中的100 可为1-254中的任何值,但要确保不和其他连接该路由器的设备IP重复,建议写100以上的值。
(7)查询此时的IP地址是否设置成功。输入以下指令并发送:
AT+CIFSR\r\n
若串口助手返回的IP地址与上步设置的一致,则IP设置成功。请牢记该IP地址。
特别说明
(1)配合App使用说明时,首先打开App,然后手机连接目标路由器Wi-Fi,接着点击首界面右上方数传连接按钮,最后在输入框输入配置的组网模块IP即可。
导入ROS工程
在此之前我们都是使用单架无人机飞行,接下来的集群飞行和单机飞行相似,但也有不同的地方:
创建工作空间
mkdir -p ~/ros_ws/src
拉取fcu_core_v2和quadrotor_msgs
readme
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/vins_estimator/odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.0.201" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="true" type="bool"/>
<param name="set_goal" value="false" type="bool"/>
<param name="simple_target" value="true" type="bool"/>
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
<node pkg="fcu_core" type="fcu_bridge_002" name="fcu_bridge_002" output="screen" >
<remap from="~odometry_002" to="odometry_002"/>
<remap from="~imu_global_002" to="imu_global_002"/>
<remap from="~odom_global_002" to="odom_global_002"/>
<remap from="~path_global_002" to="path_global_002"/>
<remap from="~path_target_002" to="path_target_002"/>
<remap from="~motion_002" to="motion_002"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_002" to="/fcu_mission/mission_002"/>
<param name="DRONE_IP" value="192.168.0.202" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="true" type="bool"/>
<param name="set_goal" value="false" type="bool"/>
<param name="simple_target" value="true" type="bool"/>
</node>
<node pkg="fcu_core" type="fcu_bridge_003" name="fcu_bridge_003" output="screen" >
<remap from="~odometry_003" to="odometry_003"/>
<remap from="~imu_global_003" to="imu_global_003"/>
<remap from="~odom_global_003" to="odom_global_003"/>
<remap from="~path_global_003" to="path_global_003"/>
<remap from="~path_target_003" to="path_target_003"/>
<remap from="~motion_003" to="motion_003"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_003" to="/fcu_mission/mission_003"/>
<param name="DRONE_IP" value="192.168.0.203" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/>
<param name="use_uwb" value="true" type="bool"/>
<param name="set_goal" value="false" type="bool"/>
<param name="simple_target" value="true" type="bool"/>
</node>
摆放基站:详见前文
设置定位参数:使用手机APP连接飞控,第一架飞机标签ID设置为1,对应通信节点001,剩下的飞机依次设置标签ID,wifi模块的ip地址要和launch文件相对应。
放置无人机:重启无人机,使用虚拟机连接无人机,此时终端应该有各个飞机的心跳包。将无人机放置在对应位置上,这里我们使用三架无人机绕圆的例程,那么001号无人机的初始位置就在(3,2)坐标点上,002号无人机的初始位置就在(2.5,2.866),003号无人机的初始位置就在(1.5,2.866)。将无人机放置在基站坐标系下的起点坐标处,机尾面朝用户。
重启无人机并开启基站

catkin_make
source ./devel/setup.bash
接着运行ros程序
roslaunch fcu_core fcu_core.launch
出现下面打印即是收到心跳包

与此同时,系统会自动弹出RViz界面(下图坐标点仅供参考,以实际运行效果为准),如下图所示。

RViz是一个三维可视化平台,可用于无人机位置及飞行轨迹的图形化显示。
图中右侧3D视图区中坐标轴图案代表世界坐标系,其中红轴为X轴,绿轴为Y轴,蓝轴为Z轴。
图中的红点代表无人机在世界坐标系中的位置。

在启动FanciSwarm™ 无人机的集群飞行之前,用户必须通过ROS对每一架无人机进行单独的试飞测试,以验证其运行状态无异常。只有当所有参与集群飞行的无人机均通过此测试,确认运行正常无误后,方可安全地执行多架无人机的集群飞行任务。


我们为了方便用户测试,开发了一套简单指令如下:
"a": 解锁
"d": 上锁
"t": 起飞
"l": 降落
工程运行后,直接在Ubuntu终端中输入:指令字符+回车,即可完成对应指令的下发。
清除RViz界面轨迹:在开始运行新的轨迹之前,必须清除RViz界面上的现有轨迹。请按照以下步骤操作:首先,退出当前的ROS工程;其次,重新运行ROS工程。
开始运行轨迹
按照上述步骤将无人机放置初始坐标。
无人机解锁

上述检查完成后,直接在终端输入“a”,接着点击“回车键”(无需输入“a”后立即点击),界面会打印“解锁”字样,如下图所示:

此时,三架FanciSwarm™右侧LED绿灯将点亮,并伴随着启动声音。FanciSwarm™ 无人机完成解锁。
一键起飞
直接在终端输入“t”,接着点击“回车键”(无需输入“t”后立即点击),界面会打印“起飞”字样,如下图所示:

此时,三架FanciSwarm™ 无人机起飞到一定高度(FanciSwarm App中设置的起飞高度)并悬停。
开始运行轨迹
直接在终端输入“r”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“运行”字样,如下图所示:

此时,三架FanciSwarm™ 无人机开始飞圆轨迹。
停止运行轨迹
直接在终端输入“s”,接着点击“回车键”(无需输入“s”后立即点击),界面会打印“停止”字样,如下图所示:

此时,三架FanciSwarm™ 无人机停止飞圆轨迹,并保持悬停状态。
一键降落
直接在终端输入“l”,接着点击“回车键”(无需输入“l”后立即点击),界面会打印“降落”字样,如下图所示:

此时,三架FanciSwarm™ 无人机开始降落,并着陆。
锁定无人机
等待FanciSwarm™ 无人机着陆后,直接在终端输入“d”,接着点击“回车键”(无需输入“d”后立即点击),界面会打印“锁定”字样,如下图所示:

此时,三架FanciSwarm™ 电调指示灯熄灭,同时Mcontroller 右侧LED绿灯熄灭、LED红灯点亮。三架FanciSwarm™ 无人机完成锁定。
关闭无人机和基站
FanciSwarm™ 无人机锁定后,如果不继续飞行,请及时关闭无人机和基站,并拔掉电池,以避免电量损耗,节省电池电量。
至此,整个UWB集群轨迹飞行过程已完成。

请观看完VINS单机飞行之后再来看
在确保每一架无人机单机的单机飞行没有问题之后,我们就可以进行多机飞行
集群飞行需要所有无人机都连接到同一个局域网。
这时候就需要我们对Mlink-esp Wi-Fi模块进行IP配置。
使用Mlink-esp调试器配置
注: Mlink-esp调试器为新增配件,这将大大简化组网配置步骤。FanciSwarm™ 集群套件用户如果手中缺少该配件,可通过之前的购买渠道向幻思创新申请,申请通过后将免费给您寄送。
该方法需要首先将Mlink-esp Wi-Fi模块与Mlink-esp调试器连接,接着将Mlink-esp调试器通过USB接口与电脑连接,操作步骤共分为以下6步:
将Mlink-esp Wi-Fi模块从FanciSwarm™ 无人机上取下,按照下图所示,将Mlink-esp Wi-Fi模块与Mlink-esp调试器连接。
特别注意,请勿接反,接反必烧!

下载并解压该文件,得到sscom5.13.1.exe文件,在电脑上双击安装即可。
打开上一步下载的串口助手软件,将Mlink-esp调试器与电脑连接。
如下图所示,首先点击端口号处的下拉按键,接着选中第一行“......USB-SERIAL CH340” , 最后在输入框输入AT指令,见下步。

在输入框中依次输入以下AT指令并发送:
(1)重启模块。输入以下指令并发送:
AT+RST\r\n
(2)恢复出厂设置。输入以下指令并发送:
AT+RESTORE\r\n
(3)设置为Station模式(默认是AP模式)。输入以下指令并发送:
AT+CWMODE_DEF=1\r\n
(4)设置要连接的路由器名称和密码。输入以下指令并发送:
特别注意,WiFi名称不能为中文名。
(例:AT+CWJAP_DEF="Fancinnov","Mcontroller"\r\n)
AT+CWJAP_DEF="WiFi名称","WiFi密码"\r\n
若串口助手接收界面显示“WIFI CONNECTED”则说明组网模块成功连接路由器,“WIFI GOT IP”代表模块分配到了IP。否则,请再次尝试,直到连接成功。
(5)查询ip地址。输入以下指令并发送:
AT+CIFSR\r\n
上述指令成功发送后,串口助手接收界面会显示路由器分配到该组网模块的ip地址。
(6)设置固定的IP地址。输入以下指令并发送:
AT+CIPSTA_DEF="ip","网关","子网掩码"\r\n
例:上步查询到的ip地址为“192.168.0.2”,则应发送的指令如下:AT+CIPSTA_DEF="192.168.0.100","192.168.0.254","255.255.255.0"(注:均为英文符号)其中”192.168.0.100" 中的100 可为1-254中的任何值,但要确保不和其他连接该路由器的设备IP重复,建议写100以上的值。
(7)查询此时的IP地址是否设置成功。输入以下指令并发送:
AT+CIFSR\r\n
若串口助手返回的IP地址与上步设置的一致,则IP设置成功。请牢记该IP地址。
导入ROS工程
在此之前我们都是使用单架无人机飞行,接下来的集群飞行和单机飞行相似,但也有不同的地方:
创建工作空间
mkdir -p ~/ros_ws/src
拉取fcu_core_v2和quadrotor_msgs
readme
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/vins_estimator/odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.0.201" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/> // offboard更改为false
<param name="use_uwb" value="false" type="bool"/> // use_uwb更改为false
<param name="set_goal" value="true" type="bool"/> // set_goal更改为true
<param name="simple_target" value="true" type="bool"/> // simple_target更改为true
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
<node pkg="fcu_core" type="fcu_bridge_002" name="fcu_bridge_002" output="screen" >
<remap from="~odometry_002" to="odometry_002"/>
<remap from="~imu_global_002" to="imu_global_002"/>
<remap from="~odom_global_002" to="odom_global_002"/>
<remap from="~path_global_002" to="path_global_002"/>
<remap from="~path_target_002" to="path_target_002"/>
<remap from="~motion_002" to="motion_002"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_002" to="/fcu_mission/mission_002"/>
<param name="DRONE_IP" value="192.168.0.202" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/> // offboard更改为false
<param name="use_uwb" value="false" type="bool"/> // use_uwb更改为false
<param name="set_goal" value="true" type="bool"/> // set_goal更改为true
<param name="simple_target" value="true" type="bool"/> // simple_target更改为true
</node>
将无人机放到地面上启动,启动顺序一定按照ID逆序启动,比如两级飞机就要先启动1号飞机,再启动0号飞机,如下图所示代表ego已经成功启动
VNC连接机载电脑的远程画面:

fcu_core_v2的rviz:

将无人机提起一段高度再放下,如下图所示轨迹正常

catkin_make
source ./devel/setup.bash
接着运行ros程序
roslaunch fcu_core fcu_core.launch
出现下面打印即是收到心跳包,即成功连接。

保证起飞的起点和终点一米以内不可以有障碍物
飞行测试注意以下事项:
完成上述检查后可以起飞无人机,如果只连接远程电脑ros,没有连接遥控器,那么默认就在电脑控制模式中,如果既连接了机载电脑又连接了遥控器,应注意,ch7位于中间档位时飞机受遥控器控制,ch7档位位于下方时,飞机受电脑控制

我们为了方便用户测试,开发了一套简单指令如下:
"a": 解锁
"d": 上锁
"t": 起飞
"l": 降落
工程运行后,直接在Ubuntu终端中输入:指令字符+回车,即可完成对应指令的下发。
按照上述步骤将无人机放置初始坐标。
无人机解锁
上述检查完成后,直接在终端输入“a”,接着点击“回车键”(无需输入“a”后立即点击),界面会打印“解锁”字样,如下图所示:

此时,Mcontroller 右侧LED绿灯将点亮,并伴随着启动声音。FanciSwarm™ 无人机完成解锁。
一键起飞
直接在终端输入“t”,接着点击“回车键”(无需输入“t”后立即点击),界面会打印“起飞”字样,如下图所示:

此时,FanciSwarm™ 无人机起飞到一定高度(FanciSwarm App中设置的起飞高度)并悬停。
开始运行目标点
直接在终端输入“1”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点1”字样,如下图所示:

回到初始目标点
直接在终端输入“4”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点4”字样,如下图所示:

一键降落
直接在终端输入“l”,接着点击“回车键”(无需输入“l”后立即点击),界面会打印“降落”字样,如下图所示:

此时,FanciSwarm™ 无人机开始降落,并着陆。
关闭无人机
FanciSwarm™ 无人机锁定后,如果不继续飞行,请及时关闭无人机,并拔掉电池,以避免电量损耗,节省电池电量。
至此,整个VINS套件的SLAM多机飞行过程已完成。
请观看完SLAM单机飞行之后再来看
在确保每一架无人机单机的单机飞行没有问题之后,我们就可以进行多机飞行
集群飞行需要所有无人机都连接到同一个局域网。
这时候就需要我们对Mlink-esp Wi-Fi模块进行IP配置。
使用Mlink-esp调试器配置
注: Mlink-esp调试器为新增配件,这将大大简化组网配置步骤。FanciSwarm™ 集群套件用户如果手中缺少该配件,可通过之前的购买渠道向幻思创新申请,申请通过后将免费给您寄送。
该方法需要首先将Mlink-esp Wi-Fi模块与Mlink-esp调试器连接,接着将Mlink-esp调试器通过USB接口与电脑连接,操作步骤共分为以下6步:
将Mlink-esp Wi-Fi模块从FanciSwarm™ 无人机上取下,按照下图所示,将Mlink-esp Wi-Fi模块与Mlink-esp调试器连接。
特别注意,请勿接反,接反必烧!

下载并解压该文件,得到sscom5.13.1.exe文件,在电脑上双击安装即可。
打开上一步下载的串口助手软件,将Mlink-esp调试器与电脑连接。
如下图所示,首先点击端口号处的下拉按键,接着选中第一行“......USB-SERIAL CH340” , 最后在输入框输入AT指令,见下步。

在输入框中依次输入以下AT指令并发送:
(1)重启模块。输入以下指令并发送:
AT+RST\r\n
(2)恢复出厂设置。输入以下指令并发送:
AT+RESTORE\r\n
(3)设置为Station模式(默认是AP模式)。输入以下指令并发送:
AT+CWMODE_DEF=1\r\n
(4)设置要连接的路由器名称和密码。输入以下指令并发送:
特别注意,WiFi名称不能为中文名。
(例:AT+CWJAP_DEF="Fancinnov","Mcontroller"\r\n)
AT+CWJAP_DEF="WiFi名称","WiFi密码"\r\n
若串口助手接收界面显示“WIFI CONNECTED”则说明组网模块成功连接路由器,“WIFI GOT IP”代表模块分配到了IP。否则,请再次尝试,直到连接成功。
(5)查询ip地址。输入以下指令并发送:
AT+CIFSR\r\n
上述指令成功发送后,串口助手接收界面会显示路由器分配到该组网模块的ip地址。
(6)设置固定的IP地址。输入以下指令并发送:
AT+CIPSTA_DEF="ip","网关","子网掩码"\r\n
例:上步查询到的ip地址为“192.168.0.2”,则应发送的指令如下:AT+CIPSTA_DEF="192.168.0.100","192.168.0.254","255.255.255.0"(注:均为英文符号)其中”192.168.0.100" 中的100 可为1-254中的任何值,但要确保不和其他连接该路由器的设备IP重复,建议写100以上的值。
(7)查询此时的IP地址是否设置成功。输入以下指令并发送:
AT+CIFSR\r\n
若串口助手返回的IP地址与上步设置的一致,则IP设置成功。请牢记该IP地址。
导入ROS工程
在此之前我们都是使用单架无人机飞行,接下来的集群飞行和单机飞行相似,但也有不同的地方:
创建工作空间
mkdir -p ~/ros_ws/src
拉取fcu_core_v2和quadrotor_msgs
readme
<node pkg="fcu_core" type="fcu_bridge_001" name="fcu_bridge_001" output="screen" >
<remap from="~odometry_001" to="/vins_estimator/odometry"/>
<remap from="~imu_global_001" to="imu_global_001"/>
<remap from="~odom_global_001" to="odom_global_001"/>
<remap from="~path_global_001" to="path_global_001"/>
<remap from="~path_target_001" to="path_target_001"/>
<remap from="~goal_001" to="/move_base_simple/goal"/>
<remap from="~motion_001" to="motion_001"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_001" to="/fcu_mission/mission_001"/>
<param name="DRONE_IP" value="192.168.0.201" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/> // offboard更改为false
<param name="use_uwb" value="false" type="bool"/> // use_uwb更改为false
<param name="set_goal" value="true" type="bool"/> // set_goal更改为true
<param name="simple_target" value="true" type="bool"/> // simple_target更改为true
<param name="odom_init_x" value="0.0"/> <!-- FLU坐标系 -->
<param name="odom_init_y" value="0.0"/>
<param name="odom_init_z" value="0.0"/>
</node>
<node pkg="fcu_core" type="fcu_bridge_002" name="fcu_bridge_002" output="screen" >
<remap from="~odometry_002" to="odometry_002"/>
<remap from="~imu_global_002" to="imu_global_002"/>
<remap from="~odom_global_002" to="odom_global_002"/>
<remap from="~path_global_002" to="path_global_002"/>
<remap from="~path_target_002" to="path_target_002"/>
<remap from="~motion_002" to="motion_002"/>
<remap from="~command" to="/fcu_command/command"/>
<remap from="~mission_002" to="/fcu_mission/mission_002"/>
<param name="DRONE_IP" value="192.168.0.202" type="string"/> // 更改为自己飞机的mlink的ip地址
<param name="USB_PORT" value="/dev/ttyACM0" type="string"/>
<param name="channel" value="1"/>
<param name="offboard" value="false" type="bool"/> // offboard更改为false
<param name="use_uwb" value="false" type="bool"/> // use_uwb更改为false
<param name="set_goal" value="true" type="bool"/> // set_goal更改为true
<param name="simple_target" value="true" type="bool"/> // simple_target更改为true
</node>
将无人机放到地面上启动,启动顺序一定按照ID逆序启动,比如两级飞机就要先启动1号飞机,再启动0号飞机,如下图所示代表ego已经成功启动
VNC连接机载电脑的远程画面:

fcu_core_v2的rviz:

将无人机提起移动一段距离再放下,如下图所示轨迹正常

catkin_make
source ./devel/setup.bash
接着运行ros程序
roslaunch fcu_core fcu_core.launch
出现下面打印即是收到心跳包,即成功连接。

保证起飞的起点和终点一米以内不可以有障碍物
飞行测试注意以下事项:
odom_global_001话题,如果z从 0 变为 -0.1 表示完成了slam启动,此时应核对xy是否在0附近,在0附近表示slam工作正常完成上述检查后可以起飞无人机,如果只连接远程电脑ros,没有连接遥控器,那么默认就在电脑控制模式中,如果既连接了机载电脑又连接了遥控器,应注意,ch7位于中间档位时飞机受遥控器控制,ch7档位位于下方时,飞机受电脑控制

我们为了方便用户测试,开发了一套简单指令如下:
"a": 解锁
"d": 上锁
"t": 起飞
"l": 降落
工程运行后,直接在Ubuntu终端中输入:指令字符+回车,即可完成对应指令的下发。
按照上述步骤将无人机放置初始坐标。
无人机解锁
上述检查完成后,直接在终端输入“a”,接着点击“回车键”(无需输入“a”后立即点击),界面会打印“解锁”字样,如下图所示:

此时,Mcontroller 右侧LED绿灯将点亮,并伴随着启动声音。FanciSwarm™ 无人机完成解锁。
一键起飞
直接在终端输入“t”,接着点击“回车键”(无需输入“t”后立即点击),界面会打印“起飞”字样,如下图所示:

此时,FanciSwarm™ 无人机起飞到一定高度(FanciSwarm App中设置的起飞高度)并悬停。
开始运行目标点
直接在终端输入“1”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点1”字样,如下图所示:

回到初始目标点
直接在终端输入“4”,接着点击“回车键”(无需输入“r”后立即点击),界面会打印“位置点4”字样,如下图所示:

一键降落
直接在终端输入“l”,接着点击“回车键”(无需输入“l”后立即点击),界面会打印“降落”字样,如下图所示:

此时,FanciSwarm™ 无人机开始降落,并着陆。
关闭无人机
FanciSwarm™ 无人机锁定后,如果不继续飞行,请及时关闭无人机,并拔掉电池,以避免电量损耗,节省电池电量。
至此,整个激光雷达套件的SLAM多机飞行过程已完成。