基于单目视觉的四旋翼定点降落——位姿估算与控制算法(五)

2023-05-16

    之前,我们已经能够成功将地标识别出来,那么如何引导无人机向地标飞行呢?我们可以分为两部分:一、计算无人机与地标的相对位置;二、根据相对位置控制无人机飞行。这部分和之前几章不太一样,理论推导较多,请大家耐心看一看。若有不对的地方,也请指正~

1 相对位置估计

    这里要提一下,目前很主流的一种估计方法使PnP,也是我最开始尝试的一种方法。但是,PnP是基于点的估计方法,我们的地标中特征点(角点)不多,而且用来估计的点噪声比较大,所以效果不太好。后来,我决定用面积来估计相对位置,以提高估计的鲁棒性和精度。整个相对位置估计可以分为相机坐标系下的估计和绝对坐标系下的估计,我们先来做相机坐标系下的估计。

1.1 相机坐标系下的估计

    首先,我们来估计Zc,估计的思路是,图像中地标面积(看成矩形)与地标实际面积的比值与高度成正比例关系,如果我们能够知道这个比例,那么就能求得无人机高度。在理论推导这个比例之前,我们先要讲一讲摄像头的内参,直接上公式吧。


其中,K即为摄像头的内参(张友正标定法可得),第一个式子表示了图像平面坐标与相机平面坐标之间的关系,是我们后面进行推导的重要依据。如果有博友的不了解的话,建议先看一下高博的《视觉SLAM十四讲》。言归正传,为了后面的推导,我们先做三个假设。

              (1)地标实际面积已知(的确,我们量一下就知道了)。

              (2)相机内参中fx与fy相等(绝大多数实际情况下他们的确是十分接近的)。

              (3)相机正对地标,即机体无滚转和俯仰(后来我会细讲,为了推导方便,暂时让它存在)。

推到直接上图了,公式打起来比较烦,请见谅。

之前由于假设(3)的限制,我们的推导只能在相机平面与地标平面平行的时候成立,这是不科学的,那么如何去掉这个假设呢?其实很简单,做一个仿射变换就可以了,公式如下:

其中,theta和phi分别为机体的俯仰角和滚转角。经过仿射变换的面积依旧是与Zc成正相关的,这样我们就成功地拿掉了假设(3)。然后,只要将求得的Zc直接代入相机模型公式就可以算出来Xc,Yc。

1.2 绝对坐标系下的估计

    要将估计结果真正用于无人机导航,就要将相机坐标系下的估计结果转换到绝对坐标系。这边提一句,我把绝对坐标系的原点选为地标中心,方向是东北天(与mavros的导航坐标系一致)。如上文所说,如果相机平面与地标平面平行(即无俯仰滚转时),我们很容易就能实现坐标系的转换。但是,当出现滚转与俯仰的情况,我们需要做一些几何处理。以下举一种例子。

这种推导是我自己推的,所以当中有不严谨的地方,还请各位博友指正。还有一种Oc与Ow在异侧的情况,我就不展开啦。好吧,将了这么多上代码吧。

// td是一个结构体,用来储存之前识别的信息,比如中心的坐标,地标的面积
//current_att 表示了机体姿态
void EstimatePose( flag_detection_t *td, Euler_t current_att)
{
	double detect_begin = clock();
	double gama = acos(cos(current_att.pitch) * cos(current_att.roll));
	float delta_l, L, H, X, Y;
	bool direction;

	if(gama / PI * 180 < 1)
		gama = 0;

	td->img_area = ComputeArea(td->corners[td->corners.size()-1]);
	td->Pc.z = sqrt(td->square_area * K.at<float>(0,0) * K.at<float>(1,1) / (td->img_area * cos(current_att.pitch) * cos(current_att.roll)));
	td->Pc.x = (td->center.x - K.at<float>(0,2)) * td->Pc.z / K.at<float>(0,0);
	td->Pc.y = (td->center.y - K.at<float>(1,2)) * td->Pc.z / K.at<float>(1,1);
	Oc.x = K.at<float>(0,2) + K.at<float>(0,0) * tan(current_att.roll);
	Oc.y = K.at<float>(1,2) - K.at<float>(1,1) * tan(current_att.pitch);
	delta_l = sqrt(td->Pc.x*td->Pc.x + td->Pc.y*td->Pc.y) * tan(fabs(gama));
	if( ((Oc.x-K.at<float>(0,2)) * (td->center.x-K.at<float>(0,2)) + (Oc.y-K.at<float>(1,2)) * (td->center.y-K.at<float>(1,2))) > 0 )
	{
		L = td->Pc.z - delta_l;
		direction = 1;
	}
	else
	{
		L = td->Pc.z + delta_l;
		direction = 0;
	}
	H = L * cos(gama);
	if(direction == 0)
	{
		Y = H * tan( current_att.pitch + atan((td->center.y - K.at<float>(1,2)) /  K.at<float>(1,1)) );
		X = H * tan( current_att.roll - atan((td->center.x - K.at<float>(0,2)) /  K.at<float>(0,0)) );
	}
	else
	{
		Y = H * tan( current_att.pitch + atan((td->center.y - K.at<float>(1,2)) /  K.at<float>(1,1)) );
		X = H * tan( current_att.roll - atan((td->center.x - K.at<float>(0,2)) /  K.at<float>(0,0)) );
	}

	td->Pw.x = X;
	td->Pw.y = Y;
	td->Pw.z = H;

	double duration = ( clock() - detect_begin ) / CLOCKS_PER_SEC * 1000;
	cout << "Estimate Time is " << duration << endl;
}

2 控制算法

    控制算法这种东西的话,是仁者见仁智者见智的,每个人都有不同的控制思路。我是用了速度控制,视觉伺服的方式,不断向目标点飞行,最后降落。我就直接贴代码吧。

 while(ros::ok()){
        ros::Time t0 = ros::Time::now();
        ros::spinOnce();

        att_stable = Att_stable(current_position);
        arrive_flag = ArriveFlag(td->Pw);

        //控制策略
        //td->result为1表示识别到地标
            if(td->result == 0)
            if(td->prev_result == 0)
                state = HOVER;
            else if( arrive_flag == 1 )
                    state = LAND;
                 else
                    state = FLY;
        else if( arrive_flag == 1 )
                    state = LAND;
                 else
                    state = FLY;
            

        //控制具体执行
        Point3d velocity;
        switch(state)
        {
            case FLY:
                    //计算velocity,其实就是一个PID
                    velocity = ComputeLinearVelocity(td->Pw);
                    twist.twist.linear.x = velocity.x;
                    twist.twist.linear.y = velocity.y;
                    twist.twist.linear.z = 0;
                    twist.twist.angular.x = 0;
                    twist.twist.angular.y = 0;
                    twist.twist.angular.z = 0;
                    local_vel_pub.publish(twist);
                break;

            case HOVER:
                twist.twist.linear.x = 0;
                twist.twist.linear.y = 0;
                twist.twist.linear.z = 0;
                local_vel_pub.publish(twist);
                break;

            case LAND:
                if( td->Pw.z > 150 )
                {
                    twist.twist.linear.x = 0;
                    twist.twist.linear.y = 0;
                    twist.twist.linear.z = -1;
                    local_vel_pub.publish(twist);
                }
                else
                    auto_land = 1;
                break;

            default:
                ROS_INFO("Unexpected Error!");
        }

        if(state == LAND && auto_land == 1){
                if( current_state.mode != "AUTO.LAND" &&
                        (ros::Time::now() - last_request > ros::Duration(5.0))){
                        if( set_mode_client.call(offb_set_mode1) &&
                            offb_set_mode1.response.mode_sent){
                            ROS_INFO("autoland enabled");
                        }
                    last_request = ros::Time::now();
                }
        }

        ros::Time t1 = ros::Time::now();
        ros::Duration d(t1 - t0);
        rate.sleep();
    }
    讲到这里,定点降落的整个理论部分我已经粗略地讲完啦。中间有许多细节没有展开,我的考虑也有很多不全面的地方,各位博友可以通过评论、私聊等形式与我交流想法。下一章,我会讲一讲定点降落的实验部分,讲一下实验的过程,展示一下实验的结果。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于单目视觉的四旋翼定点降落——位姿估算与控制算法(五) 的相关文章

  • IOS 定制中间突出UItabBar

    前言 xff1a 公司的项目需要定制一个中间突出的TabBar xff0c 在github 上找到一份可以参考的代码 xff08 虽然是四年前的 xff0c 但是还是很有参考价值 xff09 网址 xff1a https github co
  • Xcode删除所有断点

    问题是这样的 xff1a 接手新的项目 xff0c 顿时吓尿了 xff1a 整个程序有无数个断点 xff0c 那么如何一次性删除呢 xff1f 如下图 xff1a 点击Belete Breakpoints 就可以了
  • VSCode重新启用“错误波形提示”

    2020 06 09 更新常见问题 昨天晚上写伪码的时候 xff0c 看着VSCode的错误提示实在是心烦 xff0c 就手贱点了一下 禁用错误波形提示 xff0c 也就是disable error squiggles xff0c 但写完之
  • TortoiseSVN使用教程[多图超详细]

    安装及下载client 端 下载Windows 端程序 xff1a http tortoisesvn net downloads 一般而言 xff0c 如果是32 bit的Windows XP 应该使用TortoiseSVN 1 4 x x
  • 将UIColor转换为RGB值

    objc view plain copy 将UIColor转换为RGB值 NSMutableArray changeUIColorToRGB UIColor color NSMutableArray RGBStrValueArr 61 NS
  • 业余时间你在做什么,你就会变成什么样的人?

    改变 xff0c 从业余时间开始 博客定位 xff1a 技术 43 思考 其余统统不要 2017 xff0c 我来了 xff01
  • Xcode9 无证书真机调试

    写在前面 公司分配了新的测试机 证书99台名额已满 所以上网找教程 学习了一下如何使用Xcode无证书进行真机调试 一 创建证书 1 运行Xcode xff0c Xcode Preference 添加账号 xff08 能在appstore下
  • CSP考试复习:第一单元 C++语言基础 1.1 程序结构

    第一单元 C 43 43 语言基础 1 1 程序结构 1 程序框架 注释 xff1a 注释有两种 xff0c 一种是 xff0c 另一种是 必须单独放置一行 xff0c 或代码所在行 的后面 xff1b 而 成对存在 xff0c 可以插入到
  • Intel Realsense T265开箱测试

    前言 xff1a 最近因为要做VIO xff0c 在实验室蹭到一个Realsense T265来用 xff0c 仅此记录下简单测试过程 xff08 官方文档写非常清楚 xff0c 建议详细阅读 xff0c 链接 xff1a https gi
  • posix thread介绍

    xfeff xfeff posix thread是 操作系统级 xff08 OS level xff09 的API规范 xff0c 主要用来定义线程及线程间同步的相关操作 xff0c 采用C语言定义 posix规范主要在unix like类
  • PX4飞控之自主起飞Takeoff控制逻辑

    本文主要以PX4飞控1 5 5版本为例 xff0c 介绍Navigator中自主起飞 xff08 Takeoff xff09 算法控制逻辑 注 xff1a mission任务中的自主起飞与此模块不同 Takeoff与导航中的其他模块类似 x
  • PX4飞控之导航及任务架构

    本文重点介绍PX4飞控的Navigator和mission控制框架和逻辑 Navigator导航部分是无人机自主飞行控制的核心所在 xff0c 其中包括自主起飞 自主降落 自主返航 自主任务以及GPS失效保护等各个部分 搞懂这个部分有助于理
  • PX4飞控之位置控制(1)整体架构

    位置控制是无人机飞控的核心算法之一 xff0c 一方面根据commander中的flag标志位和Navigator中提供的航点信息进行控制 xff08 自主模式下 xff09 xff0c 另一方面得到期望姿态角 xff08 setpoint
  • spring整合ehcache找不到org.springframework.cache.ehcache.EhCacheCacheManager的解决方案

    一般org springframework cache ehcache EhCacheCacheManager和org springframework cache ehcache EhCacheManagerFactoryBean会同时找不
  • CC3200之GPIO引脚分析

    预备知识 xff1a xff08 1 xff09 volatile关键字 xff1a volatile定义的变量一般为无需开发者自己赋值 xff0c 会自动改变的变量 在普通的程序中 xff0c 编译器都具有优化功能 xff0c 为了避免浪
  • OpenStack 之 OVS介绍

    一 概述 Open vSwitch的官方定义 xff1a Open vSwitch是一个具有工业级质量的多层虚拟交换机 通过可编程扩展 xff0c 可以实现大规模网络的自动化 xff08 配置 管理 维护 xff09 它支持现有标准管理接口
  • OVN是OVS 5倍的性能--性能测试报告

    我们已经对OVN做了许多次的性能测试 xff0c 但是缺少一个OVN和 xff08 ML2 43 OVS xff09 的性能对比测试 我和许多人一起对比了这2种后端 本文是第一部分 xff1a 控制平面的性能对比 后面会另外发文公布数据平面
  • OpenStack 中的5种分配IPv6地址的方式

    在OpenStack Pike版本中创建IPv6子网时 xff0c 有五种设置地址方式供选择 上图中这五种方式显示不全 xff0c 现将其全部展示 No options specified xff08 Default xff09 xff0c
  • STM32 | TCP通信实例分析

    1024G 嵌入式资源大放送 xff01 包括但不限于C C 43 43 单片机 Linux等 关注微信公众号 嵌入式大杂烩 xff0c 回复1024 xff0c 即可免费获取 xff01 前言 关于socket的笔记 xff0c 之前已经
  • RT-Thread和Freertos的区别?

    关注 嵌入式大杂烩 xff0c 选择 星标公众号 一起进步 xff01 Freertos是一个国外推出的一个迷你的实时操作系统内核 xff0c 开源 xff0c 功能包括 xff1a 任务管理 时间管理 信号量 消息队列 内存管理 记录功能

随机推荐