Revision: 9079
          http://playerstage.svn.sourceforge.net/playerstage/?rev=9079&view=rev
Author:   jpgr87
Date:     2011-11-08 23:47:19 +0000 (Tue, 08 Nov 2011)
Log Message:
-----------
Applied patch #3432662: Sparkfun Razor-IMU Driver

Modified Paths:
--------------
    code/player/trunk/server/drivers/imu/CMakeLists.txt

Added Paths:
-----------
    code/player/trunk/server/drivers/imu/RazorIMU.cc

Modified: code/player/trunk/server/drivers/imu/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/imu/CMakeLists.txt 2011-11-01 00:00:59 UTC 
(rev 9078)
+++ code/player/trunk/server/drivers/imu/CMakeLists.txt 2011-11-08 23:47:19 UTC 
(rev 9079)
@@ -5,3 +5,5 @@
 PLAYERDRIVER_REJECT_OS (nimu build_nimu PLAYER_OS_WIN)
 PLAYERDRIVER_ADD_DRIVER (nimu build_nimu LINKFLAGS "-lusb" SOURCES nimu.cpp 
nimuplayer.cpp)
 
+PLAYERDRIVER_OPTION (razorimu build_razorimu ON)
+PLAYERDRIVER_ADD_DRIVER (razorimu build_razorimu SOURCES RazorIMU.cc)

Added: code/player/trunk/server/drivers/imu/RazorIMU.cc
===================================================================
--- code/player/trunk/server/drivers/imu/RazorIMU.cc                            
(rev 0)
+++ code/player/trunk/server/drivers/imu/RazorIMU.cc    2011-11-08 23:47:19 UTC 
(rev 9079)
@@ -0,0 +1,620 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2003  
+ *     Brian Gerkey
+ *                      
+ * 
+ *  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
+ *
+ */
+
+/*
+ * Author: Michael F Clarke <[email protected]>
+ * Version 1.0
+ * Intelligent Robotics Group
+ * Department of Computer Science
+ * Aberystwyth University, UK
+ */
+
+/** @ingroup drivers */
+/** @{ */
+/** @defgroup driver_razorimu razorimu
+  * @brief Sparkfun Razor IMU driver
+
+This driver provides support for the Razor IMU, sold by Sparkfun.
+For this driver to be effective on the Razor IMU, the latest version of
+the firmware from http://code.google.com/p/sf9domahrs/source/list should
+be installed on the IMU.
+
+When installing the firmware, the following settings should be configured
+as true:
+File: SF9DOF_AHRS.pde
+#define PRINT_ANALOGS 1 <-- must be set to 1.
+
+@par Compile-time dependencies
+
+- None
+
+@par Provides
+
+- @ref interface_imu
+
+@par Requires
+
+- none
+
+@par Configuration requests
+
+- PLAYER_IMU_REQ_SET_DATATYPE
+- PLAYER_IMU_REQ_RESET_EULER
+- PLAYER_IMU_REQ_RESET_ORIENTATION
+
+@par Configuration file options
+
+- serial_port
+  - Default: /dev/ttyUSB0
+  - Serial port to connect to the IMU with
+
+- baud_rate
+  - Default: 57600
+  - Baud rate for serial connection
+
+- data_packet_type
+  - Default: 4
+  - Data packet format from IMU
+
+@par Example
+@verbatim
+driver
+(
+       name "razorimu"
+       provides ["imu:0"]
+       serial_port "/dev/ttyUSB0"
+       baud_rate 57600
+       data_packet_type 5
+)
+@endverbatim
+
+@author Michael F Clarke <[email protected]>
+
+*/
+/** @} */
+
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <string.h>
+#include <libplayercore/playercore.h>
+#include <iostream>
+
+#define DEFAULT_PORT "/dev/ttyUSB0"
+#define DEFAULT_BAUD_RATE 57600
+#define MAX_RESPONSE 100
+
+using namespace std;
+
+typedef struct imu_data imu_data_t;
+struct imu_data {
+
+       float roll;
+       float pitch;
+       float yaw;
+       float acc_x;
+       float acc_y;
+       float acc_z;
+       float gyr_x;
+       float gyr_y;
+       float gyr_z;
+       float mag_x;
+       float mag_y;
+       float mag_z;
+       
+};
+
+class RazorIMU : public ThreadedDriver {
+
+       public:
+               
+               RazorIMU(ConfigFile *cf, int section);
+               ~RazorIMU();
+
+               virtual int MainSetup();
+               virtual void MainQuit();
+
+               virtual int ProcessMessage(QueuePointer &resp_queue,
+                                          player_msghdr *hdr, void *data); 
+
+       private:
+
+               const char *serialPort;
+               int imufd;
+               int baudRate;
+
+               player_imu_data_state_t     imu_data_state;
+               player_imu_data_calib_t     imu_data_calib;
+               player_imu_data_quat_t      imu_data_quat;
+               player_imu_data_euler_t     imu_data_euler;
+               player_imu_data_fullstate_t imu_data_fullstate;
+               player_imu_reset_euler_config_t imu_euler_config;
+               imu_data idata;
+
+               int dataType;
+
+               virtual void Main();
+               virtual void RefreshData();
+
+               player_imu_data_calib_t getCalibValues(imu_data_t data);
+
+               void uart_init();
+               unsigned char uart_rx();
+               void uart_tx(char data);
+               void uart_deinit();
+
+};
+
+/*
+ * Factory creation function. This function is given as an argument when
+ * the driver is added to the driver table.
+ */
+Driver *RazorIMU_Init(ConfigFile *cf, int section) {
+
+       return ((Driver *)(new RazorIMU(cf, section)));
+
+}
+
+/*
+ * Registers the driver in the driver table. Called from the player_driver_init
+ * function that the loader looks for.
+ */
+void razorimu_Register(DriverTable *table) {
+       
+       table->AddDriver("razorimu", RazorIMU_Init);
+
+}
+/*
+extern "C" {
+       int player_driver_init(DriverTable *table) {
+               razorimu_Register(table);
+               return 0;
+       }
+}
+*/
+void RazorIMU::uart_init() {
+
+       struct termios newtio;
+       
+       imufd = open(serialPort, O_RDWR | O_NOCTTY);
+       bzero(&newtio, sizeof(newtio));
+
+       if (baudRate == 4800)
+               newtio.c_cflag = B4800 | CS8 | CLOCAL | CREAD;
+       else if (baudRate == 9600)
+               newtio.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
+       else if (baudRate == 19200)
+               newtio.c_cflag = B19200 | CS8 | CLOCAL | CREAD;
+       else if (baudRate == 38400)
+               newtio.c_cflag = B38400 | CS8 | CLOCAL | CREAD;
+       else if (baudRate == 57600)
+               newtio.c_cflag = B57600 | CS8 | CLOCAL | CREAD;
+       else if (baudRate == 115200)
+               newtio.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
+
+       newtio.c_iflag = IGNPAR;
+       newtio.c_oflag = 0;
+
+       /* Set input mode (non-canonical, no echo, ...). */
+       newtio.c_lflag = 0;
+       newtio.c_cc[VTIME] = 0; /* inter-chartimer of 0.1 seconds. */
+       newtio.c_cc[VMIN]  = 1; /* blocking read until 1 char received. */
+
+       /* Flush out any old data. */
+       tcflush(imufd, TCIFLUSH);
+
+       /* Set baud rate, etc. */
+       tcsetattr(imufd, TCSANOW, &newtio);
+
+}
+
+unsigned char RazorIMU::uart_rx() {
+
+       char rec[1];
+       read(imufd, rec, 1);
+
+       return rec[0];
+
+}
+
+void RazorIMU::uart_tx(char data) {
+       write(imufd, &data, 1);
+}
+
+void RazorIMU::uart_deinit() {
+       close(imufd);
+}
+       
+
+/*
+ * Constructor. Retrieve options from the configuration file and do any
+ * pre-Setup() setup.
+ */
+RazorIMU::RazorIMU(ConfigFile *cf, int section) : ThreadedDriver(cf, 
+       section, false, PLAYER_MSGQUEUE_DEFAULT_MAXLEN, PLAYER_IMU_CODE) {
+
+       serialPort = cf->ReadString(section, "serial_port", DEFAULT_PORT);
+       baudRate = cf->ReadInt(section, "baud_rate", DEFAULT_BAUD_RATE);
+       dataType = cf->ReadInt(section, "data_packet_type", 4);
+
+       if (!(baudRate == 4800 || baudRate == 9600 || baudRate == 19200 ||
+             baudRate == 38400 || baudRate == 57600 || baudRate == 115200))
+             baudRate = DEFAULT_BAUD_RATE;
+
+       imu_euler_config.orientation.proll = 0.0;
+       imu_euler_config.orientation.ppitch = 0.0;
+       imu_euler_config.orientation.pyaw = 0.0;
+
+}
+
+/*
+ * Destructor.
+ */
+RazorIMU::~RazorIMU() {
+
+}
+
+/*
+ * Setup the device. Return 0 if things go well, and -1 otherwise.
+ */
+int RazorIMU::MainSetup() {
+
+       uart_init();
+
+       if (imufd < 0) {
+               PLAYER_ERROR("Error starting RazorIMU.");
+               return -1;
+       }
+
+       imu_euler_config.orientation.proll = 0.0;
+       imu_euler_config.orientation.ppitch = 0.0;
+       imu_euler_config.orientation.pyaw = 0.0;
+
+       PLAYER_MSG0 (1, "> RazorIMU starting up... [done]");
+       return 0;
+
+}
+
+/*
+ * Shutdown the device.
+ */
+void RazorIMU::MainQuit() {
+       
+       uart_deinit();
+       PLAYER_MSG0(1, "> RazorIMU driver shutting down... [done]");
+
+}
+
+/*
+ * Main thread.
+ */
+void RazorIMU::Main() {
+
+       while (1) {
+
+               pthread_testcancel();
+               ProcessMessages();
+               RefreshData();
+               usleep(1000);
+       
+       }
+
+}
+
+int RazorIMU::ProcessMessage(QueuePointer &resp_queue, 
+                               player_msghdr *hdr, void *data) {
+
+       assert(hdr);
+
+       if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, 
+                       PLAYER_IMU_REQ_SET_DATATYPE, device_addr)) {
+
+               player_imu_datatype_config *datatype =
+                       (player_imu_datatype_config *)data;
+
+               if ((datatype->value > 0) && (datatype->value < 6)) {
+
+                       dataType = datatype->value;
+
+                       Publish(device_addr, resp_queue,
+                                    PLAYER_MSGTYPE_RESP_ACK, hdr->subtype);
+
+               } else {
+
+                       Publish(device_addr, resp_queue, 
+                                    PLAYER_MSGTYPE_RESP_NACK, hdr->subtype);
+       
+               }
+
+               return 0;
+
+       } else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
+                       PLAYER_IMU_REQ_RESET_EULER, device_addr)) {
+
+               player_imu_reset_euler_config_t *conf =
+                       (player_imu_reset_euler_config_t *)data;
+
+
+               if (conf->orientation.proll > 90 || 
+                               conf->orientation.proll < -90) {
+
+                       PLAYER_WARN("The new roll value should be between "
+                                                               "-90 and 90.");
+
+                       Publish(device_addr, resp_queue,
+                                       PLAYER_MSGTYPE_RESP_NACK, hdr->subtype);
+
+                       return -1;
+       
+               }
+
+               if (conf->orientation.ppitch > 90 || 
+                               conf->orientation.ppitch < -90) {
+
+                       PLAYER_WARN("The new pitch value should be between "
+                                                               "-90 and 90.");
+
+                       Publish(device_addr, resp_queue,
+                                       PLAYER_MSGTYPE_RESP_NACK, hdr->subtype);
+
+                       return -1;      
+
+               }
+
+               if (conf->orientation.pyaw > 180 || 
+                               conf->orientation.pyaw < -180) {
+
+                       PLAYER_WARN("The new yaw value should be between "
+                                                            "-180 and 180.");
+
+                       Publish(device_addr, resp_queue,
+                                       PLAYER_MSGTYPE_RESP_NACK, hdr->subtype);
+
+                       return -1;      
+
+               }
+
+               /* Calculate the new offset values based on the last reading 
+                 * from the IMU and the new desired value.
+                */
+               imu_euler_config.orientation.proll = 
+                               conf->orientation.proll - idata.roll;
+               imu_euler_config.orientation.ppitch = 
+                               conf->orientation.ppitch - idata.pitch;
+               imu_euler_config.orientation.pyaw = 
+                               conf->orientation.pyaw - idata.yaw;
+
+               Publish(device_addr, resp_queue,
+                               PLAYER_MSGTYPE_RESP_ACK, hdr->subtype);
+
+               return 0;
+               
+       } else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ,
+                       PLAYER_IMU_REQ_RESET_ORIENTATION, device_addr)) {
+
+               PLAYER_WARN("The RazorIMU cannot reset its orientation.");
+
+               Publish(device_addr, resp_queue,
+                               PLAYER_MSGTYPE_RESP_ACK, hdr->subtype);
+       
+               return 0;       
+
+       } 
+
+       return -1;
+
+}
+
+/**
+ * This method is used to get the latest data from the RazorIMU. 
+ */
+void RazorIMU::RefreshData() {
+
+       int i;
+       imu_data_t data;
+
+       char str[MAX_RESPONSE + 1];
+
+       while(1) {
+       
+               while(1) {
+
+                       /* Wait for the correct start sequence. */
+                       while(1) {
+
+                               if (uart_rx() == '\n')
+                                       if (uart_rx() == '!')
+                                               if (uart_rx() == 'A')
+                                                       break;
+                       }
+
+                       /* Copy the data to the \n from the serial line. */
+                       for (i = 0; i < MAX_RESPONSE + 1; i++) {
+                               str[i] = uart_rx();
+                               if (str[i] == '\n') break;
+                       }
+
+                       /* We may have been interrupted and so we may have 
+                        * rubbish data in the buffer. Ensure that the last
+                        * character is indeed a \n.
+                        */
+                        if (str[i] == '\n') break;
+
+               }
+
+               /* NULL terinate the string and parse the resulting string. */
+               str[MAX_RESPONSE] = '\0';
+
+               i = sscanf(str, "NG:%f,%f,%f,AN:%f,%f,%f,%f,%f,%f,%f,%f,%f\n", 
+                                       &data.roll, &data.pitch, &data.yaw,
+                                       &data.gyr_x, &data.gyr_y, &data.gyr_z,
+                                       &data.acc_x, &data.acc_y, &data.acc_z,
+                                       &data.mag_x, &data.mag_y, &data.mag_z);
+
+               idata = data;
+
+               /* Adjust euler values. */
+               data.roll += imu_euler_config.orientation.proll;
+               data.pitch += imu_euler_config.orientation.ppitch;
+               data.yaw += imu_euler_config.orientation.pyaw;
+
+               /* Correct for wrap around. */
+               if (data.roll > 90) data.roll -= 180;
+               if (data.roll < -90) data.roll += 180;
+               if (data.pitch > 90) data.pitch -= 180;
+               if (data.pitch < -90) data.pitch += 180;
+               if (data.yaw > 180) data.yaw -= 360;
+               if (data.yaw < -180) data.yaw += 360;
+
+               /* And finally convert from degrees to radians. */
+               data.roll = DTOR(data.roll);
+               data.pitch = DTOR(data.pitch);
+               data.yaw = DTOR(data.yaw);
+
+               /* Finally, ensure that scanf managed to match all the data 
+                * fields correctly, and if it did, return. 
+                */
+               if (i == 12) break;
+
+       }
+
+       if (dataType == PLAYER_IMU_DATA_STATE) {
+
+               imu_data_state.pose.px = data.mag_x;
+               imu_data_state.pose.py = data.mag_y;
+               imu_data_state.pose.pz = data.mag_z;
+       
+               imu_data_state.pose.proll = data.roll;
+               imu_data_state.pose.ppitch = data.pitch;
+               imu_data_state.pose.pyaw = data.yaw;
+
+               Publish(device_addr, PLAYER_MSGTYPE_DATA,
+                               PLAYER_IMU_DATA_STATE, &imu_data_state,
+                                       sizeof(player_imu_data_state_t), NULL);
+
+
+       } else if (dataType == PLAYER_IMU_DATA_CALIB) {
+
+               imu_data_calib = getCalibValues(data);
+
+               Publish(device_addr, PLAYER_MSGTYPE_DATA,
+                               PLAYER_IMU_DATA_CALIB, &imu_data_calib,
+                                       sizeof(player_imu_data_calib_t), NULL);
+
+       } else if (dataType == PLAYER_IMU_DATA_QUAT) {
+
+               imu_data_quat.calib_data = getCalibValues(data);
+
+               /* We have to calculate these, unfortunately.
+                * Calculations are based on the Wikipedia page at:
+                * http://en.wikipedia.org/wiki/
+                *         Conversion_between_quaternions_and_Euler_angles
+                *
+                * See sections 'Conversion' and 'Relationship with Tait-Bryan
+                * angles' for definitions of roll, pitch and yaw.
+                */
+               imu_data_quat.q0 = cos(data.roll/2)  * cos(data.pitch/2) *
+                                   cos(data.yaw/2)   + sin(data.roll/2)  *
+                                   sin(data.pitch/2) * sin(data.yaw/2);
+
+               imu_data_quat.q1 = sin(data.roll/2)  * cos(data.pitch/2) *
+                                   cos(data.yaw/2)   - cos(data.roll/2)  *
+                                   sin(data.pitch/2) * sin(data.yaw/2);
+
+               imu_data_quat.q2 = cos(data.roll/2)  * sin(data.pitch/2) *
+                                   cos(data.yaw/2)   + sin(data.roll/2)  *
+                                   cos(data.pitch/2) * sin(data.yaw/2);
+
+               imu_data_quat.q3 = cos(data.roll/2)  * cos(data.pitch/2) *
+                                   sin(data.yaw/2)   - sin(data.roll/2)  *
+                                   sin(data.pitch/2) * cos(data.yaw/2);
+
+               Publish(device_addr, PLAYER_MSGTYPE_DATA,
+                               PLAYER_IMU_DATA_QUAT, &imu_data_quat,
+                                       sizeof(player_imu_data_quat_t), NULL);
+
+       } else if (dataType == PLAYER_IMU_DATA_EULER) {
+
+               imu_data_euler.calib_data = getCalibValues(data);
+
+               imu_data_euler.orientation.proll = data.roll;
+               imu_data_euler.orientation.ppitch = data.pitch;
+               imu_data_euler.orientation.pyaw = data.yaw;
+
+               Publish(device_addr, PLAYER_MSGTYPE_DATA,
+                               PLAYER_IMU_DATA_EULER, &imu_data_euler,
+                                       sizeof(player_imu_data_euler_t), NULL);
+
+       } else {
+
+               imu_data_fullstate.pose.px = data.mag_x;
+               imu_data_fullstate.pose.py = data.mag_y;
+               imu_data_fullstate.pose.pz = data.mag_z;
+               imu_data_fullstate.pose.proll = data.roll;
+               imu_data_fullstate.pose.ppitch = data.pitch;
+               imu_data_fullstate.pose.pyaw = data.yaw;
+
+               imu_data_fullstate.vel.px = data.gyr_x;
+               imu_data_fullstate.vel.py = data.gyr_y;
+               imu_data_fullstate.vel.pz = data.gyr_z;
+               imu_data_fullstate.vel.proll = data.gyr_x;
+               imu_data_fullstate.vel.ppitch = data.gyr_y;
+               imu_data_fullstate.vel.pyaw = data.gyr_z;
+
+               imu_data_fullstate.acc.px = data.acc_x;
+               imu_data_fullstate.acc.py = data.acc_y;
+               imu_data_fullstate.acc.pz = data.acc_z;
+               imu_data_fullstate.acc.ppitch = 0;
+               imu_data_fullstate.acc.pyaw = 0;
+               imu_data_fullstate.acc.proll = 0;
+
+               Publish(device_addr, PLAYER_MSGTYPE_DATA, 
+                       PLAYER_IMU_DATA_FULLSTATE, &imu_data_fullstate, 
+                               sizeof(player_imu_data_fullstate_t), NULL);
+
+       }
+
+}
+
+/**
+ * A useful method; used simply for converting a imu_data_t (directly from the
+ * IMU) into a player_imu_data_calib_t.
+ */
+player_imu_data_calib_t RazorIMU::getCalibValues(imu_data_t data) {
+
+       player_imu_data_calib_t calib_data;
+
+       calib_data.accel_x = data.acc_x;
+       calib_data.accel_y = data.acc_y;
+       calib_data.accel_z = data.acc_z;
+       calib_data.gyro_x  = data.gyr_x;
+       calib_data.gyro_y  = data.gyr_y;
+       calib_data.gyro_z  = data.gyr_z;
+       calib_data.magn_x  = data.mag_x;
+       calib_data.magn_y  = data.mag_y;
+       calib_data.magn_z  = data.mag_z;
+
+       return calib_data;
+
+}

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
RSA(R) Conference 2012
Save $700 by Nov 18
Register now
http://p.sf.net/sfu/rsa-sfdev2dev1
_______________________________________________
Playerstage-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to