MOOS-ivp 实验九 分布式旅行商问题(1)

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • MOOS-ivp 实验九 分布式旅行商问题(1)
  • 前言
  • 一、分布式TSP——准备shoreside
    • 1.创建用于生成访问点的脚本
  • 二、创建一个MOOSapp来分发点
    • 1.订阅uTimerScript脚本中发布的变量值
    • 2.配置参数vname以及参数assign_by_region
    • 3.按两种方式分配点数
    • 4.从MOOSDB上订阅变量
  • 三、编译 PointAssign查看效果
  • 总结

前言

在今天的实验中,将继续关注涉及多AUV的自主配置。实验练习将涉及分布在多个车辆上的旅行商问题(TSP)。TSP是寻找到最短路径来访问一组城市的问题,假设任意两个城市之间的距离是已知的,要求访问每个城市至少一次,并最小化总的路程。
今天的实验主要就是如何部署多辆vehicle来处理旅行商问题。给定一组要访问的点,将这些点分配给一组要访问这些点的vehicle。由于优化的旅行商问题算法可能相当复杂,为了将事情简化,要考虑到了非最佳的最短旅行距离,同时专注于让多个合作车辆运行的机制。在这个实验中:
(1)使用uTimerScript脚本工具在海滨生成随机的游览点
(2)编写一个新的MOOS应用程序并在Shoreside上运行,目的是给vehicle发放随机的游览点 。
(3)vehicle需要配置为从shoreside接受访问的游览点
(4)写一个MOOSapp用来生成一个waypoint的旅行规划
(5)自治任务为巡视这些游览点,并定期返回进行加油。
(6)恢复的任务将继续完成未完成的游览,直到完成全部的游览任务


提示:以下是本篇文章正文内容,下面案例可供参考

一、分布式TSP——准备shoreside

旅行推销员问题(TSP)是寻找一条路径来访问一组城市的问题,其中任意两个城市之间的距离是已知的,以这种方式来访问每个城市至少一次,并最小化总的旅行距离。在本节中,使用TSP问题来练习我们部署多辆车来处理TSP问题的一个版本——分布式TSP问题。给定一组要访问的点,将这些点分配给一组要访问这些点的车辆。由于优化的旅行商问题算法可能相当复杂,我们将事情简化了一点,并考虑到了非最佳的最短旅行距离,同时专注于让多个合作车辆运行的机制。
在这个部分,任务是:
(1)创建一个uTimerScript脚本,在指定区域生成100个随机点
(2)在岸边创建一个“pPointAssign MOOS”模块,将一半的点发送给一辆车,另一半发送给另一辆车
(3)确保pshare共享通信配置为向车辆共享点
(4)通过验证在一对车辆上接收到访问点来确认其工作

1.创建用于生成访问点的脚本

配置在shoreside社区上运行的uTimerScript脚本,以在操作区域的某个区域内生成随机的点序列。这个区域范围是:
-25,-25
-25,-175
200,-25
200,-175
脚本 需要 发布100个点到 MOOSDB上:

VISIT_POINT = "firstpoint"
VISIT_POINT = "x=8, y=9, id=1"
...
VISIT_POINT = "x=-11, y=-9, id=100"
VISIT_POINT = "lastpoint"

格式类似于上述所示。
根据要求可得配置uTimerScript代码如下:

//--------------------------------------------------------
// uTimerScript Configuration BlockProcessConfig = uTimerScript
{//  name = random_points_generatedpaused = truepause_var = UTS_PAUSErand_var    = varname=RND_VAL_X, min=-25, max=200, key=at_postrand_var      = varname=RND_VAL_Y, min=-175, max=-25, key=at_postevent = var=VISIT_POINT, val="firstpoint", time=0event = var=VISIT_POINT, val="x=$[RND_VAL_X], y=$[RND_VAL_Y], id=$[TCOUNT]", time=0, amt=100event = var=VISIT_POINT, val="lastpoint", time=0}

下面对上面的变量及其用法进行解释:
paused = true:设定一开始脚本不启动
pause_var = UTS_PAUSE:设置控制脚本对应的变量名称
rand_var:要在事件值中展开的随机变量宏的声明。

event:定时器脚本中单个事件的描述

event = var=<MOOSVar>, val=<var-value>, time=<time-of-event>

event组件可以包含一个表单为$[macro]的宏,这个宏可以是为数不多的内置宏之一,也可以是一个用户定义的、能够表示随机变量的宏。宏也可以组合在简单的算术表达式中,以提供进一步的表达能力。在每种情况下,宏在事件发布时展开,通常在每次后续发布时都有不同的值。
ps:我在创建脚本建立100个随机点是按照手册上uTimerScript的例程来实现的,实际上amt变量并不能识别,于是我便写了100条重复的event事件来创建100个点,虽然比较麻烦,但是效果是一样的。

二、创建一个MOOSapp来分发点

实验要求创建一个MOOSapp命名为pPointAssign ,以此app来分发点,一半的点分发给一艘vehicle,另一半的点分发给另外一艘vehicle。
(1)首先这个app应该订阅uTimerScript发布的变量值,并产生如下的输出

VISIT_POINT_HENRY = "firstpoint"
VISIT_POINT_HENRY = "x=8, y=9, id=1"
...
VISIT_POINT_HENRY = "x=33, y=29, id=50"
VISIT_POINT_HENRY = "lastpoint"
VISIT_POINT_GILDA = "firstpoint"
VISIT_POINT_GILDA = "x=19, y=111, id=51"
...
VISIT_POINT_GILDA = "x=-11, y=-9, id=100"
VISIT_POINT_GILDA = "lastpoint"

(2)pointassign发布的第一个和最后一个帖子,共享给每辆vehicle,应该是变量VNAME=“firstpoint”和访问点VNAME=“lastpoint”。这些帖子,可以确认已经发送和接收了完整的访问点。
(3)应用程序应该有一个配置参数vname,它将已知的车辆添加到将要分发的车辆列表中
(4)应用程序应该支持两种方式之一的分数分配。在第一种方式中,分数是交替分配的。第一点指向第一辆车,第二点指向第二辆车,第三点指向第一辆车,以此类推。第二种方式是根据地区来分配分数,比如东西方向。应用程序应该通过assign_by_region参数分配的地区设置为true或false来配置为任何一种模式。
(5)在按东/西区域分配点数时,不需要给每辆车分配一个精确的平均数量。要把这个区域分成两半,然后相应地分配。考虑到随机点的均匀分布,它们应该在每一边接近50-50,但不能完全保证。

1.订阅uTimerScript脚本中发布的变量值

首先在.h文件中对常用变量进行相关的定义

class PointAssign : public CMOOSApp
{public:PointAssign();~PointAssign();protected: // Standard MOOSApp functions to overload  bool OnNewMail(MOOSMSG_LIST &NewMail);bool Iterate();bool OnConnectToServer();bool OnStartUp();protected:void RegisterVariables();bool PointRegionIsEast(double x_val);void postViewPoint(double x, double y, std::string label, std::string color);private: // Configuration variablesstd::vector<std::string> m_vname_list;std::vector<std::string> m_visit_points;bool m_assign_by_region;std::string m_vname_str;bool m_reached_first_point;bool m_reached_last_point;bool m_notified_all;private: // State variables
};

接下来首先来订阅脚本发布的变量

bool PointAssign::OnNewMail(MOOSMSG_LIST &NewMail)
{MOOSMSG_LIST::iterator p;for(p=NewMail.begin(); p!=NewMail.end(); p++) {CMOOSMsg &msg = *p;string key   = msg.GetKey();string sval  = msg.GetString();if (key=="VISIT_POINT"){if (sval=="firstpoint"){m_reached_first_point = true;std::cout<<"reached first point"<<std::endl;}else if (sval == "lastpoint"){m_reached_last_point = true;std::cout<<"reached last point"<<std::endl;}else if (m_reached_first_point==true && m_reached_last_point ==false){m_visit_points.push_back(sval);std::cout<<"added point to visit points"<<std::endl;}}#if 0 // Keep these around just for templatestring key   = msg.GetKey();string comm  = msg.GetCommunity();double dval  = msg.GetDouble();string sval  = msg.GetString();string msrc  = msg.GetSource();double mtime = msg.GetTime();bool   mdbl  = msg.IsDouble();bool   mstr  = msg.IsString();
#endif}return(true);
}

一般订阅脚本中的变量这一步骤都在函数PointAssign::OnNewMail(MOOSMSG_LIST &NewMail)中来进行实现, string key = msg.GetKey()与string sval = msg.GetString()函数分别用来获取MOOSDB上发布的变量的名称以及变量值,如果变量 if (key==“VISIT_POINT”),那么就将其值进行存储m_visit_points.push_back(sval);如果发布的变量是第一个以及最后一个,那么改变相应的标志为,存储的变量都是按照发布顺序,除了第一个与最后一个发布的变量来进行存储的。
这里再讲一下pushback函数的用法:
函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素
push_back() 在Vector最后添加一个元素(参数为要插入的值)

int num = 10;
vector<int> vec;
vec.push_back(num);

或者再string中最后插入一个字符

string str;
str.push_back('d');

类似函数用法有:

pop_back() //移除最后一个元素

clear() //清空所有元素

empty() //判断vector是否为空,如果返回true为空

erase() // 删除指定元素

2.配置参数vname以及参数assign_by_region

通过配置文件给进程进行参数设置,相关代码如下,一般读取配置文件的参数这一步骤在PointAssign::OnStartUp()函数中完成:

bool PointAssign::OnStartUp()
{Notify("UTS_PAUSE","false");list<string> sParams;m_MissionReader.EnableVerbatimQuoting(false);if(m_MissionReader.GetConfiguration(GetAppName(), sParams)) {list<string>::iterator p;for(p=sParams.begin(); p!=sParams.end(); p++) {string original_line = *p;string param = stripBlankEnds(toupper(biteString(*p, '=')));string value = stripBlankEnds(*p);std::cout<<"param: "<<param<<std::endl;if(param == "VNAME") {//add to vehicle list of namesm_vname_list.push_back(value);}else if(param == "ASSIGN_BY_REGION") {if (value=="true"){m_assign_by_region = true;}else{m_assign_by_region = false;}}}}m_timewarp = GetMOOSTimeWarp();RegisterVariables();return(true);
}

Notify(“UTS_PAUSE”,“false”);设置脚本启动输出point数据
m_MissionReader.GetConfiguration(GetAppName()函数就是用来对配置文件中的相关数据进行读取和使用的, if(param == “VNAME”)
如果读取到的配置参数为"VNAME",那么记录下当前读取到的值,并将其存入姓名列表里
m_vname_list.push_back(value)。另外一个参数是用来对两种分配方式进行确定的,
if(param == “ASSIGN_BY_REGION”)那么就对相关变量 m_assign_by_region进行相应的赋值,来确定后续的point分配方式。

3.按两种方式分配点数

其中涉及到到关于stringstream常见用法介绍详情可以参考这篇博文。
设计到按两种方式分配点数的代码如下,通过判断m_assign_by_region 的值来进行不同类型的点数分配工作:

bool PointAssign::Iterate()
{// NVM: Only iterate if first and last pointsstd::cout<<"notified all: "<<m_notified_all<<std::endl;//  if (m_reached_first_point == true && m_reached_last_point == true && m_notified_all == false){//   if (m_reached_first_point == true && m_reached_last_point == true && m_notified_all == false){//  if (m_reached_first_point == true && m_reached_last_point == true){//Loop through list of points and alternate assignmentif (m_assign_by_region ==false){//  std::vector<std::string>::const_iterator i = m_vname_list.begin();int i = 0;for (std::vector<std::string>::const_iterator k = m_visit_points.begin(); k != m_visit_points.end(); ++k){std::string x_str = tokStringParse(*k, "x", ',', '=');//进行数据筛选std::string y_str = tokStringParse(*k, "y", ',', '=');std::string id_str = tokStringParse(*k,"id",',','=');std::cout<<"ID = "<<id_str<<std::endl;double x_double = 0.0;double y_double = 0.0;stringstream rr;stringstream ww;rr<<x_str;ww<<y_str;rr>>x_double;ww>>y_double;std::string color_label_str;if (i==0){m_vname_str="HENRY";i=1;color_label_str = "red";}else{m_vname_str="GILDA";i=0;color_label_str= "yellow";}stringstream ss;ss<<"VISIT_POINT_"<<m_vname_str;Notify(ss.str(),*k);postViewPoint(x_double, y_double,id_str, color_label_str);}std::cout<<"finished looping through all points"<<std::endl;m_notified_all = true;}else{ //Assign by regionstd::cout<<"Assigning by region"<<"size of visit points: "<<m_visit_points.size()<<std::endl;for (std::vector<std::string>::const_iterator k = m_visit_points.begin(); k != m_visit_points.end(); ++k){std::string x_str = tokStringParse(*k, "x", ',', '=');std::string y_str = tokStringParse(*k, "y", ',', '=');std::string id_str = tokStringParse(*k,"id",',','=');std::cout<<"ID = "<<id_str<<std::endl;double x_double = 0.0;double y_double = 0.0;stringstream rr;stringstream ww;rr<<x_str;ww<<y_str;rr>>x_double;ww>>y_double;bool is_east = PointRegionIsEast(x_double);std::cout<<"east: "<<is_east<<std::endl;if (is_east){stringstream vv;vv<<"VISIT_POINT_"<<m_vname_list[0];std::cout<<vv.str()<<std::endl;Notify(vv.str(),*k);std::cout<<"calling post view point"<<std::endl;postViewPoint(x_double, y_double, id_str, "yellow");}else{stringstream vv;vv<<"VISIT_POINT_"<<m_vname_list[1];std::cout<<vv.str()<<std::endl;Notify(vv.str(),*k);std::cout<<"calling post view point"<<std::endl;postViewPoint(x_double, y_double, id_str, "red");}}std::cout<<"finished looping through all points"<<std::endl;m_notified_all = true;}// }return(true);}

第一种分配方式是按照点数顺序随机给两台AUV分配point, std::string x_str = tokStringParse(*k, “x”, ‘,’, ‘=’);是在对传入的字符串进行数据筛选,选出x、y、id的值, 定义stringstream rr类是来进行数据的输入输出操作,主要目的是实现数据类型的转换,可以将str类型的数据转换为double型。分配完成数据点之后使用Notify(ss.str(),*k)将分配后的结果上传至MOOSDB中去,并使用postViewPoint函数将结果绘制在pMarineViewer显示的图中。
其主要代码是:

void PointAssign::postViewPoint(double x, double y, string label, string color){{XYPoint point(x, y);point.set_label(label);point.set_label_color(color);point.set_color("vertex", color);point.set_param("vertex_size", "5");string spec = point.get_spec();std::cout<<"notifying view point"<<std::endl;Notify("VIEW_POINT", spec);}

XYPoint类的主要作用就是给pMarineViewer应用上的图进行绘制,与之类似的类还有XYPolygon, XYSegList, XYPoint, XYSeglr 和 XYVector等等。
第二种方式就是按照区域进行分配,通过判断x坐标的大小,来对点数进行分配,判断的函数是 is_east = PointRegionIsEast(x_double),其代码非常简单:

bool PointAssign::PointRegionIsEast(double x_val){return (x_val<100.0);
}

4.从MOOSDB上订阅变量

这行代码非常简单,只需要对"VISIT_POINT"变量进行订阅即可,其调用在onstartup函数上进行,也就是在程序的最开始初始化时进行订阅。

void PointAssign::RegisterVariables()
{// Register("FOOBAR", 0);Register("VISIT_POINT",0);
}

三、编译 PointAssign查看效果

此处总结一下如何使用cede::block对文件进行编译:
新建工程项目选择empty project

选择C\C++header

生成新工程之后,选中Project,右键,选择“Properties”,“Project settings”选项卡中的“Makefile”填入Makefile 文件名,并勾选“This is a custom Makefile”。

注意execuytion directory选择目录moos-ivp-extend下的build文件夹:

选中 Project,右键,选择“Build options”,删除““Make” command”选项卡中所有的“$target”。更改后如下图所示

记得在该目录下添加要编译的工程名:

如果新建的工程需要一些MOOS自带的库函数,在工程目录下的Cmakelist里进行添加:

本项目就需要另外添加geometry库,否则编译时XYpoint类会报错。
点击进入相关目录下,执行

./launch

可以看到如图的显示,100个随机点分布在图形中,说明程序编写的没有问题

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

MOOS-ivp 实验九 分布式旅行商问题(1)相关推荐

  1. 实验九 根据材料编程(改变字体颜色)

    编程:在屏幕中间分别显示 绿色.绿底红色.白底蓝色的字符串'welcome to masm!' 一个字符在屏幕上占用两个字节 低地址存放ASCII码,高地址存放字符的属性 偶数地址都是存放字符的ASC ...

  2. python实验九答案_Python实验九

    安徽工程大学 python程序设计实验报告 班级:物流191       姓名:王悦   学号:3190505103       日期:2020.6.16       指导老师:修宇 [实验名称]实验 ...

  3. 云南大学计算机网络期中考试刘春花,实验九基于CRC编码的检错程序的实现.doc...

    实验九基于CRC编码的检错程序的实现.doc 云南大学软件学院实 验 报 告课程 计算机网络原理实验 任课教师 刘春花,刘宇 姓名 学号 专业 成绩 实验九.链路层实验基于 CRC 编码的检错程序的实 ...

  4. 计算机排版技能会操方案,实验九 Word的高级排版技巧.pdf

    <微机操作>实验九 课件 一.实验要求 通过本节学习,要求熟练掌握下列操作技能 (1) 能够适时适地插入页码.分页符.分节符: (2) 能够编辑相同页眉.页脚,也能建立不同节奇偶页不一样的 ...

  5. c语言程序设计 江宝钏 实验九,c语言程序设计,江宝钏著,实验九

    c语言程序设计,江宝钏著,实验九 宁波大学实验报告学号 姓名 专业 土木建设类5班 学院 阳明学院 2016年 6月 10 日实验名称:结构体与链表实验目的与要求:1. 掌握结构体类型变量的定义和使用 ...

  6. 操作系统实验一到实验九合集(哈工大李治军)

    操作系统实验 作者寄语 操作系统实验的学习是一个循序渐进的过程,初次看linux-0.11中的代码,看着满屏的汇编语言,确实头疼.但通过学习赵炯博士的Linux内核0.11完全注释,结合着王爽老师的汇 ...

  7. 实验九 使用异步方式实现文件读\写

    实验九 使用异步方式实现文件读\写 一.实验目的 了解Windows系统异步文件读/写的概念. 熟悉Windows系统文件读/写相关的API. 掌握采用异步方式实现文件读/写的相关参数设置. 二.实验 ...

  8. sqlserver数据库实验 实验九 触发器的创建与使用

    实验九触发器的创建与使用 一.实验目的 本实验的目的是使学生进一步掌握SQL Server触发器的创建及使用方法,加深SQL触发器的理解.通过对数据的更新操作体会其触发器的作用. 二.实验准备 结合课 ...

  9. ZUCC_操作系统原理实验_实验九 消息队列

    操作系统原理实验报告 课程名称 操作系统原理实验 实验项目名称 实验九 消息队列 实验目的 了解 Linux 系统的进程间通信机构 (IPC): 理解Linux 关于消息队列的概念: 掌握 Linux ...

最新文章

  1. C 语言——字符串和格式化输入/输出
  2. 构造函数调用虚函数的问题
  3. Darwin Streaming Server for Windows 安装
  4. SQLServer:GUI方式、SQL语句两种方式建立视图和GUI方式设置主键、约束等
  5. PHP获取当前页面的URL
  6. spark大数据基础概念
  7. git schnnel failed to receive handshake, SSLTLS connection failed
  8. python语言保留字的特点_python保留字及其说明
  9. 数据库(5)SQL约束
  10. 《深入理解Java虚拟机》第5章 调优案例分析与实战
  11. springboot_poi思路
  12. APICloud可视化编程
  13. 小车yolo机械臂(四)python ros 和darknet_ros 使用launch文件启动脚本
  14. vscode鼠标滚轮调整字体大小
  15. 网上流传的飞扬学院Java_收获| 云和JAVA、UI双班毕业,飞扬青春再出发!
  16. Word文档中统一字符串八大妙法(转)
  17. 广州电子路考视频发布 2014广州电子路考考点
  18. 微信内嵌浏览器打开手机浏览器下载APP(APK)的方法
  19. ios Symbol(s) not found for architecture arm64
  20. 噩梦射手(SurvivalShooter)教程(五)

热门文章

  1. 产品碳足迹ISO14067认证
  2. 2019年了桌面CPU还吊打笔记本CPU?真相了
  3. PyQt:桌面程序设计的饕餮盛宴
  4. 【科普】如何评价供应商的MES系统
  5. java实现动态加载jar包中的class(破坏双亲委派来实现)
  6. Kali Linux虚拟机——安装VPN客户端
  7. 教你如何编辑修改PDF文件内容
  8. 无线耳机哪个品牌好?四大国内蓝牙耳机品牌排行
  9. 在组装机上安装ubuntu系统-配置pytorch-GPU学习环境
  10. python excel模板_如何利用Excel与Python制作PPT