PX4无人机-Gazebo仿真实现移动物体的跟踪

2023-05-16

原文链接PX4无人机-Gazebo仿真实现移动物体的跟踪末尾有演示视频

这个学期我们有一个智能机器人系统的课设,我们组分配到的题目是《仿真环境下使用无人机及相机跟踪移动物体》,本文主要记录完成该课设的步骤以及内容。我们采用的最终方案是PX4飞控+gazebo仿真+mavros通讯控制,实现了在gazebo环境下无人机跟踪一个移动的小车。本文所使用的是Ubuntu18.04 + melodic。

试验环境介绍

首先要搞懂各个部分的关系1,以及各自的作用,才能对控制无人机有个完整的认识,我在一开始做的时候就花了很多时间都没搞懂PX4到底是个无人机还是个什么东西,mavros又是干什么的。下面我简要介绍一下各个部分的关系,让大家有个大致的了解。

PX4飞控

PX4是一个飞控固件,所谓的飞控固件,就是能够向无人机发出控制命令,控制无人机的位姿、飞行速度以及螺旋桨的转速等等。无人机的运动就需要通过飞控固件发出命令来控制。官网的用户手册在这,推荐看英文版本,中文版本有的地方翻译的实在是太烂了,我看的时候感觉像是机翻的,而且与原文的位置都不太一样。

Gazebo仿真

gazebo仿真就不用多说了吧,在学ros基本的操作的时候就应该接触过gazebo。这就是一个能够模拟现实世界的仿真软件,PX4的源代码里就提供了PX4无人机的gazebo模型,通过launch文件直接运行就能得到一个gazebo下的无人机。

MAVROS通讯

mavros里面有个ros,一看就是和ros相关的。我们看官网的介绍MAVROS – MAVLink extendable communication node for ROS with proxy for Ground Control Station’,这句话的意思是,MAVROS是MAVLink为了让ROS代理控制站的扩展交流节点。首先MAVLink是一个无人机通讯协议,也就是说与无人机交流所发出的信号或数据格式都要符合该协议,与HTTP等协议是一个道理。然后控制站其实是PX4为了控制无人机所开发的一个图形化控制站,可以通过GUI的形式来操作无人机,给一般用户很好的体验。而这里是用来代理控制站,也就是说充当控制站来控制无人机。到这里就很明显了,MAVROS就相当于代码版的控制站,若想要通过ros节点开控制无人机的飞行,那就必须通过mavros这个包,在这个包内包含了控制无人机的消息格式等。

综上所述,整个无人机的控制逻辑就是,通过mavros向PX4飞控发送控制命令,PX4再将命令发送到无人机的各个组件,以控制无人机按照用户的逻辑进行运动。而该无人机就在gazebo中,在gazebo中可以看到无人机的运动情况。

实验过程

PX4无人机的安装

1、安装环境依赖

sudo apt install -y ninja-build exiftool python-argparse python-empy python-toml python-numpy python-yaml python-dev python-pip ninja-build protobuf-compiler libeigen3-dev genromfs xmlstarlet libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

2、安装python依赖(melodic默认的是python2)

pip install pandas jinja2 pyserial cerberus pyulog numpy toml pyquaternion  -i https://pypi.tuna.tsinghua.edu.cn/simple

我安装的时候pyulog没装上,其他的都装上了,如果你们也有这个问题,就把其他的装上,pyulog后面还有个步骤会自动安装

3、安装ros与gazebo

这两个的安装就不多说了,如果没安装的话可以在网上先安装好这两个再继续操作。

4、安装mavros

sudo apt install ros-melodic-mavros ros-melodic-mavros-extras
wget https://gitee.com/robin_shaun/XTDrone/raw/master/sitl_config/mavros/install_geographiclib_datasets.sh
sudo chmod a+x ./install_geographiclib_datasets.sh
sudo ./install_geographiclib_datasets.sh #这步需要装一段时间,请耐心等待PX4配置

我在安装的时候出现两个问题。

第一个问题是sudo apt install包的时候一直出现404,未找到这个包,解决方案时sudo apt update,将软件源更新一下,很可能是原始的位置已经过期了,需要更新才能找到最新的位置。

第二个问题是最后一步的.sh文件执行太慢了,我挂那两三个小时都没结束,大概是因为被墙了吧。解决办法如下2

  • /usr/share 下目录新建 GeographicLib 目录。

  • geoids gravity magnetic 三个文件夹拷贝到 /usr/share/GeographicLib 文件夹下面。

上述三个文件夹的链接在这,提取码:dje9

5、PX4安装

这里推荐使用gitee安装,非常感谢XTDrone团队3将代码放在了gitee上,我也使用过github安装,但总是断开连接,无数次重试才完整安装完成。

cd ~
git clone https://gitee.com/robin_shaun/PX4_Firmware
cd PX4_Firmware
git checkout -b xtdrone/dev v1.11.0-beta1
bash ./Tools/setup/ubuntu.sh --no-nuttx --no-sim-tools

将.gitmodules替换为如下内容(在PX4_Firmware文件夹中ctrl+h查看隐藏文件)4

[submodule "mavlink/include/mavlink/v2.0"]
	path = mavlink/include/mavlink/v2.0
	url = https://gitee.com/robin_shaun/c_library_v2.git
	branch = master
[submodule "src/drivers/uavcan/libuavcan"]
	path = src/drivers/uavcan/libuavcan
	url = https://gitee.com/robin_shaun/uavcan.git
	branch = px4
[submodule "Tools/jMAVSim"]
	path = Tools/jMAVSim
	url = https://gitee.com/robin_shaun/jMAVSim.git
	branch = master
[submodule "Tools/sitl_gazebo"]
	path = Tools/sitl_gazebo
	url = https://gitee.com/robin_shaun/sitl_gazebo.git
	branch = master
[submodule "src/lib/matrix"]
	path = src/lib/matrix
	url = https://gitee.com/robin_shaun/Matrix.git
	branch = master
[submodule "src/lib/ecl"]
	path = src/lib/ecl
	url = https://gitee.com/robin_shaun/ecl.git
	branch = master
[submodule "boards/atlflight/cmake_hexagon"]
	path = boards/atlflight/cmake_hexagon
	url = https://gitee.com/robin_shaun/cmake_hexagon.git
	branch = px4
[submodule "src/drivers/gps/devices"]
	path = src/drivers/gps/devices
	url = https://gitee.com/robin_shaun/GpsDrivers.git
	branch = master
[submodule "src/modules/micrortps_bridge/micro-CDR"]
	path = src/modules/micrortps_bridge/micro-CDR
	url = https://gitee.com/robin_shaun/micro-CDR.git
	branch = px4
[submodule "platforms/nuttx/NuttX/nuttx"]
	path = platforms/nuttx/NuttX/nuttx
	url = https://gitee.com/robin_shaun/NuttX.git
	branch = px4_firmware_nuttx-9.1.0+
[submodule "platforms/nuttx/NuttX/apps"]
	path = platforms/nuttx/NuttX/apps
	url = https://gitee.com/robin_shaun/NuttX-apps.git
	branch = px4_firmware_nuttx-9.1.0+
[submodule "platforms/qurt/dspal"]
	path = platforms/qurt/dspal
	url = https://gitee.com/robin_shaun/dspal.git
[submodule "Tools/flightgear_bridge"]
	path = Tools/flightgear_bridge
	url = https://gitee.com/robin_shaun/PX4-FlightGear-Bridge.git
	branch = master 
[submodule "Tools/jsbsim_bridge"]
	path = Tools/jsbsim_bridge
	url = https://gitee.com/robin_shaun/px4-jsbsim-bridge.git
[submodule "src/examples/gyro_fft/CMSIS_5"]
	path = src/examples/gyro_fft/CMSIS_5
	url = https://gitee.com/mirrors/CMSIS_5

再次执行子模块更新指令

git submodule update --init --recursive

编译

make px4_sitl_default gazebo

配置环境变量,注意路径的匹配,你若修改了文件夹名要进行对应的修改,第一个catkin_ws是自己的工作目录。

source ~/catkin_ws/devel/setup.bash
source ~/PX4_Firmware/Tools/setup_gazebo.bash ~/PX4_Firmware/ ~/PX4_Firmware/build/px4_sitl_default
export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:~/PX4_Firmware
export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:~/PX4_Firmware/Tools/sitl_gazebo

下面我们就可以测试PX4无人机了,执行下面的命令

cd ~/PX4_Firmware
roslaunch px4 mavros_posix_sitl.launch

此时会打开gazebo环境,里面地面上有一个无人机。
gazebo无人机仿真
最后一步,测试无人机通讯

rostopic echo /mavros/state

若显示的消息中出现connected: True,则说明MAVROS与SITL通信成功。到此,无人机的配置就结束了。

移动小车的安装

由于实验要求的是实现移动物体的跟踪,因此我使用了一个可控制的小车来代替移动物体5,通过键盘控制节点可以控制小车在gazebo环境中移动。

本试验使用的是TurtleBot3小车,安装和控制移动都非常方便。

安装小车命令6

sudo apt-get install ros-melodic-turtlebot3-*

通过上述命令就安装好了TurtleBot小车,是不是很方便。

由于该小车有三种形态,所以还需要通过环境变量指定一种形态,否则无法运行,我实验中使用的是 burger形态,其他两种形态你们可以自己去修改,我下面都以burger形态小车来讲解。通过环境变量指定小车有一下两种方式,推荐第二种一劳永逸,但若要修改就需要进入.bashrc文件中修改

export TURTLEBOT3_MODEL=burger							# 每次打开新的终端都要执行
echo "export TURTLEBOT3_MODEL=burger" >> ~/.bashrc		#直接写入环境变量,打开终端每次都会自动执行

下面我们运行小车,测试一下小车控制

roslaunch turtlebot3_gazebo turtlebot3_world.launch

然后运行键盘控制节点

rosrun teleop_twist_keyboard teleop_twist_keyboard.py

若没安装该节点需要先安装,执行下面的命令

sudo apt-get install ros-melodic-teleop-twist-keyboard

键盘控制节点控制小车
通过I L J K ,四个键可以控制小车的运动,详细的运动控制自己看控制台的输出。到此,移动小车的安装就已经完成。

PX4无人机添加摄像头以及配置的修改

通过上面的工作,我们完成了无人机和移动物体的配置,若要完成无人机通过相机追踪小车,那相机怎么能少得了呢?默认的PX4无人机是不带摄像头的,我们需要修改配置文件使其带上一个摄像头。

给PX4添加一个深度摄像机7

cd ~/PX4_Firmware/launch
cp mavros_posix_sitl.launch mavros_posix_sitl_cp.launch		# 不修改原始无人机文件,复制一个副本进行修改
gedit mavros_posix_sitl_cp.launch

我后面的修改操作都是基于副本,先复制一份然后操作副本,以保持源代码的结构

做如下改动

添加

<arg name="my_model" default="iris_downward_depth_camera"/>

修改

<arg name="sdf" default="$(find mavlink_sitl_gazebo)/models/$(arg vehicle)/$(arg vehicle).sdf"/> 

<arg name="sdf" default="$(find mavlink_sitl_gazebo)/models/$(arg vehicle)/$(arg my_model).sdf"/> 
注意添加的代码需要在修改的上方

添加摄像头就完成了,但是该摄像头默认的分辨率是48 * 64,非常低,飞高一些就看不清地面的小车了,我们还需要修改一下摄像头的分辨率。

cd ~/PX4_Firmware/Tools/sitl_gazebo/models/depth_camera
gedit depth_camera.sdf

将对应部分修改为

<update_rate>10</update_rate>
...
<image>
<format>R8G8B8</format>
<width>400</width>
<height>400</height>
</image>

width与height对应的就是摄像头的分辨率,update_rate是图像的发布频率,由于把像素改高了,怕系统处理速度慢,因此把频率降低一倍。

下面我们来测试一下摄像头是否配置成功。

cd ~/PX4_Firmware
roslaunch px4 mavros_posix_sitl_cp.launch		# 注意我修改的都是副本,不要运行错了

无人机上的深度相机
然后打开rviz,新开一个终端输入

rviz

先添加一个接收图像的窗口
添加一个图像窗口
将图像的话题选择为/camera/rgb/image_raw,另一个对应的是深度图像,可以自己切换看看效果。

选择对应的话题
这个时候image窗口就会显示无人机摄像机拍摄下来的图像,然后在运行无人机的那个终端输入命令commander takeoff,观察该图像窗口,会随着无人机起飞变化。

无人机起飞测试
由于这个地面平坦,将小车放到这个环境中的话,小车会动不了,所以要将仿真环境改一下,改成原始的空仿真环境。

PX4仿真环境的配置文件是/home/ljw/PX4_Firmware/launch/posix_sitl.launch,与之前一样,我们不对原始文件进行修改,我们修改副本。

cd ~/PX4_Firmware/launch
cp posix_sitl.launch posix_sitl_cp.launch
gedit posix_sitl_cp.launch

<!-- Gazebo sim -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
	<arg name="gui" value="$(arg gui)"/>
	<arg name="world_name" value="$(arg world)"/>
	<arg name="debug" value="$(arg debug)"/>
	<arg name="verbose" value="$(arg verbose)"/>
	<arg name="paused" value="$(arg paused)"/>
	<arg name="respawn_gazebo" value="$(arg respawn_gazebo)"/>
</include>

修改为

<!-- Gazebo sim -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
    <arg name="gui" value="$(arg gui)"/>
    <arg name="world_name" value="$(find turtlebot3_gazebo)/worlds/empty.world"/>
    <arg name="debug" value="$(arg debug)"/>
    <arg name="verbose" value="$(arg verbose)"/>
    <arg name="paused" value="$(arg paused)"/>
    <arg name="respawn_gazebo" value="$(arg respawn_gazebo)"/>
</include>

也就是将原本的gazebo的.world文件换成turtlebot3小车的empty.world文件,这个world里什么都没有。光这样修改还没有生效,因为我们修改的是副本,原始调用这个文件的文件也需要修改,调用这个文件的文件就是之前的mavros_posix_sitl_cp.launch

gedit mavros_posix_sitl_cp.launch

<include file="$(find px4)/launch/posix_sitl.launch">

修改为

<include file="$(find px4)/launch/posix_sitl_cp.launch">

到此,无人机的配置就已经完成,可以再次运行一次无人机,看看环境是否发生变化。

合并无人机和移动小车

在前面的步骤中,我们将无人机和移动小车都准备好了,下面我们就只要将无人机和小车放在同一环境中,就能开始我们的跟踪实验了。

通过阅读turtlebot3的启动文件/opt/ros/melodic/share/turtlebot3_gazebo/turtlebot3_empty_world.launch,可以看到启动小车的代码,我们将该代码添加到启动无人机的文件中,就能同时启动小车和无人机。

cd ~/PX4_Firmware/launch
gedit posix_sitl_cp.launch

添加如下代码

<!-- car model and parameter -->
    <arg name="model" default="$(env TURTLEBOT3_MODEL)" doc="model type [burger, waffle, waffle_pi]"/>
    <arg name="x_pos" default="1.0"/>
    <arg name="y_pos" default="1.0"/>
    <arg name="z_pos" default="0.0"/>
    <param name="robot_description" command="$(find xacro)/xacro --inorder $(find turtlebot3_description)/urdf/turtlebot3_burger.urdf.xacro" />

<!-- gazebo car model -->
<node pkg="gazebo_ros" type="spawn_model" name="spawn_urdf" args="-urdf -model turtlebot3_$(arg model) -x $(arg x_pos) -y $(arg y_pos) -z $(arg z_pos) -param robot_description" />

我们让小车出生在1 1位置,无人机默认出生在0 0 位置。为了让小车更容易被无人机识别,我们将小车全身改成黑色。

roscd turtlebot3_description
cd urdf
sudo gedit turtlebot3_burger.gazebo.xacro

注意选择自己的小车文件进行修改,我这里修改的是burger小车的。

将文件中所有<material>Gazebo/xxx</material>中的xxx全部改成Black。

到此,无人机与移动小车全部配置完毕执行,我们测试一下。

cd ~/PX4_Firmware
roslaunch px4 mavros_posix_sitl_cp.launch

无人机与小车
我们可以看到中间一个无人机和一个黑乎乎的小车,周围的环境已经变成了空的了。然后启动键盘控制节点可以控制小车的运动

rosrun teleop_twist_keyboard teleop_twist_keyboard.py

控制无人机运动

通过阅读PX4官网的一个控制实例8,我们可以大致了解无人机运动的控制流程。我将实例代码复制过来并且添加注释,让大家对无人机控制代码有个理解。

/*
头文件,包括常见的geometry_msgs和mavros通信需要的mavros_msgs,添加上就行
*/
#include <ros/ros.h>
#include <geometry_msgs/PoseStamped.h>
#include <mavros_msgs/CommandBool.h>
#include <mavros_msgs/SetMode.h>
#include <mavros_msgs/State.h>


/*
current_state表示的是无人机的状态,在主函数中订阅了对应的话题,这个状态就会不断更新,表示无人机当前的状态。state_cb就是对应的回调函数,会不断的执行,更新状态。现在先不用管为什么需要这个状态,后面的代码会解释。
*/
mavros_msgs::State current_state;
void state_cb(const mavros_msgs::State::ConstPtr& msg){
    current_state = *msg;
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "offb_node");
    ros::NodeHandle nh;
    
	// 订阅无人机的状态
    ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>
            ("mavros/state", 10, state_cb);
    
    /* 
    发布一个geometry_msgs::PoseStamped的消息,需要知道的是,这个消息是控制无人机的一种方式,将指定坐标包裹进这个消息,然后发布出去,无人机就能自动飞行到指定的坐标地点
    */ 
    ros::Publisher local_pos_pub = nh.advertise<geometry_msgs::PoseStamped>
            ("mavros/setpoint_position/local", 10);
    
    /*
    无人机有一个锁,如果不解锁,无人机虽然接受了命令但是不会动被锁住了,只有解锁了才能对无人机进行控制,下面这个服务调用就是用来请求解锁无人机。上面的current_state就包含了无人机是否解锁的信息,若没解锁就需要解锁,否则就不用,其用途在这就体现出来
    */
    ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>
            ("mavros/cmd/arming");
    
    /*
    无人机飞行有很多种模式,如果需要用代码操控无人机,我们就需要切换到OFFBOARD模式。上面的current_state也包含了无人机当前的飞行模式,若不是OFFBOARD就需要切换到该模式。下面的这个服务调用就是用来请求切换无人机飞行模式。
    */
    ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>
            ("mavros/set_mode");

    //the setpoint publishing rate MUST be faster than 2Hz
    /*
    在OFFBOARD模式下,需要以>=2HZ的频率向无人机发送消息,否则无人机会回退到OFFBOARD模式之前所在的模式,因此这里的rate需要设置的比2大就行
    */
    ros::Rate rate(20.0);
	
    // wait for FCU connection
    /*
    等待无人机与控制站连接(代码的方式就是代理),只有连接了才能发送消息
    */
    while(ros::ok() && !current_state.connected){
        ros::spinOnce();
        rate.sleep();
    }

    /*
    pose就是坐标,本实例是让无人机在2m处悬空,因此z设置为2,z表示的就是高度
    */
    geometry_msgs::PoseStamped pose;
    pose.pose.position.x = 0;
    pose.pose.position.y = 0;
    pose.pose.position.z = 2;

    // 下面这个感觉有没有都无所谓
    //send a few setpoints before starting
    for(int i = 100; ros::ok() && i > 0; --i){
        local_pos_pub.publish(pose);
        ros::spinOnce();
        rate.sleep();
    }

    // 请求的切换模式的消息,设置为OFFBOARD
    mavros_msgs::SetMode offb_set_mode;
    offb_set_mode.request.custom_mode = "OFFBOARD";

    // 请求解锁的消息,arm表示解锁,设置为true,disarm是上锁
    mavros_msgs::CommandBool arm_cmd;
    arm_cmd.request.value = true;

    // 记录上次请求的时间
    ros::Time last_request = ros::Time::now();

    while(ros::ok()){
        // 如果无人机模式不是OFFBOARD并且离上次操作时间大于5秒就发送请求切换,这里的5s是为了演示清楚设置的延时
        if( current_state.mode != "OFFBOARD" &&
            (ros::Time::now() - last_request > ros::Duration(5.0))){
            if( set_mode_client.call(offb_set_mode) &&
                offb_set_mode.response.mode_sent){
                ROS_INFO("Offboard enabled");
            }
            // 更新本次请求的时间
            last_request = ros::Time::now();
        } else {
            // 如果当前未解锁且与请求时间大于5s,就发送请求解锁
            if( !current_state.armed &&
                (ros::Time::now() - last_request > ros::Duration(5.0))){
                if( arming_client.call(arm_cmd) &&
                    arm_cmd.response.success){
                    ROS_INFO("Vehicle armed");
                }
                last_request = ros::Time::now();
            }
        }
		
        // 不断发送位置消息,但是只有解锁后才能真正开始运动,如果不发送就会退出OFFBOARD模式,因为请求发送速度要>=2HZ
        local_pos_pub.publish(pose);

        ros::spinOnce();
        rate.sleep();
    }

    return 0;
}

通过官网的代码就能知道控制无人机飞行的一般流程,只要将核心代码逻辑修改一下,就能实现无人机对小车的跟踪了。可以先将上面的代码运行一次,观察一下无人机的运动。

总体流程为:

1、通过launch文件启动无人机的gazebo环境

2、新建一个工作空间,包含的依赖有roscpp、std_msgs、geometry_msgs、mavros、cv_bridge、image_transport、sensor_msgs

其中后面几个是为了后续图像处理用的,这里一并导入

3、创建一个cpp文件,将上述代码复制进去

4、修改CMakeLists

5、rosrun该节点

在无人机解锁后就能看到无人机飞到了空中2m处

控制无人机跟踪运动小车

通过上述无人机悬空的代码我们已经了解了控制无人机飞行的代码流程:首先定义好需要发送与接收的话题消息,并且定义好各个请求,然后将无人机切换到OFFBOARD模式,接着解锁无人机,同时需要一直给无人机发送运动控制的消息,包括位置控制或速度控制,并且频率要大于2HZ。通过以上流程框架,我们就能设计一个自动跟踪移动小车的代码。

通过网上查阅资料看到,控制无人机运动不仅可以通过发送位置消息9,还可以像控制小乌龟一样,发送速度消息10,这就为跟踪小车的提供了方案。我设计的跟踪思路:

之前案例订阅和发布的话题就不用多说了,全部都要订阅,然后需要额外订阅的是无人机发送的图像,之前设置了10HZ,也就是一秒钟无人机会发送十帧图像过来,在该订阅的回调函数内,对图像进行处理,检查图像内是否有小车,如果有的话,就通过比较小车像素点的位置和图像中心像素点的位置,来判断方位,并相应的设置速度。图像处理回调函数如下:

#include <ros/ros.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/Twist.h>
#include <mavros_msgs/CommandBool.h>
#include <mavros_msgs/SetMode.h>
#include <mavros_msgs/State.h>
#include <mavros_msgs/Altitude.h>


#include "sensor_msgs/Image.h"
#include "cv_bridge/cv_bridge.h"

#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
// 一些全局变量

// 悬空高度(追踪小车的高度)
const double h = 4;
// 调整高度的速度(上升或下降)
const double hv = 0.1;

// 控制无人机的速度
geometry_msgs::Twist velocity;

// 无人机当前的高度
double curH;

// 无人机是否已经稳定在空中的标志
bool start = false;

void doImg(const sensor_msgs::Image::ConstPtr &msg) {
    
    if(!start) return;
    
    // 将无人机发布的图像先转化为灰度图,再进行二值化,就能得到黑白图像,若小车出现,那么在图像内有黑色的像素,否则图像全是白色像素,这也是我将小车改成黑色的原因,若改成其它颜色就不好进行分离
    cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
	cv::Mat img = cv_ptr -> image;
    cv::Mat gray, bin;
    cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
    cv::threshold(gray, bin, 127, 255, cv::THRESH_BINARY);
    
    // 获得图像的宽和高
    static int row = bin.rows, col = bin.cols;
    // 图像中心点的位置,我们假设图像中心点的位置就是无人机的位置,这样就能很方便的发布速度来控制无人机
    static double centX = row / 2, centY = col / 2;
    
    // x y用来记录小车在该帧图像出现的位置
    int x, y;
    // 是否找到小车的标记
    bool findCar = false;
    
    // 遍历图像,若图像内有黑色像素则代表发现了小车,记录下此时的x y位置
    for(int i = 0; i < row; i++) {
        for(int j = 0; j < col; j++) {
            uchar point = bin.at<uchar>(i, j);
            if(point == 0) {
                findCar = true;
                x = i, y = j;
                break;
            }
        }
        if(findCar) break;
    }
    
    // 记录最后一次找到小车的时间
    static ros::Time last_find_time = ros::Time::now();
    if(findCar) {
        ROS_INFO("找到目标位置, x = %d, y = %d", x, y);
        // 将小车(所在像素点)相对无人机(图像中心像素点)的位置归一化到0 ~ 1之间,并以此作为控制无人机的速度,小车离无人机越远,则无人机的速度越大,否则无人机的速度越小
        double vx = abs(centX - x) / centX;
        double vy = abs(centY - y) / centY;
        
        // 经测试,无人机发送的图像的垂直方向是无人机的x方向,图像的水平方向是无人机的y方向
        // 因此,若小车(像素位置)在无人机(像素位置)上方,需要发送一个正的x方向速度,否则要发送一个负方向的速度
        if(x < centX) velocity.linear.x = vx;
        else velocity.linear.x = -vx;
        
		// y方向同理
        if(y < centY) velocity.linear.y = vy;
        else velocity.linear.y = -vy;

        // 若不给无人机发送z方向的速度,无人机会时上时下,因此通过下面这个代码控制无人机高度,若低于一定高度,就发布z方向的速度,若高于某个高度,就发送一个-z方向的速度,让无人机下降
        if(curH < h - 0.5) velocity.linear.z = hv;
        else if(curH < h + 0.5) velocity.linear.z = 0;
        else velocity.linear.z = (curH - h) * -hv;
        ROS_INFO("发布速度 x : %f, y : %f, z : %f", velocity.linear.x, velocity.linear.y, velocity.linear.z);
        // 记录无人机最后一次发现小车的时间,后面有用
        last_find_time = ros::Time::now();
    } else {
        ros::Time now = ros::Time::now();
        velocity.linear.x = 0;
        velocity.linear.y = 0;
        // 无人机丢失目标五秒内,什么都不操作
        if(now - last_find_time < ros::Duration(5)) {
            ROS_INFO("没有找到目标...");
        } else {
            // 无人机丢失目标五秒后,开始向上飞行(扩大视野)来搜寻小车,搜寻的最高高度是无人机跟踪小车高度的两倍,这也是前面代码中控制无人机下降的原因,若无人机在升空过程中发现目标小车,会立刻下降跟踪小车
            if(curH < 2 * h - 1) {
                ROS_INFO("上升高度寻找,当前高度为:%.2f", curH);
                velocity.linear.z = hv;
            } else {
                if(curH > 2 * h + 1) velocity.linear.z = -hv;
                else velocity.linear.z = 0;
                ROS_INFO("目标丢失。。。");
            }
        }
    }
}

上面的回调函数完成了对无人机追踪小车速度的控制,其运行逻辑是:若无人机发现了小车,就通过小车相对无人机的方位,发送x y方向的速度,否则如果丢失的话,在五秒内不进行任何操作,超过五秒后,开始提升无人机的高度扩大视野来寻找小车,最大高度是跟踪小车高度的两倍,一旦发现小车立刻下降并跟踪小车。

上面只是一个回调函数,还要主函数来控制无人机。

void do_H(const mavros_msgs::Altitude::ConstPtr& msg) {
    curH = msg->local;
}

mavros_msgs::State current_state;
void state_cb(const mavros_msgs::State::ConstPtr& msg){
    current_state = *msg;
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "offb_node");
    ros::NodeHandle nh;
    setlocale(LC_ALL, "");

    ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>
            ("mavros/state", 10, state_cb);
    ros::Publisher local_pos_pub = nh.advertise<geometry_msgs::PoseStamped>
            ("mavros/setpoint_position/local", 10);
    ros::Publisher local_vec_pub = nh.advertise<geometry_msgs::Twist>
            ("/mavros/setpoint_velocity/cmd_vel_unstamped", 10);
    ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>
            ("mavros/cmd/arming");
    ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>
            ("mavros/set_mode");
    ros::Subscriber img_sub = nh.subscribe<sensor_msgs::Image>("/camera/rgb/image_raw", 10, doImg);

    ros::Subscriber height_sub = nh.subscribe<mavros_msgs::Altitude>
            ("/mavros/altitude", 10, do_H);

    //the setpoint publishing rate MUST be faster than 2Hz
    ros::Rate rate(20.0);

    // wait for FCU connection
    while(ros::ok() && !current_state.connected){
        ros::spinOnce();
        rate.sleep();
    }

    geometry_msgs::PoseStamped pose;
    pose.pose.position.x = 0;
    pose.pose.position.y = 0;
    pose.pose.position.z = h;

    velocity.linear.x = 0;
    velocity.linear.y = 0;
    velocity.linear.z = 0;

    mavros_msgs::SetMode offb_set_mode;
    offb_set_mode.request.custom_mode = "OFFBOARD";

    mavros_msgs::CommandBool arm_cmd;
    arm_cmd.request.value = true;

    ros::Time last_request = ros::Time::now();

    bool takeoff = false;

    while(ros::ok()){
        if(!takeoff) {
            if( current_state.mode != "OFFBOARD" &&
                (ros::Time::now() - last_request > ros::Duration(2.0))){
                if( set_mode_client.call(offb_set_mode) &&
                    offb_set_mode.response.mode_sent){
                    ROS_INFO("Offboard enabled");
                }
                last_request = ros::Time::now();
            }

            if( !current_state.armed &&
                (ros::Time::now() - last_request > ros::Duration(2.0))){
                if( arming_client.call(arm_cmd) &&
                    arm_cmd.response.success){
                    ROS_INFO("Vehicle armed");
                }
                last_request = ros::Time::now();
            }

            if( current_state.armed && 
                (ros::Time::now() - last_request > ros::Duration(5.0))) {
                    takeoff = true;
                    ROS_INFO("Vehicle stabled");
                    start = true;
                    ROS_INFO("开始追踪...");
                    last_request = ros::Time::now();
                }

            local_pos_pub.publish(pose);

        } else {
            local_vec_pub.publish(velocity);
        }

        ros::spinOnce();
        rate.sleep();
    }

    return 0;
}

上述代码的逻辑比较好理解,我就不加注释,流程是:先通过位置控制无人机,让无人机在高度h处稳定悬空,当无人机稳定悬停在空中时,将控制无人机的方式改为速度控制,也就是通过发送速度来控制无人机。

为了方便讲解,将代码分成了两部分贴,将两部分合起来放在一个cpp里,就能正常执行。代码中无人机是否稳定悬空是通过一个时间延迟实现的,假定无人机能在五秒内悬停在指定点,五秒前都是通过位置控制无人机,五秒后就一直通过速度控制无人机。

到此,无人机跟踪运动小车的整个实验就完成了。

参考


  1. APM,PX4,GAZEBO,MAVLINK,MAVROS,ROS之间的关系以及科研设备选型 ↩︎

  2. 执行 install_geographiclib_datasets.sh 错误! ↩︎

  3. XTDrone仿真平台基础配置 ↩︎

  4. Ubuntu18.04下基于ROS和PX4的无人机仿真平台的基础配置搭建 ↩︎

  5. 在gazebo中导入移动小车+二维码 ↩︎

  6. ROS-melodic学习turtlebot3笔记<一功能包导入与测试> ↩︎

  7. PX4+gazebo仿真给无人机添加摄像头 ↩︎

  8. MAVROS Offboard control example ↩︎

  9. 使用ROS节点控制PX4——位置控制 ↩︎

  10. PX4学习笔记3: 速度控制 ↩︎

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PX4无人机-Gazebo仿真实现移动物体的跟踪 的相关文章

  • PX4模块设计之四:MAVLink简介

    PX4模块设计之四 xff1a MAVLink简介 1 MAVLink PX4 应用简介2 MAVLink v2 0新特性3 MAVLink协议版本4 MAVLink通信协议帧4 1 MAVLink v1 0 帧格式4 2 MAVLink
  • PX4模块设计之六:PX4-Fast RTPS(DDS)简介

    64 TOC PX4模块设计之六 xff1a PX4 Fast RTPS DDS 简介 基于PX4开源软件框架简明简介的框架设计 xff0c 逐步分析内部模块功能设计 PX4 Fast RTPS DDS 具有实时发布 订阅uORB消息接口
  • PX4模块设计之十七:ModuleBase模块

    PX4模块设计之十七 xff1a ModuleBase模块 1 ModuleBase模块介绍2 ModuleBase类介绍3 ModuleBase类功能介绍3 1 模块入口3 2 模块启动3 3 模块停止3 4 状态查询3 5 任务回调3
  • CMU,[gazebo]process has died.报错解决.VLP-16.velodyne

    解决方法 xff1a sudo apt get install ros noetic velodyne 报错 xff1a roslaunch vehicle simulator system garage launch 一输入就报各种各样的
  • ROS+Gazebo----Unable to find uri[model:// ]

    基于ROS 43 Gazebo环境 xff0c 用roslaunch把sdf模型加载到gazebo仿真世界 目录结构如下 输入命令roslaunch my simulation my world launch 报错 xff1a 1 不接入网
  • ROS/Gazebo练习2: stack light simulation

    light description urdf light xacro span class token prolog lt xml version 61 34 1 0 34 gt span span class token comment
  • PX4-4-任务调度

    PX4所有的功能都封装在独立的模块中 xff0c uORB是任务间数据交互和同步的工具 xff0c 而管理和调度每个任务 xff0c PX4也提供了一套很好的机制 xff0c 这一篇我们分享PX4的任务调度机制 我们以PX4 1 11 3版
  • gazebo 中创建含有二维码的墙的模型

    1 新建空白墙的模型 在gazebo中添加一个Edit gt Building Editor xff0c 生成sdf文件 xff0c 放在 gazebo models文件夹下 如图Untitled1 编辑model sdf文件 xff0c
  • Gazebo添加动态障碍物插件及插件配置过程

    1 运行空白环境 43 添加动态障碍物 参考https blog csdn net zyh821351004 article details 128203687 actor标签范围内的模型配置 人会在多点间运动 span class tok
  • PX4软件在环仿真注意点

    注 xff1a 最新内容参考PX4 user guide 点击此处 PX4下载指定版本代码和刷固件的三种方式 点击此处 PX4sitl固件编译方法 点击此处 PX4开发指南 点击此处 PX4无人机仿真 Gazebo 点击此处 px4仿真 知
  • 解决ROS中运行gazebo出现process has died的情况

    项目场景 xff1a gazebo 1 process has died pid 397 exit code 255 cmd opt ros melodic lib gazebo ros gzserver e ode worlds empt
  • PX4模块设计之二十七:LandDetector模块

    PX4模块设计之二十七 xff1a LandDetector模块 1 LandDetector模块简介2 模块入口函数2 1 主入口land detector main2 2 自定义子命令custom command 3 LandDetec
  • Gazebo 加载xacro文件和URDF文件的方式

    版权声明 xff1a lt 本博客所有内容均为自己在学习工作中的总结 摘录等 转载请注明出处 如有侵权请联系删除 gt https blog csdn net xuehuafeiwu123 article details 71108959
  • Gazebo Plugins教程

    Overview of Gazebo plugins Gazebo插件通过标准C 43 43 类直接控制Gazebo模型 xff0c 其具有以下优点 可以控制gazebo中几乎各个方面 xff1b 容易共享 xff1b 能够在运行的系统中插
  • PX4通过参数脚本给飞控导入参数

    PX4通过参数脚本给飞控导入参数 先找一架正常能飞的无人机连接地面站 在参数页面右上角点击工具 gt 保存到文件 保存的时候文件名注明参数的相关信息 然后将需要加载参数的无人机连接至地面站 xff0c 注意需要加载参数的无人机必须和保存的参
  • 无人机PX4使用动捕系统mocap的位置实现控制+MAVROS

    动捕系统Optitrack xff0c 有很高的定位精度 xff0c 能够给无人机提供比较精确的位置信息 xff0c 因此如果实验室有条件 xff0c 都可以买一套动捕系统 动捕系统的原理 xff1a 光学式动作捕捉依靠一整套精密而复杂的光
  • Blender一步一步用灰度图生成3D模型用于Gazebo/gzweb

    我们经常能在SDF格式文件中见到 dae stl模型文件 比如如下代码
  • 大神浅谈无人机飞控软件设计 系统性总结

    写在前面 深感自己对飞控软件 算法的知识点过于杂乱 很久没有进行系统的总结了 因此决定写几篇文章记录一些飞控开发过程的知识点 主要是针对一些软件 算法部分进行讨论 如内容有错误 欢迎指出 1 飞控软件的基本模块 无人机能够飞行主要是依靠传感
  • 【Gazebo安装教程】2023年最新安装全流程详解!

    安装 实验环境 ubuntu22 04LTS 安装 Gazebo 首先我们需要安装必须的工具 sudo apt get update sudo apt get install lsb release wget gnupg 之后修改源 并 u
  • Exception sending a multicast message:Network is unreachable故障

    出现这个故障就是没有连接到网络 如果虚拟机没有连接到本机 那么就会出现这个情况 当虚拟机连接到本机就会自动消失 同时如果是用电脑直接安装Ubuntu运行也会出现这个情况 应该是要连接到一个路由器里面这个情况才会消失

随机推荐

  • 个人面试分享(小厂)

    个人面试分享 xff08 小厂 xff09 今天终于提起键盘了 xdm xff0c 假期过得如何 xff0c 祝大家节日快乐 上个月利用一些请假面试了几家公司 xff0c 然后想跟大家分享下一些面试题 xff08 面试的公司规模大概是50
  • 在HAL库中的使用printf()函数和sprintf()函数

    在HAL库中的使用printf xff08 xff09 函数和sprintf xff08 xff09 函数 1 printf xff08 xff09 2 sprintf xff08 xff09 xff1a 运行环境为 xff1a HAL库
  • 内部函数和外部函数

    内部函数 xff08 静态函数 xff09 xff1a 只能被本文件中其它函数调用 定义内部函数时 xff0c 在函数名核函数类型的前面加上static static int fun int a int b fun是内部函数 xff0c 不
  • C语言文件操作函数fwrite导致写入文件的内容乱码的问题解决方案

    fwrite 函数用来向文件中写入块数据 xff0c 它的原型为 xff1a size t fwrite void ptr size t size size t count FILE fp 参数说明 xff1a ptr 为内存区块的指针 x
  • 通信协议(三)——IIC协议

    1 基础知识 1 1 概念 IIC通讯协议 Inter xff0d Integrated Circuit xff0c 也常被写作I2C 是由 Philips 公司开发的一种简单 双向二线制同步串行总线 xff0c 只需要两根线即可在连接于总
  • linux经典面试题----开机启动流程

    linux开机启动流程 1 xff0c power on 开机 2 xff0c POST开机自检 由主板上的BIOS程序来完成 3 xff0c BIOS对硬件进行检测 BIOS xff1a 基本输入输出系统 xff0c 是个人电脑启动时加载
  • 高可用和负载均衡学习笔记

    负载均衡 LB xff1a load balancer 化整为零 高可用 xff08 HA xff09 xff1a High Avilibility 互相备份 xff0c 互相替换 防止单点故障 好处 xff1a 防止单点故障 xff0c
  • 浅谈列表和元组的区别

    常见面试题 列表 xff08 list xff09 和元组 xff08 tuple xff09 的区别和使用场景 一 相同点 列表和元组都是序列化类型的容器对象 xff0c 可以存放任何类型的数据 xff0c 支持切片和迭代 二 不同点 列
  • 详解三次握手

    简述三次握手的过程 应用场景 xff1a 当客户端向服务器端发送数据之前 xff0c 需要建立一个TCP连接 第一次握手 xff1a 客户端向服务器端发送一个SYN请求包 xff08 序列号syn为x xff09 并进入SYN SENT状态
  • win10 切换Fn热键

    切换Fn热键 新买的联想小新 xff0c 到手发现F1 F12的使用需要热键Fn的配合 xff0c 这对一个开发人员来说 xff0c 是件麻烦事儿 xff0c 解决方法如下 xff1a 重启电脑 xff0c Fn 43 F2 不停戳 进入B
  • LAMP和LNMP详解,面试必备

    LAMP简介 LAMP 是指一个集成开发环境 一般用来建立web应用平台 L Linux A Apache M Mysql P PHP或指Perl或指Python 1 Linux xff1a 是一个性能稳定的多用户网络操作系统 xff0c
  • 系统运行缓慢该怎么排查

    对于系统运行缓慢问题 xff0c 要分情况讨论 xff1a 如果该问题导致线上系统不可用 出现这种情况可能的原因主要有两种 xff1a 代码中某个位置读取数据量较大 xff0c 导致系统内存耗尽 xff0c 从而导致 Full GC 次数过
  • 编译安装Nginx步骤详解

    编译安装Nginx步骤详解 1 xff0c 去Nginx官方网站下载源码包并解压 curl O 或wget 跟下载链接 tar xf 解压 2 xff0c 进入nginx解压后的目录执行 configure configure prefix
  • python2和python3的差异,超详细总结

    python2与python3的区别 1 xff0c 市场差异 python2 xff1a 官方通知python2 2020开始不再维护 xff0c 但企业很多代码都是python2 python2有很大的用户基群故会出现历史遗留问题 xf
  • mysql学习笔记--主从复制

    主从复制简介 首先需要两台机器 xff0c 并且主从复制的前提是需要先做数据同步 xff0c 先在我的master机器上用mysqldump 将所有的数据备份 xff0c 然后scp传输到我的slave机器上 xff0c 然后在slave机
  • mysql学习笔记---sql语句

    基本select查询 1 xff0c 设置系统变量 64 64 sql mode xff0c 有一些限制规则 xff0c 日期不能为0 xff0c 除数不能为0 xff0c 自增不能从0开始 xff0c 授权用户密码不能为空 64 64 s
  • minikube 学习笔记 -- deployment 详解

    minikube 是什么 minikube 可以理解为一个可以运行在本地的 xff0c 单节点的 Kubernetes xff0c 我们可以通过在里面创建 Pods 来创建对应的服务 kubernetes 是什么 Kubernetes 是容
  • minikube 学习笔记 -- service && HPA 详解

    按照下面的文档完成 service 实验 https www cnblogs com backups p k8s 1 html service Service 的作用 xff1a 提供服务的自动发现和负载均衡 因为 Pod 随时会被销毁和重
  • ROS :process has died

    项目场景 xff1a ROS编译报错 问题描述 xff1a 有的时候自己在ROS的框架下写代码会遇到如下的问题 xff1a 以前遇到这种问题基本上心里就觉得凉了 xff0c 因为这种编译之后的运行报错不好定位位置 xff0c 所以之前遇到这
  • PX4无人机-Gazebo仿真实现移动物体的跟踪

    原文链接PX4无人机 Gazebo仿真实现移动物体的跟踪末尾有演示视频 这个学期我们有一个智能机器人系统的课设 xff0c 我们组分配到的题目是 仿真环境下使用无人机及相机跟踪移动物体 xff0c 本文主要记录完成该课设的步骤以及内容 我们