水中机器人(鱼)搜救策略

背景

研究生实训课程《水中机器人实训》结课考核为机器鱼比赛,比赛规则如下:
池子设计如下:
在这里插入图片描述
需控制鱼从初始位置避让障碍游到球位置,然后将球顶回鱼的初始位置,计时完成,时间短得分高,时长不超过5分钟。

所编写的策略的思想

对机器鱼的控制分为以下四个阶段来进行:

  1. 以障碍为界,分为左右两侧。球在左侧,鱼在右侧时,定义为初始阶段,此时鱼的任务是避开障碍游到左侧。因此本阶段鱼的目标位置为障碍开口位置的中心点。本控制算法中使用简便方法,即将障碍定义为一个偏移的球门,球门中心位置即障碍开口的中心点,因此在此阶段可以直接使用现有的鱼游动控制程序,目标位置为球门;
  2. 球在左侧,鱼在左侧时,为顶球过门阶段。此阶段又分为两个子阶段:
    a.球在鱼左侧,这时鱼不可直接顶球,需要控制鱼游到球的左侧临时目标位置。而临时目标位置不能直接设为球的正左侧一定距离,因为若鱼与球y坐标相同时,鱼仍然会顶到球。因此本控制程序将这个临时目标点设为球的左上角;
    b.球在鱼右侧,这时可以直接顶球,并可以直接调用鱼顶球过门的程序,球门点设为障碍物开口中心点。
  3. 球在右侧,鱼在左侧时,与初始阶段相同,鱼的目标仍为穿过障碍物,目标点为障碍开口的中心点;
  4. 球在右侧,鱼在右侧时,为顶球到初始位置阶段。此阶段类似于顶球过门阶段,又分为两个子阶段:
    a.球在鱼下侧,这时鱼不可直接顶球,需要控制鱼游到球的下侧临时目标位置。而临时目标位置不能直接设为球的正下侧一定距离,因为若鱼与球x坐标相同时,鱼仍然会顶到球。因此本控制程序将这个临时目标点设为球的左下角;
    b.球在鱼上侧,这时可以直接顶球,并可以直接调用鱼顶球过门的程序,球门点设为初始位置。

本组粗糙的策略

//Purpose: 鱼从起始点避障至球处,并运球避障至与起始点(一个障碍)
//Author:Jace, cheng
//Data:2022.1.7
#include "../Header/StdAfx.h"
#include "../Header/Strategy.h"

CStrategy::CStrategy(void)
{
	m_StatePeriod=15;
	m_TurnYuan=0;
}

CStrategy::~CStrategy(void)
{
}

BOOL CStrategy::Strategy(IplImage* imgOrig,IplImage* imgRecog ,CFishAction action[], int n_action, CFishInfo fishinfo[], int n_fishinfo,
			  CBallInfo ballinfo[], int n_ballinfo, OBSTAINFO obst[],int n_obst, CHANNEL channel[], int n_channel)
{
	return TRUE;
}

//以下id及鱼信息、鱼动作信息数组相关

void CStrategy::BasicActionStop(CFishInfo fish[],CFishAction action[],int id)
{
	action[id].speed=0;
	action[id].direction=7;
	action[id].mode=0;
	action[id].state++;
	if(action[id].state==m_StatePeriod)
	{
		action[id].state=0;
		m_TurnYuan=0;
	}
	fish[id].SetAction(action[id]);
}

void CStrategy::BasicActionGo(CFishInfo fish[],CFishAction action[],int id,int speed,int direction,int mode)
{
	action[id].speed=speed;
	action[id].direction=direction;
	action[id].mode=mode;

	if(m_TurnYuan)//正在执行原始转弯模式
	{
		action[id].state++;
		if(action[id].state>=m_StatePeriod)
		{
			action[id].state=0;
			m_TurnYuan=0;
		}
	}
	else
	{
		if(mode)
		{
			action[id].state=0;
			m_TurnYuan=mode;
		}
		else
		{
			action[id].state++;
			if(action[id].state>=m_StatePeriod)
				action[id].state=0;
		}		
	}
	fish[id].SetAction(action[id]);
}

BOOL CStrategy::Strategy0(CFishAction m_action[], CFishInfo m_FishInfo[], CBallInfo  &m_goalinfo, OBSTAINFO m_obst[], CHANNEL m_Channel[])
{
	//初始化动作参数
	m_action[0].state = 0;
	m_action[0].mode = 0;

	CPoint temp0,temp2;
	CPoint temp11;
	CPoint temp12;
	CPoint temp211;
	CPoint temp212;
	CPoint temp22;

	CPoint f_pt;//鱼的中心点坐标
	f_pt=m_FishInfo[0].GetCenterPoint();//GetCenterPt()返回中心点坐标

	double f_dir;//鱼的方向,[-PI,PI]
	f_dir=m_FishInfo[0].GetDirection();

	CPoint f_headpt;//point of fish's head
	f_headpt=m_FishInfo[0].GetHeaderPoint();

	CPoint b_pt;//球的中心点坐标
	b_pt=m_goalinfo.GetBallPoint();

	CPoint g_pt;//球门中心点坐标//distance
	g_pt=m_Channel[0].center;

    CPoint start;//起始点
    start.x = 562;
    start.y = 100;


	bool m_right;
	if (start.x>312)
		m_right = true;
	else
		m_right = false;

    if(m_right)
	{
        if(f_pt.x>g_pt.x&&b_pt.x<g_pt.x)//鱼在B区且球在A区
        {
            temp0.x = g_pt.x-50;//第一阶段和第三阶段鱼的目标点是球门,临时目标点为球门向左一点
            temp0.y = g_pt.y;

            //**********全速游过球门**********
			m_action[0].mode=0;
			m_action[0].speed=14;
			Roundp2p1(temp0,m_action[0],0,m_FishInfo[0]);//全速游向球门
        }

        else if(f_pt.x<g_pt.x&&b_pt.x<g_pt.x)//鱼在A区且球在A区
        {
            g_pt.x = g_pt.x + 50;

            double dirfishtoball;
            dirfishtoball = this->Angle(f_pt, b_pt);
            double disfishtoball;
            disfishtoball = this->Distance(f_pt, b_pt);
            double dirballtogoal;
            dirballtogoal = this->Angle(b_pt, g_pt);
            double dirgoaltoball;
            dirgoaltoball = this->Distance(g_pt, b_pt);

            dirfishtoball -= f_dir;
            dirfishtoball = this->Checkangle(dirfishtoball);
            double dir = dirfishtoball * 180 / PI;

			double Cf_dir=this->Checkangle(f_dir);

			double r = 6;

            temp11.x = b_pt.x - r*cos(dirballtogoal);//第二阶段鱼的目标是顶球过球门,临时目标点为顶球位置
            temp11.y = b_pt.y - r*sin(dirballtogoal);

			temp12.x = b_pt.x - r - 30;//鱼在球的右侧时的临时目标点
            temp12.y = b_pt.y - r - 40;

            if(f_headpt.x<b_pt.x+r)//鱼在球左上边,开始顶球
            {	
                if(disfishtoball<80)//低速进近
                {	
                    m_action[0].mode=0;
                    m_action[0].speed=4;
                }
                else//高速推进
                {
                    m_action[0].mode=0;
                    m_action[0].speed=8;
                }
                Roundp2p1(temp11,m_action[0],0,m_FishInfo[0]);
            }         
			else
			{
				m_action[0].mode=0;
                m_action[0].speed=14;
				Roundp2p1(temp12,m_action[0],0,m_FishInfo[0]);
			}
        }

        else if(f_pt.x<g_pt.x&&b_pt.x>g_pt.x)//鱼在A区且球在B区
        {
            temp0.x = g_pt.x+50;//第一阶段和第三阶段鱼的目标点是球门,临时目标点为球门向右一点
            temp0.y = g_pt.y;

            //**********全速游向球门**********
			m_action[0].mode=0;
			m_action[0].speed=12;
			Roundp2p1(temp0,m_action[0],0,m_FishInfo[0]);//全速游向球门
        }

        else if(f_pt.x>g_pt.x&&b_pt.x>g_pt.x)//鱼在B区且球在B区
        {
            //**********原版顶球进门策略降速版**********
            double dirfishtoball;
            dirfishtoball = this->Angle(f_pt, b_pt);
            double disfishtoball;
            disfishtoball = this->Distance(f_pt, b_pt);
            double dirballtostart;
            dirballtostart = this->Angle(b_pt, start);
            double dirstarttoball;
            dirstarttoball = this->Distance(start, b_pt);

            dirfishtoball -= f_dir;
            dirfishtoball = this->Checkangle(dirfishtoball);
            double dir = dirfishtoball * 180 / PI;

            double r=6;
            temp211.x = b_pt.x - r*sin(dirballtostart);//第四阶段鱼的目标是顶球进起始点,临时目标点为顶球位置
            temp211.y = b_pt.y + r*cos(dirballtostart);
			temp212.x = b_pt.x + r*sin(dirballtostart);//第四阶段鱼的目标是顶球进起始点,临时目标点为顶球位置
            temp212.y = b_pt.y + r*cos(dirballtostart);
			temp22.x = b_pt.x - r - 30;//鱼在上侧时的临时目标点
			temp22.y = b_pt.y + r + 40;

			
            if (f_headpt.y>b_pt.y + r)//鱼在球下边,直接顶球
            {
                if (disfishtoball<80)//the velosity change easy to approach
                {
                    m_action[0].mode = 0;
                    m_action[0].speed = 4;
                }
                else
                {
                    m_action[0].mode = 0;
                    m_action[0].speed = 8;
                }
				if (start.x>b_pt.x)//球在起始点左边
				{
					Roundp2p1(temp211, m_action[0], 0, m_FishInfo[0]);
				}
				else//球在起始点右边
				{
					Roundp2p1(temp212, m_action[0], 0, m_FishInfo[0]);
				}
            }
            else//鱼在球上边,先游到下边临时点
			{
				m_action[0].mode = 0;
				m_action[0].speed = 14;
				Roundp2p1(temp22, m_action[0], 0, m_FishInfo[0]);
			}
        }
    }
    return TRUE;
}


double CStrategy::Distance(CPoint point,CPoint aimer)
{
	double a;
	a=sqrt((double)(point.x-aimer.x)*(point.x-aimer.x)+(double)(point.y-aimer.y)*(point.y-aimer.y));
	return a;
}
double CStrategy::Checkangle(double dir)//把dir换算成[-pi~pi]之间的数
{
	if(dir>PI)
		dir-=2*PI;
	else if(dir<-PI)
		dir+=2*PI;
	return dir;
}
void CStrategy::Roundp2p(CPoint aimpt,CFishAction &action,int fishID,CFishInfo m_FishInfo)
{
	CPoint FishPt;// 鱼中心的位置
	double FishDir;// 鱼头的方向
	CPoint FishHeadPt;//鱼头的位置
	CPoint LastFishPt;
	CPoint FishRotatePt;//鱼的转动位置
	CPoint FishTailPt;//鱼尾巴的位置		
	CPoint GoalPt1, GoalPt2;//临时目标点,GoalPt2没有用到
	CPoint Pt1, Pt2, Pt3, Pt4;
	//CPoint f_headpt;//point of fish's head
	//f_headpt=m_FishInfo[0].GetHeadPoint();

	double dir1=0;
	double dir0;
	double dist0;
	double dist1=0;
	double dir2=0;
	double dist2=0;//临时用到的方向和距离
	CPoint centerpt1,centerpt2;//作为鱼要绕的中心来用	
	CPoint centerpt3,centerpt4,centerpt5,centerpt6;
	CPoint centerpt0,centerpt00;
	double dis1,dis2;
	dis1=0;
	dis2=0;		 
	double radius;///需要游动的半径,可以随意的设置.
	FishPt=m_FishInfo.GetCenterPoint();
	FishDir=m_FishInfo.GetDirection();
	dist1=Distance(FishPt,aimpt);
	dir1=Angle(FishPt,aimpt);
	dir1-=FishDir;
	dir1=Checkangle(dir1)*180/PI;
	action.speed=15;
	m_FishInfo.SetAction(action);
	if(dist1>100)
	{
		if(dir1>-5&&dir1<5)
			action.direction=7;
		else if(dir1<-5&&dir1>-10)
			action.direction=5;
		else if(dir1<-10&&dir1>-30)
			action.direction=4;
		else if(dir1<-30&&dir1>-50)
			action.direction=2;
		else if(dir1<-50&&dir1>-70)
			action.direction=1;
		else if(dir1<-70&&dir1>-90)
			action.direction=0;
		else if(dir1<-90)
			action.direction=0;
		else if(dir1>5&&dir1<10)
			action.direction=9;
		else if(dir1>10&&dir1<20)
			action.direction=10;
		else if(dir1>20&&dir1<40)
			action.direction=12;
		else if(dir1>40&&dir1<50)
			action.direction=12;
		else if(dir1>50&&dir1<80)
			action.direction=14;
		else if(dir1>80&&dir1<90)
			action.direction=14;
		else 
			action.direction=14;
	}

	else 
	{
		if(dir1>-5&&dir1<5)
			action.direction=7;
		else if(dir1<-5&&dir1>-20)
			action.direction=4;
		else if(dir1<-20&&dir1>-40)
			action.direction=1;
		else if(dir1<-40&&dir1>-60)
			action.direction=0;
		else if(dir1<-60&&dir1>-70)
			action.direction=0;
		else if(dir1<-70&&dir1>-90)
			action.direction=0;
		else if(dir1<-120)
			action.direction=0;
		else if(dir1>5&&dir1<20)
			action.direction=10;
		else if(dir1>20&&dir1<30)
			action.direction=12;
		else if(dir1>30&&dir1<50)
			action.direction=13;
		else if(dir1>50&&dir1<70)
			action.direction=14;
		else if(dir1>70&&dir1<90)
			action.direction=14;
		else if(dir1<120)
			action.direction=14;
		else 
			action.direction=14;

	}
	m_FishInfo.SetAction(action);
}
void CStrategy::con2tempt(CPoint aimpt,CFishAction m_action[],CFishInfo m_FishInfo[])//游到一个临时点后退出
{
	//获取鱼的信息
	CPoint f_pt;//鱼的中心点坐标
	f_pt=m_FishInfo[0].GetCenterPoint();//GetCenterPt()返回中心点坐标
	double f_dir;//鱼的方向,[-PI,PI]
	f_dir=m_FishInfo[0].GetDirection();

	CPoint f_headpt;//point of fish's head
	f_headpt=m_FishInfo[0].GetHeaderPoint();


	double disheadtoaimpt;
	disheadtoaimpt=this->Distance(f_headpt,aimpt);

	bool tem=true;

	if(tem)
	{
		Roundp2p(aimpt,m_action[0],0,m_FishInfo[0]);
		if(disheadtoaimpt<10)
			tem=false;
	}
}


double CStrategy::Angle(CPoint point,CPoint aimer)
{	
	double a;
	a=atan2((double)(aimer.y-point.y),(double)(aimer.x-point.x));
	return a;
	//return point.x;
}
void CStrategy::Point2point(CPoint aimpt,CFishAction &action, int fishID, CFishInfo m_FishInfo)
{
	CPoint FishPt;// 鱼中心的位置
	double FishDir;// 鱼头的方向
	CPoint FishHeadPt;//鱼头的位置
	CPoint LastFishPt;
	CPoint FishRotatePt;//鱼的转动位置
	CPoint FishTailPt;//鱼尾巴的位置		
	CPoint GoalPt1, GoalPt2;//临时目标点,GoalPt2没有用到
	CPoint Pt1, Pt2, Pt3, Pt4;
	double dir1=0;
	double dir0;
	double dist0;
	double dist1=0;
	double dir2=0;
	double dist2=0;//临时用到的方向和距离
	CPoint centerpt1,centerpt2;//作为鱼要绕的中心来用	
	CPoint centerpt3,centerpt4,centerpt5,centerpt6;
	CPoint centerpt0,centerpt00;
	double dis1,dis2;
	dis1=0;
	dis2=0;		 
	double radius;///需要游动的半径,可以随意的设置.
	FishPt=m_FishInfo.GetCenterPoint();
	FishDir=m_FishInfo.GetDirection();
	dist1=Distance(FishPt,aimpt);
	dir1=Angle(FishPt,aimpt);
	dir1-=FishDir;
	dir1=Checkangle(dir1)*180/PI;
	action.speed=15;	
	if(dir1>-5&&dir1<5)
		action.direction=7;
	else if(dir1<-5&&dir1>-10)
		action.direction=5;
	else if(dir1<-10&&dir1>-30)
		action.direction=4;
	else if(dir1<-30&&dir1>-50)
		action.direction=2;
	else if(dir1<-50&&dir1>-70)
		action.direction=1;
	else if(dir1<-70&&dir1>-90)
		action.direction=0;
	else if(dir1<-90)
		action.direction=0;
	else if(dir1>5&&dir1<10)
		action.direction=9;
	else if(dir1>10&&dir1<20)
		action.direction=10;
	else if(dir1>20&&dir1<40)
		action.direction=12;
	else if(dir1>40&&dir1<50)
		action.direction=12;
	else if(dir1>50&&dir1<80)
		action.direction=14;
	else if(dir1>80&&dir1<90)
		action.direction=14;
	else 
		action.direction=14;
}

效果

控制较为粗糙,且有BUG,实际效果依运气,最快可以47秒完成任务,最慢会超过5分钟。

备注与反思

  1. 本控制程序比较粗糙,原因是其中的角度调用函数使用方法未知,手上也没有相应的例程,编写的BUG无法排除,又临近课题验收,因此只能就此结束。若有时间可以通过角度函数实现精准的多的控制方法;
  2. 更进一步,由于鱼顶球的时候,球同样在运动,因此顶球控制程序实际应当可以将球的移动速度作为考虑因素之一,利用一些滤波估计算法得到下一时刻球的估计位置,可以实现更为精准的控制。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值