Obstacle avoidance finished
This commit is contained in:
parent
a0e2ef1afc
commit
00ce7d5a48
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* 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.geometry
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A closed polygon in 2D space
|
||||||
|
*/
|
||||||
|
public interface Polygon<T> {
|
||||||
|
public val points: List<Vector2D<T>>
|
||||||
|
}
|
@ -63,7 +63,7 @@ internal class ProjectionOntoLineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun projectionOntoLine3d() = with(Euclidean3DSpace) {
|
fun projectionOntoLine3d() = with(Euclidean3DSpace) {
|
||||||
val line = Line3D(
|
val line = Line(
|
||||||
base = vector(1.0, 3.5, 0.07),
|
base = vector(1.0, 3.5, 0.07),
|
||||||
direction = vector(2.0, -0.0037, 11.1111)
|
direction = vector(2.0, -0.0037, 11.1111)
|
||||||
)
|
)
|
||||||
@ -77,7 +77,7 @@ internal class ProjectionOntoLineTest {
|
|||||||
val result = projectToLine(v, line)
|
val result = projectToLine(v, line)
|
||||||
|
|
||||||
// assert that result is on the line
|
// assert that result is on the line
|
||||||
assertTrue(isCollinear(result - line.base, line.direction))
|
assertTrue(isCollinear(result - line.start, line.direction))
|
||||||
// assert that PV vector is orthogonal to direction vector
|
// assert that PV vector is orthogonal to direction vector
|
||||||
assertTrue(isOrthogonal(v - result, line.direction))
|
assertTrue(isOrthogonal(v - result, line.direction))
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ private fun outerTangent(from: Circle2D, to: Circle2D, direction: Direction): St
|
|||||||
}
|
}
|
||||||
return StraightTrajectory2D(
|
return StraightTrajectory2D(
|
||||||
p1,
|
p1,
|
||||||
vector(p1.x + (centers.end.x - centers.start.x), p1.y + (centers.end.y - centers.start.y))
|
vector(p1.x + (centers.end.x - centers.begin.x), p1.y + (centers.end.y - centers.begin.y))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ public object DubinsPath {
|
|||||||
val c1 = start.getRightCircle(turningRadius)
|
val c1 = start.getRightCircle(turningRadius)
|
||||||
val c2 = end.getRightCircle(turningRadius)
|
val c2 = end.getRightCircle(turningRadius)
|
||||||
val s = outerTangent(c1, c2, L)
|
val s = outerTangent(c1, c2, L)
|
||||||
val a1 = CircleTrajectory2D.of(c1.center, start, s.start, R)
|
val a1 = CircleTrajectory2D.of(c1.center, start, s.begin, R)
|
||||||
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, R)
|
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, R)
|
||||||
return CompositeTrajectory2D(a1, s, a3)
|
return CompositeTrajectory2D(a1, s, a3)
|
||||||
}
|
}
|
||||||
@ -216,7 +216,7 @@ public object DubinsPath {
|
|||||||
val c1 = start.getLeftCircle(turningRadius)
|
val c1 = start.getLeftCircle(turningRadius)
|
||||||
val c2 = end.getLeftCircle(turningRadius)
|
val c2 = end.getLeftCircle(turningRadius)
|
||||||
val s = outerTangent(c1, c2, R)
|
val s = outerTangent(c1, c2, R)
|
||||||
val a1 = CircleTrajectory2D.of(c1.center, start, s.start, L)
|
val a1 = CircleTrajectory2D.of(c1.center, start, s.begin, L)
|
||||||
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, L)
|
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, L)
|
||||||
return CompositeTrajectory2D(a1, s, a3)
|
return CompositeTrajectory2D(a1, s, a3)
|
||||||
}
|
}
|
||||||
@ -227,7 +227,7 @@ public object DubinsPath {
|
|||||||
val s = innerTangent(c1, c2, R)
|
val s = innerTangent(c1, c2, R)
|
||||||
if (s == null || c1.center.distanceTo(c2.center) < turningRadius * 2) return null
|
if (s == null || c1.center.distanceTo(c2.center) < turningRadius * 2) return null
|
||||||
|
|
||||||
val a1 = CircleTrajectory2D.of(c1.center, start, s.start, R)
|
val a1 = CircleTrajectory2D.of(c1.center, start, s.begin, R)
|
||||||
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, L)
|
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, L)
|
||||||
return CompositeTrajectory2D(a1, s, a3)
|
return CompositeTrajectory2D(a1, s, a3)
|
||||||
}
|
}
|
||||||
@ -238,7 +238,7 @@ public object DubinsPath {
|
|||||||
val s = innerTangent(c1, c2, L)
|
val s = innerTangent(c1, c2, L)
|
||||||
if (s == null || c1.center.distanceTo(c2.center) < turningRadius * 2) return null
|
if (s == null || c1.center.distanceTo(c2.center) < turningRadius * 2) return null
|
||||||
|
|
||||||
val a1 = CircleTrajectory2D.of(c1.center, start, s.start, L)
|
val a1 = CircleTrajectory2D.of(c1.center, start, s.begin, L)
|
||||||
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, R)
|
val a3 = CircleTrajectory2D.of(c2.center, s.end, end, R)
|
||||||
return CompositeTrajectory2D(a1, s, a3)
|
return CompositeTrajectory2D(a1, s, a3)
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ private fun dubinsTangentsToCircles(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Obstacle(
|
internal class Obstacle(
|
||||||
public val circles: List<Circle2D>,
|
public val circles: List<Circle2D>,
|
||||||
) {
|
) {
|
||||||
internal val tangents: List<Tangent> = boundaryTangents().first
|
internal val tangents: List<Tangent> = boundaryTangents().first
|
||||||
@ -281,7 +281,7 @@ public class Obstacle(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun Obstacle(vararg circles: Circle2D): Obstacle = Obstacle(listOf(*circles))
|
internal fun Obstacle(vararg circles: Circle2D): Obstacle = Obstacle(listOf(*circles))
|
||||||
|
|
||||||
private fun LineSegment2D.intersectSegment(other: LineSegment2D): Boolean {
|
private fun LineSegment2D.intersectSegment(other: LineSegment2D): Boolean {
|
||||||
fun crossProduct(v1: DoubleVector2D, v2: DoubleVector2D): Double {
|
fun crossProduct(v1: DoubleVector2D, v2: DoubleVector2D): Double {
|
||||||
@ -594,7 +594,19 @@ internal fun findAllPaths(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public object Obstacles {
|
||||||
|
public fun allPathsAvoiding(
|
||||||
|
start: DubinsPose2D,
|
||||||
|
finish: DubinsPose2D,
|
||||||
|
trajectoryRadius: Double,
|
||||||
|
obstaclePolygons: List<Polygon<Double>>,
|
||||||
|
): List<CompositeTrajectory2D> {
|
||||||
|
val obstacles: List<Obstacle> = obstaclePolygons.map { polygon ->
|
||||||
|
Obstacle(polygon.points.map { point -> Circle2D(point, trajectoryRadius) })
|
||||||
|
}
|
||||||
|
return findAllPaths(start, trajectoryRadius, finish, trajectoryRadius, obstacles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,17 +41,15 @@ public sealed interface Trajectory2D {
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("straight")
|
@SerialName("straight")
|
||||||
public data class StraightTrajectory2D(
|
public data class StraightTrajectory2D(
|
||||||
public val start: DoubleVector2D,
|
override val begin: DoubleVector2D,
|
||||||
public val end: DoubleVector2D,
|
override val end: DoubleVector2D,
|
||||||
) : Trajectory2D {
|
) : Trajectory2D, LineSegment2D {
|
||||||
|
|
||||||
override val length: Double get() = start.distanceTo(end)
|
override val length: Double get() = begin.distanceTo(end)
|
||||||
|
|
||||||
public val bearing: Angle get() = (atan2(end.x - start.x, end.y - start.y).radians).normalized()
|
public val bearing: Angle get() = (atan2(end.x - begin.x, end.y - begin.y).radians).normalized()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun StraightTrajectory2D.toSegment(): LineSegment<Vector2D<Double>> = LineSegment(start, end)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An arc segment
|
* An arc segment
|
||||||
*/
|
*/
|
||||||
|
@ -20,7 +20,7 @@ class DubinsTests {
|
|||||||
val lineP1 = straight.shift(1, 10.0).inverse()
|
val lineP1 = straight.shift(1, 10.0).inverse()
|
||||||
|
|
||||||
val start = DubinsPose2D(straight.end, straight.bearing)
|
val start = DubinsPose2D(straight.end, straight.bearing)
|
||||||
val end = DubinsPose2D(lineP1.start, lineP1.bearing)
|
val end = DubinsPose2D(lineP1.begin, lineP1.bearing)
|
||||||
val radius = 2.0
|
val radius = 2.0
|
||||||
val dubins = DubinsPath.all(start, end, radius)
|
val dubins = DubinsPath.all(start, end, radius)
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class DubinsTests {
|
|||||||
assertTrue(a.end.equalsFloat(b.start))
|
assertTrue(a.end.equalsFloat(b.start))
|
||||||
assertTrue(c.start.equalsFloat(b.end))
|
assertTrue(c.start.equalsFloat(b.end))
|
||||||
} else if (b is StraightTrajectory2D) {
|
} else if (b is StraightTrajectory2D) {
|
||||||
assertTrue(a.end.equalsFloat(DubinsPose2D(b.start, b.bearing)))
|
assertTrue(a.end.equalsFloat(DubinsPose2D(b.begin, b.bearing)))
|
||||||
assertTrue(c.start.equalsFloat(DubinsPose2D(b.end, b.bearing)))
|
assertTrue(c.start.equalsFloat(DubinsPose2D(b.end, b.bearing)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,14 @@ import space.kscience.kmath.geometry.sin
|
|||||||
fun DubinsPose2D.equalsFloat(other: DubinsPose2D) =
|
fun DubinsPose2D.equalsFloat(other: DubinsPose2D) =
|
||||||
x.equalsFloat(other.x) && y.equalsFloat(other.y) && bearing.radians.equalsFloat(other.bearing.radians)
|
x.equalsFloat(other.x) && y.equalsFloat(other.y) && bearing.radians.equalsFloat(other.bearing.radians)
|
||||||
|
|
||||||
fun StraightTrajectory2D.inverse() = StraightTrajectory2D(end, start)
|
fun StraightTrajectory2D.inverse() = StraightTrajectory2D(end, begin)
|
||||||
|
|
||||||
fun StraightTrajectory2D.shift(shift: Int, width: Double): StraightTrajectory2D = with(Euclidean2DSpace) {
|
fun StraightTrajectory2D.shift(shift: Int, width: Double): StraightTrajectory2D = with(Euclidean2DSpace) {
|
||||||
val dX = width * sin(inverse().bearing)
|
val dX = width * sin(inverse().bearing)
|
||||||
val dY = width * sin(bearing)
|
val dY = width * sin(bearing)
|
||||||
|
|
||||||
return StraightTrajectory2D(
|
return StraightTrajectory2D(
|
||||||
vector(start.x - dX * shift, start.y - dY * shift),
|
vector(begin.x - dX * shift, begin.y - dY * shift),
|
||||||
vector(end.x - dX * shift, end.y - dY * shift)
|
vector(end.x - dX * shift, end.y - dY * shift)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user