📄 laservisualbarcode.cc
字号:
/* * Player - One Hell of a Robot Server * Copyright (C) 2000 Brian Gerkey & Kasper Stoy * gerkey@usc.edu kaspers@robotics.usc.edu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *//////////////////////////////////////////////////////////////////////////////// Desc: Driver for detecting combined laser/visual barcodes.// Author: Andrew Howard// Date: 17 Aug 2002// CVS: $Id: laservisualbarcode.cc,v 1.1 2002/11/29 17:07:24 gerkey Exp $//// Theory of operation:// Parses a laser scan to find the retro-reflective patches (lines or// circles), then points the camera at the patch, zooms in, and// attempts to read the colored barcode. Will not return sensible// orientations for circular patches.//// Requires:// Laser, PTZ and blobfinder devices./////////////////////////////////////////////////////////////////////////////#include "player.h"#include <errno.h>#include <string.h>#include <math.h>#include <stdlib.h> // for atoi(3)#include <netinet/in.h> // for htons(3)#include <unistd.h>#include "device.h"#include "devicetable.h"#include "drivertable.h"// Driver for detecting laser retro-reflectors.class LaserVisualBarcode : public CDevice{ // Constructor public: LaserVisualBarcode(char* interface, ConfigFile* cf, int section); // Setup/shutdown routines. public: virtual int Setup(); public: virtual int Shutdown(); // Main function for device thread. private: virtual void Main(); // Process requests. Returns 1 if the configuration has changed. private: int HandleRequests(); // Handle geometry requests. private: void HandleGetGeom(void *client, void *req, int reqlen); // Process laser data. // Returns non-zero if the laser data has been updated. private: int UpdateLaser(); // Info on potential fiducials. private: struct fiducial_t { // Id (-1) if undetermined. int id; // Pose of fiducial. double pose[3]; // Uncertainty in pose. double upose[3]; // Time at which fiducial was last seen by the laser. double laser_time; // Time at which ptz selected this fiducial, // and the time at which the ptz locked on to this fiducial. double ptz_select_time; double ptz_lockon_time; // Time at which the fiducial was identified. double id_time; }; // Info on valid blobs. private: struct blob_t { // Blob channel. int ch; // Blob position in image. int x, y; }; // Analyze the laser data to find fidicuials (reflectors). private: void FindLaserFiducials(double time, player_laser_data_t *data); // Find the line of best fit for the given segment of the laser // scan. Fills in the pose of the reflector relative to the laser. private: void FitLaserFiducial(player_laser_data_t *data, int first, int last, double pose[3]); // Match a new laser fiducial against the ones we are already // tracking. The pose is relative to the laser. private: void MatchLaserFiducial(double time, double pose[3]); // Retire fiducials we havent seen for a while. private: void RetireLaserFiducials(double time, player_laser_data_t *data); // Update the PTZ to point at one of the laser reflectors. private: int UpdatePtz(); // Select a target fiducial for the PTZ to inspect. private: void SelectPtzTarget(double time, player_ptz_data_t *data); // Servo the PTZ to a target fiducial. private: void ServoPtz(double time, player_ptz_data_t *data); // Process any new blobfinder data. private: int UpdateBlobfinder(); // Find blobs with valid properties. private: void FindBlobs(double time, player_blobfinder_data_t *data); // Search the list of valid blobs to locate the visual fiducials. private: int FindVisualFiducials(double time, player_blobfinder_data_t *data, int depth, blob_t *prevblob); // Update the device data (the data going back to the client). private: void UpdateData(); // Fiducial properties. private: int barcount; private: double barwidth, barheight; // Max time to spend looking at a fiducial. private: double max_ptz_attention; // Retirement age for fiducials that havent been seen for a while. private: double retire_time; // Max distance between fiducials in successive laser scans. private: double max_dist; // Laser stuff. private: int laser_index; private: CDevice *laser; private: double laser_time; // PTZ stuff private: int ptz_index; private: CDevice *ptz; private: double ptz_time; // Blobfinder stuff. private: int blobfinder_index; private: CDevice *blobfinder; private: double blobfinder_time; // List of currently tracked fiducials. private: int fiducial_count; private: fiducial_t fiducials[256]; // The current selected fiducial for the ptz, the time at which we // selected it, and the time at which we first locked on to it. private: fiducial_t *ptz_fiducial; // Dimensions of the zoomed image for the target fiducial (m). private: double zoomwidth, zoomheight; // List of current valid blobs. private: int blob_count; private: blob_t blobs[256]; // Local copy of the current fiducial data. private: player_fiducial_data_t fdata;};// Initialization functionCDevice* LaserVisualBarcode_Init(char* interface, ConfigFile* cf, int section){ if (strcmp(interface, PLAYER_FIDUCIAL_STRING) != 0) { PLAYER_ERROR1("driver \"laservisualbarcode\" does not support interface \"%s\"\n", interface); return (NULL); } return ((CDevice*) (new LaserVisualBarcode(interface, cf, section)));}// a driver registration functionvoid LaserVisualBarcode_Register(DriverTable* table){ table->AddDriver("laservisualbarcode", PLAYER_READ_MODE, LaserVisualBarcode_Init);}////////////////////////////////////////////////////////////////////////////////// ConstructorLaserVisualBarcode::LaserVisualBarcode(char* interface, ConfigFile* cf, int section) : CDevice(sizeof(player_fiducial_data_t), 0, 10, 10){ this->laser_index = cf->ReadInt(section, "laser", 0); this->laser = NULL; this->laser_time = 0; this->ptz_index = cf->ReadInt(section, "ptz", 0); this->ptz = NULL; this->ptz_time = 0; this->blobfinder_index = cf->ReadInt(section, "blobfinder", 0); this->blobfinder = NULL; this->blobfinder_time = 0; this->max_ptz_attention = cf->ReadFloat(section, "max_ptz_attention", 2.0); this->retire_time = cf->ReadFloat(section, "retire_time", 1.0); this->max_dist = cf->ReadFloat(section, "max_dist", 0.2); // Default fiducial properties. this->barcount = cf->ReadInt(section, "bit_count", 3); this->barwidth = cf->ReadLength(section, "bit_width", 0.08); this->barheight = cf->ReadLength(section, "bit_height", 0.02); // Reset fiducial list. this->fiducial_count = 0; // Reset PTZ target. this->ptz_fiducial = NULL; // Reset blob list. this->blob_count = 0; return;}////////////////////////////////////////////////////////////////////////////////// Set up the device (called by server thread).int LaserVisualBarcode::Setup(){ player_device_id_t id; // Subscribe to the laser. id.code = PLAYER_LASER_CODE; id.index = this->laser_index; id.port = this->device_id.port; this->laser = deviceTable->GetDevice(id); if (!this->laser) { PLAYER_ERROR("unable to locate suitable laser device"); return(-1); } if (this->laser->Subscribe(this) != 0) { PLAYER_ERROR("unable to subscribe to laser device"); return(-1); } // Subscribe to the PTZ. id.code = PLAYER_PTZ_CODE; id.index = this->ptz_index; id.port = this->device_id.port; this->ptz = deviceTable->GetDevice(id); if (!this->ptz) { PLAYER_ERROR("unable to locate suitable PTZ device"); return(-1); } if (this->ptz->Subscribe(this) != 0) { PLAYER_ERROR("unable to subscribe to PTZ device"); return(-1); } // Subscribe to the blobfinder. id.code = PLAYER_BLOBFINDER_CODE; id.index = this->blobfinder_index; id.port = this->device_id.port; this->blobfinder = deviceTable->GetDevice(id); if (!this->blobfinder) { PLAYER_ERROR("unable to locate suitable blobfinder device"); return(-1); } if (this->blobfinder->Subscribe(this) != 0) { PLAYER_ERROR("unable to subscribe to blobfinder device"); return(-1); } // Reset blob list. this->blob_count = 0; // Start the driver thread. this->StartThread(); return 0;}////////////////////////////////////////////////////////////////////////////////// Shutdown the device (called by server thread).int LaserVisualBarcode::Shutdown(){ // Stop the driver thread. StopThread(); // Unsubscribe from devices. this->blobfinder->Unsubscribe(this); this->ptz->Unsubscribe(this); this->laser->Unsubscribe(this); return 0;}////////////////////////////////////////////////////////////////////////////////// Main function for device threadvoid LaserVisualBarcode::Main() { while (true) { // Go to sleep for a while (this is a polling loop). usleep(10000); // Test if we are supposed to cancel this thread. pthread_testcancel(); // Process any pending requests. HandleRequests(); // Process any new laser data. if (UpdateLaser()) { // Update the device data (the data going back to the client). UpdateData(); } // Process any new PTZ data. UpdatePtz(); // Process any new blobfinder data. UpdateBlobfinder(); }}////////////////////////////////////////////////////////////////////////////////// Process requests. Returns 1 if the configuration has changed.int LaserVisualBarcode::HandleRequests(){ void *client; char request[PLAYER_MAX_REQREP_SIZE]; int len; while ((len = GetConfig(&client, &request, sizeof(request))) > 0) { switch (request[0]) { case PLAYER_FIDUCIAL_GET_GEOM: HandleGetGeom(client, request, len); break; default: if (PutReply(client, PLAYER_MSGTYPE_RESP_NACK) != 0) PLAYER_ERROR("PutReply() failed"); break; } } return 0;}////////////////////////////////////////////////////////////////////////////////// Handle geometry requests.void LaserVisualBarcode::HandleGetGeom(void *client, void *request, int len){ unsigned short reptype; struct timeval ts; int replen; player_laser_geom_t lgeom; player_fiducial_geom_t fgeom; // Get the geometry from the laser replen = this->laser->Request(&this->laser->device_id, this, request, len, &reptype, &ts, &lgeom, sizeof(lgeom)); if (replen <= 0 || replen != sizeof(lgeom)) { PLAYER_ERROR("unable to get geometry from laser device"); if (PutReply(client, PLAYER_MSGTYPE_RESP_NACK) != 0) PLAYER_ERROR("PutReply() failed"); } fgeom.pose[0] = lgeom.pose[0]; fgeom.pose[1] = lgeom.pose[1]; fgeom.pose[2] = lgeom.pose[2]; fgeom.size[0] = lgeom.size[0]; fgeom.size[1] = lgeom.size[1]; fgeom.fiducial_size[0] = ntohs((int) (this->barwidth * 1000)); fgeom.fiducial_size[1] = ntohs((int) (this->barwidth * 1000)); if (PutReply(client, PLAYER_MSGTYPE_RESP_ACK, &ts, &fgeom, sizeof(fgeom)) != 0) PLAYER_ERROR("PutReply() failed"); return;}////////////////////////////////////////////////////////////////////////////////// Process laser data.int LaserVisualBarcode::UpdateLaser(){ int i; player_laser_data_t data; size_t size; uint32_t timesec, timeusec; double time; // Get the laser data. size = this->laser->GetData(this,(unsigned char*) &data, sizeof(data), ×ec, &timeusec); time = (double) timesec + ((double) timeusec) * 1e-6; // Dont do anything if this is old data. if (time == this->laser_time) return 0; this->laser_time = time; // Do some byte swapping on the laser data. data.resolution = ntohs(data.resolution); data.min_angle = ntohs(data.min_angle); data.max_angle = ntohs(data.max_angle); data.range_count = ntohs(data.range_count); for (i = 0; i < data.range_count; i++) data.ranges[i] = ntohs(data.ranges[i]); // Find possible fiducials in this scan. this->FindLaserFiducials(time, &data); // Retire fiducials we havent seen for a while. this->RetireLaserFiducials(time, &data); return 1;}////////////////////////////////////////////////////////////////////////////////// Analyze the laser data to find fidicuials (reflectors).void LaserVisualBarcode::FindLaserFiducials(double time, player_laser_data_t *data){ int i, h; int valid; double r, b; double db, dr; double mn, mr, mb, mrr, mbb; double pose[3]; // Empty the fiducial list. this->fdata.count = 0; // Initialise patch statistics. mn = 0.0; mr = 0.0; mb = 0.0; mrr = 0.0; mbb = 0.0; // Look for a candidate patch in scan. for (i = 0; i < data->range_count; i++) { r = (double) (data->ranges[i]) / 1000; b = (double) (data->min_angle + i * data->resolution) / 100.0 * M_PI / 180; h = (int) (data->intensity[i]); // If there is a reflection... if (h > 0) { mn += 1; mr += r; mb += b; mrr += r * r; mbb += b * b; } // If there is no reflection and we have a patch... else if (mn > 0) { // Compute the moments of the patch. mr /= mn; mb /= mn; mrr = mrr / mn - mr * mr; mbb = mbb / mn - mb * mb; // Test moments to see if they are valid. valid = 1; valid &= (mn >= 1.0); dr = this->barwidth / 2; db = atan2(this->barwidth / 2, mr); valid &= (mrr < (dr * dr)); valid &= (mbb < (db * db)); if (valid) { // Do a best fit to determine the pose of the reflector.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -