diff --git a/README.md b/README.md
index 179405a..8222f00 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,6 @@ BreezySLAM
-NOTE: This branch (legacy) contains legacy support for Matlab, and Java, languages that I am no longer supporting in the [master](https://github.com/simondlevy/BreezySLAM) branch. If you're working in Python or C++, you should be using the master branch.
-
Simple, efficient, open-source package for Simultaneous Localization and Mapping in Python and C++
This repository contains everything you need to
diff --git a/examples/Log2PGM.java b/examples/Log2PGM.java
new file mode 100644
index 0000000..47d2100
--- /dev/null
+++ b/examples/Log2PGM.java
@@ -0,0 +1,363 @@
+/*
+ Log2PGM.java : BreezySLAM demo. Reads logfile with odometry and scan data from
+ Paris Mines Tech and produces a .PGM image file showing robot path
+ and final map.
+
+ For details see
+
+ @inproceedings{,
+ author = {Bruno Steux and Oussama El Hamzaoui},
+ title = {SinglePositionSLAM: a SLAM Algorithm in less than 200 lines of C code},
+ booktitle = {11th International Conference on Control, Automation, Robotics and Vision, ICARCV 2010, Singapore, 7-10
+ December 2010, Proceedings},
+ pages = {1975-1979},
+ publisher = {IEEE},
+ year = {2010}
+ }
+
+ Copyright (C) 2014 Simon D. Levy
+
+ This code is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This code is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this code. If not, see .
+ */
+
+import edu.wlu.cs.levy.breezyslam.components.Map;
+import edu.wlu.cs.levy.breezyslam.components.Scan;
+import edu.wlu.cs.levy.breezyslam.components.Position;
+import edu.wlu.cs.levy.breezyslam.components.URG04LX;
+import edu.wlu.cs.levy.breezyslam.components.PoseChange;
+
+import edu.wlu.cs.levy.breezyslam.robots.WheeledRobot;
+
+import edu.wlu.cs.levy.breezyslam.algorithms.RMHCSLAM;
+import edu.wlu.cs.levy.breezyslam.algorithms.DeterministicSLAM;
+import edu.wlu.cs.levy.breezyslam.algorithms.SinglePositionSLAM;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Vector;
+
+public class Log2PGM
+{
+ private static int MAP_SIZE_PIXELS = 800;
+ private static double MAP_SIZE_METERS = 32;
+ private static int SCAN_SIZE = 682;
+
+ private SinglePositionSLAM slam;
+
+ private boolean use_odometry;
+ private int random_seed;
+ private Rover robot;
+
+ static int coords2index(double x, double y)
+ {
+ return (int)(y * MAP_SIZE_PIXELS + x);
+ }
+
+ int mm2pix(double mm)
+ {
+ return (int)(mm / (MAP_SIZE_METERS * 1000. / MAP_SIZE_PIXELS));
+ }
+
+
+ // Class for Mines verison of URG-04LX Lidar -----------------------------------
+
+ private class MinesURG04LX extends URG04LX
+ {
+ public MinesURG04LX()
+ {
+ super( 70, // detectionMargin
+ 145); // offsetMillimeters
+ }
+ }
+
+ // Class for MinesRover custom robot -------------------------------------------
+
+ private class Rover extends WheeledRobot
+ {
+ public Rover()
+ {
+ super(77, // wheelRadiusMillimeters
+ 165); // halfAxleLengthMillimeters
+ }
+
+ protected WheeledRobot.WheelOdometry extractOdometry(
+ double timestamp,
+ double leftWheelOdometry,
+ double rightWheelOdometry)
+ {
+ // Convert microseconds to seconds, ticks to angles
+ return new WheeledRobot.WheelOdometry(
+ timestamp / 1e6,
+ ticksToDegrees(leftWheelOdometry),
+ ticksToDegrees(rightWheelOdometry));
+ }
+
+ protected String descriptorString()
+ {
+ return String.format("ticks_per_cycle=%d", this.TICKS_PER_CYCLE);
+ }
+
+
+ private double ticksToDegrees(double ticks)
+ {
+ return ticks * (180. / this.TICKS_PER_CYCLE);
+ }
+
+ private int TICKS_PER_CYCLE = 2000;
+ }
+
+ // Progress-bar class ----------------------------------------------------------------
+ // Adapted from http://code.activestate.com/recipes/168639-progress-bar-class/
+ // Downloaded 12 January 2014
+
+ private class ProgressBar
+ {
+ public ProgressBar(int minValue, int maxValue, int totalWidth)
+ {
+ this.progBar = "[]";
+ this.min = minValue;
+ this.max = maxValue;
+ this.span = maxValue - minValue;
+ this.width = totalWidth;
+ this.amount = 0; // When amount == max, we are 100% done
+ this.updateAmount(0); // Build progress bar string
+ }
+
+ void updateAmount(int newAmount)
+ {
+ if (newAmount < this.min)
+ {
+ newAmount = this.min;
+ }
+ if (newAmount > this.max)
+ {
+ newAmount = this.max;
+ }
+
+ this.amount = newAmount;
+
+ // Figure out the new percent done, round to an integer
+ float diffFromMin = (float)(this.amount - this.min);
+ int percentDone = (int)java.lang.Math.round((diffFromMin / (float)this.span) * 100.0);
+
+ // Figure out how many hash bars the percentage should be
+ int allFull = this.width - 2;
+ int numHashes = (int)java.lang.Math.round((percentDone / 100.0) * allFull);
+
+ // Build a progress bar with hashes and spaces
+ this.progBar = "[";
+ this.addToProgBar("#", numHashes);
+ this.addToProgBar(" ", allFull-numHashes);
+ this.progBar += "]";
+
+ // Figure out where to put the percentage, roughly centered
+ int percentPlace = (this.progBar.length() / 2) - ((int)(java.lang.Math.log10(percentDone+1)) + 1);
+ String percentString = String.format("%d%%", percentDone);
+
+ // Put it there
+ this.progBar = this.progBar.substring(0,percentPlace) + percentString + this.progBar.substring(percentPlace+percentString.length());
+ }
+
+ String str()
+ {
+ return this.progBar;
+ }
+
+ private String progBar;
+ private int min;
+ private int max;
+ private int span;
+ private int width;
+ private int amount;
+
+ private void addToProgBar(String s, int n)
+ {
+ for (int k=0; k 0 ?
+ new RMHCSLAM(laser, MAP_SIZE_PIXELS, MAP_SIZE_METERS, random_seed) :
+ new DeterministicSLAM(laser, MAP_SIZE_PIXELS, MAP_SIZE_METERS);
+
+ this.use_odometry = use_odometry;
+ this.random_seed = random_seed;
+ }
+
+ public byte [] processData(Vector scans, Vector odometries)
+ {
+ // Report what we're doing
+ int nscans = scans.size();
+ ProgressBar progbar = new ProgressBar(0, nscans, 80);
+
+ System.out.printf("Processing %d scans with%s odometry / with%s particle filter...\n",
+ nscans, this.use_odometry ? "" : "out", this.random_seed > 0 ? "" : "out");
+
+ Vector trajectory = new Vector();
+
+ for (int scanno=0; scanno ");
+ System.err.println("Example: java log2pgm exp2 1 9999");
+ System.exit(1);
+ }
+
+ // Grab input args
+ String dataset = argv[0];
+ boolean use_odometry = Integer.parseInt(argv[1]) == 0 ? false : true;
+ int random_seed = argv.length > 2 ? Integer.parseInt(argv[2]) : 0;
+
+ // Load the data from the file
+
+ Vector scans = new Vector();
+ Vector odometries = new Vector();;
+
+ String filename = dataset + ".dat";
+ System.out.printf("Loading data from %s ... \n", filename);
+
+ BufferedReader input = null;
+
+ try
+ {
+ FileReader fstream = new FileReader(filename);
+ input = new BufferedReader(fstream);
+ while (true)
+ {
+ String line = input.readLine();
+ if (line == null)
+ {
+ break;
+ }
+
+ String [] toks = line.split(" +");
+
+ long [] odometry = new long [3];
+ odometry[0] = Long.parseLong(toks[0]);
+ odometry[1] = Long.parseLong(toks[2]);
+ odometry[2] = Long.parseLong(toks[3]);
+ odometries.add(odometry);
+
+ int [] scan = new int [SCAN_SIZE];
+ for (int k=0; k.
+
+ properties (Access = 'private')
+ TICKS_PER_CYCLE = 2000;
+ end
+
+ methods (Access = 'private')
+
+ function degrees = ticks_to_degrees(obj, ticks)
+
+ degrees = ticks * (180. / obj.TICKS_PER_CYCLE);
+
+ end
+
+ end
+
+ methods (Access = 'public')
+
+ function robot = MinesRover()
+
+ robot = robot@WheeledRobot(77, 165);
+
+ end
+
+ function disp(obj)
+ % Displays information about this MinesRover
+
+ fprintf('<%s ticks_per_cycle=%d>\n', obj.str(), obj.TICKS_PER_CYCLE)
+
+ end
+
+ function [poseChange, obj] = computePoseChange(obj, odometry)
+
+ [poseChange, obj] = computePoseChange@WheeledRobot(obj, odometry(1), odometry(2), odometry(3));
+
+ end
+
+ function [timestampSeconds, leftWheelDegrees, rightWheelDegrees] = ...
+ extractOdometry(obj, timestamp, leftWheel, rightWheel)
+
+ % Convert microseconds to seconds
+ timestampSeconds = timestamp / 1e6;
+
+ % Convert ticks to angles
+ leftWheelDegrees = obj.ticks_to_degrees(leftWheel);
+ rightWheelDegrees = obj.ticks_to_degrees(rightWheel);
+
+ end
+
+ end
+
+end
+
diff --git a/examples/logdemo.m b/examples/logdemo.m
new file mode 100644
index 0000000..c59e336
--- /dev/null
+++ b/examples/logdemo.m
@@ -0,0 +1,175 @@
+%
+% logdemo.m : BreezySLAM Matlab demo. Reads logfile with odometry and scan
+% data from Paris Mines Tech and displays the map and robot
+% position in real time.
+%
+% Usage:
+%
+% >> logdemo(dataset, [use_odometry], [random_seed])
+%
+% Examples:
+%
+% >> logdemo('exp2')
+%
+% For details see
+%
+% @inproceedings{coreslam-2010,
+% author = {Bruno Steux and Oussama El Hamzaoui},
+% title = {CoreSLAM: a SLAM Algorithm in less than 200 lines of C code},
+% booktitle = {11th International Conference on Control, Automation,
+% Robotics and Vision, ICARCV 2010, Singapore, 7-10
+% December 2010, Proceedings},
+% pages = {1975-1979},
+% publisher = {IEEE},
+% year = {2010}
+% }
+%
+% Copyright (C) 2014 Simon D. Levy
+%
+% This code is free software: you can redistribute it and/or modify
+% it under the terms of the GNU Lesser General Public License as
+% published by the Free Software Foundation, either version 3 of the
+% License, or (at your option) any later version.
+%
+% This code is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU Lesser General Public License
+% along with this code. If not, see .
+
+function logdemo(dataset, use_odometry, seed)
+
+% Params
+
+MAP_SIZE_PIXELS = 800;
+MAP_SIZE_METERS = 32;
+ROBOT_SIZE_PIXELS = 10;
+
+% Grab input args
+
+if nargin < 2
+ use_odometry = 0;
+end
+
+if nargin < 3
+ seed = 0;
+end
+
+% Load data from file
+[times, scans, odometries] = load_data(dataset);
+
+% Build a robot model if we want odometry
+robot = [];
+if use_odometry
+ robot = MinesRover();
+end
+
+% Create a CoreSLAM object with laser params and optional robot object
+if seed
+ slam = RMHC_SLAM(MinesLaser(), MAP_SIZE_PIXELS, MAP_SIZE_METERS, seed);
+else
+ slam = Deterministic_SLAM(MinesLaser(), MAP_SIZE_PIXELS, MAP_SIZE_METERS);
+end
+
+% Initialize previous time for delay
+prevTime = 0;
+
+% Loop over scans
+for scanno = 1:size(scans, 1)
+
+ if use_odometry
+
+ % Convert odometry to velocities
+ [velocities,robot] = robot.computePoseChange(odometries(scanno, :));
+
+ % Update SLAM with lidar and velocities
+ slam = slam.update(scans(scanno,:), velocities);
+
+ else
+
+ % Update SLAM with lidar alone
+ slam = slam.update(scans(scanno,:));
+ end
+
+ % Get new position
+ [x_mm, y_mm, theta_degrees] = slam.getpos();
+
+ % Get current map
+ map = slam.getmap();
+
+ % Display map
+ hold off
+ image(map/4) % Keep bytes in [0,64] for colormap
+ axis('square')
+ colormap('gray')
+ hold on
+
+ % Generate a polyline to represent the robot
+ [x_pix, y_pix] = robot_polyline(ROBOT_SIZE_PIXELS);
+
+ % Rotate the polyline based on the robot's angular rotation
+ theta_radians = pi * theta_degrees / 180;
+ x_pix_r = x_pix * cos(theta_radians) - y_pix * sin(theta_radians);
+ y_pix_r = x_pix * sin(theta_radians) + y_pix * cos(theta_radians);
+
+ % Add the robot's position as offset to the polyline
+ x_pix = x_pix_r + mm2pix(x_mm, MAP_SIZE_METERS, MAP_SIZE_PIXELS);
+ y_pix = y_pix_r + mm2pix(y_mm, MAP_SIZE_METERS, MAP_SIZE_PIXELS);
+
+ % Add robot image to map
+ fill(x_pix, y_pix, 'r')
+ drawnow
+
+ % Delay based on current time in microseconds
+ currTime = times(scanno);
+ if scanno > 1
+ pause((currTime-prevTime)/1e6)
+ end
+ prevTime = times(scanno);
+
+
+end
+
+% Function to generate a x, y points for a polyline to display the robot
+% Currently we just generate a triangle.
+function [x_pix, y_pix] = robot_polyline(robot_size)
+HEIGHT_RATIO = 1.25;
+x_pix = [-robot_size, robot_size, -robot_size];
+y_pix = [-robot_size/HEIGHT_RATIO, 0 , robot_size/HEIGHT_RATIO];
+
+% Function to convert millimeters to pixels -------------------------------
+
+function pix = mm2pix(mm, MAP_SIZE_METERS, MAP_SIZE_PIXELS)
+pix = floor(mm / (MAP_SIZE_METERS * 1000. / MAP_SIZE_PIXELS));
+
+
+% Function to load all from file ------------------------------------------
+% Each line in the file has the format:
+%
+% TIMESTAMP ... Q1 Q1 ... Distances
+% (usec) (mm)
+% 0 ... 2 3 ... 24 ...
+%
+%where Q1, Q2 are odometry values
+
+function [times, scans,odometries] = load_data(dataset)
+
+data = load([dataset '.dat']);
+
+times = data(:,1);
+scans = data(:,25:end-1); % avoid final ' '
+odometries = data(:,[1,3,4]);
+
+% Function to build a Laser data structure for the Hokuyo URG04LX used for
+% collecting the logfile data.
+
+function laser = MinesLaser()
+
+laser.scan_size = 682;
+laser.scan_rate_hz = 10;
+laser.detection_angle_degrees = 240;
+laser.distance_no_detection_mm = 4000;
+laser.detection_margin = 70;
+laser.offset_mm = 145;
diff --git a/examples/rpslam.py b/examples/rpslam.py
index 84b7140..f1099ed 100755
--- a/examples/rpslam.py
+++ b/examples/rpslam.py
@@ -30,6 +30,9 @@ from rplidar import RPLidar as Lidar
from pltslamshow import SlamShow
+from scipy.interpolate import interp1d
+import numpy as np
+
if __name__ == '__main__':
# Connect to Lidar unit
@@ -52,36 +55,33 @@ if __name__ == '__main__':
while True:
- try:
+ # Extrat (quality, angle, distance) triples from current scan
+ items = [item for item in next(iterator)]
- # Extrat (quality, angle, distance) triples from current scan
- items = [item for item in next(iterator)]
+ # Extract distances and angles from triples
+ distances = [item[2] for item in items]
+ angles = [item[1] for item in items]
- # Extract distances and angles from triples
- distances = [item[2] for item in items]
- angles = [item[1] for item in items]
- print(angles)
-
- except KeyboardInterrupt:
- break
+ # Interpolate to get 360 angles from 0 through 359, and corresponding distances
+ f = interp1d(angles, distances, fill_value='extrapolate')
+ distances = list(f(np.arange(360)))
# Update SLAM with current Lidar scan, using third element of (quality, angle, distance) triples
- #slam.update([item[2] for item in next(iterator)])
+ slam.update(distances)
# Get current robot position
- #x, y, theta = slam.getpos()
+ x, y, theta = slam.getpos()
# Get current map bytes as grayscale
- #slam.getmap(mapbytes)
+ slam.getmap(mapbytes)
- #display.displayMap(mapbytes)
+ display.displayMap(mapbytes)
- #display.setPose(x, y, theta)
+ display.setPose(x, y, theta)
- # Exit on ESCape
- #key = display.refresh()
- #if key != None and (key&0x1A):
- # exit(0)
+ # Exit on window close
+ if not display.refresh():
+ exit(0)
lidar.stop()
lidar.disconnect()
diff --git a/java/edu/wlu/cs/levy/breezyslam/algorithms/CoreSLAM.java b/java/edu/wlu/cs/levy/breezyslam/algorithms/CoreSLAM.java
new file mode 100644
index 0000000..a57a162
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/algorithms/CoreSLAM.java
@@ -0,0 +1,133 @@
+/**
+*
+* CoreSLAM.java abstract Java class for CoreSLAM algorithm in BreezySLAM
+*
+* Copyright (C) 2014 Simon D. Levy
+
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+
+package edu.wlu.cs.levy.breezyslam.algorithms;
+
+import edu.wlu.cs.levy.breezyslam.components.Laser;
+import edu.wlu.cs.levy.breezyslam.components.PoseChange;
+import edu.wlu.cs.levy.breezyslam.components.Map;
+import edu.wlu.cs.levy.breezyslam.components.Scan;
+
+/**
+* CoreSLAM is an abstract class that uses the classes Position, Map, Scan, and Laser
+* to run variants of the simple CoreSLAM (tinySLAM) algorithm described in
+*
+* @inproceedings{coreslam-2010,
+* author = {Bruno Steux and Oussama El Hamzaoui},
+* title = {CoreSLAM: a SLAM Algorithm in less than 200 lines of C code},
+* booktitle = {11th International Conference on Control, Automation,
+* Robotics and Vision, ICARCV 2010, Singapore, 7-10
+* December 2010, Proceedings},
+* pages = {1975-1979},
+* publisher = {IEEE},
+* year = {2010}
+* }
+*
+* Implementing classes should provide the method
+*
+* void updateMapAndPointcloud(int * scan_mm, PoseChange & poseChange)
+*
+* to update the map and point-cloud (particle cloud).
+*
+*/
+public abstract class CoreSLAM {
+
+ /**
+ * The quality of the map (0 through 255)
+ */
+ public int map_quality = 50;
+
+ /**
+ * The width in millimeters of each "hole" in the map (essentially, wall width)
+ */
+ public double hole_width_mm = 600;
+
+ protected Laser laser;
+
+ protected PoseChange poseChange;
+
+ protected Map map;
+
+ protected Scan scan_for_mapbuild;
+ protected Scan scan_for_distance;
+
+ public CoreSLAM(Laser laser, int map_size_pixels, double map_size_meters)
+ {
+ // Set default params
+ this.laser = new Laser(laser);
+
+ // Initialize poseChange (dxyMillimeters, dthetaDegrees, dtSeconds) for odometry
+ this.poseChange = new PoseChange();
+
+ // Initialize a scan for computing distance to map, and one for updating map
+ this.scan_for_mapbuild = this.scan_create(3);
+ this.scan_for_distance = this.scan_create(1);
+
+ // Initialize the map
+ this.map = new Map(map_size_pixels, map_size_meters);
+ }
+
+ private Scan scan_create(int span)
+ {
+ return new Scan(this.laser, span);
+ }
+
+ private void scan_update(Scan scan, int [] scan_mm)
+ {
+ scan.update(scan_mm, this.hole_width_mm, this.poseChange);
+ }
+
+
+ public void update(int [] scan_mm, PoseChange poseChange)
+ {
+ // Build a scan for computing distance to map, and one for updating map
+ this.scan_update(this.scan_for_mapbuild, scan_mm);
+ this.scan_update(this.scan_for_distance, scan_mm);
+
+ // Update poseChange
+ this.poseChange.update(poseChange.getDxyMm(), poseChange.getDthetaDegrees(), poseChange.getDtSeconds());
+
+ // Implementing class updates map and pointcloud
+ this.updateMapAndPointcloud(poseChange);
+ }
+
+ /**
+ * Updates the scan, and calls the the implementing class's updateMapAndPointcloud method with zero poseChange
+ * (no odometry).
+ * @param scan_mm Lidar scan values, whose count is specified in the scan_size
+ * attribute of the Laser object passed to the CoreSlam constructor
+ */
+ public void update(int [] scan_mm)
+ {
+ PoseChange zero_poseChange = new PoseChange();
+
+ this.update(scan_mm, zero_poseChange);
+ }
+
+
+ protected abstract void updateMapAndPointcloud(PoseChange poseChange);
+
+ public void getmap(byte [] mapbytes)
+ {
+ this.map.get(mapbytes);
+ }
+
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/algorithms/DeterministicSLAM.java b/java/edu/wlu/cs/levy/breezyslam/algorithms/DeterministicSLAM.java
new file mode 100644
index 0000000..458943d
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/algorithms/DeterministicSLAM.java
@@ -0,0 +1,52 @@
+/**
+* DeterministicSLAM implements SinglePositionSLAM using by returning the starting position instead of searching
+* on it; i.e., using odometry alone.
+*
+* Copyright (C) 2014 Simon D. Levy
+*
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+
+package edu.wlu.cs.levy.breezyslam.algorithms;
+
+import edu.wlu.cs.levy.breezyslam.components.Position;
+import edu.wlu.cs.levy.breezyslam.components.Laser;
+
+public class DeterministicSLAM extends SinglePositionSLAM
+{
+
+ /**
+ * Creates a DeterministicSLAM object.
+ * @param laser a Laser object containing parameters for your Lidar equipment
+ * @param map_size_pixels the size of the desired map (map is square)
+ * @param map_size_meters the size of the area to be mapped, in meters
+ * @return a new CoreSLAM object
+ */
+ public DeterministicSLAM(Laser laser, int map_size_pixels, double map_size_meters)
+ {
+ super(laser, map_size_pixels, map_size_meters);
+ }
+
+ /**
+ * Returns a new position identical to the starting position. Called automatically by
+ * SinglePositionSLAM::updateMapAndPointcloud()
+ * @param start_pos the starting position
+ */
+ protected Position getNewPosition(Position start_position)
+ {
+ return new Position(start_position.x_mm, start_position.y_mm, start_position.theta_degrees);
+ }
+
+} // DeterministicSLAM
diff --git a/java/edu/wlu/cs/levy/breezyslam/algorithms/Makefile b/java/edu/wlu/cs/levy/breezyslam/algorithms/Makefile
new file mode 100644
index 0000000..45b5a43
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/algorithms/Makefile
@@ -0,0 +1,90 @@
+# Makefile for BreezySLAM algorithms in Java
+#
+# Copyright (C) 2014 Simon D. Levy
+#
+# This code is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This code is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public License
+# along with this code. If not, see .
+
+BASEDIR = ../../../../../../..
+JAVADIR = $(BASEDIR)/java
+CDIR = $(BASEDIR)/c
+JFLAGS = -Xlint
+JDKINC = -I /usr/lib/jvm/java-7-openjdk-amd64/include
+#JDKINC = -I /opt/jdk1.7.0_67/include -I /opt/jdk1.7.0_67/include/linux
+
+# Set library extension based on OS
+ifeq ("$(shell uname)","Darwin")
+ LIBEXT = dylib
+else ifeq ("$(shell uname)","Linux")
+ CFLAGS = -fPIC
+ LIBEXT = so
+else
+ LIBEXT = dll
+endif
+
+# Set SIMD compile params based on architecture
+ifeq ("$(ARCH)","armv7l")
+ SIMD_FLAGS = -mfpu=neon
+else ifeq ("$(ARCH)","i686")
+ SIMD_FLAGS = -msse3
+else
+ ARCH = sisd
+endif
+
+
+ALL = libjnibreezyslam_algorithms.$(LIBEXT) CoreSLAM.class SinglePositionSLAM.class DeterministicSLAM.class RMHCSLAM.class
+
+all: $(ALL)
+
+libjnibreezyslam_algorithms.$(LIBEXT): jnibreezyslam_algorithms.o coreslam.o random.o ziggurat.o coreslam_$(ARCH).o
+ gcc -shared -Wl,-soname,libjnibreezyslam_algorithms.so -o libjnibreezyslam_algorithms.so jnibreezyslam_algorithms.o \
+ coreslam.o coreslam_$(ARCH).o random.o ziggurat.o
+
+jnibreezyslam_algorithms.o: jnibreezyslam_algorithms.c RMHCSLAM.h ../jni_utils.h
+ gcc $(JDKINC) -fPIC -c jnibreezyslam_algorithms.c
+
+
+CoreSLAM.class: CoreSLAM.java
+ javac -classpath $(JAVADIR):. CoreSLAM.java
+
+
+SinglePositionSLAM.class: SinglePositionSLAM.java
+ javac -classpath $(JAVADIR):. SinglePositionSLAM.java
+
+DeterministicSLAM.class: DeterministicSLAM.java
+ javac -classpath $(JAVADIR):. DeterministicSLAM.java
+
+RMHCSLAM.class: RMHCSLAM.java
+ javac -classpath $(JAVADIR):. RMHCSLAM.java
+
+RMHCSLAM.h: RMHCSLAM.class
+ javah -o RMHCSLAM.h -classpath $(JAVADIR) -jni edu.wlu.cs.levy.breezyslam.algorithms.RMHCSLAM
+
+coreslam.o: $(CDIR)/coreslam.c $(CDIR)/coreslam.h
+ gcc -O3 -c -Wall $(CFLAGS) $(CDIR)/coreslam.c
+
+coreslam_$(ARCH).o: $(CDIR)/coreslam_$(ARCH).c $(CDIR)/coreslam.h
+ gcc -O3 -c -Wall $(CFLAGS) $(SIMD_FLAGS) $(CDIR)/coreslam_$(ARCH).c
+
+random.o: $(CDIR)/random.c
+ gcc -O3 -c -Wall $(CFLAGS) $(CDIR)/random.c
+
+ziggurat.o: $(CDIR)/ziggurat.c
+ gcc -O3 -c -Wall $(CFLAGS) $(CDIR)/ziggurat.c
+
+clean:
+ rm -f *.class *.o *.h *.$(LIBEXT) *~
+
+backup:
+ cp *.java bak
+ cp Makefile bak
diff --git a/java/edu/wlu/cs/levy/breezyslam/algorithms/RMHCSLAM.java b/java/edu/wlu/cs/levy/breezyslam/algorithms/RMHCSLAM.java
new file mode 100644
index 0000000..06269ab
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/algorithms/RMHCSLAM.java
@@ -0,0 +1,103 @@
+/**
+* RMHCSLAM implements SinglePositionSLAM using random-mutation hill-climbing search on the starting position.
+*
+* Copyright (C) 2014 Simon D. Levy
+*
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+package edu.wlu.cs.levy.breezyslam.algorithms;
+
+import edu.wlu.cs.levy.breezyslam.components.Position;
+import edu.wlu.cs.levy.breezyslam.components.Laser;
+import edu.wlu.cs.levy.breezyslam.components.PoseChange;
+import edu.wlu.cs.levy.breezyslam.components.Map;
+import edu.wlu.cs.levy.breezyslam.components.Scan;
+
+public class RMHCSLAM extends SinglePositionSLAM
+{
+ private static final double DEFAULT_SIGMA_XY_MM = 100;
+ private static final double DEFAULT_SIGMA_THETA_DEGREES = 20;
+ private static final int DEFAULT_MAX_SEARCH_ITER = 1000;
+
+ static
+ {
+ System.loadLibrary("jnibreezyslam_algorithms");
+ }
+
+ private native void init(int random_seed);
+
+ private native Object positionSearch(
+ Position start_pos,
+ Map map,
+ Scan scan,
+ double sigma_xy_mm,
+ double sigma_theta_degrees,
+ int max_search_iter);
+
+
+ private long native_ptr;
+
+ /**
+ * Creates an RMHCSLAM object.
+ * @param laser a Laser object containing parameters for your Lidar equipment
+ * @param map_size_pixels the size of the desired map (map is square)
+ * @param map_size_meters the size of the area to be mapped, in meters
+ * @param random_seed seed for psuedorandom number generator in particle filter
+ * @return a new CoreSLAM object
+ */
+ public RMHCSLAM(Laser laser, int map_size_pixels, double map_size_meters, int random_seed)
+ {
+ super(laser, map_size_pixels, map_size_meters);
+
+ this.init(random_seed);
+ }
+
+ /**
+ * The standard deviation in millimeters of the Gaussian distribution of
+ * the (X,Y) component of position in the particle filter; default = 100
+ */
+ public double sigma_xy_mm = DEFAULT_SIGMA_XY_MM;;
+
+ /**
+ * The standard deviation in degrees of the Gaussian distribution of
+ * the angular rotation component of position in the particle filter; default = 20
+ */
+ public double sigma_theta_degrees = DEFAULT_SIGMA_THETA_DEGREES;
+
+ /**
+ * The maximum number of iterations for particle-filter search; default = 1000
+ */
+ public int max_search_iter = DEFAULT_MAX_SEARCH_ITER;
+
+ /**
+ * Returns a new position based on RMHC search from a starting position. Called automatically by
+ * SinglePositionSLAM::updateMapAndPointcloud()
+ * @param start_position the starting position
+ */
+ protected Position getNewPosition(Position start_position)
+ {
+ Position newpos =
+ (Position)positionSearch(
+ start_position,
+ this.map,
+ this.scan_for_distance,
+ this.sigma_xy_mm,
+ this.sigma_theta_degrees,
+ this.max_search_iter);
+
+ return newpos;
+ }
+
+} // RMHCSLAM
diff --git a/java/edu/wlu/cs/levy/breezyslam/algorithms/SinglePositionSLAM.java b/java/edu/wlu/cs/levy/breezyslam/algorithms/SinglePositionSLAM.java
new file mode 100644
index 0000000..085064b
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/algorithms/SinglePositionSLAM.java
@@ -0,0 +1,118 @@
+/**
+* SinglePositionSLAM is an abstract class that implements CoreSLAM using a point-cloud
+* with a single point (particle, position). Implementing classes should provide the method
+*
+* Position getNewPosition(Position start_position)
+*
+* to compute a new position based on searching from a starting position.
+*
+* Copyright (C) 2014 Simon D. Levy
+*
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+package edu.wlu.cs.levy.breezyslam.algorithms;
+
+import edu.wlu.cs.levy.breezyslam.components.Position;
+import edu.wlu.cs.levy.breezyslam.components.PoseChange;
+import edu.wlu.cs.levy.breezyslam.components.Laser;
+
+
+public abstract class SinglePositionSLAM extends CoreSLAM
+{
+ /**
+ * Returns the current position.
+ * @return the current position as a Position object.
+ */
+ public Position getpos()
+ {
+ return this.position;
+ }
+
+ /**
+ * Creates a SinglePositionSLAM object.
+ * @param laser a Laser object containing parameters for your Lidar equipment
+ * @param map_size_pixels the size of the desired map (map is square)
+ * @param map_size_meters the size of the area to be mapped, in meters
+ * @return a new SinglePositionSLAM object
+ */
+ protected SinglePositionSLAM(Laser laser, int map_size_pixels, double map_size_meters)
+ {
+ super(laser, map_size_pixels, map_size_meters);
+
+ this.position = new Position(this.init_coord_mm(), this.init_coord_mm(), 0);
+ }
+
+
+ /**
+ * Updates the map and point-cloud (particle cloud). Called automatically by CoreSLAM::update()
+ * @param poseChange poseChange for odometry
+ */
+ protected void updateMapAndPointcloud(PoseChange poseChange)
+ {
+ // Start at current position
+ Position start_pos = new Position(this.position);
+
+ // Add effect of poseChange
+ start_pos.x_mm += poseChange.getDxyMm() * this.costheta();
+ start_pos.y_mm += poseChange.getDxyMm() * this.sintheta();
+ start_pos.theta_degrees += poseChange.getDthetaDegrees();
+
+ // Add offset from laser
+ start_pos.x_mm += this.laser.getOffsetMm() * this.costheta();
+ start_pos.y_mm += this.laser.getOffsetMm() * this.sintheta();
+
+ // Get new position from implementing class
+ Position new_position = this.getNewPosition(start_pos);
+
+ // Update the map with this new position
+ this.map.update(this.scan_for_mapbuild, new_position, this.map_quality, this.hole_width_mm);
+
+ // Update the current position with this new position, adjusted by laser offset
+ this.position = new Position(new_position);
+ this.position.x_mm -= this.laser.getOffsetMm() * this.costheta();
+ this.position.y_mm -= this.laser.getOffsetMm() * this.sintheta();
+ }
+
+ /**
+ * Returns a new position based on searching from a starting position. Called automatically by
+ * SinglePositionSLAM::updateMapAndPointcloud()
+ * @param start_pos the starting position
+ */
+
+ protected abstract Position getNewPosition(Position start_pos);
+
+ private Position position;
+
+ private double theta_radians()
+ {
+ return java.lang.Math.toRadians(this.position.theta_degrees);
+ }
+
+ private double costheta()
+ {
+ return java.lang.Math.cos(this.theta_radians());
+ }
+
+ private double sintheta()
+ {
+ return java.lang.Math.sin(this.theta_radians());
+ }
+
+ private double init_coord_mm()
+ {
+ // Center of map
+ return 500 * this.map.sizeMeters();
+ }
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/algorithms/jnibreezyslam_algorithms.c b/java/edu/wlu/cs/levy/breezyslam/algorithms/jnibreezyslam_algorithms.c
new file mode 100644
index 0000000..e2e8d8f
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/algorithms/jnibreezyslam_algorithms.c
@@ -0,0 +1,67 @@
+/*
+ * jnibreezyslam_algorithms.c Java Native Interface code for BreezySLAM algorithms
+ *
+ * Copyright (C) 2014 Simon D. Levy
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ */
+
+
+#include "../../../../../../../c/random.h"
+#include "../jni_utils.h"
+
+#include
+
+
+// RMHC_SLAM methods -----------------------------------------------------------------------------------------
+
+JNIEXPORT void JNICALL Java_edu_wlu_cs_levy_breezyslam_algorithms_RMHCSLAM_init (JNIEnv *env, jobject thisobject, jint random_seed)
+{
+ ptr_to_obj(env, thisobject, random_new(random_seed));
+}
+
+JNIEXPORT jobject JNICALL Java_edu_wlu_cs_levy_breezyslam_algorithms_RMHCSLAM_positionSearch (JNIEnv *env, jobject thisobject,
+ jobject startpos_object,
+ jobject map_object,
+ jobject scan_object,
+ jdouble sigma_xy_mm,
+ jdouble sigma_theta_degrees,
+ jint max_search_iter)
+{
+ position_t startpos;
+
+ startpos.x_mm = get_double_field(env, startpos_object, "x_mm");
+ startpos.y_mm = get_double_field(env, startpos_object, "y_mm");
+ startpos.theta_degrees = get_double_field(env, startpos_object, "theta_degrees");
+
+ void * random = ptr_from_obj(env, thisobject);
+
+ position_t newpos =
+ rmhc_position_search(
+ startpos,
+ cmap_from_jmap(env, map_object),
+ cscan_from_jscan(env, scan_object),
+ sigma_xy_mm,
+ sigma_theta_degrees,
+ max_search_iter,
+ random);
+
+ jclass cls = (*env)->FindClass(env, "edu/wlu/cs/levy/breezyslam/components/Position");
+
+ jmethodID constructor = (*env)->GetMethodID(env, cls, "", "(DDD)V");
+
+ jobject newpos_object = (*env)->NewObject(env, cls, constructor, newpos.x_mm, newpos.y_mm, newpos.theta_degrees);
+
+ return newpos_object;
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/components/Laser.java b/java/edu/wlu/cs/levy/breezyslam/components/Laser.java
new file mode 100644
index 0000000..aa77425
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/components/Laser.java
@@ -0,0 +1,108 @@
+/**
+*
+* BreezySLAM: Simple, efficient SLAM in Java
+*
+* Laser.java - Java code for Laser model class
+*
+* Copyright (C) 2014 Simon D. Levy
+*
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+package edu.wlu.cs.levy.breezyslam.components;
+
+/**
+* A class for scanning laser rangefinder (Lidar) parameters.
+*/
+public class Laser
+{
+
+ protected int scan_size;
+ protected double scan_rate_hz;
+ protected double detection_angle_degrees;
+ protected double distance_no_detection_mm;
+ protected int detection_margin;
+ protected double offset_mm;
+
+ /**
+ * Builds a Laser object from parameters based on the specifications for your
+ * Lidar unit.
+ * @param scan_size number of rays per scan
+ * @param scan_rate_hz laser scan rate in Hertz
+ * @param detection_angle_degrees detection angle in degrees (e.g. 240, 360)
+ * @param detection_margin number of rays at edges of scan to ignore
+ * @param offset_mm forward/backward offset of laser motor from robot center
+ * @return a new Laser object
+ *
+ */
+ public Laser(
+ int scan_size,
+ double scan_rate_hz,
+ double detection_angle_degrees,
+ double distance_no_detection_mm,
+ int detection_margin,
+ double offset_mm)
+ {
+ this.scan_size = scan_size;
+ this.scan_rate_hz = scan_rate_hz;
+ this.detection_angle_degrees = detection_angle_degrees;
+ this.distance_no_detection_mm = distance_no_detection_mm;
+ this.detection_margin = detection_margin;
+ this.offset_mm = offset_mm;
+ }
+
+ /**
+ * Builds a Laser object by copying another Laser object.
+ * Lidar unit.
+ * @param laser the other Laser object
+ *
+ */
+ public Laser(Laser laser)
+ {
+ this.scan_size = laser.scan_size;
+ this.scan_rate_hz = laser.scan_rate_hz;
+ this.detection_angle_degrees = laser.detection_angle_degrees;
+ this.distance_no_detection_mm = laser.distance_no_detection_mm;
+ this.detection_margin = laser.detection_margin;
+ this.offset_mm = laser.offset_mm;
+ }
+
+ /**
+ * Returns a string representation of this Laser object.
+ */
+
+ public String toString()
+ {
+ String format = "scan_size=%d | scan_rate=%3.3f hz | " +
+ "detection_angle=%3.3f deg | " +
+ "distance_no_detection=%7.4f mm | " +
+ "detection_margin=%d | offset=%4.4f m";
+
+ return String.format(format, this.scan_size, this.scan_rate_hz,
+ this.detection_angle_degrees,
+ this.distance_no_detection_mm,
+ this.detection_margin,
+ this.offset_mm);
+
+ }
+
+ /**
+ * Returns the offset of the laser in mm, from the center of the robot.
+ *
+ */
+ public double getOffsetMm()
+ {
+ return this.offset_mm;
+ }
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/components/Makefile b/java/edu/wlu/cs/levy/breezyslam/components/Makefile
new file mode 100644
index 0000000..35a37bc
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/components/Makefile
@@ -0,0 +1,89 @@
+# Makefile for BreezySLAM components in Java
+#
+# Copyright (C) 2014 Simon D. Levy
+#
+# This code is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This code is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public License
+# along with this code. If not, see .
+
+BASEDIR = ../../../../../../..
+JAVADIR = $(BASEDIR)/java
+CDIR = $(BASEDIR)/c
+JFLAGS = -Xlint
+JDKINC = -I /usr/lib/jvm/java-7-openjdk-amd64/include
+#JDKINC = -I /opt/jdk1.7.0_67/include -I /opt/jdk1.7.0_67/include/linux
+
+# Set library extension based on OS
+ifeq ("$(shell uname)","Darwin")
+ LIBEXT = dylib
+else ifeq ("$(shell uname)","Linux")
+ CFLAGS = -fPIC
+ LIBEXT = so
+else
+ LIBEXT = dll
+endif
+
+ARCH = $(shell uname -m)
+
+# Set SIMD compile params based on architecture
+ifeq ("$(ARCH)","armv7l")
+ SIMD_FLAGS = -mfpu=neon
+else ifeq ("$(ARCH)","i686")
+ SIMD_FLAGS = -msse3
+else
+ ARCH = sisd
+endif
+
+ALL = libjnibreezyslam_components.$(LIBEXT) Laser.class Position.class PoseChange.class URG04LX.class
+
+all: $(ALL)
+
+libjnibreezyslam_components.$(LIBEXT): jnibreezyslam_components.o coreslam.o coreslam_$(ARCH).o
+ gcc -shared -Wl,-soname,libjnibreezyslam_components.so -o libjnibreezyslam_components.so jnibreezyslam_components.o \
+ coreslam.o coreslam_$(ARCH).o
+
+jnibreezyslam_components.o: jnibreezyslam_components.c Map.h Scan.h ../jni_utils.h
+ gcc $(JDKINC) -fPIC -c jnibreezyslam_components.c
+
+coreslam.o: $(CDIR)/coreslam.c $(CDIR)/coreslam.h
+ gcc -O3 -c -Wall $(CFLAGS) $(CDIR)/coreslam.c
+
+coreslam_$(ARCH).o: $(CDIR)/coreslam_$(ARCH).c $(CDIR)/coreslam.h
+ gcc -O3 -c -Wall $(CFLAGS) $(SIMD_FLAGS) $(CDIR)/coreslam_$(ARCH).c
+
+Map.h: Map.class
+ javah -o Map.h -classpath $(JAVADIR) -jni edu.wlu.cs.levy.breezyslam.components.Map
+
+Map.class: Map.java Scan.class Position.class
+ javac $(JFLAGS) -classpath $(JAVADIR) Map.java
+
+Scan.h: Scan.class
+ javah -o Scan.h -classpath $(JAVADIR) -jni edu.wlu.cs.levy.breezyslam.components.Scan
+
+Scan.class: Scan.java Laser.class
+ javac $(JFLAGS) -classpath $(JAVADIR) Scan.java
+
+Laser.class: Laser.java
+ javac $(JFLAGS) Laser.java
+
+URG04LX.class: URG04LX.java Laser.class
+ javac $(JFLAGS) -classpath $(JAVADIR) URG04LX.java
+
+PoseChange.class: PoseChange.java
+ javac $(JFLAGS) PoseChange.java
+
+
+Position.class: Position.java
+ javac $(JFLAGS) Position.java
+
+clean:
+ rm -f *.so *.class *.o *.h *~
diff --git a/java/edu/wlu/cs/levy/breezyslam/components/Map.java b/java/edu/wlu/cs/levy/breezyslam/components/Map.java
new file mode 100644
index 0000000..4bfa2f6
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/components/Map.java
@@ -0,0 +1,96 @@
+/**
+*
+* BreezySLAM: Simple, efficient SLAM in Java
+*
+* Map.java - Java code for Map class
+*
+* Copyright (C) 2014 Simon D. Levy
+*
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+package edu.wlu.cs.levy.breezyslam.components;
+
+/**
+* A class for maps used in SLAM.
+*/
+public class Map
+{
+ static
+ {
+ System.loadLibrary("jnibreezyslam_components");
+ }
+
+ private native void init(int size_pixels, double size_meters);
+
+ private double size_meters;
+
+ private native void update(
+ Scan scan,
+ double position_x_mm,
+ double position_y_mm,
+ double position_theta_degrees,
+ int quality,
+ double hole_width_mm);
+
+ private long native_ptr;
+
+ /**
+ * Returns a string representation of this Map object.
+ */
+ public native String toString();
+
+ /**
+ * Builds a square Map object.
+ * @param size_pixels size in pixels
+ * @param size_meters size in meters
+ *
+ */
+ public Map(int size_pixels, double size_meters)
+ {
+ this.init(size_pixels, size_meters);
+
+ // for public accessor
+ this.size_meters = size_meters;
+ }
+
+ /**
+ * Puts current map values into bytearray, which should of which should be of
+ * this.size map_size_pixels ^ 2.
+ * @param bytes byte array that gets the map values
+ */
+ public native void get(byte [] bytes);
+
+ /**
+ * Updates this map object based on new data.
+ * @param scan a new scan
+ * @param position a new postion
+ * @param quality speed with which scan is integerate into map (0 through 255)
+ * @param hole_width_mm hole width in millimeters
+ *
+ */
+ public void update(Scan scan, Position position, int quality, double hole_width_mm)
+ {
+ this.update(scan, position.x_mm, position.y_mm, position.theta_degrees, quality, hole_width_mm);
+ }
+
+ /**
+ * Returns the size of this map in meters.
+ */
+ public double sizeMeters()
+ {
+ return this.size_meters;
+ }
+
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/components/PoseChange.java b/java/edu/wlu/cs/levy/breezyslam/components/PoseChange.java
new file mode 100644
index 0000000..7682724
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/components/PoseChange.java
@@ -0,0 +1,115 @@
+/**
+*
+* BreezySLAM: Simple, efficient SLAM in Java
+*
+* PoseChange.java - Java code for PoseChange class, encoding triple
+* (dxy_mm, dtheta_degrees, dt_seconds)
+*
+* Copyright (C) 2014 Simon D. Levy
+*
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+package edu.wlu.cs.levy.breezyslam.components;
+
+/**
+* A class representing the forward and angular velocities of a robot as determined by odometry.
+*/
+public class PoseChange
+{
+
+ /**
+ * Creates a new PoseChange object with specified velocities.
+ */
+ public PoseChange(double dxy_mm, double dtheta_degrees, double dtSeconds)
+ {
+ this.dxy_mm = dxy_mm;
+ this.dtheta_degrees = dtheta_degrees;
+ this.dt_seconds = dtSeconds;
+ }
+
+ /**
+ * Creates a new PoseChange object with zero velocities.
+ */
+ public PoseChange()
+ {
+ this.dxy_mm = 0;
+ this.dtheta_degrees = 0;
+ this.dt_seconds = 0;
+ }
+
+ /**
+ * Updates this PoseChange object.
+ * @param dxy_mm new forward distance traveled in millimeters
+ * @param dtheta_degrees new angular rotation in degrees
+ * @param dtSeconds time in seconds since last velocities
+ */
+ public void update(double dxy_mm, double dtheta_degrees, double dtSeconds)
+ {
+ double velocity_factor = (dtSeconds > 0) ? (1 / dtSeconds) : 0;
+
+ this.dxy_mm = dxy_mm * velocity_factor;
+
+ this.dtheta_degrees = dtheta_degrees * velocity_factor;
+ }
+
+ /**
+ * Returns a string representation of this PoseChange object.
+ */
+ public String toString()
+ {
+ return String.format(".
+ */
+
+package edu.wlu.cs.levy.breezyslam.components;
+
+/**
+ * A class representing the position of a robot.
+ */
+public class Position
+{
+
+ /**
+ * Distance of robot from left edge of map, in millimeters.
+ */
+ public double x_mm;
+
+ /**
+ * Distance of robot from top edge of map, in millimeters.
+ */
+ public double y_mm;
+
+ /**
+ * Clockwise rotation of robot with respect to three o'clock (east), in degrees.
+ */
+ public double theta_degrees;
+
+ /**
+ * Constructs a new position.
+ * @param x_mm X coordinate in millimeters
+ * @param y_mm Y coordinate in millimeters
+ * @param theta_degrees rotation angle in degrees
+ */
+ public Position(double x_mm, double y_mm, double theta_degrees)
+ {
+ this.x_mm = x_mm;
+ this.y_mm = y_mm;
+ this.theta_degrees = theta_degrees;
+ }
+
+ /**
+ * Constructs a new Position object by copying another.
+ * @param the other Positon object
+ */
+
+ public Position(Position position)
+ {
+ this.x_mm = position.x_mm;
+ this.y_mm = position.y_mm;
+ this.theta_degrees = position.theta_degrees;
+ }
+
+ /**
+ * Returns a string representation of this Position object.
+ */
+ public String toString()
+ {
+ String format = "";
+
+ return String.format(format, this.x_mm, this.y_mm, this.theta_degrees);
+
+ }
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/components/Scan.java b/java/edu/wlu/cs/levy/breezyslam/components/Scan.java
new file mode 100644
index 0000000..9f898a8
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/components/Scan.java
@@ -0,0 +1,97 @@
+/**
+*
+* BreezySLAM: Simple, efficient SLAM in Java
+*
+* Scan.java - Java code for Scan class
+*
+* Copyright (C) 2014 Simon D. Levy
+*
+* This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this code. If not, see .
+*/
+
+package edu.wlu.cs.levy.breezyslam.components;
+
+/**
+* A class for Lidar scans.
+*/
+public class Scan
+{
+ static
+ {
+ System.loadLibrary("jnibreezyslam_components");
+ }
+
+ private native void init(
+ int span,
+ int scan_size,
+ double scan_rate_hz,
+ double detection_angle_degrees,
+ double distance_no_detection_mm,
+ int detection_margin,
+ double offset_mm);
+
+ private long native_ptr;
+
+ public native String toString();
+
+ /**
+ * Returns a string representation of this Scan object.
+ */
+ public native void update(
+ int [] lidar_mm,
+ double hole_width_mm,
+ double poseChange_dxy_mm,
+ double poseChange_dtheta_degrees);
+
+
+ /**
+ * Builds a Scan object.
+ * @param laser laser parameters
+ * @param span supports spanning laser scan to cover the space better.
+ *
+ */
+ public Scan(Laser laser, int span)
+ {
+ this.init(span,
+ laser.scan_size,
+ laser.scan_rate_hz,
+ laser.detection_angle_degrees,
+ laser.distance_no_detection_mm,
+ laser.detection_margin,
+ laser.offset_mm);
+ }
+
+ /**
+ * Builds a Scan object.
+ * @param laser laser parameters
+ *
+ */
+ public Scan(Laser laser)
+ {
+ this(laser, 1);
+ }
+
+ /**
+ * Updates this Scan object with new values from a Lidar scan.
+ * @param scanvals_mm scanned Lidar distance values in millimeters
+ * @param hole_width_millimeters hole width in millimeters
+ * @param poseChange forward velocity and angular velocity of robot at scan time
+ *
+ */
+ public void update(int [] scanvals_mm, double hole_width_millimeters, PoseChange poseChange)
+ {
+ this.update(scanvals_mm, hole_width_millimeters, poseChange.dxy_mm, poseChange.dtheta_degrees);
+ }
+}
+
diff --git a/java/edu/wlu/cs/levy/breezyslam/components/URG04LX.java b/java/edu/wlu/cs/levy/breezyslam/components/URG04LX.java
new file mode 100644
index 0000000..053b174
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/components/URG04LX.java
@@ -0,0 +1,29 @@
+package edu.wlu.cs.levy.breezyslam.components;
+
+/**
+ * A class for the Hokuyo URG-04LX laser.
+ */
+public class URG04LX extends Laser
+{
+
+ /**
+ * Builds a URG04LX object.
+ * Lidar unit.
+ * @param detection_margin number of rays at edges of scan to ignore
+ * @param offset_mm forward/backward offset of laser motor from robot center
+ * @return a new URG04LX object
+ *
+ */
+ public URG04LX(int detection_margin, double offset_mm)
+ {
+ super(682, 10, 240, 4000, detection_margin, offset_mm);
+ }
+
+ /**
+ * Builds a URG04LX object with zero detection margin, offset mm.
+ */
+ public URG04LX()
+ {
+ this(0, 0);
+ }
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/components/jnibreezyslam_components.c b/java/edu/wlu/cs/levy/breezyslam/components/jnibreezyslam_components.c
new file mode 100644
index 0000000..6e4d8fa
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/components/jnibreezyslam_components.c
@@ -0,0 +1,132 @@
+/*
+ * jnibreezyslam_components.c Java Native Interface code for BreezySLAM components
+ *
+ * Copyright (C) 2014 Simon D. Levy
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ */
+
+#include "../jni_utils.h"
+
+#include "Map.h"
+#include "Scan.h"
+
+#include
+#include
+
+#define MAXSTR 100
+
+// Map methods -----------------------------------------------------------------------------------------
+
+JNIEXPORT void JNICALL Java_edu_wlu_cs_levy_breezyslam_components_Map_init (JNIEnv *env, jobject thisobject, jint size_pixels, jdouble size_meters)
+{
+ map_t * map = (map_t *)malloc(sizeof(map_t));
+ map_init(map, (int)size_pixels, (double)size_meters);
+ ptr_to_obj(env, thisobject, map);
+}
+
+JNIEXPORT jstring JNICALL Java_edu_wlu_cs_levy_breezyslam_components_Map_toString (JNIEnv *env, jobject thisobject)
+{
+ map_t * map = cmap_from_jmap(env, thisobject);
+
+ char str[MAXSTR];
+
+ map_string(*map, str);
+
+ return (*env)->NewStringUTF(env, str);
+}
+
+JNIEXPORT void JNICALL Java_edu_wlu_cs_levy_breezyslam_components_Map_get (JNIEnv *env, jobject thisobject, jbyteArray bytes)
+{
+ map_t * map = cmap_from_jmap(env, thisobject);
+
+ jbyte * ptr = (*env)->GetByteArrayElements(env, bytes, NULL);
+
+ map_get(map, ptr);
+
+ (*env)->ReleaseByteArrayElements(env, bytes, ptr, 0);
+}
+
+JNIEXPORT void JNICALL Java_edu_wlu_cs_levy_breezyslam_components_Map_update (JNIEnv *env, jobject thisobject,
+ jobject scanobject,
+ jdouble position_x_mm,
+ jdouble position_y_mm,
+ jdouble position_theta_degrees,
+ jint quality,
+ jdouble hole_width_mm)
+{
+ map_t * map = cmap_from_jmap(env, thisobject);
+
+ scan_t * scan = cscan_from_jscan(env, scanobject);
+
+ position_t position;
+
+ position.x_mm = position_x_mm;
+ position.y_mm = position_y_mm;
+ position.theta_degrees = position_theta_degrees;
+
+ map_update(map, scan, position, quality, hole_width_mm);
+}
+
+
+// Scan methods -----------------------------------------------------------------------------------------
+
+JNIEXPORT void JNICALL Java_edu_wlu_cs_levy_breezyslam_components_Scan_init (JNIEnv *env, jobject thisobject,
+ jint span,
+ jint scan_size,
+ jdouble scan_rate_hz,
+ jdouble detection_angle_degrees,
+ jdouble distance_no_detection_mm,
+ jint detection_margin,
+ jdouble offset_mm)
+ {
+ scan_t * scan = (scan_t *)malloc(sizeof(scan_t));
+
+ scan_init(scan,
+ span,
+ scan_size,
+ scan_rate_hz,
+ detection_angle_degrees,
+ distance_no_detection_mm,
+ detection_margin,
+ offset_mm);
+
+ ptr_to_obj(env, thisobject, scan);
+}
+
+JNIEXPORT jstring JNICALL Java_edu_wlu_cs_levy_breezyslam_components_Scan_toString (JNIEnv *env, jobject thisobject)
+{
+ scan_t * scan = cscan_from_jscan(env, thisobject);
+
+ char str[MAXSTR];
+
+ scan_string(*scan, str);
+
+ return (*env)->NewStringUTF(env, str);
+}
+
+JNIEXPORT void JNICALL Java_edu_wlu_cs_levy_breezyslam_components_Scan_update (JNIEnv *env, jobject thisobject,
+ jintArray lidar_mm,
+ jdouble hole_width_mm,
+ jdouble velocities_dxy_mm,
+ jdouble velocities_dtheta_degrees)
+{
+ scan_t * scan = cscan_from_jscan(env, thisobject);
+
+ jint * lidar_mm_c = (*env)->GetIntArrayElements(env, lidar_mm, 0);
+
+ scan_update(scan, lidar_mm_c, hole_width_mm, velocities_dxy_mm, velocities_dtheta_degrees);
+
+ (*env)->ReleaseIntArrayElements(env, lidar_mm, lidar_mm_c, 0);
+}
diff --git a/java/edu/wlu/cs/levy/breezyslam/jni_utils.h b/java/edu/wlu/cs/levy/breezyslam/jni_utils.h
new file mode 100644
index 0000000..acaa3a5
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/jni_utils.h
@@ -0,0 +1,64 @@
+/*
+ * jni_utils.h Utilities for Java Native Interface code
+ *
+ * Copyright (C) 2014 Simon D. Levy
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ */
+
+
+#include
+#include
+#include "../../../../../../c/coreslam.h"
+
+#include
+#include
+
+static jfieldID get_fid(JNIEnv *env, jobject object, const char * fieldname, const char * fieldsig)
+{
+ jclass cls = (*env)->GetObjectClass(env, object);
+ return (*env)->GetFieldID(env, cls, fieldname, fieldsig);
+}
+
+static jfieldID get_this_fid(JNIEnv *env, jobject thisobject)
+{
+ return get_fid(env, thisobject, "native_ptr", "J");
+}
+
+static void * ptr_from_obj(JNIEnv *env, jobject thisobject)
+{
+ return (void *)(*env)->GetLongField (env, thisobject, get_this_fid(env, thisobject));
+}
+
+static void ptr_to_obj(JNIEnv *env, jobject thisobject, void * ptr)
+{
+ (*env)->SetLongField (env, thisobject, get_this_fid(env, thisobject), (long)ptr);
+}
+
+static scan_t * cscan_from_jscan(JNIEnv *env, jobject thisobject)
+{
+ return (scan_t *)ptr_from_obj(env, thisobject);
+}
+
+static map_t * cmap_from_jmap(JNIEnv *env, jobject thisobject)
+{
+ return (map_t *)ptr_from_obj(env, thisobject);
+}
+
+static double get_double_field(JNIEnv *env, jobject object, const char * fieldname)
+{
+ return (*env)->GetDoubleField (env, object, get_fid(env, object, fieldname, "D"));
+}
+
+
diff --git a/java/edu/wlu/cs/levy/breezyslam/robots/WheeledRobot.java b/java/edu/wlu/cs/levy/breezyslam/robots/WheeledRobot.java
new file mode 100644
index 0000000..f2b2822
--- /dev/null
+++ b/java/edu/wlu/cs/levy/breezyslam/robots/WheeledRobot.java
@@ -0,0 +1,130 @@
+/**
+ *
+ * BreezySLAM: Simple, efficient SLAM in Java
+ *
+ * WheeledRobot.java - Java class for wheeled robots
+ *
+ * Copyright (C) 2014 Simon D. Levy
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ */
+
+
+package edu.wlu.cs.levy.breezyslam.robots;
+
+import edu.wlu.cs.levy.breezyslam.components.PoseChange;
+
+
+/**
+ * An abstract class for wheeled robots. Supports computation of forward and angular
+ * poseChange based on odometry. Your subclass should should implement the
+ * extractOdometry method.
+ */
+public abstract class WheeledRobot
+{
+
+ /**
+ * Builds a WheeledRobot object. Parameters should be based on the specifications for
+ * your robot.
+ * @param wheel_radius_mm radius of each odometry wheel, in meters
+ * @param half_axle_length_mm half the length of the axle between the odometry wheels, in meters
+ * @return a new WheeledRobot object
+ */
+ protected WheeledRobot(double wheel_radius_mm, double half_axle_length_mm)
+ {
+ this.wheel_radius_mm = wheel_radius_mm;
+ this.half_axle_length_mm = half_axle_length_mm;
+
+ this.timestamp_seconds_prev = 0;
+ this.left_wheel_degrees_prev = 0;
+ this.right_wheel_degrees_prev = 0;
+ }
+
+ public String toString()
+ {
+
+ return String.format("",
+ this.wheel_radius_mm, this.half_axle_length_mm, this.descriptorString());
+ }
+
+ /**
+ * Computes forward and angular poseChange based on odometry.
+ * @param timestamp time stamp, in whatever units your robot uses
+ * @param left_wheel_odometry odometry for left wheel, in whatever units your robot uses
+ * @param right_wheel_odometry odometry for right wheel, in whatever units your robot uses
+ * @return poseChange object representing poseChange for these odometry values
+ */
+
+ public PoseChange computePoseChange( double timestamp, double left_wheel_odometry, double right_wheel_odometry)
+ {
+ WheelOdometry odometry = this.extractOdometry(timestamp, left_wheel_odometry, right_wheel_odometry);
+
+ double dxy_mm = 0;
+ double dtheta_degrees = 0;
+ double dt_seconds = 0;
+
+ if (this.timestamp_seconds_prev > 0)
+ {
+ double left_diff_degrees = odometry.left_wheel_degrees - this.left_wheel_degrees_prev;
+ double right_diff_degrees = odometry.right_wheel_degrees - this.right_wheel_degrees_prev;
+
+ dxy_mm = this.wheel_radius_mm * (java.lang.Math.toRadians(left_diff_degrees) + java.lang.Math.toRadians(right_diff_degrees));
+
+ dtheta_degrees = this.wheel_radius_mm / this.half_axle_length_mm * (right_diff_degrees - left_diff_degrees);
+
+ dt_seconds = odometry.timestamp_seconds - this.timestamp_seconds_prev;
+ }
+
+ // Store current odometry for next time
+ this.timestamp_seconds_prev = odometry.timestamp_seconds;
+ this.left_wheel_degrees_prev = odometry.left_wheel_degrees;
+ this.right_wheel_degrees_prev = odometry.right_wheel_degrees;
+
+ return new PoseChange(dxy_mm, dtheta_degrees, dt_seconds);
+ }
+
+ /**
+ * Extracts usable odometry values from your robot's odometry.
+ * @param timestamp time stamp, in whatever units your robot uses
+ * @param left_wheel_odometry odometry for left wheel, in whatever units your robot uses
+ * @param right_wheel_odometry odometry for right wheel, in whatever units your robot uses
+ * @return WheelOdometry object containing timestamp in seconds, left wheel degrees, right wheel degrees
+ */
+ protected abstract WheelOdometry extractOdometry(double timestamp, double left_wheel_odometry, double right_wheel_odometry);
+
+ protected abstract String descriptorString();
+
+ protected class WheelOdometry
+ {
+ public WheelOdometry(double timestamp_seconds, double left_wheel_degrees, double right_wheel_degrees)
+ {
+ this.timestamp_seconds = timestamp_seconds;
+ this.left_wheel_degrees = left_wheel_degrees;
+ this.right_wheel_degrees = right_wheel_degrees;
+ }
+
+ public double timestamp_seconds;
+ public double left_wheel_degrees;
+ public double right_wheel_degrees;
+
+ }
+
+
+ private double wheel_radius_mm;
+ private double half_axle_length_mm;
+
+ private double timestamp_seconds_prev;
+ private double left_wheel_degrees_prev;
+ private double right_wheel_degrees_prev;
+}
diff --git a/matlab/CoreSLAM.m b/matlab/CoreSLAM.m
new file mode 100644
index 0000000..ad17c74
--- /dev/null
+++ b/matlab/CoreSLAM.m
@@ -0,0 +1,136 @@
+classdef CoreSLAM
+ %CoreSLAM CoreSLAM abstract class for BreezySLAM
+ % CoreSLAM is an abstract class that uses the classes Position,
+ % Map, Scan, and Laser to run variants of the simple CoreSLAM
+ % (tinySLAM) algorithm described in
+ %
+ % @inproceedings{coreslam-2010,
+ % author = {Bruno Steux and Oussama El Hamzaoui},
+ % title = {CoreSLAM: a SLAM Algorithm in less than 200 lines of C code},
+ % booktitle = {11th International Conference on Control, Automation,
+ % Robotics and Vision, ICARCV 2010, Singapore, 7-10
+ % December 2010, Proceedings},
+ % pages = {1975-1979},
+ % publisher = {IEEE},
+ % year = {2010}
+ % }
+ %
+ %
+ % Implementing classes should provide the method
+ %
+ % updateMapAndPointcloud(scan_mm, velocities)
+ %
+ % to update the map and point-cloud (particle cloud).
+ %
+ % Copyright (C) 2014 Simon D. Levy
+ %
+ % This code is free software: you can redistribute it and/or modify
+ % it under the terms of the GNU Lesser General Public License as
+ % published by the Free Software Foundation, either version 3 of the
+ % License, or (at your option) any later version.
+ %
+ % This code is distributed in the hope that it will be useful,
+ % but WITHOUT ANY WARRANTY without even the implied warranty of
+ % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ % GNU General Public License for more details.
+ %
+ % You should have received a copy of the GNU Lesser General Public License
+ % along with this code. If not, see .
+
+ properties (Access = 'public')
+ map_quality = 50; % The quality of the map (0 through 255); default = 50
+ hole_width_mm = 600; % The width in millimeters of each "hole" in the map (essentially, wall width); default = 600
+ end
+
+ properties (Access = 'protected')
+ laser % (internal)
+ scan_for_distance % (internal)
+ scan_for_mapbuild % (internal)
+ map % (internal)
+ velocities % (internal)
+ end
+
+ methods (Abstract, Access = 'protected')
+
+ updateMapAndPointcloud(slam, velocities)
+
+ end
+
+ methods (Access = 'private')
+
+ function scan_update(slam, scanobj, scans_mm)
+ scanobj.update(scans_mm, slam.hole_width_mm, slam.velocities)
+ end
+
+ end
+
+ methods (Access = 'protected')
+
+ function slam = CoreSLAM(laser, map_size_pixels, map_size_meters)
+ % Creates a CoreSLAM object suitable for updating with new Lidar and odometry data.
+ % CoreSLAM(laser, map_size_pixels, map_size_meters)
+ % laser is a Laser object representing the specifications of your Lidar unit
+ % map_size_pixels is the size of the square map in pixels
+ % map_size_meters is the size of the square map in meters
+
+ % Store laser for later
+ slam.laser = laser;
+
+ % Initialize velocities (dxyMillimeters, dthetaDegrees, dtSeconds) for odometry
+ slam.velocities = [0, 0, 0];
+
+ % Initialize a scan for computing distance to map, and one for updating map
+ slam.scan_for_distance = Scan(laser, 1);
+ slam.scan_for_mapbuild = Scan(laser, 3);
+
+ % Initialize the map
+ slam.map = Map(map_size_pixels, map_size_meters);
+
+ end
+ end
+
+ methods (Access = 'public')
+
+ function slam = update(slam, scans_mm, velocities)
+ % Updates the scan and odometry.
+ % Calls the the implementing class's updateMapAndPointcloud()
+ % method with the specified velocities.
+ %
+ % slam = update(slam, scans_mm, [velocities])
+ %
+ % scan_mm is a list of Lidar scan values, whose count is specified in the scan_size
+ % velocities is an optional list of velocities [dxy_mm, dtheta_degrees, dt_seconds] for odometry
+
+ % Build a scan for computing distance to map, and one for updating map
+ slam.scan_update(slam.scan_for_mapbuild, scans_mm)
+ slam.scan_update(slam.scan_for_distance, scans_mm)
+
+ % Default to zero velocities
+ if nargin < 3
+ velocities = [0, 0, 0];
+ end
+
+ % Update velocities
+ velocity_factor = 0;
+ if velocities(3) > 0
+ velocity_factor = 1 / velocities(3);
+ end
+ new_dxy_mm = velocities(1) * velocity_factor;
+ new_dtheta_degrees = velocities(2) * velocity_factor;
+ slam.velocities = [new_dxy_mm, new_dtheta_degrees, 0];
+
+ % Implementing class updates map and pointcloud
+ slam = slam.updateMapAndPointcloud(velocities);
+
+ end
+
+ function map = getmap(slam)
+ % Returns the current map.
+ % Map is returned as an occupancy grid (matrix of pixels).
+ map = slam.map.get();
+ end
+
+ end
+
+end
+
diff --git a/matlab/Deterministic_SLAM.m b/matlab/Deterministic_SLAM.m
new file mode 100644
index 0000000..2b7c8e7
--- /dev/null
+++ b/matlab/Deterministic_SLAM.m
@@ -0,0 +1,44 @@
+classdef Deterministic_SLAM < SinglePositionSLAM
+ %Deterministic_SLAM SLAM with no Monte Carlo search
+ % Implements the getNewPosition() method of SinglePositionSLAM
+ % by copying the start position.
+ %
+ % Copyright (C) 2014 Simon D. Levy
+ %
+ % This code is free software: you can redistribute it and/or modify
+ % it under the terms of the GNU Lesser General Public License as
+ % published by the Free Software Foundation, either version 3 of the
+ % License, or (at your option) any later version.
+ %
+ % This code is distributed in the hope that it will be useful,
+ % but WITHOUT ANY WARRANTY without even the implied warranty of
+ % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ % GNU General Public License for more details.
+ %
+ % You should have received a copy of the GNU Lesser General Public License
+ % along with this code. If not, see .
+
+ methods
+
+ function slam = Deterministic_SLAM(laser, map_size_pixels, map_size_meters)
+ %Creates a Deterministic_SLAM object suitable for updating with new Lidar and odometry data.
+ % slam = Deterministic_SLAM(laser, map_size_pixels, map_size_meters)
+ % laser is a Laser object representing the specifications of your Lidar unit
+ % map_size_pixels is the size of the square map in pixels
+ % map_size_meters is the size of the square map in meters
+
+ slam = slam@SinglePositionSLAM(laser, map_size_pixels, map_size_meters);
+
+ end
+
+ function new_pos = getNewPosition(~, start_pos)
+ % Implements the _getNewPosition() method of SinglePositionSLAM.
+ % new_pos = getNewPosition(~, start_pos) simply returns start_pos
+
+ new_pos = start_pos;
+ end
+
+ end
+
+end
+
diff --git a/matlab/Map.m b/matlab/Map.m
new file mode 100644
index 0000000..e3f960e
--- /dev/null
+++ b/matlab/Map.m
@@ -0,0 +1,59 @@
+classdef Map
+ %A class for maps (occupancy grids) used in SLAM
+ %
+ % Copyright (C) 2014 Simon D. Levy
+ %
+ % This code is free software: you can redistribute it and/or modify
+ % it under the terms of the GNU Lesser General Public License as
+ % published by the Free Software Foundation, either version 3 of the
+ % License, or (at your option) any later version.
+ %
+ % This code is distributed in the hope that it will be useful,
+ % but WITHOUT ANY WARRANTY without even the implied warranty of
+ % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ % GNU General Public License for more details.
+ %
+ % You should have received a copy of the GNU Lesser General Public License
+ % along with this code. If not, see .
+
+ properties (Access = {?RMHC_SLAM})
+
+ c_map
+ end
+
+ methods (Access = 'public')
+
+ function map = Map(size_pixels, size_meters)
+ % Map creates an empty square map
+ % map = Map(size_pixels, size_meters)
+ map.c_map = mex_breezyslam('Map_init', size_pixels, size_meters);
+
+ end
+
+ function disp(map)
+ % Displays data about this map
+ mex_breezyslam('Map_disp', map.c_map)
+
+ end
+
+ function update(map, scan, new_position, map_quality, hole_width_mm)
+ % Updates this map with a new scan and position
+ %
+ % update(map, scan, new_position, map_quality, hole_width_mm)
+ mex_breezyslam('Map_update', map.c_map, scan.c_scan, new_position, int32(map_quality), hole_width_mm)
+
+ end
+
+ function bytes = get(map)
+ % Returns occupancy grid matrix of bytes for this map
+ %
+ % bytes = get(map)
+
+ % Transpose for uniformity with Python, C++ versions
+ bytes = mex_breezyslam('Map_get', map.c_map)';
+
+ end
+
+ end % methods
+
+end % classdef
diff --git a/matlab/RMHC_SLAM.m b/matlab/RMHC_SLAM.m
new file mode 100644
index 0000000..0062574
--- /dev/null
+++ b/matlab/RMHC_SLAM.m
@@ -0,0 +1,72 @@
+classdef RMHC_SLAM < SinglePositionSLAM
+ %RMHC_SLAM Random Mutation Hill-Climbing SLAM
+ % Implements the getNewPosition() method of SinglePositionSLAM
+ % using Random-Mutation Hill-Climbing search. Uses its own internal
+ % pseudorandom-number generator for efficiency.
+ %
+ % Copyright (C) 2014 Simon D. Levy
+ %
+ % This code is free software: you can redistribute it and/or modify
+ % it under the terms of the GNU Lesser General Public License as
+ % published by the Free Software Foundation, either version 3 of the
+ % License, or (at your option) any later version.
+ %
+ % This code is distributed in the hope that it will be useful,
+ % but WITHOUT ANY WARRANTY without even the implied warranty of
+ % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ % GNU General Public License for more details.
+ %
+ % You should have received a copy of the GNU Lesser General Public License
+ % along with this code. If not, see .
+
+ properties (Access = 'public')
+ sigma_xy_mm = 100; % std. dev. of X/Y component of search
+ sigma_theta_degrees = 20; % std. dev. of angular component of search
+ max_search_iter = 1000; % max. # of search iterations per update
+ end
+
+ properties (Access = 'private')
+ c_randomizer
+ end
+
+ methods
+
+ function slam = RMHC_SLAM(laser, map_size_pixels, map_size_meters, random_seed)
+ %Creates an RMHC_SLAM object suitable for updating with new Lidar and odometry data.
+ % slam = RMHC_SLAM(laser, map_size_pixels, map_size_meters, random_seed)
+ % laser is a Laser object representing the specifications of your Lidar unit
+ % map_size_pixels is the size of the square map in pixels
+ % map_size_meters is the size of the square map in meters
+ % random_seed supports reproducible results; defaults to system time if unspecified
+
+ slam = slam@SinglePositionSLAM(laser, map_size_pixels, map_size_meters);
+
+ if nargin < 3
+ random_seed = floor(cputime) & hex2dec('FFFF');
+ end
+
+ slam.c_randomizer = mex_breezyslam('Randomizer_init', random_seed);
+
+ end
+
+ function new_pos = getNewPosition(slam, start_pos)
+ % Implements the _getNewPosition() method of SinglePositionSLAM.
+ % Uses Random-Mutation Hill-Climbing search to look for a
+ % better position based on a starting position.
+
+ [new_pos.x_mm,new_pos.y_mm,new_pos.theta_degrees] = ...
+ mex_breezyslam('rmhcPositionSearch', ...
+ start_pos, ...
+ slam.map.c_map, ...
+ slam.scan_for_distance.c_scan, ...
+ slam.laser,...
+ slam.sigma_xy_mm,...
+ slam.sigma_theta_degrees,...
+ slam.max_search_iter,...
+ slam.c_randomizer);
+ end
+
+ end
+
+end
+
diff --git a/matlab/Scan.m b/matlab/Scan.m
new file mode 100644
index 0000000..4004572
--- /dev/null
+++ b/matlab/Scan.m
@@ -0,0 +1,66 @@
+classdef Scan
+ %A class for Lidar scans used in SLAM
+ %
+ % Copyright (C) 2014 Simon D. Levy
+ %
+ % This code is free software: you can redistribute it and/or modify
+ % it under the terms of the GNU Lesser General Public License as
+ % published by the Free Software Foundation, either version 3 of the
+ % License, or (at your option) any later version.
+ %
+ % This code is distributed in the hope that it will be useful,
+ % but WITHOUT ANY WARRANTY without even the implied warranty of
+ % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ % GNU General Public License for more details.
+ %
+ % You should have received a copy of the GNU Lesser General Public License
+ % along with this code. If not, see .
+
+ properties (Access = {?Map, ?RMHC_SLAM})
+
+ c_scan
+ end
+
+ methods
+
+ function scan = Scan(laser, span)
+ % Creates a new scan object
+ % scan = Scan(laser, [span])
+ % laser is a structure with the fields:
+ % scan_size
+ % scan_rate_hz
+ % detection_angle_degrees
+ % distance_no_detection_mm
+ % distance_no_detection_mm
+ % detection_margin
+ % offset_mm = offset_mm
+ % span (default=1) supports spanning laser scan to cover the space better
+
+ if nargin < 2
+ span = 1;
+ end
+
+ scan.c_scan = mex_breezyslam('Scan_init', laser, span);
+
+ end
+
+ function disp(scan)
+ % Displays information about this Scan
+
+ mex_breezyslam('Scan_disp', scan.c_scan)
+
+ end
+
+ function update(scan, scans_mm, hole_width_mm, velocities)
+ % Updates scan with new lidar and velocity data
+ % update(scans_mm, hole_width_mm, [velocities])
+ % scans_mm is a list of integers representing scanned distances in mm.
+ % hole_width_mm is the width of holes (obstacles, walls) in millimeters.
+ % velocities is an optional list[dxy_mm, dtheta_degrees]
+ % i.e., robot's (forward, rotational velocity) for improving the quality of the scan.
+ mex_breezyslam('Scan_update', scan.c_scan, int32(scans_mm), hole_width_mm, velocities)
+ end
+
+ end
+
+end
diff --git a/matlab/SinglePositionSLAM.m b/matlab/SinglePositionSLAM.m
new file mode 100644
index 0000000..787753c
--- /dev/null
+++ b/matlab/SinglePositionSLAM.m
@@ -0,0 +1,101 @@
+classdef SinglePositionSLAM < CoreSLAM
+ %SinglePositionSLAM Abstract class for CoreSLAM with single-point cloud
+ % Implementing classes should provide the method
+ %
+ % getNewPosition(self, start_position)
+ %
+ % to compute a new position based on searching from a starting position.
+ %
+ % Copyright (C) 2014 Simon D. Levy
+ %
+ % This code is free software: you can redistribute it and/or modify
+ % it under the terms of the GNU Lesser General Public License as
+ % published by the Free Software Foundation, either version 3 of the
+ % License, or (at your option) any later version.
+ %
+ % This code is distributed in the hope that it will be useful,
+ % but WITHOUT ANY WARRANTY without even the implied warranty of
+ % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ % GNU General Public License for more details.
+ %
+ % You should have received a copy of the GNU Lesser General Public License
+ % along with this code. If not, see .
+
+ properties (Access = 'private')
+ position
+ end
+
+ methods (Abstract)
+ getNewPosition(slam, start_pos)
+ end
+
+ methods (Access = 'private')
+
+ function c = costheta(slam)
+ c = cosd(slam.position.theta_degrees);
+ end
+
+ function s = sintheta(slam)
+ s = sind(slam.position.theta_degrees);
+ end
+
+ end
+
+ methods (Access = 'public')
+
+ function slam = SinglePositionSLAM(laser, map_size_pixels, map_size_meters)
+
+ slam = slam@CoreSLAM(laser, map_size_pixels, map_size_meters);
+
+ % Initialize the position (x, y, theta)
+ init_coord_mm = 500 * map_size_meters; % center of map
+ slam.position.x_mm = init_coord_mm;
+ slam.position.y_mm = init_coord_mm;
+ slam.position.theta_degrees = 0;
+
+ end
+
+ function [x_mm, y_mm, theta_degrees] = getpos(slam)
+ % Returns the current position.
+ % [x_mm, y_mm, theta_degrees] = getpos(slam)
+
+ x_mm = slam.position.x_mm;
+ y_mm = slam.position.y_mm;
+ theta_degrees = slam.position.theta_degrees;
+ end
+
+ end
+
+ methods (Access = 'protected')
+
+ function slam = updateMapAndPointcloud(slam, velocities)
+
+ % Start at current position
+ start_pos = slam.position;
+
+ % Add effect of velocities
+ start_pos.x_mm = start_pos.x_mm + velocities(1) * slam.costheta();
+ start_pos.y_mm = start_pos.y_mm + velocities(1) * slam.sintheta();
+ start_pos.theta_degrees = start_pos.theta_degrees + velocities(2);
+
+ % Add offset from laser
+ start_pos.x_mm = start_pos.x_mm + slam.laser.offset_mm * slam.costheta();
+ start_pos.y_mm = start_pos.y_mm + slam.laser.offset_mm * slam.sintheta();
+
+ % Get new position from implementing class
+ new_position = slam.getNewPosition(start_pos);
+
+ % Update the map with this new position
+ slam.map.update(slam.scan_for_mapbuild, new_position, slam.map_quality, slam.hole_width_mm)
+
+ % Update the current position with this new position, adjusted by laser offset
+ slam.position = new_position;
+ slam.position.x_mm = slam.position.x_mm - slam.laser.offset_mm * slam.costheta();
+ slam.position.y_mm = slam.position.y_mm - slam.laser.offset_mm * slam.sintheta();
+
+ end
+
+ end
+
+end
+
diff --git a/matlab/WheeledRobot.m b/matlab/WheeledRobot.m
new file mode 100644
index 0000000..ac155c7
--- /dev/null
+++ b/matlab/WheeledRobot.m
@@ -0,0 +1,112 @@
+classdef WheeledRobot
+ %WheeledRobot An abstract class supporting ododmetry for wheeled robots.
+ % Your implementing class should provide the method:
+ %
+ % extractOdometry(obj, timestamp, leftWheel, rightWheel) -->
+ % (timestampSeconds, leftWheelDegrees, rightWheelDegrees)
+ %
+ % Copyright (C) 2014 Simon D. Levy
+ %
+ % This code is free software: you can redistribute it and/or modify
+ % it under the terms of the GNU Lesser General Public License as
+ % published by the Free Software Foundation, either version 3 of the
+ % License, or (at your option) any later version.
+ %
+ % This code is distributed in the hope that it will be useful,
+ % but WITHOUT ANY WARRANTY without even the implied warranty of
+ % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ % GNU General Public License for more details.
+ %
+ % You should have received a copy of the GNU Lesser General Public License
+ % along with this code. If not, see .
+
+ properties (Access = 'private')
+
+ wheelRadiusMillimeters
+ halfAxleLengthMillimeters
+
+ timestampSecondsPrev
+ leftWheelDegreesPrev
+ rightWheelDegreesPrev
+
+ end
+
+ methods (Access = 'protected')
+
+ function s = str(obj)
+ % Returns a string representation of this WheeledRobot
+ s = sprintf('', ...
+ obj.wheelRadiusMillimeters, obj.halfAxleLengthMillimeters);
+ end
+
+ function robot = WheeledRobot(wheelRadiusMillimeters, halfAxleLengthMillimeters)
+ % Constructs a WheeledRobot object
+ % robot = WheeledRobot(wheelRadiusMillimeters, halfAxleLengthMillimeters)
+ robot.wheelRadiusMillimeters = wheelRadiusMillimeters;
+ robot.halfAxleLengthMillimeters = halfAxleLengthMillimeters;
+
+ end
+
+ function r = deg2rad(~, d)
+ % Converts degrees to radians
+ r = d * pi / 180;
+ end
+
+ end
+
+ methods (Abstract)
+
+ extractOdometry(obj, timestamp, leftWheel, rightWheel)
+
+ end
+
+ methods (Access = 'public')
+
+ function [poseChange, obj] = computePoseChange(obj, timestamp, leftWheelOdometry, rightWheelOdometry)
+ % Computes forward and angular poseChange based on odometry.
+ %
+ % Parameters:
+
+ % timestamp time stamp, in whatever units your robot uses
+ % leftWheelOdometry odometry for left wheel, in whatever form your robot uses
+ % rightWheelOdometry odometry for right wheel, in whatever form your robot uses
+ %
+ % Returns a tuple (dxyMillimeters, dthetaDegrees, dtSeconds)
+
+ % dxyMillimeters forward distance traveled, in millimeters
+ % dthetaDegrees change in angular position, in degrees
+ % dtSeconds elapsed time since previous odometry, in seconds
+
+ dxyMillimeters = 0;
+ dthetaDegrees = 0;
+ dtSeconds = 0;
+
+ [timestampSecondsCurr, leftWheelDegreesCurr, rightWheelDegreesCurr] = ...
+ obj.extractOdometry(timestamp, leftWheelOdometry, rightWheelOdometry);
+
+ if obj.timestampSecondsPrev
+
+ leftDiffDegrees = leftWheelDegreesCurr - obj.leftWheelDegreesPrev;
+ rightDiffDegrees = rightWheelDegreesCurr - obj.rightWheelDegreesPrev;
+
+ dxyMillimeters = obj.wheelRadiusMillimeters * ...
+ (obj.deg2rad(leftDiffDegrees) + obj.deg2rad(rightDiffDegrees));
+
+ dthetaDegrees = (obj.wheelRadiusMillimeters / obj.halfAxleLengthMillimeters) * ...
+ (rightDiffDegrees - leftDiffDegrees);
+
+ dtSeconds = timestampSecondsCurr - obj.timestampSecondsPrev;
+ end
+
+ % Store current odometry for next time
+ obj.timestampSecondsPrev = timestampSecondsCurr;
+ obj.leftWheelDegreesPrev = leftWheelDegreesCurr;
+ obj.rightWheelDegreesPrev = rightWheelDegreesCurr;
+
+ % Return linear velocity, angular velocity, time difference
+ poseChange = [dxyMillimeters, dthetaDegrees, dtSeconds];
+
+ end
+ end
+end
+
diff --git a/matlab/make.m b/matlab/make.m
new file mode 100644
index 0000000..b00df47
--- /dev/null
+++ b/matlab/make.m
@@ -0,0 +1,19 @@
+% Script for building BreezySLAM C extensions
+
+% Copyright (C) 2014 Simon D. Levy
+%
+% This code is free software: you can redistribute it and/or modify
+% it under the terms of the GNU Lesser General Public License as
+% published by the Free Software Foundation, either version 3 of the
+% License, or (at your option) any later version.
+%
+% This code is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU Lesser General Public License
+% along with this code. If not, see .
+
+
+mex mex_breezyslam.c ../c/coreslam.c ../c/coreslam_sisd.c ../c/random.c ../c/ziggurat.c
diff --git a/matlab/mex_breezyslam.c b/matlab/mex_breezyslam.c
new file mode 100644
index 0000000..b74cbf3
--- /dev/null
+++ b/matlab/mex_breezyslam.c
@@ -0,0 +1,294 @@
+/*
+ * mex_breezyslam.c : C extensions for BreezySLAM in Matlab
+ *
+ * Copyright (C) 2014 Simon D. Levy
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ */
+
+#include "mex.h"
+
+#include "../c/coreslam.h"
+#include "../c/random.h"
+
+#define MAXSTR 100
+
+/* Helpers ------------------------------------------------------------- */
+
+static int _streq(char * s, const char * t)
+{
+ return !strcmp(s, t);
+}
+
+static void _insert_obj_lhs(mxArray *plhs[], void * obj, int pos)
+{
+ long * outptr = NULL;
+
+ plhs[pos] = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL);
+
+ outptr = (long *) mxGetPr(plhs[pos]);
+
+ mexMakeMemoryPersistent(obj);
+
+ *outptr = (long)obj;
+}
+
+static double _get_field(const mxArray * pm, const char * fieldname)
+{
+ mxArray * field_array_ptr = mxGetField(pm, 0, fieldname);
+
+ return mxGetScalar(field_array_ptr);
+}
+
+static long _rhs2ptr(const mxArray * prhs[], int index)
+{
+ long * inptr = (long *) mxGetPr(prhs[index]);
+
+ return *inptr;
+}
+
+static scan_t * _rhs2scan(const mxArray * prhs[], int index)
+{
+ long inptr = _rhs2ptr(prhs, index);
+
+ return (scan_t *)inptr;
+}
+
+static map_t * _rhs2map(const mxArray * prhs[], int index)
+{
+ long inptr = _rhs2ptr(prhs, index);
+
+ return (map_t *)inptr;
+}
+
+static position_t _rhs2pos(const mxArray * prhs[], int index)
+{
+ position_t position;
+
+ position.x_mm = _get_field(prhs[index], "x_mm");
+ position.y_mm = _get_field(prhs[index], "y_mm");
+ position.theta_degrees = _get_field(prhs[index], "theta_degrees");
+
+ return position;
+}
+
+/* Class methods ------------------------------------------------------- */
+
+static void _map_init(mxArray *plhs[], const mxArray * prhs[])
+{
+
+ int size_pixels = (int)mxGetScalar(prhs[1]);
+
+ double size_meters = mxGetScalar(prhs[2]);
+
+ map_t * map = (map_t *)mxMalloc(sizeof(map_t));
+
+ map_init(map, size_pixels, size_meters);
+
+ _insert_obj_lhs(plhs, map, 0);
+}
+
+static void _map_disp(const mxArray * prhs[])
+{
+ char str[MAXSTR];
+
+ map_t * map = _rhs2map(prhs, 1);
+
+ map_string(*map, str);
+
+ printf("%s\n", str);
+}
+
+static void _map_update(const mxArray * prhs[])
+{
+ map_t * map = _rhs2map(prhs, 1);
+
+ scan_t * scan = _rhs2scan(prhs, 2);
+
+ position_t position = _rhs2pos(prhs, 3);
+
+ int map_quality = (int)mxGetScalar(prhs[4]);
+
+ double hole_width_mm = mxGetScalar(prhs[5]);
+
+ map_update(map, scan, position, map_quality, hole_width_mm);
+}
+
+static void _map_get(mxArray *plhs[], const mxArray * prhs[])
+{
+ map_t * map = _rhs2map(prhs, 1);
+
+ unsigned char * pointer = NULL;
+
+ plhs[0] = mxCreateNumericMatrix(map->size_pixels, map->size_pixels,
+ mxUINT8_CLASS, mxREAL);
+
+ pointer = (unsigned char *)mxGetPr(plhs[0]);
+
+ map_get(map, pointer);
+}
+
+
+static void _scan_init(mxArray *plhs[], const mxArray * prhs[])
+{
+
+ scan_t * scan = (scan_t *)mxMalloc(sizeof(scan_t));
+
+ int span = (int)mxGetScalar(prhs[2]);
+
+ const mxArray * laser = prhs[1];
+ int scan_size = (int)_get_field(laser, "scan_size");
+ double scan_rate_hz = _get_field(laser, "scan_rate_hz");
+ double detection_angle_degrees = _get_field(laser, "detection_angle_degrees");
+ double distance_no_detection_mm = _get_field(laser, "distance_no_detection_mm");
+ int detection_margin = (int)_get_field(laser, "detection_margin");
+ double offset_mm = _get_field(laser, "offset_mm");
+
+ scan_init(
+ scan,
+ span,
+ scan_size,
+ scan_rate_hz,
+ detection_angle_degrees,
+ distance_no_detection_mm,
+ detection_margin,
+ offset_mm);
+
+ _insert_obj_lhs(plhs, scan, 0);
+}
+
+static void _scan_disp(const mxArray * prhs[])
+{
+ char str[MAXSTR];
+
+ scan_t * scan = _rhs2scan(prhs, 1);
+
+ scan_string(*scan, str);
+
+ printf("%s\n", str);
+}
+
+static void _scan_update(const mxArray * prhs[])
+{
+ scan_t * scan = _rhs2scan(prhs, 1);
+
+ int scansize = (int)mxGetNumberOfElements(prhs[2]);
+
+ int * lidar_mm = (int *)mxGetPr(prhs[2]);
+
+ double hole_width_mm = mxGetScalar(prhs[3]);
+
+ double * velocities = mxGetPr(prhs[4]);
+
+ scan_update(scan, lidar_mm, hole_width_mm, velocities[0], velocities[1]);
+}
+
+static void _randomizer_init(mxArray *plhs[], const mxArray * prhs[])
+{
+ int seed = (int)mxGetScalar(prhs[1]);
+
+ void * r = mxMalloc(random_size());
+
+ random_init(r, seed);
+
+ _insert_obj_lhs(plhs, r, 0);
+}
+
+static void _rmhcPositionSearch(mxArray *plhs[], const mxArray * prhs[])
+{
+ position_t start_pos = _rhs2pos(prhs, 1);
+
+ map_t * map = _rhs2map(prhs, 2);
+
+ scan_t * scan = _rhs2scan(prhs, 3);
+
+ position_t new_pos;
+
+ double sigma_xy_mm = mxGetScalar(prhs[5]);
+
+ double sigma_theta_degrees = mxGetScalar(prhs[6]);
+
+ int max_search_iter = (int)mxGetScalar(prhs[7]);
+
+ void * randomizer = (void *)(long)mxGetScalar(prhs[8]);
+
+ new_pos = rmhc_position_search(
+ start_pos,
+ map,
+ scan,
+ sigma_xy_mm,
+ sigma_theta_degrees,
+ max_search_iter,
+ randomizer);
+
+ plhs[0] = mxCreateDoubleScalar(new_pos.x_mm);
+ plhs[1] = mxCreateDoubleScalar(new_pos.y_mm);
+ plhs[2] = mxCreateDoubleScalar(new_pos.theta_degrees);
+}
+
+/* The gateway function ------------------------------------------------ */
+void mexFunction( int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray * prhs[])
+{
+
+ char methodname[MAXSTR];
+
+ mxGetString(prhs[0], methodname, 100);
+
+ if (_streq(methodname, "Map_init"))
+ {
+ _map_init(plhs, prhs);
+ }
+
+ else if (_streq(methodname, "Map_disp"))
+ {
+ _map_disp(prhs);
+ }
+
+ else if (_streq(methodname, "Map_update"))
+ {
+ _map_update(prhs);
+ }
+
+ else if (_streq(methodname, "Map_get"))
+ {
+ _map_get(plhs, prhs);
+ }
+
+ else if (_streq(methodname, "Scan_init"))
+ {
+ _scan_init(plhs, prhs);
+ }
+
+ else if (_streq(methodname, "Scan_disp"))
+ {
+ _scan_disp(prhs);
+ }
+
+ else if (_streq(methodname, "Scan_update"))
+ {
+ _scan_update(prhs);
+ }
+
+ else if (_streq(methodname, "Randomizer_init"))
+ {
+ _randomizer_init(plhs, prhs);
+ }
+
+ else if (_streq(methodname, "rmhcPositionSearch"))
+ {
+ _rmhcPositionSearch(plhs, prhs);
+ }
+}
+
diff --git a/matlab/mex_breezyslam.mexa64 b/matlab/mex_breezyslam.mexa64
new file mode 100755
index 0000000..0e94256
Binary files /dev/null and b/matlab/mex_breezyslam.mexa64 differ
diff --git a/matlab/mex_breezyslam.mexmaci64 b/matlab/mex_breezyslam.mexmaci64
new file mode 100644
index 0000000..97fc14b
Binary files /dev/null and b/matlab/mex_breezyslam.mexmaci64 differ
diff --git a/matlab/mex_breezyslam.mexw64 b/matlab/mex_breezyslam.mexw64
new file mode 100644
index 0000000..28bdc0a
Binary files /dev/null and b/matlab/mex_breezyslam.mexw64 differ