diff --git a/coreslam.c b/coreslam.c new file mode 100644 index 0000000..4790491 --- /dev/null +++ b/coreslam.c @@ -0,0 +1,544 @@ +/* +coreslam.c adapted from CoreSLAM.c, CoreSLAM_state.c, and CoreSLAM.h +downloaded from openslam.org on 01 January 2014. Contains implementations +of scan and map methods. + +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 . + +Change log + +07-FEB-2014 : Simon D. Levy - Initial release +01-MAR-2014 : SDL - Converted millimeters to meters for API +21-JUN-2014 : SDL - Added support for SSE and NEON +10-JUL-2014 : SDL - Changed Laser scan rate and angles from int to double +21-JUL-2014 : SDL - Made RMHC position search avoid looping when max count is zero +23-JUL-2014 : SDL - Simplified laser detection angle min, max to total angle +07-SEP-2014 : SDL - Migrated to github +*/ + + +#include +#include +#include +#include +#include + +#include "coreslam.h" +#include "coreslam_internals.h" + +#include "random.h" + +/* Local helpers--------------------------------------------------- */ + +static void * safe_malloc(size_t size) +{ + void * v = malloc(size); + + if (!v) + { + fprintf(stderr, "Unable to allocate %lu bytes\n", (unsigned long)size); + exit(1); + } + + return v; +} + + +static double * double_alloc(int size) +{ + return (double *)safe_malloc(size * sizeof(double)); +} + +static float * float_alloc(int size) +{ + return (float *)safe_malloc(size * sizeof(float)); +} + + +static void +swap(int * a, int * b) +{ + int tmp = *a; + *a = *b; + *b = tmp; +} + + +static int roundup(double x) +{ + return (int)floor(x + 0.5); +} + + +static int +out_of_bounds(int value, int bound) +{ + return value < 0 || value >= bound; +} + + + +static int +clip(int *xyc, int * yxc, int xy, int yx, int map_size) +{ + + if (*xyc < 0) + { + if (*xyc == xy) + { + return 1; + } + *yxc += (*yxc - yx) * (- *xyc) / (*xyc - xy); + *xyc = 0; + } + + if (*xyc >= map_size) + { + if (*xyc == xy) + { + return 1; + } + *yxc += (*yxc - yx) * (map_size - 1 - *xyc) / (*xyc - xy); + *xyc = map_size - 1; + } + + return 0; +} + + +static void +map_laser_ray( + pixel_t * map_pixels, + int map_size, + int x1, + int y1, + int x2, + int y2, + int xp, + int yp, + int value, + int alpha) +{ + + if (out_of_bounds(x1, map_size) || out_of_bounds(y1, map_size)) + { + return; + } + + int x2c = x2; + int y2c = y2; + + if (clip(&x2c, &y2c, x1, y1, map_size) || clip(&y2c, &x2c, y1, x1, map_size)) + { + return; + } + + int dx = abs(x2 - x1); + int dy = abs(y2 - y1); + int dxc = abs(x2c - x1); + int dyc = abs(y2c - y1); + int incptrx = (x2 > x1) ? 1 : -1; + int incptry = (y2 > y1) ? map_size : -map_size; + int sincv = (value > NO_OBSTACLE) ? 1 : -1; + + int derrorv = 0; + + if (dx > dy) + { + derrorv = abs(xp - x2); + } + else + { + swap(&dx, &dy); + swap(&dxc, &dyc); + swap(&incptrx, &incptry); + derrorv = abs(yp - y2); + } + + if (!derrorv) + { /* XXX should probably throw an exception */ + fprintf(stderr, "map_update: No error gradient: try increasing hole width\n"); + exit(1); + } + + int error = 2 * dyc - dxc; + int horiz = 2 * dyc; + int diago = 2 * (dyc - dxc); + int errorv = derrorv / 2; + + int incv = (value - NO_OBSTACLE) / derrorv; + + int incerrorv = value - NO_OBSTACLE - derrorv * incv; + + pixel_t * ptr = map_pixels + y1 * map_size + x1; + int pixval = NO_OBSTACLE; + + int x = 0; + for (x = 0; x <= dxc; x++, ptr += incptrx) + { + if (x > dx - 2 * derrorv) + { + if (x <= dx - derrorv) + { + pixval += incv; + errorv += incerrorv; + if (errorv > derrorv) + { + pixval += sincv; + errorv -= derrorv; + } + } + else + { + pixval -= incv; + errorv -= incerrorv; + if (errorv < 0) + { + pixval -= sincv; + errorv += derrorv; + } + } + } + + /* Integration into the map */ + *ptr = ((256 - alpha) * (*ptr) + alpha * pixval) >> 8; + + if (error > 0) + { + ptr += incptry; + error += diago; + } else + { + error += horiz; + } + } +} + + +static void +scan_update_xy( + scan_t * scan, + int offset, + int distance, + int scanval, + laser_t laser, + double horz_mm, + double rotation) +{ + int j; + for (j=0; jspan; ++j) + { + double k = (double)(offset*scan->span+j) * laser.detection_angle_degrees / + (laser.scan_size * scan->span - 1); + double angle = radians(-laser.detection_angle_degrees/2 + k * rotation); + + double x = distance * cos(angle) - k * horz_mm; + double y = distance * sin(angle); + + scan->value[scan->npoints] = scanval; + + scan->x_mm[scan->npoints] = x; + scan->y_mm[scan->npoints] = y; + scan->npoints++; + } +} + + +/* Exported functions --------------------------------------------------------*/ + +int * +int_alloc( + int size) +{ + return (int *)safe_malloc(size * sizeof(int)); +} + +void +map_init( + map_t * map, + int size_pixels, + double size_meters) +{ + int npix = size_pixels * size_pixels; + + map->pixels = (pixel_t *)safe_malloc(npix * sizeof(pixel_t)); + + int k; + for (k=0; kpixels[k] = (OBSTACLE + NO_OBSTACLE) / 2; + } + + map->size_pixels = size_pixels; + map->size_meters = size_meters; + + /* precompute scale for efficiency */ + map->scale_pixels_per_mm = size_pixels / (size_meters * 1000); +} + +void +map_free( + map_t * map) +{ + free(map->pixels); +} + +void map_string( + map_t map, + char * str) +{ + sprintf(str, "size = %d x %d pixels | = %f meters", + map.size_pixels, map.size_pixels, map.size_meters); +} + +void +map_update( + map_t * map, + scan_t * scan, + position_t position, + int map_quality, + double hole_width_mm) +{ + + double position_theta_radians = radians(position.theta_degrees); + double costheta = cos(position_theta_radians); + double sintheta = sin(position_theta_radians); + + int x1 = roundup(position.x_mm * map->scale_pixels_per_mm); + int y1 = roundup(position.y_mm * map->scale_pixels_per_mm); + + int i = 0; + for (i = 0; i != scan->npoints; i++) + { + double x2p = costheta * scan->x_mm[i] - sintheta * scan->y_mm[i]; + double y2p = sintheta * scan->x_mm[i] + costheta * scan->y_mm[i]; + + int xp = roundup((position.x_mm + x2p) * map->scale_pixels_per_mm); + int yp = roundup((position.y_mm + y2p) * map->scale_pixels_per_mm); + + + double dist = sqrt(x2p * x2p + y2p * y2p); + double add = hole_width_mm / 2 / dist; + x2p *= map->scale_pixels_per_mm * (1 + add); + y2p *= map->scale_pixels_per_mm * (1 + add); + + + int x2 = roundup(position.x_mm * map->scale_pixels_per_mm + x2p); + int y2 = roundup(position.y_mm * map->scale_pixels_per_mm + y2p); + + int value = OBSTACLE; + int q = map_quality; + + if (scan->value[i] == NO_OBSTACLE) + { + q = map_quality / 4; + value = NO_OBSTACLE; + } + + map_laser_ray(map->pixels, map->size_pixels, x1, y1, x2, y2, xp, yp, value, q); + } +} + +void +map_get( + map_t * map, + char * bytes) +{ + int k; + for (k=0; ksize_pixels*map->size_pixels; ++k) + { + bytes[k] = map->pixels[k] >> 8; + } + +} + + +void +map_set( + map_t * map, + char * bytes) +{ + int k; + for (k=0; ksize_pixels*map->size_pixels; ++k) + { + map->pixels[k] = bytes[k]; + map->pixels[k] <<= 8; + } +} + +void +scan_init( + scan_t * scan, + int size, + int span) +{ + scan->x_mm = double_alloc(size*span); + scan->y_mm = double_alloc(size*span); + scan->value = int_alloc(size*span); + scan->span = span; + + /* assure size multiple of 4 for SSE */ + scan->obst_x_mm = float_alloc(size*span+4); + scan->obst_y_mm = float_alloc(size*span+4); +} + + +void +scan_free( + scan_t * scan) +{ + free(scan->x_mm); + free(scan->y_mm); + free(scan->value); + + free(scan->obst_x_mm); + free(scan->obst_y_mm); +} + +void scan_string( + scan_t scan, + char * str) +{ + sprintf(str, "%d obstacle points | %d free points", scan.obst_npoints, scan.npoints-scan.obst_npoints); +} + +void +scan_update( + scan_t * scan, + int * lidar_mm, + laser_t laser, + double hole_width_mm, + double velocities_dxy_mm, + double velocities_dtheta_degrees) +{ + scan->npoints = 0; + scan->obst_npoints = 0; + + /* Take velocity into account */ + int degrees_per_second = laser.scan_rate_hz * 360; + double horz_mm = velocities_dxy_mm / degrees_per_second; + double rotation = 1 + velocities_dtheta_degrees / degrees_per_second; + + /* Span the laser scans to better cover the space */ + int i; + for (i=laser.detection_margin+1; i hole_width_mm / 2) + { + int oldstart = scan->npoints; + + scan_update_xy(scan, i, lidar_value_mm, OBSTACLE, laser, horz_mm, rotation); + + /* Store obstacles separately for SSE */ + int j; + for (j=oldstart; jnpoints; ++j) + { + if (scan->value[j] == OBSTACLE) + { + scan->obst_x_mm[scan->obst_npoints] = scan->x_mm[j]; + scan->obst_y_mm[scan->obst_npoints] = scan->y_mm[j]; + scan->obst_npoints++; + } + } + } + } +} + +void +laser_string( + laser_t laser, + char * str) +{ + sprintf(str, + "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", + laser.scan_size, + laser.scan_rate_hz, + laser.detection_angle_degrees, + laser.distance_no_detection_mm, + laser.detection_margin, + laser.offset_mm + ); +} + +position_t +rmhc_position_search( + position_t start_pos, + map_t * map, + scan_t * scan, + laser_t laser, + double sigma_xy_mm, + double sigma_theta_degrees, + int max_search_iter, + void * randomizer) +{ + position_t currentpos = start_pos; + position_t bestpos = start_pos; + position_t lastbestpos = start_pos; + + int current_distance = distance_scan_to_map(map, scan, currentpos); + + int lowest_distance = current_distance; + int last_lowest_distance = current_distance; + + int counter = 0; + + while (counter < max_search_iter) + { + currentpos = lastbestpos; + + currentpos.x_mm = random_normal(randomizer, currentpos.x_mm, sigma_xy_mm); + currentpos.y_mm = random_normal(randomizer, currentpos.y_mm, sigma_xy_mm); + currentpos.theta_degrees = random_normal(randomizer, currentpos.theta_degrees, sigma_theta_degrees); + + current_distance = distance_scan_to_map(map, scan, currentpos); + + /* -1 indicates infinity */ + if ((current_distance > -1) && (current_distance < lowest_distance)) + { + lowest_distance = current_distance; + bestpos = currentpos; + } + else + { + counter++; + } + + if (counter > max_search_iter / 3) + { + if (lowest_distance < last_lowest_distance) + { + lastbestpos = bestpos; + last_lowest_distance = lowest_distance; + counter = 0; + sigma_xy_mm *= 0.5; + sigma_theta_degrees *= 0.5; + } + } + + } + + return bestpos; +}