예제 #1
0
        //Smooth a path with constrained batch gradient descent (x = x - gamma * grad(x))
        //https://www.youtube.com/watch?v=a3whnj4H4DQ
        public static void ConstrainedGradientDescent(
            List <Vector3> path,
            List <bool> isNodeFixed,
            Map map,
            bool isCircular,
            float gamma,
            bool isDebugOn)
        {
            //The list with smooth coordinates
            List <Vector3> smoothPath = new List <Vector3>();

            //Add the old positions
            for (int i = 0; i < path.Count; i++)
            {
                smoothPath.Add(path[i]);
            }
            //Debug.Log(smoothPath.Count);

            //Stop smoothing when all points have moved a distance less than this distance
            //If 0.00001 we need more than 1000 iterations
            float tolerance = 0.001f;

            //How far has all points together moved this iteration?
            //We are comparing the distance sqr instead of magnitude which is faster
            float totalChangeSqr = tolerance * tolerance;

            //So we dont end up in an infinite loop, is generally < 100
            int iterations = 0;

            //If we find a nan value its super slow to print that we did each iteration, so we do it once in the end
            bool hasFoundNan = false;

            while (totalChangeSqr >= tolerance * tolerance)
            {
                if (iterations > 1000)
                {
                    break;
                }

                iterations += 1;

                totalChangeSqr = 0f;

                //We are using surrounding values, so we need an array where we add values found during the iteration
                List <Vector3> newValuesThisIteration = new List <Vector3>(smoothPath.Count);

                for (int i = 0; i < path.Count; i++)
                {
                    //Dont move nodes that are fixed
                    if (isNodeFixed[i])
                    {
                        newValuesThisIteration.Add(smoothPath[i]);

                        continue;
                    }

                    //Clamp when we reach end and beginning of list
                    //The first and last node should be set to fixed if the path is not set to circular
                    int i_plus_one = HelpStuff.ClampListIndex(i + 1, path.Count);

                    int i_minus_one = HelpStuff.ClampListIndex(i - 1, path.Count);

                    int i_plus_two = HelpStuff.ClampListIndex(i + 2, path.Count);

                    int i_minus_two = HelpStuff.ClampListIndex(i - 2, path.Count);

                    if (!isCircular)
                    {
                        if (i_plus_two == 0)
                        {
                            i_plus_two = path.Count - 1;
                        }
                        if (i_minus_two == path.Count - 1)
                        {
                            i_minus_two = 0;
                        }
                    }

                    //Smooth!
                    Vector3 newSmoothPos = smoothPath[i];

                    //1. Minimize the distance between this position and the surrounding positions
                    newSmoothPos += gamma * (smoothPath[i_plus_one] + smoothPath[i_minus_one] - 2f * smoothPath[i]);


                    //2. Right side
                    newSmoothPos += gamma * 0.5f * (2f * smoothPath[i_minus_one] - smoothPath[i_minus_two] - smoothPath[i]);


                    //3. Left side
                    newSmoothPos += gamma * 0.5f * (2f * smoothPath[i_plus_one] - smoothPath[i_plus_two] - smoothPath[i]);



                    //Sometimes the algorithm is unstable and shoots the vertices far away
                    //Maybe better to check for Nan in each part, so if the pos is NaN after optimizing to obstacle, dont add it???
                    if (float.IsNaN(newSmoothPos.x) || float.IsNaN(newSmoothPos.x) || float.IsNaN(newSmoothPos.x))
                    {
                        newSmoothPos = smoothPath[i];

                        hasFoundNan = true;
                    }

                    //Check if the new pos is within the map and if it is a value
                    if (map != null && !map.IsPosWithinGrid(newSmoothPos))
                    {
                        newSmoothPos = smoothPath[i];
                    }


                    //How far did we move the position this update?
                    totalChangeSqr += (newSmoothPos - smoothPath[i]).sqrMagnitude;


                    newValuesThisIteration.Add(newSmoothPos);
                }

                //Add the new values we created this iteration
                for (int i = 0; i < smoothPath.Count; i++)
                {
                    smoothPath[i] = newValuesThisIteration[i];
                }
            }

            if (isDebugOn)
            {
                string debugText = DisplayController.GetDisplayText("Smooth path iterations", iterations, null);

                Debug.Log(debugText);
            }

            if (hasFoundNan)
            {
                Debug.Log("Found Nan value");
            }



            //Add the new smooth positions to the original waypoints
            for (int i = 0; i < path.Count; i++)
            {
                path[i] = smoothPath[i];
            }
        }
예제 #2
0
        //
        // Generate a path with Hybrid A*
        //
        public static List <Node> GeneratePath(Car startCar, Car endCar, Map map, List <Node> allExpandedNodes, Car startTrailer)
        {
            //Reset timers
            timer_selectLowestCostNode    = 0;
            timer_addNodeToHeap           = 0;
            timer_findChildren            = 0;
            timer_isCollidingWithObstacle = 0;
            timer_ReedsSheppNode          = 0;
            timer_ReedsSheppHeuristics    = 0;
            timer_TrailerCollision        = 0;
            //Other data we want to track
            //How many nodes did we prune?
            int prunedNodes = 0;
            //To track max number of nodes in the heap
            int maxNodesInHeap = 0;


            //Init the data structure we need
            int mapWidth = map.MapWidth;

            //Is faster to cache this than using map.cellData
            Cell[,] cellData = map.cellData;

            //Open nodes - the parameter is how many items can fit in the heap
            //If we lower the heap size it will still find a path, which is more drunk
            Heap <Node> openNodes = new Heap <Node>(200000);

            //int in the dictionaries below is the rounded heading used to enter a cell
            HashSet <int>[,] closedCells = new HashSet <int> [mapWidth, mapWidth];
            //The node in the cell with the lowest g-cost at a certain angle
            Dictionary <int, Node>[,] lowestCostNodes = new Dictionary <int, Node> [mapWidth, mapWidth];
            //Trailer
            //int in the dictionaries below is the rounded heading used to enter a cell
            HashSet <int>[,] closedCellsTrailer     = new HashSet <int> [mapWidth, mapWidth];
            HashSet <int>[,] lowestCostNodesTrailer = new HashSet <int> [mapWidth, mapWidth];

            for (int x = 0; x < mapWidth; x++)
            {
                for (int z = 0; z < mapWidth; z++)
                {
                    closedCells[x, z]     = new HashSet <int>();
                    lowestCostNodes[x, z] = new Dictionary <int, Node>();

                    //Trailer
                    closedCellsTrailer[x, z]     = new HashSet <int>();
                    lowestCostNodesTrailer[x, z] = new HashSet <int>();
                }
            }


            //Create the first node
            //Why rear wheel? Because we need that position when simulating the "skeleton" car
            //and then it's easier if everything is done from the rear wheel positions
            IntVector2 startCellPos = map.ConvertWorldToCell(startCar.rearWheelPos);

            Node node = new Node(
                previousNode: null,
                rearWheelPos: startCar.rearWheelPos,
                heading: startCar.HeadingInRadians,
                isReversing: false);

            if (startTrailer != null)
            {
                node.TrailerHeadingInRadians = startTrailer.HeadingInRadians;
            }

            node.AddCosts(gCost: 0f, hCost: cellData[startCellPos.x, startCellPos.z].heuristics);

            openNodes.Add(node);


            //The end of the path, which we will return
            Node finalNode = null;


            //bools so we can break out of the main loop
            //Set when search is complete
            bool found = false;
            //Set if we can't find a node to expand
            bool resign = false;

            //To break out of the loop if it takes too long time
            int iterations = 0;

            IntVector2 goalCellPos = map.ConvertWorldToCell(endCar.rearWheelPos);

            while (!found && !resign)
            {
                if (iterations > 400000)
                {
                    Debug.Log("Stuck in infinite loop");

                    break;
                }

                iterations += 1;

                //If we don't have any nodes to expand
                if (openNodes.Count == 0)
                {
                    resign = true;

                    Debug.Log("Failed to find a path");
                }
                //We have nodes to expand
                else
                {
                    if (openNodes.Count > maxNodesInHeap)
                    {
                        maxNodesInHeap = openNodes.Count;
                    }

                    //Get the node with the lowest f cost
                    int timeBefore = Environment.TickCount;

                    Node nextNode = openNodes.RemoveFirst();

                    timer_selectLowestCostNode += Environment.TickCount - timeBefore;


                    //Close this cell
                    IntVector2 cellPos = map.ConvertWorldToCell(nextNode.rearWheelPos);

                    int roundedHeading = HelpStuff.RoundValue(nextNode.HeadingInDegrees, headingResolution);

                    HashSet <int> closedHeadingsInThisCell = closedCells[cellPos.x, cellPos.z];

                    bool haveAlreadyClosedCell = false;

                    //Close the cell, but we can still drive into this cell from another angle
                    if (!closedHeadingsInThisCell.Contains(roundedHeading))
                    {
                        closedHeadingsInThisCell.Add(roundedHeading);
                    }
                    else if (startTrailer == null)
                    {
                        haveAlreadyClosedCell = true;
                    }


                    if (startTrailer != null)
                    {
                        int roundedHeadingTrailer = HelpStuff.RoundValue(nextNode.TrailerHeadingInDegrees, headingResolutionTrailer);

                        HashSet <int> closedTrailerHeadingsInThisCell = closedCellsTrailer[cellPos.x, cellPos.z];

                        if (!closedTrailerHeadingsInThisCell.Contains(roundedHeadingTrailer))
                        {
                            closedTrailerHeadingsInThisCell.Add(roundedHeadingTrailer);
                        }
                        else
                        {
                            haveAlreadyClosedCell = true;
                        }
                    }

                    //We have already expanded a better node with the same heading so dont expand this node
                    if (haveAlreadyClosedCell)
                    {
                        iterations -= 1;

                        continue;
                    }


                    //For debugging
                    allExpandedNodes.Add(nextNode);

                    //Check if the vehicle has reached the target
                    float distanceSqrToGoal = (nextNode.rearWheelPos - endCar.rearWheelPos).sqrMagnitude;

                    //But we also need to make sure the vehiclke has correct heading
                    float headingDifference = Mathf.Abs(endCar.HeadingInDegrees - nextNode.HeadingInDegrees);

                    //If we end up in the same cell or is within a certain distance from the goal
                    if ((distanceSqrToGoal < posAccuracy * posAccuracy || (cellPos.x == goalCellPos.x && cellPos.z == goalCellPos.z)) &&
                        headingDifference < headingAccuracy)
                    {
                        found = true;

                        Debug.Log("Found a path");

                        finalNode = nextNode;

                        //Make sure the end node has the same position as the target
                        finalNode.rearWheelPos.x = endCar.rearWheelPos.x;
                        finalNode.rearWheelPos.z = endCar.rearWheelPos.z;
                    }
                    //If we havent found the goal, then expand this node
                    else
                    {
                        //Get all child nodes
                        timeBefore = Environment.TickCount;

                        List <Node> children = GetChildrenToNode(nextNode, map, cellData, startCar.carData, endCar, startTrailer);

                        timer_findChildren += Environment.TickCount - timeBefore;


                        //Should we add any of the child nodes to the open nodes?
                        foreach (Node child in children)
                        {
                            IntVector2 childCell = map.ConvertWorldToCell(child.rearWheelPos);

                            int roundedChildHeading = HelpStuff.RoundValue(child.HeadingInDegrees, headingResolution);

                            //Has this cell been closed with this heading?
                            //If so, it means we already have a path at this cell with this heading,
                            //and the existing node is cheaper because we have already expanded it
                            if (closedCells[childCell.x, childCell.z].Contains(roundedChildHeading) && startTrailer == null)
                            {
                                prunedNodes += 1;

                                continue;
                            }
                            //If we have a trailer
                            else if (closedCells[childCell.x, childCell.z].Contains(roundedChildHeading) && startTrailer != null)
                            {
                                int roundedTrailerHeading = HelpStuff.RoundValue(child.TrailerHeadingInDegrees, headingResolutionTrailer);

                                if (closedCellsTrailer[childCell.x, childCell.z].Contains(roundedTrailerHeading))
                                {
                                    prunedNodes += 1;

                                    continue;
                                }
                            }

                            //Have we already expanded a node with lower cost in this cell at this heading?
                            float costSoFar = child.gCost;

                            //The dictionary with lowest cost nodes in this cell
                            Dictionary <int, Node> nodesWithLowestCost = lowestCostNodes[childCell.x, childCell.z];

                            //Have we expanded with this angle to the cell before?
                            if (nodesWithLowestCost.ContainsKey(roundedChildHeading) && startTrailer == null)
                            {
                                //If the open node has a large gCost then we need to update that node with data
                                //from the child node
                                if (costSoFar < nodesWithLowestCost[roundedChildHeading].gCost)
                                {
                                    //If this child node is better then we should update the node in the open list
                                    //which is faster than deleting the old node and adding this child node
                                    Node existingNode = nodesWithLowestCost[roundedChildHeading];

                                    child.StealDataFromThisNode(existingNode);

                                    //Modify the heap-position of the node already in the open nodes
                                    openNodes.UpdateItem(existingNode);
                                }
                                //If the open node has a smaller gCost, then we dont need this child node, so do nothing

                                prunedNodes += 1;

                                continue;
                            }
                            //We have a trailer
                            else if (nodesWithLowestCost.ContainsKey(roundedChildHeading) && startTrailer != null)
                            {
                                //Have we expanded to this node before with this trailer heading
                                int roundedTrailerHeading = HelpStuff.RoundValue(child.TrailerHeadingInDegrees, headingResolutionTrailer);

                                if (lowestCostNodesTrailer[childCell.x, childCell.z].Contains(roundedTrailerHeading))
                                {
                                    //If the open node has a large gCost then we need to update that node with data
                                    //from the child node
                                    if (costSoFar < nodesWithLowestCost[roundedChildHeading].gCost)
                                    {
                                        //If this child node is better then we should update the node in the open list
                                        //which is faster than deleting the old node and adding this child node
                                        Node existingNode = nodesWithLowestCost[roundedChildHeading];

                                        child.StealDataFromThisNode(existingNode);

                                        //Modify the heap-position of the node already in the open nodes
                                        openNodes.UpdateItem(existingNode);
                                    }
                                    //If the open node has a smaller gCost, then we dont need this child node, so do nothing

                                    prunedNodes += 1;

                                    continue;
                                }
                            }
                            else
                            {
                                //Add the node to the cell with this angle
                                nodesWithLowestCost[roundedChildHeading] = child;

                                if (startTrailer != null)
                                {
                                    int roundedTrailerHeading = HelpStuff.RoundValue(child.TrailerHeadingInDegrees, headingResolutionTrailer);

                                    lowestCostNodesTrailer[childCell.x, childCell.z].Add(roundedTrailerHeading);
                                }
                            }


                            //Dont add the node if its colliding with an obstacle or is outside of map
                            timeBefore = Environment.TickCount;
                            if (ObstaclesDetection.HasCarInvalidPosition(child.rearWheelPos, child.heading, startCar.carData, map))
                            {
                                prunedNodes += 1;

                                continue;
                            }
                            timer_isCollidingWithObstacle += Environment.TickCount - timeBefore;


                            //Trailer obstacle calculations
                            int startTrailerTimer = Environment.TickCount;

                            if (startTrailer != null)
                            {
                                //Now we need to check if this new position is valid by checking for collision with obstacles and the drag vehicle
                                //To do that we need the rear wheel pos of the trailer with this heading
                                //We know where the trailer is attached to the drag vehicle
                                Vector3 trailerAttachmentPoint = startCar.carData.GetTrailerAttachmentPoint(child.rearWheelPos, child.HeadingInRadians);

                                //Now we need the trailer's rear-wheel pos based on the new heading
                                Vector3 trailerRearWheelPos = startTrailer.carData.GetTrailerRearWheelPos(trailerAttachmentPoint, child.TrailerHeadingInRadians);

                                //Obstacle detection
                                //With the environment
                                if (ObstaclesDetection.HasCarInvalidPosition(trailerRearWheelPos, child.TrailerHeadingInRadians, startTrailer.carData, map))
                                {
                                    prunedNodes += 1;

                                    //Debug.Log("Semi trailer environment collision");

                                    continue;
                                }
                                //With the drag vehicle
                                if (ObstaclesDetection.IsTrailerCollidingWithDragVehicle(
                                        child.rearWheelPos, child.HeadingInRadians, startCar.carData,
                                        trailerRearWheelPos, child.TrailerHeadingInRadians, startTrailer.carData))
                                {
                                    prunedNodes += 1;

                                    //Debug.Log("Semi trailer collision");

                                    continue;
                                }
                            }
                            timer_TrailerCollision += Environment.TickCount - startTrailerTimer;


                            timeBefore = Environment.TickCount;

                            openNodes.Add(child);

                            timer_addNodeToHeap += Environment.TickCount - timeBefore;
                        }
                    }
                }
            }


            //Generate the final path when Hybrid A* has found the goal
            List <Node> finalPath = GenerateFinalPath(finalNode);


            //Display how long time everything took
            string display = DisplayController.GetDisplayTimeText(timer_selectLowestCostNode, "Select lowest cost node");

            display += DisplayController.GetDisplayTimeText(timer_addNodeToHeap, "Add new node to heap");

            display += DisplayController.GetDisplayTimeText(timer_findChildren, "Find children");

            display += DisplayController.GetDisplayTimeText(timer_isCollidingWithObstacle, "Is node colliding");

            display += DisplayController.GetDisplayTimeText(timer_ReedsSheppNode, "Reeds-Shepp Node");

            display += DisplayController.GetDisplayTimeText(timer_ReedsSheppHeuristics, "Reeds-Shepp Heuristics");

            display += DisplayController.GetDisplayTimeText(timer_TrailerCollision, "Trailer collision");

            display += DisplayController.GetDisplayText("Max nodes in heap", maxNodesInHeap, ". ");

            display += DisplayController.GetDisplayText("Expanded nodes", allExpandedNodes.Count, ". ");

            display += DisplayController.GetDisplayText("Pruned nodes", prunedNodes, null);

            Debug.Log(display);


            //Display car positions along the final path
            DisplayController.DisplayVehicleAlongPath(finalPath, startCar.carData, startTrailer);


            return(finalPath);
        }
예제 #3
0
        //Smooth a path with batch gradient descent (x = x - gamma * grad(x))
        //https://www.youtube.com/watch?v=umAeJ7LMCfU
        public static void GradientDescent(
            List <Vector3> path,
            List <bool> isNodeFixed,
            List <Obstacle> obstacles,
            Map map,
            bool isCircular,
            float alpha, float beta, float gamma, float delta,
            bool isDebugOn)
        {
            //Using map.VoronoiField is slow in each iteration, so should cache
            VoronoiFieldCell[,] voronoiField = null;

            if (map != null && map.VoronoiField != null)
            {
                voronoiField = map.VoronoiField;
            }



            //The list with smooth coordinates
            List <Vector3> smoothPath = new List <Vector3>();

            //Add the old positions
            for (int i = 0; i < path.Count; i++)
            {
                smoothPath.Add(path[i]);
            }


            //Stop smoothing when all points have moved a distance less than this distance
            //If 0.00001 we need more than 1000 iterations
            float tolerance = 0.001f;

            //How far has all points together moved this iteration?
            //We are comparing the distance sqr instead of magnitude which is faster
            float totalChangeSqr = tolerance * tolerance;

            //So we dont end up in an infinite loop, is generally < 100
            int iterations = 0;

            //If we find a nan value its super slow to print that we did each iteration, so we do it once in the end
            bool hasFoundNan = false;

            while (totalChangeSqr >= tolerance * tolerance)
            {
                if (iterations > 1000)
                {
                    break;
                }

                iterations += 1;

                totalChangeSqr = 0f;

                //We are using surrounding values, so we need an array where we add values found during the iteration
                List <Vector3> newValuesThisIteration = new List <Vector3>(smoothPath.Count);

                for (int i = 0; i < path.Count; i++)
                {
                    //Dont move nodes that are fixed
                    if (isNodeFixed[i])
                    {
                        newValuesThisIteration.Add(smoothPath[i]);

                        continue;
                    }

                    //Clamp when we reach end and beginning of list
                    //The first and last node should be set to fixed if the path is not set to circular
                    int i_plus_one = HelpStuff.ClampListIndex(i + 1, path.Count);

                    int i_minus_one = HelpStuff.ClampListIndex(i - 1, path.Count);


                    //Smooth!

                    //1. Minimize the distance between the smooth path and the original path
                    Vector3 newSmoothPos = smoothPath[i] + alpha * (path[i] - smoothPath[i]);


                    //2. Minimize the distance between this position and the surrounding positions
                    newSmoothPos += beta * (smoothPath[i_plus_one] + smoothPath[i_minus_one] - 2f * smoothPath[i]);


                    //3. Maximize the distance to the closest obstacle
                    //Is sometimes unstable because we use the closest obstacle, so is bouncing back and forth
                    //until the loop stops because of infinite check
                    //if (obstacles != null)
                    //{
                    //    Vector3 closestObstaclePos = FindClosestObstaclePos(smoothPath[i], obstacles);

                    //    Vector3 dirToObstacle = closestObstaclePos - smoothPath[i];

                    //    //Ignore obstacles far away
                    //    float maxDist = 10f;

                    //    if (dirToObstacle.sqrMagnitude < maxDist * maxDist)
                    //    {
                    //        float distanceToObstacle = dirToObstacle.magnitude;

                    //        //To make obstacles closer more important
                    //        float scaler = 1f - (distanceToObstacle / maxDist);

                    //        newSmoothPos -= gamma * dirToObstacle.normalized * scaler;
                    //    }
                    //}

                    //We can also use the voronoi field to find the closest obstacle
                    if (voronoiField != null && gamma > 0f)
                    {
                        Vector3 nodePos = smoothPath[i];

                        //Get the data for this cell
                        IntVector2 cellPos = map.ConvertWorldToCell(nodePos);

                        //The data for this cell
                        VoronoiFieldCell v = voronoiField[cellPos.x, cellPos.z];

                        Vector3 closestObstaclePos = v.ClosestObstaclePos(nodePos, voronoiField);

                        if (closestObstaclePos.x != -1f)
                        {
                            Vector3 dirToObstacle = closestObstaclePos - nodePos;

                            //Ignore obstacles far away
                            float maxDist = 10f;

                            if (dirToObstacle.sqrMagnitude < maxDist * maxDist)
                            {
                                float distanceToObstacle = dirToObstacle.magnitude;

                                //To make obstacles closer more important
                                float scaler = 1f - (distanceToObstacle / maxDist);

                                newSmoothPos -= gamma * dirToObstacle.normalized * scaler;
                            }
                        }
                    }



                    //4. Use the Voronoi field to push vertices away from obstacles
                    //We need to find the derivative of the voronoi field function with respect to:
                    //- distance to obstacle edge - should be maximized
                    //- distance to voronoi edge - should be minimized
                    //...to optimize the distance to both edges
                    //This is kinda slow and useless since we are already pushing away the path from obstacles above?
                    if (voronoiField != null && delta > 0f)
                    {
                        Vector3 nodePos = smoothPath[i];

                        //Get the data for this cell
                        IntVector2 cellPos = map.ConvertWorldToCell(nodePos);

                        //The data for this cell
                        VoronoiFieldCell v = voronoiField[cellPos.x, cellPos.z];

                        //Same each iteration
                        float d_max = v.d_obs_max;
                        float a     = v.alpha;

                        Vector3 closestObstaclePos = v.ClosestObstaclePos(nodePos, voronoiField);
                        Vector3 closestEdgePos     = v.ClosestEdgePos(nodePos, voronoiField);

                        //If we are inside an obstacle when debugging, we wont have a closest
                        if (closestObstaclePos.x != -1f && closestEdgePos.x != -1f)
                        {
                            //The directions
                            Vector3 dirToObstacle = closestObstaclePos - nodePos;
                            Vector3 dirToEdge     = closestEdgePos - nodePos;

                            //The distances
                            float d_obs = dirToObstacle.magnitude;
                            float d_edg = dirToEdge.magnitude;


                            //The distance to voronoi edge
                            float upper_edge = a * d_obs * (d_obs - d_max) * (d_obs - d_max);
                            float lower_edge = d_max * d_max * (d_obs + a) * (d_edg + d_obs) * (d_edg + d_obs);

                            newSmoothPos -= delta * (upper_edge / lower_edge) * (-dirToEdge / d_edg);


                            //The distance to voronoi obstacle
                            float upper_obs = a * d_edg * (d_obs - d_max) * ((d_edg + 2f * d_max + a) * d_obs + (d_max + 2f * a) + a * d_max);
                            float lower_obs = d_max * d_max * (d_obs + a) * (d_obs + a) * (d_obs + d_edg) * (d_obs + d_edg);

                            newSmoothPos += delta * (upper_obs / lower_obs) * (dirToObstacle / d_obs);
                        }
                    }


                    //Sometimes the algorithm is unstable and shoots the vertices far away
                    //Maybe better to check for Nan in each part, so if the pos is NaN after optimizing to obstacle, dont add it???
                    if (float.IsNaN(newSmoothPos.x) || float.IsNaN(newSmoothPos.x) || float.IsNaN(newSmoothPos.x))
                    {
                        newSmoothPos = smoothPath[i];

                        hasFoundNan = true;
                    }

                    //Check if the new pos is within the map and if it is a value
                    if (map != null && !map.IsPosWithinGrid(newSmoothPos))
                    {
                        newSmoothPos = smoothPath[i];
                    }


                    //How far did we move the position this update?
                    totalChangeSqr += (newSmoothPos - smoothPath[i]).sqrMagnitude;


                    newValuesThisIteration.Add(newSmoothPos);
                }

                //Add the new values we created this iteration
                for (int i = 0; i < smoothPath.Count; i++)
                {
                    smoothPath[i] = newValuesThisIteration[i];
                }
            }

            if (isDebugOn)
            {
                string debugText = DisplayController.GetDisplayText("Smooth path iterations", iterations, null);

                Debug.Log(debugText);
            }

            if (hasFoundNan)
            {
                Debug.Log("Found Nan value");
            }



            //Add the new smooth positions to the original waypoints
            for (int i = 0; i < path.Count; i++)
            {
                path[i] = smoothPath[i];
            }
        }