/* 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; }