]> rtime.felk.cvut.cz Git - hubacji1/iamcar.git/commitdiff
Merge branch 'feature/dubins-path'
authorJiri Vlasak <hubacji1@fel.cvut.cz>
Wed, 19 Jun 2019 11:56:00 +0000 (13:56 +0200)
committerJiri Vlasak <hubacji1@fel.cvut.cz>
Wed, 19 Jun 2019 11:56:00 +0000 (13:56 +0200)
16 files changed:
CHANGELOG.md
CMakeLists.txt
README.md
base/main.cc
base/rrtbase.cc
base/rrtnode.cc
decision_control/rrtplanner.cc
gplot.py
incl/bcar.h
incl/dubins.h [new file with mode: 0644]
incl/rrtnode.h
incl/steer.h
perception/obstacle.cc
vehicle_platform/bcar.cc
vehicle_platform/dubins.cc [new file with mode: 0644]
vehicle_platform/steer.cc

index b90c7cfc7699cb8ba7428cee4011f4d9bfee3ded..351d99b2a7df7ecf3a849252a3c8ee678017ace0 100644 (file)
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog][] and this project adheres to
 - Forward perpendicular parking methods for `SlotPlanner`.
 - Angle forward/backward parking.
 - Multiple `firsts` points tested before random sampling.
+- Dubins path steering procedure.
 
 ### Changed
 - Parking slot in scenario starts with the point closest to the init pose.
index 9a102c96dae7938d01dac5ae544bb67aeebe22da..9c2263ceadec0b204d3d1ebf62527e1cdba85e0d 100644 (file)
@@ -41,6 +41,7 @@ add_executable(go_car_go
 
         vehicle_platform/bcar.cc
         vehicle_platform/cost.cc
+        vehicle_platform/dubins.cc
         vehicle_platform/reeds_shepp.cpp
         vehicle_platform/steer.cc
 )
index 67255b3a399c90c56afe5fe54b2c4d88e04c64e8..69fa0b053327330def4263d4ed95e63a6369a0a9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -67,6 +67,7 @@ Implemented Steering procedures:
 - `st2` - Steer with maximum turning radius and direction in mind.
 - `st3` - Reeds and Shepp steer procedure.
 - `st4` - Very basic closed-loop simulator.
+- `st5` - Dubins paths.
 
 To disable *OpenMP*, add `-DCMAKE_DISABLE_FIND_PACKAGE_OpenMP=TRUE` to `cmake`
 command or to `build.sh` script.
index 9779164f37564762067fa219b8f7c9afca4c0447..4cafb4f1fb03509af538703546bafb1ead44a840 100644 (file)
@@ -272,7 +272,7 @@ int main()
                         p.tlog(p.findt());
                 }
         }
-        if (ps.slotType() == PARALLEL)
+        if (p.goal_found() && ps.slotType() == PARALLEL)
                 p.tlog(p.findt(p.slot_cusp().back()));
 #elif defined USE_PTHREAD
         bool gf = false;
index 8a44e6e572cbfff7a61fc79b92b579226c1e1cff..7a7c194d3e7df5519b2cd4cb223bc037cce30745 100644 (file)
@@ -399,12 +399,52 @@ bool RRTBase::glplot()
                 glVertex2f(GLVERTEX(this->samples_.back()));
                 glEnd();
         }
-        // Plot nodes
+        // Plot nodes (position, orientation
         std::vector<RRTNode *> s; // DFS stack
         std::vector<RRTNode *> r; // reset visited_
         RRTNode *tmp;
         glBegin(GL_LINES);
         s.push_back(this->root_);
+        while (s.size() > 0) {
+                tmp = s.back();
+                s.pop_back();
+                if (!tmp->visit()) {
+                        r.push_back(tmp);
+                        for (auto ch: tmp->children()) {
+                                s.push_back(ch);
+                                glColor3f(0.5, 0.5, 0.5);
+                                BicycleCar bc(tmp->x(), tmp->y(), tmp->h());
+                                glVertex2f(
+                                        bc.lfx() * GLPLWSCALE,
+                                        bc.lfy() * GLPLHSCALE
+                                );
+                                glVertex2f(
+                                        bc.lrx() * GLPLWSCALE,
+                                        bc.lry() * GLPLHSCALE
+                                );
+                                glVertex2f(
+                                        bc.lrx() * GLPLWSCALE,
+                                        bc.lry() * GLPLHSCALE
+                                );
+                                glVertex2f(
+                                        bc.rrx() * GLPLWSCALE,
+                                        bc.rry() * GLPLHSCALE
+                                );
+                                glVertex2f(
+                                        bc.rrx() * GLPLWSCALE,
+                                        bc.rry() * GLPLHSCALE
+                                );
+                                glVertex2f(
+                                        bc.rfx() * GLPLWSCALE,
+                                        bc.rfy() * GLPLHSCALE
+                                );
+                        }
+                }
+        }
+        glEnd();
+        // Plot nodes
+        glBegin(GL_LINES);
+        s.push_back(this->root_);
         while (s.size() > 0) {
                 tmp = s.back();
                 s.pop_back();
@@ -748,6 +788,8 @@ bool RRTBase::optp_dijkstra(
                         ch_cost = dnodes[tmp.ni].c +
                                 this->cost(cusps[tmp.ni], cusps[i]);
                         steered = this->steer(cusps[tmp.ni], cusps[i]);
+                        if (steered.size() <= 0)
+                                break;
                         for (unsigned int j = 0; j < steered.size() - 1; j++)
                                 steered[j]->add_child(steered[j + 1], 1);
                         if (this->collide(
@@ -883,7 +925,10 @@ bool RRTBase::opt_path()
                 return false;
         std::vector<RRTNode *> cusps;
         for (unsigned int i = 0; i < tmp_cusps.size(); i++) {
-                if (tmp_cusps[i] != tmp_cusps[(i + 1) % tmp_cusps.size()])
+                if (!IS_NEAR(
+                        tmp_cusps[i],
+                        tmp_cusps[(i + 1) % tmp_cusps.size()]
+                ))
                         cusps.push_back(tmp_cusps[i]);
         }
         std::reverse(cusps.begin(), cusps.end());
index 59d5d4a07852ae225c5e2babb976b41efa135a4a..11ec99c87f6bc9b9a11e0b6b830e4340e7f2be28 100644 (file)
@@ -254,6 +254,18 @@ bool RRTNode::visit()
         return false;
 }
 
+bool RRTNode::inFront(RRTNode *n)
+{
+        float nx = cos(this->h());
+        float ny = sin(this->h());
+        float dx = n->x() - this->x();
+        float dy = n->y() - this->y();
+        float dot = nx * dx + ny * dy;
+        if (dot > 0)
+                return true;
+        return false;
+}
+
 RRTEdge::RRTEdge():
         init_(new RRTNode()),
         goal_(new RRTNode())
index a3c5f60461768e20b4db4cb00b7345cca1038566..cdaca08a1cce42e46b4e36d9baf08ea0387b045e 100644 (file)
@@ -454,47 +454,6 @@ bool T2::next()
                         }
                 }
         }
-        en_add = true;
-        cusps = 0;
-        for (auto ns: this->steer(pn, rs, 0.01)) {
-                ns->rs(rs);
-                if (!en_add) {
-                        delete ns;
-                } else if (IS_NEAR(pn, ns)) {
-                        delete ns;
-                } else {
-                        if (sgn(ns->s()) == 0 || sgn(pn->s()) != sgn(ns->s()))
-                                cusps++;
-                        if (cusps > 4)
-                                en_add = false;
-                        nvs = this->nv(
-                                        ns,
-                                        MIN(
-                                                GAMMA_RRTSTAR(
-                                                        this->nodes().size()),
-                                                0.2)); // TODO const
-                        this->nodes().push_back(ns);
-                        this->add_iy(ns);
-                        // connect
-                        if (!this->connect(pn, ns, nvs)) {
-                                this->iy_[IYI(ns->y())].pop_back();
-                                this->nodes().pop_back();
-                                delete ns;
-                                en_add = false;
-                        } else {
-                                this->add_ixy(ns);
-                                // rewire
-                                this->rewire(nvs, ns);
-                                pn = ns;
-                                newly_added.push_back(pn);
-                                if (this->goal_found(pn, CO)) {
-                                        this->goal_cost();
-                                        this->tlog(this->findt());
-                                        en_add = false;
-                                }
-                        }
-                }
-        }
         if (this->samples().size() <= 1)
                 return this->goal_found();
         // steer to goal
@@ -535,41 +494,6 @@ bool T2::next()
                                 }
                         }
                 }
-                en_add = true;
-                cusps = 0;
-                for (auto ns: this->steer(pn, this->goal(), 0.01)) {
-                        ns->rs(rs);
-                        if (!en_add) {
-                                delete ns;
-                        } else if (IS_NEAR(pn, ns)) {
-                                delete ns;
-                        } else {
-                                if (sgn(pn->s()) != sgn(ns->s()))
-                                        cusps++;
-                                if (cusps > 4)
-                                        en_add = false;
-                                this->nodes().push_back(ns);
-                                this->add_iy(ns);
-                                pn->add_child(ns, this->cost(pn, ns));
-                                if (this->collide(pn, ns)) {
-                                        pn->children().pop_back();
-                                        ns->remove_parent();
-                                        this->iy_[IYI(ns->y())].pop_back();
-                                        this->nodes().pop_back();
-                                        delete ns;
-                                        en_add = false;
-                                } else {
-                                        this->add_ixy(ns);
-                                        this->ocost(ns);
-                                        pn = ns;
-                                        if (this->goal_found(pn, CO)) {
-                                                this->goal_cost();
-                                                this->tlog(this->findt());
-                                                en_add = false;
-                                        }
-                                }
-                        }
-                }
         }
         // steer to goals
         for (auto na: newly_added) {
index 0cc1168665b16ff3173d6eaf6b0005e82e6f209c..1e9005163fd5603cd68578bfa81a3745b8f56d71 100644 (file)
--- a/gplot.py
+++ b/gplot.py
@@ -263,139 +263,9 @@ def print_successrate():
         print("{}: {}".format(a["f"], v[a["f"]]))
 
 if __name__ == "__main__":
-    plt.rcParams["font.size"] = 29
-    res = []
-    LOGF = "log-slotplanner"
-    for d in listdir(LOGF):
-        r = {}
-        for sf in [i["f"] for i in LOG]:
-            r[sf] = load_trajectories("{}/{}/{}".format(LOGF, d, sf))
-            res.append({
-                "f": d,
-                "elap": get_val_if_exist(r["T2"], "elap"),
-                "rrte": get_val_if_exist(r["T2"], "rrte"),
-                "ppse": get_val_if_exist(r["T2"], "ppse"),
-                "succ": (
-                    count_if_exist(r["T2"], "traj") /
-                        count_if_exist(r["T2"], "elap")
-                ),
-            })
-    res2 = []
-    LOGF = "log-rrt"
-    for d in listdir(LOGF):
-        r = {}
-        for sf in [i["f"] for i in LOG]:
-            r[sf] = load_trajectories("{}/{}/{}".format(LOGF, d, sf))
-            res2.append({
-                "f": d,
-                "elap": get_val_if_exist(r["T2"], "elap"),
-                "rrte": get_val_if_exist(r["T2"], "rrte"),
-                "ppse": get_val_if_exist(r["T2"], "ppse"),
-                "succ": (
-                    count_if_exist(r["T2"], "traj") /
-                        count_if_exist(r["T2"], "elap")
-                ),
-            })
-
-    fig = plt.figure()
-    # For color scheme
-    # see https://github.com/vega/vega/wiki/Scales#scale-range-literals
-    ax = fig.add_subplot(111)
-    ax.set_title("""Elapsed time for different lengths
-                of parallel parking slot""")
-
-    ax.set_ylabel("Time [s]")
-    ax.set_xlabel("Parking slot length [m]")
-
-    # res Slot Planner
-    coord = [float(r["f"].split("_")[1]) for r in res]
-
-    #val = [sum(r["ppse"])/len(r["ppse"]) for r in res]
-    #fin = [(x, y) for (x, y) in zip(coord, val)]
-    #fin.sort()
-    #plt.plot(
-    #    [x for (x, y) in fin],
-    #    [y for (x, y) in fin],
-    #    color = "g",
-    #    label = "Slot Planner",
-    #)
-
-    val = [max(r["elap"]) for r in res]
-    fin = [(x, y) for (x, y) in zip(coord, val)]
-    fin.sort()
-    plt.plot(
-        [x for (x, y) in fin],
-        [y for (x, y) in fin],
-        color = "#e6550d",
-        linestyle = "--",
-        label = "Elapsed worst",
-    )
-
-    #val = [sum(r["rrte"])/len(r["rrte"]) for r in res]
-    #fin = [(x, y) for (x, y) in zip(coord, val)]
-    #fin.sort()
-    #plt.plot(
-    #    [x for (x, y) in fin],
-    #    [y for (x, y) in fin],
-    #    color = "#fd8d3c",
-    #    label = "RRT",
-    #)
-
-    val = [sum(r["elap"])/len(r["elap"]) for r in res]
-    fin = [(x, y) for (x, y) in zip(coord, val)]
-    fin.sort()
-    plt.plot(
-        [x for (x, y) in fin],
-        [y for (x, y) in fin],
-        color = "#e6550d",
-        label = "Elapsed average",
-    )
-
-    val = [r["succ"] for r in res]
-    fin = [(x, y) for (x, y) in zip(coord, val)]
-    fin.sort()
-    plt.plot(
-        [x for (x, y) in fin],
-        [y for (x, y) in fin],
-        #color = "#fd8d3c",
-        color = "#fdae6b",
-        label = "Success rate",
-    )
-
-    # res2 RRT
-    coord = [float(r["f"].split("_")[1]) for r in res2]
-
-    val = [max(r["elap"]) for r in res2]
-    fin = [(x, y) for (x, y) in zip(coord, val)]
-    fin.sort()
-    plt.plot(
-        [x for (x, y) in fin],
-        [y for (x, y) in fin],
-        color = "#3182bd",
-        linestyle = "--",
-        label = "Elapsed worst",
-    )
-
-    val = [sum(r["elap"])/len(r["elap"]) for r in res2]
-    fin = [(x, y) for (x, y) in zip(coord, val)]
-    fin.sort()
-    plt.plot(
-        [x for (x, y) in fin],
-        [y for (x, y) in fin],
-        color = "#3182bd",
-        label = "Elapsed average",
-    )
-
-    val = [r["succ"] for r in res2]
-    fin = [(x, y) for (x, y) in zip(coord, val)]
-    fin.sort()
-    plt.plot(
-        [x for (x, y) in fin],
-        [y for (x, y) in fin],
-        #color = "#6baed6",
-        color = "#9ecae1",
-        label = "Success rate",
-    )
-
-    plt.legend(bbox_to_anchor=(1, 1), loc=1, borderaxespad=0)
-    plt.show()
+    r = {}
+    for sf in [i["f"] for i in LOG]:
+        r[sf] = load_trajectories("{}/{}".format(LOGF, sf))
+    print_successrate()
+    plot_maxtime()
+    plot_costdist()
index fabbfafcf3d4926d6908a723a16c25dfae8a3959..5f6ac31a5ef24408d03e6b5eca3a749b4afcd730 100644 (file)
@@ -111,6 +111,14 @@ class BicycleCar: public RRTNode {
                 float alfa();
                 /** Return true if `n` not inside of `ccl`, `ccr` */
                 bool drivable(RRTNode *n);
+                /** Return ``RRTNode`` where ``BicycleCar`` may steer to.
+
+                When the node ``n`` is not drivable, use guess.
+
+                @param n The node to drive to.
+
+                */
+                RRTNode *drivableNode(RRTNode *n);
                 /** Return node rotated by `dh` around `c` */
                 BicycleCar *move(RRTNode *c, float dh);
 };
diff --git a/incl/dubins.h b/incl/dubins.h
new file mode 100644 (file)
index 0000000..61b0860
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2008-2018, Andrew Walker
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef DUBINS_H
+#define DUBINS_H
+
+typedef enum
+{
+    LSL = 0,
+    LSR = 1,
+    RSL = 2,
+    RSR = 3,
+    RLR = 4,
+    LRL = 5
+} DubinsPathType;
+
+typedef struct
+{
+    /* the initial configuration */
+    double qi[3];
+    /* the lengths of the three segments */
+    double param[3];
+    /* model forward velocity / model angular velocity */
+    double rho;
+    /* the path type described */
+    DubinsPathType type;
+} DubinsPath;
+
+#define EDUBOK        (0)   /* No error */
+#define EDUBCOCONFIGS (1)   /* Colocated configurations */
+#define EDUBPARAM     (2)   /* Path parameterisitation error */
+#define EDUBBADRHO    (3)   /* the rho value is invalid */
+#define EDUBNOPATH    (4)   /* no connection between configurations with this word */
+
+/**
+ * Callback function for path sampling
+ *
+ * @note the q parameter is a configuration
+ * @note the t parameter is the distance along the path
+ * @note the user_data parameter is forwarded from the caller
+ * @note return non-zero to denote sampling should be stopped
+ */
+typedef int (*DubinsPathSamplingCallback)(double q[3], double t, void* user_data);
+
+/**
+ * Generate a path from an initial configuration to
+ * a target configuration, with a specified maximum turning
+ * radii
+ *
+ * A configuration is (x, y, theta), where theta is in radians, with zero
+ * along the line x = 0, and counter-clockwise is positive
+ *
+ * @param path  - the resultant path
+ * @param q0    - a configuration specified as an array of x, y, theta
+ * @param q1    - a configuration specified as an array of x, y, theta
+ * @param rho   - turning radius of the vehicle (forward velocity divided by maximum angular velocity)
+ * @return      - non-zero on error
+ */
+int dubins_shortest_path(DubinsPath* path, double q0[3], double q1[3], double rho);
+
+/**
+ * Generate a path with a specified word from an initial configuration to
+ * a target configuration, with a specified turning radius
+ *
+ * @param path     - the resultant path
+ * @param q0       - a configuration specified as an array of x, y, theta
+ * @param q1       - a configuration specified as an array of x, y, theta
+ * @param rho      - turning radius of the vehicle (forward velocity divided by maximum angular velocity)
+ * @param pathType - the specific path type to use
+ * @return         - non-zero on error
+ */
+int dubins_path(DubinsPath* path, double q0[3], double q1[3], double rho, DubinsPathType pathType);
+
+/**
+ * Calculate the length of an initialised path
+ *
+ * @param path - the path to find the length of
+ */
+double dubins_path_length(DubinsPath* path);
+
+/**
+ * Return the length of a specific segment in an initialized path
+ *
+ * @param path - the path to find the length of
+ * @param i    - the segment you to get the length of (0-2)
+ */
+double dubins_segment_length(DubinsPath* path, int i);
+
+/**
+ * Return the normalized length of a specific segment in an initialized path
+ *
+ * @param path - the path to find the length of
+ * @param i    - the segment you to get the length of (0-2)
+ */
+double dubins_segment_length_normalized( DubinsPath* path, int i );
+
+/**
+ * Extract an integer that represents which path type was used
+ *
+ * @param path    - an initialised path
+ * @return        - one of LSL, LSR, RSL, RSR, RLR or LRL
+ */
+DubinsPathType dubins_path_type(DubinsPath* path);
+
+/**
+ * Calculate the configuration along the path, using the parameter t
+ *
+ * @param path - an initialised path
+ * @param t    - a length measure, where 0 <= t < dubins_path_length(path)
+ * @param q    - the configuration result
+ * @returns    - non-zero if 't' is not in the correct range
+ */
+int dubins_path_sample(DubinsPath* path, double t, double q[3]);
+
+/**
+ * Walk along the path at a fixed sampling interval, calling the
+ * callback function at each interval
+ *
+ * The sampling process continues until the whole path is sampled, or the callback returns a non-zero value
+ *
+ * @param path      - the path to sample
+ * @param stepSize  - the distance along the path for subsequent samples
+ * @param cb        - the callback function to call for each sample
+ * @param user_data - optional information to pass on to the callback
+ *
+ * @returns - zero on successful completion, or the result of the callback
+ */
+int dubins_path_sample_many(DubinsPath* path,
+                            double stepSize,
+                            DubinsPathSamplingCallback cb,
+                            void* user_data);
+
+/**
+ * Convenience function to identify the endpoint of a path
+ *
+ * @param path - an initialised path
+ * @param q    - the configuration result
+ */
+int dubins_path_endpoint(DubinsPath* path, double q[3]);
+
+/**
+ * Convenience function to extract a subset of a path
+ *
+ * @param path    - an initialised path
+ * @param t       - a length measure, where 0 < t < dubins_path_length(path)
+ * @param newpath - the resultant path
+ */
+int dubins_extract_subpath(DubinsPath* path, double t, DubinsPath* newpath);
+
+
+#endif /* DUBINS_H */
+
index 4a501bfa8b804869c73192110fc3a3bdb8b435e1..a515386044d94aa3f320b145c8b0c5cca56c0893 100644 (file)
@@ -109,6 +109,11 @@ class RRTNode {
                 static bool comp_ccost(RRTNode *n1, RRTNode *n2);
                 float update_ccost();
                 bool visit();
+                /** Return ``true`` if ``n`` is in front of ``this``.
+                *
+                * @param n The node that is beeing checked.
+                */
+                bool inFront(RRTNode *n);
 };
 
 class RRTEdge {
index 291ceb52e421874f772b529b683abd5a6c41cf56..2abe4c36ea891f988f13ff6d2ee494fc1d8dac1e 100644 (file)
@@ -26,5 +26,8 @@ std::vector<RRTNode *> st2(RRTNode *init, RRTNode *goal);
 std::vector<RRTNode *> st3(RRTNode *init, RRTNode *goal);
 std::vector<RRTNode *> st3(RRTNode *init, RRTNode *goal, float step);
 std::vector<RRTNode *> st4(RRTNode *init, RRTNode *goal);
+std::vector<RRTNode *> st5(RRTNode *init, RRTNode *goal);
+std::vector<RRTNode *> st5(RRTNode *init, RRTNode *goal, bool bw);
+std::vector<RRTNode *> st5(RRTNode *init, RRTNode *goal, float step, bool bw);
 
 #endif
index 03a0d8b3984ecb9dce40771f7cf27a885b3ccd8c..ad911f6f72ca11a1db82fab6e469564a60ffbd0a 100644 (file)
@@ -38,54 +38,52 @@ bool CircleObstacle::collide(RRTNode *n)
 
 bool CircleObstacle::collide(RRTEdge *e)
 {
-        std::vector<RRTEdge *> edges;
-        edges.push_back(e);
-        return this->collide(edges);
+        // see http://doswa.com/2009/07/13/circle-segment-intersectioncollision.html
+        // Find the closest point on seq.
+        float seg_v[] = {
+                e->goal()->x() - e->init()->x(),
+                e->goal()->y() - e->init()->y()
+        };
+        float pt_v[] = {
+                this->x() - e->init()->x(),
+                this->y() - e->init()->y()
+        };
+        float seg_vl = sqrt(pow(seg_v[0], 2) + pow(seg_v[1], 2));
+        // seg_vl must be > 0 otherwise it is invalid segment length.
+        if (seg_vl <= 0)
+                return false;
+        float seg_v_unit[] = {seg_v[0] / seg_vl, seg_v[1] / seg_vl};
+        float proj = pt_v[0]*seg_v_unit[0] + pt_v[1]*seg_v_unit[1];
+        float closest[] = {0, 0};
+        if (proj <= 0) {
+                closest[0] = e->init()->x();
+                closest[1] = e->init()->y();
+        } else if (proj >= seg_vl) {
+                closest[0] = e->goal()->x();
+                closest[1] = e->goal()->y();
+        } else {
+                float proj_v[] = {seg_v_unit[0] * proj, seg_v_unit[1] * proj};
+                closest[0] = proj_v[0] + e->init()->x();
+                closest[1] = proj_v[1] + e->init()->y();
+        }
+        // Find the segment circle.
+        float dist_v[] = {this->x() - closest[0], this->y() - closest[1]};
+        float dist = sqrt(pow(dist_v[0], 2) + pow(dist_v[1], 2));
+        if (dist <= this->r())
+                return true;
+        return false;
+        // Offset computation.
+        // float offset[] = {
+        //        dist_v[0] / dist * (BCAR_TURNING_RADIUS - dist),
+        //        dist_v[1] / dist * (BCAR_TURNING_RADIUS - dist)
+        // };
 }
 
 bool CircleObstacle::collide(std::vector<RRTEdge *> &edges)
 {
-        std::vector<RRTEdge *> bedges;
-        float radi = this->r() / cos(M_PI / 4); // TODO 4 is square
-        float angl = 2 * M_PI / 4;
-        float x1;
-        float y1;
-        float x2;
-        float y2;
-        int i;
-        for (i = 0; i < 4; i++) {
-                x1 = radi * cos(i * angl);
-                y1 = radi * sin(i * angl);
-                x2 = radi * cos((i + 1) * angl);
-                y2 = radi * sin((i + 1) * angl);
-                x1 += this->x();
-                y1 += this->y();
-                x2 += this->x();
-                y2 += this->y();
-                bedges.push_back(new RRTEdge(
-                                        new RRTNode(x1, y1, 0),
-                                        new RRTNode(x2, y2, 0)));
-        }
-        for (auto &be: bedges) {
-                for (auto &e: edges) {
-                        if (SegmentObstacle(
-                                                be->init(),
-                                                be->goal())
-                                        .collide(e)) {
-                                for (auto e: bedges) {
-                                        delete e->init();
-                                        delete e->goal();
-                                        delete e;
-                                }
-                                return true;
-                        }
-                }
-        }
-        for (auto e: bedges) {
-                delete e->init();
-                delete e->goal();
-                delete e;
-        }
+        for (auto e: edges)
+                if (this->collide(e))
+                        return true;
         return false;
 }
 
index f1580a184597d64f2d2ad3bf4764b17fb4c3cd55..b9ae800e50e225db1bfbb8d7595e503f829c9a44 100644 (file)
@@ -280,11 +280,37 @@ bool BicycleCar::drivable(RRTNode *n)
         RRTNode *ccr = this->ccr();
         if (pow(ccl->x() - n->x(), 2) + pow(ccl->y() - n->y(), 2) <=
                         pow(this->turning_radius_, 2))
-                drivable = false;
+                return false;
         if (pow(ccr->x() - n->x(), 2) + pow(ccr->y() - n->y(), 2) <=
                         pow(this->turning_radius_, 2))
-                drivable = false;
-        return drivable;
+                return false;
+        return true;
+}
+
+RRTNode *BicycleCar::drivableNode(RRTNode *n)
+{
+        bool drivable = true;
+        RRTNode *ccl = this->ccl();
+        if (
+                pow(ccl->x() - n->x(), 2) + pow(ccl->y() - n->y(), 2)
+                <= pow(this->turning_radius_, 2)
+        ) {
+                if (this->inFront(n))
+                        return this->move(ccl, M_PI);
+                else
+                        return this->move(ccl, -M_PI);
+        }
+        RRTNode *ccr = this->ccr();
+        if (
+                pow(ccr->x() - n->x(), 2) + pow(ccr->y() - n->y(), 2)
+                <= pow(this->turning_radius_, 2)
+        ) {
+                if (this->inFront(n))
+                        return this->move(ccr, -M_PI);
+                else
+                        return this->move(ccr, M_PI);
+        }
+        return n;
 }
 
 BicycleCar *BicycleCar::move(RRTNode *c, float dh)
diff --git a/vehicle_platform/dubins.cc b/vehicle_platform/dubins.cc
new file mode 100644 (file)
index 0000000..20813d0
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2008-2018, Andrew Walker
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifdef WIN32
+#define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+#include "dubins.h"
+
+#define EPSILON (10e-10)
+
+typedef enum
+{
+    L_SEG = 0,
+    S_SEG = 1,
+    R_SEG = 2
+} SegmentType;
+
+/* The segment types for each of the Path types */
+const SegmentType DIRDATA[][3] = {
+    { L_SEG, S_SEG, L_SEG },
+    { L_SEG, S_SEG, R_SEG },
+    { R_SEG, S_SEG, L_SEG },
+    { R_SEG, S_SEG, R_SEG },
+    { R_SEG, L_SEG, R_SEG },
+    { L_SEG, R_SEG, L_SEG }
+};
+
+typedef struct
+{
+    double alpha;
+    double beta;
+    double d;
+    double sa;
+    double sb;
+    double ca;
+    double cb;
+    double c_ab;
+    double d_sq;
+} DubinsIntermediateResults;
+
+
+int dubins_word(DubinsIntermediateResults* in, DubinsPathType pathType, double out[3]);
+int dubins_intermediate_results(DubinsIntermediateResults* in, double q0[3], double q1[3], double rho);
+
+/**
+ * Floating point modulus suitable for rings
+ *
+ * fmod doesn't behave correctly for angular quantities, this function does
+ */
+double fmodr( double x, double y)
+{
+    return x - y*floor(x/y);
+}
+
+double mod2pi( double theta )
+{
+    return fmodr( theta, 2 * M_PI );
+}
+
+int dubins_shortest_path(DubinsPath* path, double q0[3], double q1[3], double rho)
+{
+    int i, errcode;
+    DubinsIntermediateResults in;
+    double params[3];
+    double cost;
+    double best_cost = INFINITY;
+    int best_word = -1;
+    errcode = dubins_intermediate_results(&in, q0, q1, rho);
+    if(errcode != EDUBOK) {
+        return errcode;
+    }
+
+
+    path->qi[0] = q0[0];
+    path->qi[1] = q0[1];
+    path->qi[2] = q0[2];
+    path->rho = rho;
+
+    for( i = 0; i < 6; i++ ) {
+        DubinsPathType pathType = (DubinsPathType)i;
+        errcode = dubins_word(&in, pathType, params);
+        if(errcode == EDUBOK) {
+            cost = params[0] + params[1] + params[2];
+            if(cost < best_cost) {
+                best_word = i;
+                best_cost = cost;
+                path->param[0] = params[0];
+                path->param[1] = params[1];
+                path->param[2] = params[2];
+                path->type = pathType;
+            }
+        }
+    }
+    if(best_word == -1) {
+        return EDUBNOPATH;
+    }
+    return EDUBOK;
+}
+
+int dubins_path(DubinsPath* path, double q0[3], double q1[3], double rho, DubinsPathType pathType)
+{
+    int errcode;
+    DubinsIntermediateResults in;
+    errcode = dubins_intermediate_results(&in, q0, q1, rho);
+    if(errcode == EDUBOK) {
+        double params[3];
+        errcode = dubins_word(&in, pathType, params);
+        if(errcode == EDUBOK) {
+            path->param[0] = params[0];
+            path->param[1] = params[1];
+            path->param[2] = params[2];
+            path->qi[0] = q0[0];
+            path->qi[1] = q0[1];
+            path->qi[2] = q0[2];
+            path->rho = rho;
+            path->type = pathType;
+        }
+    }
+    return errcode;
+}
+
+double dubins_path_length( DubinsPath* path )
+{
+    double length = 0.;
+    length += path->param[0];
+    length += path->param[1];
+    length += path->param[2];
+    length = length * path->rho;
+    return length;
+}
+
+double dubins_segment_length( DubinsPath* path, int i )
+{
+    if( (i < 0) || (i > 2) )
+    {
+        return INFINITY;
+    }
+    return path->param[i] * path->rho;
+}
+
+double dubins_segment_length_normalized( DubinsPath* path, int i )
+{
+    if( (i < 0) || (i > 2) )
+    {
+        return INFINITY;
+    }
+    return path->param[i];
+}
+
+DubinsPathType dubins_path_type( DubinsPath* path )
+{
+    return path->type;
+}
+
+void dubins_segment( double t, double qi[3], double qt[3], SegmentType type)
+{
+    double st = sin(qi[2]);
+    double ct = cos(qi[2]);
+    if( type == L_SEG ) {
+        qt[0] = +sin(qi[2]+t) - st;
+        qt[1] = -cos(qi[2]+t) + ct;
+        qt[2] = t;
+    }
+    else if( type == R_SEG ) {
+        qt[0] = -sin(qi[2]-t) + st;
+        qt[1] = +cos(qi[2]-t) - ct;
+        qt[2] = -t;
+    }
+    else if( type == S_SEG ) {
+        qt[0] = ct * t;
+        qt[1] = st * t;
+        qt[2] = 0.0;
+    }
+    qt[0] += qi[0];
+    qt[1] += qi[1];
+    qt[2] += qi[2];
+}
+
+int dubins_path_sample( DubinsPath* path, double t, double q[3] )
+{
+    /* tprime is the normalised variant of the parameter t */
+    double tprime = t / path->rho;
+    double qi[3]; /* The translated initial configuration */
+    double q1[3]; /* end-of segment 1 */
+    double q2[3]; /* end-of segment 2 */
+    const SegmentType* types = DIRDATA[path->type];
+    double p1, p2;
+
+    if( t < 0 || t > dubins_path_length(path) ) {
+        return EDUBPARAM;
+    }
+
+    /* initial configuration */
+    qi[0] = 0.0;
+    qi[1] = 0.0;
+    qi[2] = path->qi[2];
+
+    /* generate the target configuration */
+    p1 = path->param[0];
+    p2 = path->param[1];
+    dubins_segment( p1,      qi,    q1, types[0] );
+    dubins_segment( p2,      q1,    q2, types[1] );
+    if( tprime < p1 ) {
+        dubins_segment( tprime, qi, q, types[0] );
+    }
+    else if( tprime < (p1+p2) ) {
+        dubins_segment( tprime-p1, q1, q,  types[1] );
+    }
+    else {
+        dubins_segment( tprime-p1-p2, q2, q,  types[2] );
+    }
+
+    /* scale the target configuration, translate back to the original starting point */
+    q[0] = q[0] * path->rho + path->qi[0];
+    q[1] = q[1] * path->rho + path->qi[1];
+    q[2] = mod2pi(q[2]);
+
+    return EDUBOK;
+}
+
+int dubins_path_sample_many(DubinsPath* path, double stepSize,
+                            DubinsPathSamplingCallback cb, void* user_data)
+{
+    int retcode;
+    double q[3];
+    double x = 0.0;
+    double length = dubins_path_length(path);
+    while( x <  length ) {
+        dubins_path_sample( path, x, q );
+        retcode = cb(q, x, user_data);
+        if( retcode != 0 ) {
+            return retcode;
+        }
+        x += stepSize;
+    }
+    return 0;
+}
+
+int dubins_path_endpoint( DubinsPath* path, double q[3] )
+{
+    return dubins_path_sample( path, dubins_path_length(path) - EPSILON, q );
+}
+
+int dubins_extract_subpath( DubinsPath* path, double t, DubinsPath* newpath )
+{
+    /* calculate the true parameter */
+    double tprime = t / path->rho;
+
+    if((t < 0) || (t > dubins_path_length(path)))
+    {
+        return EDUBPARAM;
+    }
+
+    /* copy most of the data */
+    newpath->qi[0] = path->qi[0];
+    newpath->qi[1] = path->qi[1];
+    newpath->qi[2] = path->qi[2];
+    newpath->rho   = path->rho;
+    newpath->type  = path->type;
+
+    /* fix the parameters */
+    newpath->param[0] = fmin( path->param[0], tprime );
+    newpath->param[1] = fmin( path->param[1], tprime - newpath->param[0]);
+    newpath->param[2] = fmin( path->param[2], tprime - newpath->param[0] - newpath->param[1]);
+    return 0;
+}
+
+int dubins_intermediate_results(DubinsIntermediateResults* in, double q0[3], double q1[3], double rho)
+{
+    double dx, dy, D, d, theta, alpha, beta;
+    if( rho <= 0.0 ) {
+        return EDUBBADRHO;
+    }
+
+    dx = q1[0] - q0[0];
+    dy = q1[1] - q0[1];
+    D = sqrt( dx * dx + dy * dy );
+    d = D / rho;
+    theta = 0;
+
+    /* test required to prevent domain errors if dx=0 and dy=0 */
+    if(d > 0) {
+        theta = mod2pi(atan2( dy, dx ));
+    }
+    alpha = mod2pi(q0[2] - theta);
+    beta  = mod2pi(q1[2] - theta);
+
+    in->alpha = alpha;
+    in->beta  = beta;
+    in->d     = d;
+    in->sa    = sin(alpha);
+    in->sb    = sin(beta);
+    in->ca    = cos(alpha);
+    in->cb    = cos(beta);
+    in->c_ab  = cos(alpha - beta);
+    in->d_sq  = d * d;
+
+    return EDUBOK;
+}
+
+int dubins_LSL(DubinsIntermediateResults* in, double out[3])
+{
+    double tmp0, tmp1, p_sq;
+
+    tmp0 = in->d + in->sa - in->sb;
+    p_sq = 2 + in->d_sq - (2*in->c_ab) + (2 * in->d * (in->sa - in->sb));
+
+    if(p_sq >= 0) {
+        tmp1 = atan2( (in->cb - in->ca), tmp0 );
+        out[0] = mod2pi(tmp1 - in->alpha);
+        out[1] = sqrt(p_sq);
+        out[2] = mod2pi(in->beta - tmp1);
+        return EDUBOK;
+    }
+    return EDUBNOPATH;
+}
+
+
+int dubins_RSR(DubinsIntermediateResults* in, double out[3])
+{
+    double tmp0 = in->d - in->sa + in->sb;
+    double p_sq = 2 + in->d_sq - (2 * in->c_ab) + (2 * in->d * (in->sb - in->sa));
+    if( p_sq >= 0 ) {
+        double tmp1 = atan2( (in->ca - in->cb), tmp0 );
+        out[0] = mod2pi(in->alpha - tmp1);
+        out[1] = sqrt(p_sq);
+        out[2] = mod2pi(tmp1 -in->beta);
+        return EDUBOK;
+    }
+    return EDUBNOPATH;
+}
+
+int dubins_LSR(DubinsIntermediateResults* in, double out[3])
+{
+    double p_sq = -2 + (in->d_sq) + (2 * in->c_ab) + (2 * in->d * (in->sa + in->sb));
+    if( p_sq >= 0 ) {
+        double p    = sqrt(p_sq);
+        double tmp0 = atan2( (-in->ca - in->cb), (in->d + in->sa + in->sb) ) - atan2(-2.0, p);
+        out[0] = mod2pi(tmp0 - in->alpha);
+        out[1] = p;
+        out[2] = mod2pi(tmp0 - mod2pi(in->beta));
+        return EDUBOK;
+    }
+    return EDUBNOPATH;
+}
+
+int dubins_RSL(DubinsIntermediateResults* in, double out[3])
+{
+    double p_sq = -2 + in->d_sq + (2 * in->c_ab) - (2 * in->d * (in->sa + in->sb));
+    if( p_sq >= 0 ) {
+        double p    = sqrt(p_sq);
+        double tmp0 = atan2( (in->ca + in->cb), (in->d - in->sa - in->sb) ) - atan2(2.0, p);
+        out[0] = mod2pi(in->alpha - tmp0);
+        out[1] = p;
+        out[2] = mod2pi(in->beta - tmp0);
+        return EDUBOK;
+    }
+    return EDUBNOPATH;
+}
+
+int dubins_RLR(DubinsIntermediateResults* in, double out[3])
+{
+    double tmp0 = (6. - in->d_sq + 2*in->c_ab + 2*in->d*(in->sa - in->sb)) / 8.;
+    double phi  = atan2( in->ca - in->cb, in->d - in->sa + in->sb );
+    if( fabs(tmp0) <= 1) {
+        double p = mod2pi((2*M_PI) - acos(tmp0) );
+        double t = mod2pi(in->alpha - phi + mod2pi(p/2.));
+        out[0] = t;
+        out[1] = p;
+        out[2] = mod2pi(in->alpha - in->beta - t + mod2pi(p));
+        return EDUBOK;
+    }
+    return EDUBNOPATH;
+}
+
+int dubins_LRL(DubinsIntermediateResults* in, double out[3])
+{
+    double tmp0 = (6. - in->d_sq + 2*in->c_ab + 2*in->d*(in->sb - in->sa)) / 8.;
+    double phi = atan2( in->ca - in->cb, in->d + in->sa - in->sb );
+    if( fabs(tmp0) <= 1) {
+        double p = mod2pi( 2*M_PI - acos( tmp0) );
+        double t = mod2pi(-in->alpha - phi + p/2.);
+        out[0] = t;
+        out[1] = p;
+        out[2] = mod2pi(mod2pi(in->beta) - in->alpha -t + mod2pi(p));
+        return EDUBOK;
+    }
+    return EDUBNOPATH;
+}
+
+int dubins_word(DubinsIntermediateResults* in, DubinsPathType pathType, double out[3])
+{
+    int result;
+    switch(pathType)
+    {
+    case LSL:
+        result = dubins_LSL(in, out);
+        break;
+    case RSL:
+        result = dubins_RSL(in, out);
+        break;
+    case LSR:
+        result = dubins_LSR(in, out);
+        break;
+    case RSR:
+        result = dubins_RSR(in, out);
+        break;
+    case LRL:
+        result = dubins_LRL(in, out);
+        break;
+    case RLR:
+        result = dubins_RLR(in, out);
+        break;
+    default:
+        result = EDUBNOPATH;
+    }
+    return result;
+}
+
+
index 5f4114ce29730e595e54dd4780d0dbaddfe94fbf..d89a06e68d2517ef25262b3c57e157e2d8e42137 100644 (file)
@@ -15,8 +15,10 @@ You should have received a copy of the GNU General Public License
 along with I am car. If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <algorithm>
 #include <cmath>
 #include "bcar.h"
+#include "dubins.h"
 #include "reeds_shepp.h"
 #include "steer.h"
 
@@ -35,6 +37,9 @@ along with I am car. If not, see <http://www.gnu.org/licenses/>.
 #define ST4KI 0
 #define ST4KD 0
 
+#define ST5TURNINGRADIUS 10.82
+#define ST5STEP 0.2
+
 std::vector<RRTNode *> st1(RRTNode *init, RRTNode *goal)
 {
         float angl;
@@ -150,3 +155,40 @@ std::vector<RRTNode *> st4(RRTNode *init, RRTNode *goal)
         }
         return nodes;
 }
+
+int cb_st5(double q[3], double t, void *user_data)
+{
+        std::vector<RRTNode *> *nodes = (std::vector<RRTNode *> *) user_data;
+        nodes->push_back(new RRTNode(q[0], q[1], q[2]));
+        return 0;
+}
+
+std::vector<RRTNode *> st5(RRTNode *init, RRTNode *goal)
+{
+        if (init->inFront(goal))
+                return st5(init, goal, ST5STEP, false);
+        return st5(init, goal, ST5STEP, true);
+}
+
+std::vector<RRTNode *> st5(RRTNode *init, RRTNode *goal, bool bw)
+{
+        return st5(init, goal, ST5STEP, bw);
+}
+
+std::vector<RRTNode *> st5(RRTNode *init, RRTNode *goal, float step, bool bw)
+{
+        std::vector<RRTNode *> nodes;
+        BicycleCar *bc = new BicycleCar(init->x(), init->y(), init->h());
+        RRTNode *dg = bc->drivableNode(goal); // drivable goal
+        double q0[] = {init->x(), init->y(), init->h()};
+        double q1[] = {dg->x(), dg->y(), dg->h()};
+        DubinsPath path;
+        if (bw)
+                dubins_shortest_path(&path, q1, q0, ST5TURNINGRADIUS);
+        else
+                dubins_shortest_path(&path, q0, q1, ST5TURNINGRADIUS);
+        dubins_path_sample_many(&path, step, cb_st5, &nodes);
+        if (bw)
+                std::reverse(nodes.begin(), nodes.end());
+        return nodes;
+}