。
本章旨在详细讲解AGV在线段(Segment)上的占用矩形生成逻辑算法。为后续静态交通管制提供基础技术支持。
更多精彩内容请前往AGV调度管理系统技术中心。

本章目录
AGV占用矩形
初始化AGV占用矩形树
StartSegment的选择
生成占用矩形
各种驱动类型下的占用矩形生成
AGV占用矩形
占用矩形的生成我们在前面讲过,但是并没有详解其中的细节,下面我们开始详解如何生成AGV占用矩形。
AGV占用矩形:在路径规划软件中以可视化矩形向软件使用者展示AGV所在路径/线段上的一种轮廓。
占用矩形的生成是基于线段和 AGV类型的,线段不同、AGV类型不同,生成的矩形大小也不同,如果一个项目包含多种AGV类型,那么就会有多种矩形。
影响矩形的主要因素包括:AGV类型中的AGV几何尺寸(长、宽),线段的驱动方式(是否平移?)等。
生成占用矩形所必须的参数包括:坐标点X、坐标点Y、agv在线段上的车体角度、导航点坐标、agv长度、agv宽度、agvOrigoOffset。

①坐标点X、Y:用于当导航点位于线段上的该坐标点时生成AGV占用矩形。
②Agv在线段上的车体角度:用来确定占用矩形在坐标点上的角度。
③导航点坐标:AGV车体上的一个坐标点,用来映射agv占用矩形到线段坐标上,即线段轨迹是agv上哪个点的行驶轨迹。
④agvLengthIncludingSafety:包括安全区域(比如前后安全扫描仪)在内的agv长度。
⑤Agv宽度:包括安全区域和负载宽度在内的agv宽度。
⑥AgvOrigoOffset:用于计算占用矩形的一个参数,X=车前右.X+安全区域长度;Y=车前右.Y-安全区域宽度(单边)
以上便是生成AGV占用矩形所必须提供的6大参数。当然,根据线段属性的不同,这些参数值也不同,但是,无论线段属性如何变化,以上参数必须提供(即我们接下来的讲解就是找它们 )。其中参数①②和具体线段有关,参数③到⑥和车体几何参数有关

在生成AGV占用矩形前我们先初始化AGV占用矩形树
初始化AGV占用矩形树
初始化树的目的是为了限制(缩小)我们在静态交通管制测试相交的占用矩形时遍历的区域,更何况如果我们不初始化这样一个树,我们的算法也没有一个矩形边界,没有这个边界就无法为算法提供参数。
我们需要为这个树Tree建立一个根Root,之后将为其建立子叶(Leafs),它们都基于一个类PositionedObjectsTree,这个类除了应该包括的边界参数外还包括一个区域内最大的AGV占用矩形数量,每个子叶中的AGV占用矩形的数量不超过1000,如果当前子叶中的占用矩形数量超过1000,那就要为这个子叶建立子叶,同样如果子叶的子叶也超过1000时,继续为子叶的子叶建立子叶。。远了,讲的远了现在可以不用明白。
如何初始化?遍历所有线段的覆盖矩形(覆盖矩形与AGV占用矩形是两码事),线段作为一维空间的成员为什么会有覆盖矩形?线段是有覆盖区域的,比如斜线段。一个和X、Y轴平行的线段的覆盖区域也是一个线段,我们通常认为它们覆盖矩形的上下值或者左右值相等,这样覆盖矩形就变成了一个线段。
遍历找到布局中所有线段的覆盖矩形的上,下,左,右的极值,极值:如果当前遍历的线段的四个属性中的一个比之前遍历过的线段的四个参数的值还大或者还小就替换掉之前的值。
代码:
Point topRight = new Point(int.MinValue, int.MinValue);
PointbottomLeft = new Point(int.MaxValue, int.MaxValue);
foreach(SegmentDefinitionsegmentDefinition insystemConfig.SegmentDefinitions)
{
if(segmentDefinition.CoveringRectangleBottom < bottomLeft.Y)
{
bottomLeft.Y= segmentDefinition.CoveringRectangleBottom;
}
if(segmentDefinition.CoveringRectangleLeft < bottomLeft.X)
{
bottomLeft.X= segmentDefinition.CoveringRectangleLeft;
}
if(segmentDefinition.CoveringRectangleTop > topRight.Y)
{
topRight.Y= segmentDefinition.CoveringRectangleTop;
}
if(segmentDefinition.CoveringRectangleRight > topRight.X)
{
topRight.X= segmentDefinition.CoveringRectangleRight;
}
}
agvOccupancyTree = newPositionedObjectsTree(bottomLeft, topRight, 1000);
初始化占用矩形树之后,我们准备一个字典类型的字段agvOccupancyBySegmentTable=new Dictionary<int,List<AgvOccupancyRegion>>();
目的是为了存放每一条线段的占用矩形组,以便于之后可以通过线段id来找到其占用矩形。
再准备一个字典类型的字段
segmentDataTable = new Dictionary<int,SegmentData>();
目的是为了存放每一条线段的线段数据SegmentData,SegmentData包括线段的结束角度EndOri和角度距离矩形组,角度距离矩形组中的字段/属性包括之前的线段Id组、占用矩形、启动距离和起始方向(其中启动距离我们后面再讲)
之后建立一个根据AGV类型来获取其涉及的线段的字典
Dictionary<AgvTypeDefinition,Hashtable>remainSegByAgvTypeTable=newDictionary<AgvTypeDefinition, Hashtable>();将所有符合该AGV类型驱动的线段放入key为该AGV类型的值中。
Ok,现在我们有了remainSegByAgvTypeTable,之后我们将从中为每一个AGV类型的remainSegments生成占用矩形,我们先为第一个AgvType及其remainSegments生成占用矩形。
已知参数agvType、remainSegs。
使用一个while循环,当remainSegments中有值时就为其中的线段生成占用矩形,每为一条线段生成一个占用矩形就将该线段从该agvType的remainSegments中移除Remove。
开始为第一条段startSegment生成占用矩形,这“第一条段”也是有讲究的,当AGV行驶方向改变180度,也就是反向行驶时,导航点也会发生变化(从前/后导航点转为后/前导航点),由于AGV上的“导航点”会变化,这会影响转变之后线段的占用矩形在线段上的角度,所以我们必须做一些限制(如果没有为agv指定起始角度,则第一条线段不能是曲线),且尽可能满足如下条件。
StartSegment的选择方法:
在文章“agv车体角度在曲线上的计算"时,线段的起始角度来自之前的线段的结束角度,但是第一条线段的之前的线段我们暂时不确定,所以也无法确定StartOri,没有startOri就无法确定结束角度endOri,所以StartSegment必须是非曲线,虽然非曲线的起始角度无法确定,但是结束角度可以确定,当然,有一些曲线之后的直线段的AGV车体角度也不是与直线段平行的,所以我们还需要一些其他的筛选条件,目的是为了尽量保证agv占用矩形在该线段上的任何位置都与直线段的方向平行。
方法1(优先)
后退线段的后一条前进 线段,且该线段不是曲线,且该线段属于该agvType的remainSegments。
注意:有一些后退线段可能没有下一条线段。
方法2
如果找不到方法1条件的线段(几乎不可能找不到),那就使用方法2,站点的退出线段也是一条startSegment,但是这里仅限“闲置站点”,并且startSegment应该是站点退出线段组中最长的一条,线段最长很好理解,如果agv车体在一条线段的起始坐标不与线段平行,那随着线段的延长,平行的可能性也将大大增加。
方法3
如果前面两个方法还是找不到startSegment(几乎不可能),那就从该agvType中随便找一条非曲线线段,同样的遍历完所有线段,找到所有线段中距离最长的线段。
Ok,现在我们有了第一条段startSegment,我们可以开始生成占用矩形了。
生成占用矩形(startSegment,agvType,remainSegments)

