/* Copyright 2017-2021 The MathWorks, Inc. */ /** * @file * Main interfaces for Reeds-Shepp motion primitive calculations. * To fully support code generation, note that this file needs to be fully * compliant with the C++98 standard. */ #ifndef AUTONOMOUSCODEGEN_REEDSSHEPP_H_ #define AUTONOMOUSCODEGEN_REEDSSHEPP_H_ #include // for sqrt, trigonometric functions #include // for max #include #ifdef BUILDING_LIBMWAUTONOMOUSCODEGEN #include "autonomouscodegen/autonomouscodegen_reeds_shepp_primitives.hpp" #else // To deal with the fact that PackNGo has no include file hierarchy during test #include "autonomouscodegen_reeds_shepp_primitives.hpp" #endif namespace autonomous { namespace reedsshepp { /* * The code for ReedsShepp primitives and computation of the various * segment paths is inspired from Open Motion Planning Library source. */ /* * RSSegmentType - Enumeration for holding segment type */ enum RSSegmentType { Left = 0, Right, Straight, NOP }; const RSSegmentType pathToSegment[18][5] = { {autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Left, autonomous::reedsshepp::NOP, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Right, autonomous::reedsshepp::NOP, autonomous::reedsshepp::NOP}, {autonomous::reedsshepp::Left, autonomous::reedsshepp::Right, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Left, autonomous::reedsshepp::Right}, {autonomous::reedsshepp::Right, autonomous::reedsshepp::Left, autonomous::reedsshepp::Straight, autonomous::reedsshepp::Right, autonomous::reedsshepp::Left}}; /* * shortestReedsSheppPath returns shortest normalized Reeds-Shepp path. */ inline autonomous::reedsshepp::ReedsSheppPath shortestReedsSheppPath(real64_T x, real64_T y, real64_T phi, real64_T reverseCost) { autonomous::reedsshepp::ReedsSheppPath minPath, tempPath; real64_T minLength, tempLength; autonomous::reedsshepp::computeCSCPath(x, y, phi, reverseCost, minPath); minLength = minPath.length(); autonomous::reedsshepp::computeCCCPath(x, y, phi, reverseCost, tempPath); tempLength = tempPath.length(); if (tempLength < minLength) { minLength = tempLength; minPath = tempPath; } autonomous::reedsshepp::computeCCCCPath(x, y, phi, reverseCost, tempPath); tempLength = tempPath.length(); if (tempLength < minLength) { minLength = tempLength; minPath = tempPath; } autonomous::reedsshepp::computeCCSCPath(x, y, phi, reverseCost, tempPath); tempLength = tempPath.length(); if (tempLength < minLength) { minLength = tempLength; minPath = tempPath; } autonomous::reedsshepp::computeCCSCCPath(x, y, phi, reverseCost, tempPath); tempLength = tempPath.length(); if (tempLength < minLength) { minLength = tempLength; minPath = tempPath; } return minPath; } /* * computeReedsSheppPath - compute Reeds-Shepp shortest/all path. */ inline autonomous::reedsshepp::ReedsSheppPath computeReedsSheppPath( const real64_T initialConfiguration[3], const real64_T finalConfiguration[3], const real64_T turningRadius, const real64_T reverseCost) { real64_T x1 = initialConfiguration[0]; real64_T y1 = initialConfiguration[1]; real64_T theta1 = initialConfiguration[2]; real64_T x2 = finalConfiguration[0]; real64_T y2 = finalConfiguration[1]; real64_T theta2 = finalConfiguration[2]; real64_T dx = x2 - x1; real64_T dy = y2 - y1; real64_T c = cos(theta1); real64_T s = sin(theta1); real64_T x = c * dx + s * dy; real64_T y = -s * dx + c * dy; real64_T phi = theta2 - theta1; return shortestReedsSheppPath(x / turningRadius, y / turningRadius, phi, reverseCost); } /* * AllReedsSheppPath returns all normalized Reeds-Shepp path. */ inline std::vector AllReedsSheppPath( real64_T x, real64_T y, real64_T phi, real64_T forwardCost, real64_T reverseCost, const boolean_T isOptimal, const boolean_T allPathTypes[autonomous::reedsshepp::TotalNumPaths], const uint32_T numTotalPathTypes) { std::vector allPaths; allPaths.reserve(numTotalPathTypes); autonomous::reedsshepp::computeCSCPathAll(x, y, phi, forwardCost, reverseCost, &allPathTypes[0], &allPaths); autonomous::reedsshepp::computeCCCPathAll(x, y, phi, forwardCost, reverseCost, &allPathTypes[0], &allPaths); autonomous::reedsshepp::computeCCCCPathAll(x, y, phi, forwardCost, reverseCost, &allPathTypes[0], &allPaths); autonomous::reedsshepp::computeCCSCPathAll(x, y, phi, forwardCost, reverseCost, &allPathTypes[0], &allPaths); autonomous::reedsshepp::computeCCSCCPathAll(x, y, phi, forwardCost, reverseCost, &allPathTypes[0], &allPaths); if (isOptimal) { real64_T optimalPath = autonomous::inf; uint32_T optimalInd = 0; // Round up to 15 decimal point to get same optimal path in // all system (maci, linux, windows). (Geck --> g1770360) real64_T round_val = pow(10.0, 15); for (uint32_T ind = 0; ind < numTotalPathTypes; ++ind) { // Round up to 15 decimal point to get same optimal path in // all system (maci, linux, windows . (Geck --> g1770360) real64_T tempcost = round(allPaths[ind].cost() * round_val) / round_val; if (optimalPath > tempcost) { optimalPath = tempcost; optimalInd = ind; } } std::vector path(1); path[0] = allPaths[optimalInd]; return path; } return allPaths; } /* * computeAllReedsSheppPaths - compute Reeds-Shepp shortest/all path. */ inline std::vector computeAllReedsSheppPaths( const real64_T initialConfiguration[3], const real64_T finalConfiguration[3], const real64_T turningRadius, const real64_T forwardCost, const real64_T reverseCost, const boolean_T isOptimal, const boolean_T allPathTypes[autonomous::reedsshepp::TotalNumPaths], const uint32_T numTotalPathTypes) { real64_T x1 = initialConfiguration[0]; real64_T y1 = initialConfiguration[1]; real64_T theta1 = initialConfiguration[2]; real64_T x2 = finalConfiguration[0]; real64_T y2 = finalConfiguration[1]; real64_T theta2 = finalConfiguration[2]; real64_T dx = x2 - x1; real64_T dy = y2 - y1; real64_T c = cos(theta1); real64_T s = sin(theta1); real64_T x = c * dx + s * dy; real64_T y = -s * dx + c * dy; real64_T phi = theta2 - theta1; return AllReedsSheppPath(x / turningRadius, y / turningRadius, phi, forwardCost, reverseCost, isOptimal, &allPathTypes[0], numTotalPathTypes); } /* * Compute direction value for start/goal pose. * * 1) For segmentLengths input {0, 9, 2, -10, 0}, * * computeDirectionAtEdges({0, 9, 2, -10, 0}, true) or * computeDirectionAtEdges({0, 9, 2, -10, 0}) will return 1, sign of first * non-zero segment (sign(9)) i.e. direction value of start pose. * * computeDirectionAtEdges({0, 9, 2, -10, 0}, false) will return -1, sign * of last non-zero segment (sign(-10)) i.e. direction value of goal pose. * * 2) For segmentLengths input {-8, -1, 2, 10, 0}, * * computeDirectionAtEdges({-8, -1, 2, 10, 0}, true) or * computeDirectionAtEdges({-8, -1, 2, 10, 0}) will return -1, sign of first * non-zero segment (sign(-8)) i.e. direction value of start pose. * * computeDirectionAtEdges({-8, -1, 2, 10, 0}, false) will return 1, sign * of last non-zero segment (sign(10)) i.e. direction value of goal pose. * */ inline real64_T computeDirectionAtEdges(const real64_T* segmentLengths, boolean_T startFlag = true) { real64_T direction = 1; for (uint32_T i = 0; i < 5; i++){ if(segmentLengths[i] != 0){ direction = ((segmentLengths[i]<0) ? -1 : 1); //Exit from loop for start pose direction computation if (startFlag){ break; } } } return direction; } /* * interpolateAlongInitializedRSPath - interpolate points along a * pre-computed ReedsShepp path. */ inline real64_T interpolateAlongInitializedRSPath(const real64_T pathLength, const RSSegmentType* segments, const real64_T* segmentLengths, const real64_T* from, const real64_T* towards, const real64_T t, const real64_T turningRadius, real64_T* state) { real64_T direction = 1; if (t <= 0.0) { state[0] = from[0]; state[1] = from[1]; state[2] = from[2]; // To ensure first pose direction is correct direction = computeDirectionAtEdges(segmentLengths); } else if (t >= 1.0) { state[0] = towards[0]; state[1] = towards[1]; state[2] = towards[2]; // To ensure last pose direction is correct direction = computeDirectionAtEdges(segmentLengths, false); } else { // Initialize at [0, 0, theta] state[0] = 0; state[1] = 0; state[2] = from[2]; real64_T seg = t * pathLength; real64_T v, phi; // Compute normalized update. for (uint32_T i = 0; i < 5 && seg > 0; ++i) { if (segmentLengths[i] < 0) { v = std::max(-seg, segmentLengths[i]); seg += v; direction = -1; } else { v = std::min(seg, segmentLengths[i]); seg -= v; direction = 1; } phi = state[2]; switch (segments[i]) { case autonomous::reedsshepp::Left: state[0] += sin(phi + v) - sin(phi); state[1] += -cos(phi + v) + cos(phi); state[2] = autonomous::wrapToTwoPi(phi + v); break; case autonomous::reedsshepp::Right: state[0] += -sin(phi - v) + sin(phi); state[1] += cos(phi - v) - cos(phi); state[2] = autonomous::wrapToTwoPi(phi - v); break; case autonomous::reedsshepp::Straight: state[0] += v * cos(phi); state[1] += v * sin(phi); break; case autonomous::reedsshepp::NOP: // no update needed break; } } // Denormalize and update. state[0] = from[0] + state[0] * turningRadius; state[1] = from[1] + state[1] * turningRadius; } return direction; } /* * interpolateRS - interpolate along ReedsShepp curve between states. */ inline void interpolateRS(const real64_T* from, const real64_T* towards, const real64_T maxDistance, const uint32_T numSteps, const real64_T turningRadius, const real64_T reverseCost, real64_T* state) { // Compute ReedsShepp path. autonomous::reedsshepp::ReedsSheppPath path = computeReedsSheppPath(from, towards, turningRadius, reverseCost); const RSSegmentType* segments = pathToSegment[path.getPathType()]; // Compute the fraction of path to be traversed based on maximum // connection distance (maxDistance). // * If the distance between the states is less than maxDistance, we // interpolate all the through to the destination state (towards). // * If the distance between the states is more than maxDistance, we // only interpolate a fraction of the path between the two states. // Find the distance between the states. const real64_T pathLength = path.length(); const real64_T dist = pathLength * turningRadius; // Find the fraction of the path to interpolate. const real64_T t = std::min(maxDistance / dist, 1.0); // Find interpolation step based on number of steps. const real64_T step = t / static_cast(numSteps); std::vector temp(3, 0); real64_T fraction = 0; // Interpolate along transition points const uint32_T numTransitions = 4; const uint32_T arrLength = numSteps + numTransitions; const real64_T* segLengths = path.getSegmentLengths(); for (uint32_T n = 0; n < numTransitions; ++n) { // Compute fraction along path corresponding to n-th transition // point fraction += (autonomous::abs(segLengths[n]) / pathLength); // Saturate at maxDistance. This has the effect of returning // the ending pose for all transition poses that come after the // maxDistance has been reached. fraction = std::min(fraction, t); interpolateAlongInitializedRSPath(pathLength, segments, segLengths, from, towards, fraction, turningRadius, &temp[0]); state[n] = temp[0]; state[n + arrLength] = temp[1]; state[n + 2 * arrLength] = temp[2]; } // Interpolate along path at equidistant intervals fraction = 0; for (uint32_T n = numTransitions; n < arrLength; ++n) { fraction += step; interpolateAlongInitializedRSPath(pathLength, segments, segLengths, from, towards, fraction, turningRadius, &temp[0]); state[n] = temp[0]; state[n + arrLength] = temp[1]; state[n + 2 * arrLength] = temp[2]; } } /* * interpolateReedsSheppSegments - interpolate along given states and Reeds-Shepp curve. */ inline void interpolateReedsSheppSegments(const real64_T* from, const real64_T* towards, const real64_T* samples, const uint32_T numSamples, const real64_T turningRadius, const real64_T* segmentLengths, const int32_T* segmentDirections, const uint32_T* segmentTypes, real64_T* state, real64_T* directions) { RSSegmentType segments[5] = {static_cast(segmentTypes[0]), static_cast(segmentTypes[1]), static_cast(segmentTypes[2]), static_cast(segmentTypes[3]), static_cast(segmentTypes[4])}; real64_T temp[3] = {0, 0, 0}; const real64_T pathLength = segmentLengths[0] + segmentLengths[1] + segmentLengths[2] + segmentLengths[3] + segmentLengths[4]; real64_T normalizedSegmentLengths[5] = {segmentLengths[0] / turningRadius, segmentLengths[1] / turningRadius, segmentLengths[2] / turningRadius, segmentLengths[3] / turningRadius, segmentLengths[4] / turningRadius}; const real64_T normalizedPathLength = normalizedSegmentLengths[0] + normalizedSegmentLengths[1] + normalizedSegmentLengths[2] + normalizedSegmentLengths[3] + normalizedSegmentLengths[4]; real64_T round_val = pow(10.0, 15); normalizedSegmentLengths[0] = round(normalizedSegmentLengths[0] * round_val) / round_val; normalizedSegmentLengths[1] = round(normalizedSegmentLengths[1] * round_val) / round_val; normalizedSegmentLengths[2] = round(normalizedSegmentLengths[2] * round_val) / round_val; normalizedSegmentLengths[3] = round(normalizedSegmentLengths[3] * round_val) / round_val; normalizedSegmentLengths[4] = round(normalizedSegmentLengths[4] * round_val) / round_val; // Round up to 15 decimal point to get same optimal path in all system // (maci, linux, windows). (Geck --> g1770360) normalizedSegmentLengths[0] = normalizedSegmentLengths[0]*static_cast(segmentDirections[0]); normalizedSegmentLengths[1] = normalizedSegmentLengths[1]*static_cast(segmentDirections[1]); normalizedSegmentLengths[2] = normalizedSegmentLengths[2]*static_cast(segmentDirections[2]); normalizedSegmentLengths[3] = normalizedSegmentLengths[3]*static_cast(segmentDirections[3]); normalizedSegmentLengths[4] = normalizedSegmentLengths[4]*static_cast(segmentDirections[4]); for (uint32_T n = 0; n < numSamples; ++n) { // Compute fraction along path corresponding to n-th transition // point // Round up to 15 decimal point to get same optimal path in all // system (maci, linux, windows). (Geck --> g1770360) real64_T roundedSample = round(samples[n] * round_val) / round_val; roundedSample = roundedSample / pathLength; if (directions){ directions[n] = interpolateAlongInitializedRSPath(normalizedPathLength, segments, normalizedSegmentLengths, from, towards, roundedSample, turningRadius, &temp[0]); }else{ interpolateAlongInitializedRSPath(normalizedPathLength, segments, normalizedSegmentLengths, from, towards, roundedSample, turningRadius, &temp[0]); } state[n] = round(temp[0] * round_val) / round_val; state[n + numSamples] = round(temp[1] * round_val) / round_val; state[n + 2 * numSamples] = round(temp[2] * round_val) / round_val; } } } // namespace reedsshepp } // namespace autonomous #endif /* AUTONOMOUSCODEGEN_REEDSSHEPP_H_ */