search for shortest path algorithm

This commit is contained in:
Artyom Degtyarev 2023-03-04 21:31:06 +03:00
parent 6288eb9d97
commit 61d43ae5fa
3 changed files with 225 additions and 40 deletions

View File

@ -276,7 +276,7 @@ public fun tangentsAlongTheObstacle(initialCircle: Circle2D,
public fun allFinished(paths: List<List<DubinsTangent>>, public fun allFinished(paths: List<List<DubinsTangent>>,
finalObstacle: DubinsObstacle): Boolean { finalObstacle: DubinsObstacle): Boolean {
for (path in paths) { for (path in paths) {
if (path[-1].endObstacle != finalObstacle) { if (path.last().endObstacle != finalObstacle) {
return false return false
} }
} }
@ -296,8 +296,8 @@ public fun pathLength(path: List<DubinsTangent>): Double {
return tangentsLength + arcsLength return tangentsLength + arcsLength
} }
public fun shortestPath(path: List<List<DubinsTangent>>): List<List<DubinsTangent>> { public fun shortestPath(path: List<List<DubinsTangent>>): List<DubinsTangent> {
return path.sortedBy { pathLength(it) } return path.sortedBy { pathLength(it) }[0]
} }
public typealias Path = List<DubinsTangent> public typealias Path = List<DubinsTangent>
@ -309,7 +309,7 @@ public fun findAllPaths(
finalDirection: DoubleVector2D, finalDirection: DoubleVector2D,
finalRadius: Double, finalRadius: Double,
obstacles: List<DubinsObstacle> obstacles: List<DubinsObstacle>
) { ): List<MutableList<DubinsTangent>> {
val initialCircles = constructTangentCircles( val initialCircles = constructTangentCircles(
startingPoint, startingPoint,
startingDirection, startingDirection,
@ -318,15 +318,15 @@ public fun findAllPaths(
finalPoint, finalPoint,
finalDirection, finalDirection,
finalRadius) finalRadius)
var outputTangents = mutableMapOf<PathTypes, List<Path>>() var outputTangents = mutableMapOf<PathTypes, MutableList<MutableList<DubinsTangent>>>()
for (i in listOf(DubinsPath.SimpleType.L, DubinsPath.SimpleType.R)) { for (i in listOf(DubinsPath.SimpleType.L, DubinsPath.SimpleType.R)) {
for (j in listOf(DubinsPath.SimpleType.L, DubinsPath.SimpleType.R)) { for (j in listOf(DubinsPath.SimpleType.L, DubinsPath.SimpleType.R)) {
val finalCircle = finalCircles[j]!! val finalCircle = finalCircles[j]!!
val finalObstacle = DubinsObstacle(listOf(finalCircle)) val finalObstacle = DubinsObstacle(listOf(finalCircle))
outputTangents[listOf(i, outputTangents[listOf(i,
DubinsPath.SimpleType.S, DubinsPath.SimpleType.S,
j)] = listOf( j)] = mutableListOf(
listOf(DubinsTangent( mutableListOf(DubinsTangent(
initialCircles[i]!!, initialCircles[i]!!,
initialCircles[i]!!, initialCircles[i]!!,
DubinsObstacle(listOf(initialCircles[i]!!)), DubinsObstacle(listOf(initialCircles[i]!!)),
@ -338,13 +338,13 @@ public fun findAllPaths(
while (!allFinished(outputTangents[listOf(i, while (!allFinished(outputTangents[listOf(i,
DubinsPath.SimpleType.S, DubinsPath.SimpleType.S,
j)]!!, finalObstacle)) { j)]!!, finalObstacle)) {
var newOutputTangents = listOf<Path>() var newOutputTangents = mutableListOf<MutableList<DubinsTangent>>()
for (line in outputTangents[listOf(i, for (line in outputTangents[listOf(i,
DubinsPath.SimpleType.S, DubinsPath.SimpleType.S,
j)]!!) { j)]!!) {
var currentCircle = line[-1].endCircle var currentCircle = line.last().endCircle
var currentDirection = line[-1].route[-1] var currentDirection = line.last().route.last()
var currentObstacle = line[-1].endObstacle var currentObstacle = line.last().endObstacle
var nextObstacle = DubinsObstacle(listOf()) var nextObstacle = DubinsObstacle(listOf())
if (currentObstacle != finalObstacle) { if (currentObstacle != finalObstacle) {
var tangentToFinal = outerTangents(currentObstacle, finalObstacle)[DubinsPath.toType(listOf( var tangentToFinal = outerTangents(currentObstacle, finalObstacle)[DubinsPath.toType(listOf(
@ -362,20 +362,7 @@ public fun findAllPaths(
nextObstacle = finalObstacle nextObstacle = finalObstacle
} }
var nextTangents = outerTangents(currentObstacle, nextObstacle) var nextTangents = outerTangents(currentObstacle, nextObstacle)
// for (pathType in listOf(
// listOf(DubinsPath.SimpleType.L,
// DubinsPath.SimpleType.S,
// DubinsPath.SimpleType.L),
// listOf(DubinsPath.SimpleType.L,
// DubinsPath.SimpleType.S,
// DubinsPath.SimpleType.R),
// listOf(DubinsPath.SimpleType.R,
// DubinsPath.SimpleType.S,
// DubinsPath.SimpleType.L),
// listOf(DubinsPath.SimpleType.R,
// DubinsPath.SimpleType.S,
// DubinsPath.SimpleType.R)
// )) {
for (pathType in nextTangents.keys) { for (pathType in nextTangents.keys) {
for (obstacle in obstacles) { for (obstacle in obstacles) {
// in Python code here try/except was used, but seems unneeded // in Python code here try/except was used, but seems unneeded
@ -396,13 +383,118 @@ public fun findAllPaths(
nextTangents.filter {(DubinsPath.toSimpleTypes(it.key)[0] == currentDirection)} nextTangents.filter {(DubinsPath.toSimpleTypes(it.key)[0] == currentDirection)}
as MutableMap<DubinsPath.Type, DubinsTangent> as MutableMap<DubinsPath.Type, DubinsTangent>
} }
TODO("rewrite fragment from Python") val tangentsAlong = mutableListOf<DubinsTangent>()
for (tangent in nextTangents.values) {
if (tangent.startCircle == line.last().endCircle) {
val lengthMaxPossible = arcLength(
tangent.startCircle,
line.last().lineSegment.end,
tangent.startObstacle.nextTangent(
tangent.startCircle,
DubinsPath.toType(listOf(currentDirection, DubinsPath.SimpleType.S, currentDirection)),
).lineSegment.begin,
currentDirection
)
val lengthCalculated = arcLength(
tangent.startCircle,
line.last().lineSegment.end,
tangent.lineSegment.begin,
currentDirection)
if (lengthCalculated > lengthMaxPossible) {
val tangentsAlong = tangentsAlongTheObstacle(
currentCircle,
DubinsPath.toType(listOf(
currentDirection,
DubinsPath.SimpleType.S,
currentDirection)),
tangent.startCircle,
currentObstacle
)
}
else {
val tangentsAlong = mutableListOf<DubinsTangent>()
}
}
else {
val tangentsAlong = tangentsAlongTheObstacle(
currentCircle,
DubinsPath.toType(listOf(
currentDirection,
DubinsPath.SimpleType.S,
currentDirection)),
tangent.startCircle,
currentObstacle
)
}
newOutputTangents.add((line + tangentsAlong + listOf(tangent)).toMutableList())
}
outputTangents[listOf(
i,
DubinsPath.SimpleType.S,
j
)] = newOutputTangents
}
else {
// minor changes from Python code
newOutputTangents.add(line)
outputTangents[listOf(
i,
DubinsPath.SimpleType.S,
j
)] = newOutputTangents
} }
} }
} }
for (lineId in outputTangents[listOf(
i,
DubinsPath.SimpleType.S,
j
)]!!.indices) {
val lastDirection = outputTangents[listOf(
i,
DubinsPath.SimpleType.S,
j
)]!![lineId].last().route[2]
outputTangents[listOf(
i,
DubinsPath.SimpleType.S,
j
)]!![lineId].add(DubinsTangent(
finalCircles[j]!!,
finalCircles[j]!!,
DubinsObstacle(
listOf(finalCircles[j]!!)
),
DubinsObstacle(
listOf(finalCircles[j]!!)
),
LineSegment2D(finalPoint, finalPoint),
listOf(
lastDirection,
DubinsPath.SimpleType.S,
j
)
))
} }
} }
} }
return outputTangents[listOf(
DubinsPath.SimpleType.L,
DubinsPath.SimpleType.S,
DubinsPath.SimpleType.L
)]!! + outputTangents[listOf(
DubinsPath.SimpleType.L,
DubinsPath.SimpleType.S,
DubinsPath.SimpleType.R
)]!! + outputTangents[listOf(
DubinsPath.SimpleType.R,
DubinsPath.SimpleType.S,
DubinsPath.SimpleType.L
)]!! + outputTangents[listOf(
DubinsPath.SimpleType.R,
DubinsPath.SimpleType.S,
DubinsPath.SimpleType.L)]!!
}

View File

@ -39,14 +39,14 @@ public fun Circle2D.tangentsToCircle(
} else { } else {
r1.absoluteValue + r2.absoluteValue r1.absoluteValue + r2.absoluteValue
} }
if (distance <= r) TODO("Intersecting circles are not supported yet") if (distance * distance >= r * r) {
val l = sqrt(distance * distance - r * r) val l = sqrt(distance * distance - r * r)
angle2 = if (r1.absoluteValue > r2.absoluteValue) { angle2 = if (r1.absoluteValue > r2.absoluteValue) {
angle1 + r1.sign * atan2(r.absoluteValue, l) angle1 + r1.sign * atan2(r.absoluteValue, l)
} else { } else {
angle1 - r2.sign * atan2(r.absoluteValue, l) angle1 - r2.sign * atan2(r.absoluteValue, l)
} }
val w = vector(-cos(angle2), sin(angle2)) val w = Euclidean2DSpace.vector(-cos(angle2), sin(angle2))
put( put(
route, route,
LineSegment( LineSegment(
@ -54,7 +54,61 @@ public fun Circle2D.tangentsToCircle(
other.center + w * r2 other.center + w * r2
) )
) )
} else {
throw Exception("Circles should not intersect")
}
}
}
}
public fun dubinsTangentsToCircles(
firstCircle: Circle2D,
secondCircle: Circle2D,
firstObstacle: DubinsObstacle,
secondObstacle: DubinsObstacle
): Map<DubinsPath.Type, DubinsTangent> = with(Euclidean2DSpace) {
val line = LineSegment(firstCircle.center, secondCircle.center)
val distance = line.begin.distanceTo(line.end)
val angle1 = atan2(secondCircle.center.x - firstCircle.center.x,
secondCircle.center.y - firstCircle.center.y)
var r: Double
var angle2: Double
val routes = mapOf(
DubinsPath.Type.RSR to Pair(firstCircle.radius, secondCircle.radius),
DubinsPath.Type.RSL to Pair(firstCircle.radius, -secondCircle.radius),
DubinsPath.Type.LSR to Pair(-firstCircle.radius, secondCircle.radius),
DubinsPath.Type.LSL to Pair(-firstCircle.radius, -secondCircle.radius)
)
return buildMap {
for ((route, r1r2) in routes) {
val r1 = r1r2.first
val r2 = r1r2.second
r = if (r1.sign == r2.sign) {
r1.absoluteValue - r2.absoluteValue
} else {
r1.absoluteValue + r2.absoluteValue
}
if (distance * distance >= r * r) {
val l = sqrt(distance * distance - r * r)
angle2 = if (r1.absoluteValue > r2.absoluteValue) {
angle1 + r1.sign * atan2(r.absoluteValue, l)
} else {
angle1 - r2.sign * atan2(r.absoluteValue, l)
}
val w = Euclidean2DSpace.vector(-cos(angle2), sin(angle2))
put(route, DubinsTangent(Circle2D(firstCircle.center, firstCircle.radius),
secondCircle,
firstObstacle,
secondObstacle,
LineSegment2D(
firstCircle.center + w * r1,
secondCircle.center + w * r2
),
DubinsPath.toSimpleTypes(route))
)
} else {
throw Exception("Circles should not intersect")
}
} }
} }
} }

View File

@ -0,0 +1,39 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.trajectory
import space.kscience.kmath.geometry.Circle2D
import space.kscience.kmath.geometry.DoubleVector2D
import space.kscience.kmath.geometry.Euclidean2DSpace.vector
import kotlin.test.Test
import kotlin.test.assertTrue
class DubinsTest {
@Test
fun firstPath() {
val startPoint = vector(-5.0, -1.0)
val startDirection = vector(1.0, 1.0)
val startRadius = 0.5
val finalPoint = vector(20.0, 4.0)
val finalDirection = vector(1.0, -1.0)
val finalRadius = 0.5
val obstacles = listOf(DubinsObstacle(listOf(
Circle2D(vector(7.0, 1.0), 5.0))))
val outputTangents = findAllPaths(
startPoint,
startDirection,
startRadius,
finalPoint,
finalDirection,
finalRadius,
obstacles)
val length = pathLength(shortestPath(outputTangents))
TODO("fix negative indices in boundaryTangents and accomplish test")
assertTrue(false)
}
}