diff --git a/ros_ws/src/arduino/motor/motor.ino b/ros_ws/src/arduino/motor/motor.ino new file mode 100644 index 0000000..2260c7f --- /dev/null +++ b/ros_ws/src/arduino/motor/motor.ino @@ -0,0 +1,355 @@ +/** + * @file motor.ino + * @brief Controls the Motors + * + * Code to be put on the Arduino based Motor Controllers + * Converts Angular Velocity (Rad/Sec) to float (0-255) + * + */ + +#include +#include +#include +#include + +/************* + * Constants * + *************/ + +/** + * @brief Place to Change which Wheel we want to Program + * + * In order to correctly inturpret the Wheel Velocity's, + * we need to define which wheel we are uploading code to. + * This will be used to insure that the proper wheel gets the + * Proper commands + */ +#define R_WHEEL + +#if defined R_WHEEL +const char* VELOCITY_TOPIC = "right_vel"; +const char* ENCODER_TOPIC = "right_tick"; +#elif defined L_WHEEL +const char* VELOCITY_TOPIC = "left_vel"; +const char* ENCODER_TOPIC = "left_tick"; +#endif + +/** + * @brief value for the desierd refresh rate of the encoder + * + * Variable is the number of Millisecond Delay between publishes + */ + const float REFRESH_RATE_DESIRED = 1000; + +/****************************************************************** + * Adjust for the changing of the arudino timer funciton to * + * remove motor whine. This will only work with a divisor of 8! * + * If Divisor changes, please look into how this effects arduino * + * Timing Functons. * + * * + * For Referance please see below website * + * http://playground.arduino.cc/Main/TimerPWMCheatsheet * + * ****************************************************************/ + //const float REFRESH_RATE = (REFRESH_RATE_DESIRED * 8); + + //Replace this line with the line above it if motor whine + // is still an issue + const float REFRESH_RATE = (REFRESH_RATE_DESIRED); + +/** + * @brief Radians Per Second at full Throtle + * + * todo !!!!!!!STILL NEEDS TO BE UPDATED!!!!!!!!! + * + */ +const float MAX_RAD_SEC = 5; + +/** + * @brief Pin the encoder is attached + */ +const int ENCODER_PIN = 2; + +/** + * @brief Pins used to control the Motor + */ +const int FORWARD_PWM_PIN = 5; +const int REVERSE_PWM_PIN = 6; +const int ENABLE_PIN = A3; + +/** + * @brief Float used to scale the Speed to + */ +float desired_speed = 0; + +/** + * @brief Boolean used to store the desired direction + * + * True is Forward, and False is Backwards + */ +float desired_direction; + +/** + * @brief Values to be updated when the inturrupt is triggered for encoder + */ +volatile unsigned int encoder_ticks = 0; + +/** + * @brief Values used to keep track of current time for multitasking + */ + int current_mills = 0; + int old_mills = 0; + +/************************ + * Forward Declerations * + ************************/ +void encoderCount(); +void updateEncoder(); +void set_pwm_frequency(); +void velocityCallback(const std_msgs::Float64& msg); +float fScale( float originalMin, float originalMax, float newBegin, + float newEnd, float inputValue, float curve); + +/** + * @brief ROS node handle + */ +ros::NodeHandle node_handle; + +/** + * @brief ROS message used for publishing the encoder data + */ +control::Encoder encoderMessage; + +/** + * @brief ROS Subscriber for the Velocity Message + */ +ros::Subscriber velocitySub(VELOCITY_TOPIC, &velocityCallback); + +/** + * @brief ROS Publisher for the Encoder Message + */ +ros::Publisher encoderPub(ENCODER_TOPIC, &encoderMessage); + +void setup() +{ + //Fix the Motor Whine + //set_pwm_frequency(); + + //setup pins + pinMode(FORWARD_PWM_PIN, OUTPUT); + pinMode(REVERSE_PWM_PIN, OUTPUT); + pinMode(ENABLE_PIN, OUTPUT); + pinMode(ENCODER_PIN, INPUT); + + //Set Robot to break when starting + digitalWrite(ENABLE_PIN, HIGH); + analogWrite(FORWARD_PWM_PIN, 0); + analogWrite(REVERSE_PWM_PIN, 0); + + //Setup ROS node and topics + node_handle.initNode(); + node_handle.subscribe(velocitySub); + node_handle.advertise(encoderPub); + + //Set the Inturupt on Pin 2 + attachInterrupt(0, encoderCount, RISING); +} + +void loop() +{ + //update the current time for multitasking + current_mills = millis(); + + //Only update the Encoder data when the Refresh Rate says to + if(current_mills-old_mills >= REFRESH_RATE) + { + updateEncoder(); + old_mills = current_mills; + } + + //Only used to get the messages from ROS + node_handle.spinOnce(); +} + +/** + * @brief The callback function for velocity messages + * + * This function accepts velocity messages and convertes them into wheel speeds + * This will convert a float into an intger(0-255) + * + * @param msg The message witch is recived from the velocity publisher + */ +void velocityCallback(const std_msgs::Float64& msg) +{ + desired_speed = fScale(0, MAX_RAD_SEC, 0, 255, abs(msg.data), 0); + //If msg.data is positive, the set to TRUE + desired_direction = (msg.data > 0); + + if(msg.data > 0) + { + //Go Forward + digitalWrite(ENABLE_PIN, HIGH); + analogWrite(FORWARD_PWM_PIN, desired_speed); + analogWrite(REVERSE_PWM_PIN, 0); + } + else if(msg.data < 0) + { + //Go in Reverse + digitalWrite(ENABLE_PIN, HIGH); + analogWrite(FORWARD_PWM_PIN, 0); + analogWrite(REVERSE_PWM_PIN, desired_speed); + } + else + { + //active break + digitalWrite(ENABLE_PIN, HIGH); + analogWrite(FORWARD_PWM_PIN, 0); + analogWrite(REVERSE_PWM_PIN, 0); + } +} + +/** + * @brief The Function will send the updated encoder value to ROS + * + * This Function is used to update the main ROS program with + * the current number of ticks per timestamp + */ +void updateEncoder() +{ + //update the value of the message + encoderMessage.ticks = encoder_ticks; + encoderMessage.header.stamp = node_handle.now(); + + //publish message + encoderPub.publish(&encoderMessage); + + //reset the count to 0 + encoder_ticks = 0; +} + +/** + * @brief The Function will update the encoder + * + * This Function is called whenever there is a change + * to the value of pin 2, it is part of the attachInterrupt routine + * It updates the value of encoder_ticks + */ +void encoderCount() +{ + if(desired_direction) + { + encoder_ticks++; + } + else + { + encoder_ticks--; + } +} + +/** + * @brief The Function will Scale the Input to the Expected Output + * + * This function accepts the input value, and input range, + * It will the scale the value to the expected output inside + * the given output range + * This Function will also allow the input to be scaled + * with a curve, It is not onyl linear. + * + * @param originalMin - minimum input expected by function + * originalMax - maximum input expected by function + * newBegin - minimum input returned by the function + * newEnd - maximum input returned by the function + * inputValue - the value you want scaled + * curve - excepts a value -10 through 10, 0 being linear + * scaling the values with a curve + */ +float fScale( float original_min, float original_max, float new_begin, + float new_end, float input_value, float curve) +{ + + float original_range = 0; + float new_range = 0; + float zero_ref_cur_val = 0; + float normalized_cur_val = 0; + float ranged_value = 0; + boolean inv_flag = 0; + + + // condition curve parameter + // limit range + if (curve > 10) + { + curve = 10; + } + if (curve < -10) + { + curve = -10; + } + + // - invert and scale - this seems more intuitive - + // postive numbers give more weight to high end on output + curve = (curve * -.1) ; + // convert linear scale into lograthimic exponent for other pow function + curve = pow(10, curve); + + // Check for out of range inputValues + if (input_value < original_min) + { + input_value = original_min; + } + if (input_value > original_max) + { + input_value = original_max; + } + + // Zero Refference the values + original_range = original_max - original_min; + + if (new_end > new_begin) + { + new_range = new_end - new_begin; + } + else + { + new_range = new_begin - new_end; + inv_flag = 1; + } + + zero_ref_cur_val = input_value - original_min; + + // normalize to 0 - 1 float + normalized_cur_val = zero_ref_cur_val / original_range; + + // Check for originalMin > originalMax - the math for all other cases + // i.e. negative numbers seems to work out fine + if (original_min > original_max ) + { + return 0; + } + + if (inv_flag == 0) + { + ranged_value = (pow(normalized_cur_val, curve) * new_range) + new_begin; + } + else // invert the ranges + { + ranged_value = new_begin - (pow(normalized_cur_val, curve) * new_range); + } + + return ranged_value; +} + +/** + * @brief The Function will change frequency of Timer 0 + * + * This function will change the frequency of the Timer + * that controlls the PWM signal. This will alos effect + * any other functions on Timer 0 + */ +void set_pwm_frequency() +{ + + //set mode of timer 0 + TCCR0B = TCCR0B & 0b11111000 | 0x02; + + return; +} diff --git a/ros_ws/src/arudino/imu/imu.ino b/ros_ws/src/arudino/imu/imu.ino deleted file mode 100644 index 85389a3..0000000 --- a/ros_ws/src/arudino/imu/imu.ino +++ /dev/null @@ -1,179 +0,0 @@ -#include - -#define GYROADDR 0x68 -#define COMPASSADDR 0x1e -#define ACCELADDR 0x53 - -union XYZBuffer { - struct { - short x,y,z; - } value; - byte buff[6]; -}; - -void changeEndian(union XYZBuffer *xyz) { - for (int i=0;i<6;i+=2) { - byte t=xyz->buff[i]; - xyz->buff[i]=xyz->buff[i+1]; - xyz->buff[i+1]=t; - } -} - -// Generically useful reading into a union type -void readXYZ(int device,union XYZBuffer *xyz) { - Wire.requestFrom(device, 6); - long start=millis(); - while (!Wire.available() && (millis()-start)<100); - if (millis()-start<100) { - for (int i=0;i<6;i++) - xyz->buff[i]=Wire.read(); - } -} - -void setupAccel(int device) { - // Check ID to see if we are communicating - Wire.beginTransmission(device); - Wire.write(0x00); // One Reading - Wire.endTransmission(); - Wire.requestFrom(device,1); - while (!Wire.available()); - byte ch=Wire.read(); - Serial.print("Accel id is 0x"); - Serial.println(ch,HEX); - // Should output E5 - - // https://www.sparkfun.com/datasheets/Sensors/Accelerometer/ADXL345.pdf - // Page 16 - Wire.beginTransmission(device); - Wire.write(0x2d); - Wire.write(0x08); - Wire.endTransmission(); - Wire.beginTransmission(device); - Wire.write(0x38); - Wire.write(0x84); - Wire.endTransmission(); - -} -void readAccel(int device,union XYZBuffer *xyz) { - Wire.beginTransmission(device); - Wire.write(0x32); // One Reading - Wire.endTransmission(); - readXYZ(device,xyz); -} - -void setupCompass(int device) { - // Check ID to see if we are communicating - Serial.print("Compass id is "); - Wire.beginTransmission(device); - Wire.write(10); // One Reading - Wire.endTransmission(); - Wire.requestFrom(device,2); - while (!Wire.available()); - char ch=Wire.read(); - Serial.print(ch); - ch=Wire.read(); - Serial.println(ch); - // Should output H4 - -// Page 18 -// at http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Magneto/HMC5883L-FDS.pdf - Wire.beginTransmission(device); - Wire.write(0x00); Wire.write(0x70); - Wire.endTransmission(); - Wire.beginTransmission(device); - Wire.write(0x01); Wire.write(0xA0); - Wire.endTransmission(); - Wire.beginTransmission(device); - Wire.write(0x02); Wire.write(0x00); // Reading - Wire.endTransmission(); - delay(6); -} -void readCompass(int device,union XYZBuffer *xyz) { - readXYZ(device,xyz); - changeEndian(xyz); - Wire.beginTransmission(device); - Wire.write(0x03); - Wire.endTransmission(); -} - -void setupGyro(int device) { - // Check ID to see if we are communicating - Wire.beginTransmission(device); - Wire.write(0x00); // One Reading - Wire.endTransmission(); - Wire.requestFrom(device,1); - while (!Wire.available()); - byte ch=Wire.read(); - Serial.print("Gyro id is 0x"); - Serial.println(ch,HEX); - // Should output 69 -} -void readGyro(int device,union XYZBuffer *xyz) { - // https://www.sparkfun.com/datasheets/Sensors/Gyro/PS-ITG-3200-00-01.4.pdf - // page 20 - Wire.beginTransmission(device); - Wire.write(0x1d); - Wire.endTransmission(); - readXYZ(device,xyz); - changeEndian(xyz); -} - -void pad(int width,int number) { - int n=abs(number); - int w=width; - if (number<0) w--; - while (n>0) { - w--; - n/=10; - } - if (number==0) w--; - for (int i=0;i -#include - -/************* - * Constants * - *************/ - -/** - * @brief Define Pi for use in conversion - */ -const float PI_CONST = 3.14159265359; - -/** - * @brief Radians Per Second at full Throtle - */ -const float MAX_RAD_SEC = 5; - -/** - * @brief Pins used to control the Motor - */ -const int FORWARD_PWM_PIN = 5; -const int REVERSE_PWM_PIN = 6; -const int ENABLE_PIN = A3; - -/** - * @brief Const used to change the Motor Whine Divisor - * - * This is set to 8 for IGVC Robot - */ -const int FREQUENCY_DIVISOR = 8; - -/** - * @brief Place to Change which Wheel we want to Program - * - * In order to correctly inturpret the Wheel Velocity's, - * we need to define which wheel we are uploading code to. - * This will be used to insure that the proper wheel gets the - * Proper commands - */ -#define RIGHT_WHEEL - -#if defined RIGHT_WHEEL -const char* VELOCITY_TOPIC = "right_vel"; -#elif defined LEFT_WHEEL -const char* VELOCITY_TOPIC = "left_vel"; -#endif - -/** - * @brief Float used to scale the Speed to - */ -float speed = 0; - -/************************ - * Forward Declerations * - ************************/ -void velocityCallback(const std_msgs::Float64& msg); -float fscale( float originalMin, float originalMax, float newBegin, - float newEnd, float inputValue, float curve); - -/** - * @brief ROS node handle - */ -ros::NodeHandle nodeHandle; - -/** - * @brief ROS Subscriber for the Velocity Message - */ -ros::Subscriber velocitySub(VELOCITY_TOPIC, &velocityCallback); - -/** - * @brief The callback function for velocity messages - * - * This function accepts velocity messages and convertes them into wheel speeds - * This will convert a float into an intger(0-255) - * - * @param msg The message witch is recived from the velocity publisher - */ -void velocityCallback(const std_msgs::Float64& msg) -{ - speed = fscale(0, MAX_RAD_SEC, 0, 255, abs(msg.data), 0); - if(msg.data > 0) - { - //Go Forward - digitalWrite(ENABLE_PIN, HIGH); - analogWrite(FORWARD_PWM_PIN, speed); - analogWrite(REVERSE_PWM_PIN, 0); - } - else if(msg.data < 0) - { - //Go in Reverse - digitalWrite(ENABLE_PIN, HIGH); - analogWrite(FORWARD_PWM_PIN, 0); - analogWrite(REVERSE_PWM_PIN, speed); - } - else - { - //active break - digitalWrite(ENABLE_PIN, HIGH); - analogWrite(FORWARD_PWM_PIN, 0); - analogWrite(REVERSE_PWM_PIN, 0); - } -} - -void setup() -{ - //Fix the Motor Whine - //After testing on IGVC 8 gave the best results - set_pwm_frequency(FREQUENCY_DIVISOR); - - //setup pins - pinMode(FORWARD_PWM_PIN, OUTPUT); - pinMode(REVERSE_PWM_PIN, OUTPUT); - pinMode(ENABLE_PIN, OUTPUT); - - //Set Robot to break when starting - digitalWrite(ENABLE_PIN, HIGH); - analogWrite(FORWARD_PWM_PIN, 0); - analogWrite(REVERSE_PWM_PIN, 0); - - //Setup ROS node and topics - nodeHandle.initNode(); - nodeHandle.subscribe(velocitySub); -} - -void loop() -{ - nodeHandle.spinOnce(); -} - - - - - -/** - * @brief The Function will Scale the Input to the Expected Output - * - * This function accepts the input value, and input range, - * It will the scale the value to the expected output inside - * the given output range - * This Function will also allow the input to be scaled - * with a curve, It is not onyl linear. - * - * @param originalMin - minimum input expected by function - * originalMax - maximum input expected by function - * newBegin - minimum input returned by the function - * newEnd - maximum input returned by the function - * inputValue - the value you want scaled - * curve - excepts a value -10 through 10, 0 being linear - * scaling the values with a curve - */ -float fscale( float originalMin, float originalMax, float newBegin, - float newEnd, float inputValue, float curve) -{ - - float OriginalRange = 0; - float NewRange = 0; - float zeroRefCurVal = 0; - float normalizedCurVal = 0; - float rangedValue = 0; - boolean invFlag = 0; - - - // condition curve parameter - // limit range - if (curve > 10) - { - curve = 10; - } - if (curve < -10) - { - curve = -10; - } - - // - invert and scale - this seems more intuitive - - // postive numbers give more weight to high end on output - curve = (curve * -.1) ; - // convert linear scale into lograthimic exponent for other pow function - curve = pow(10, curve); - - // Check for out of range inputValues - if (inputValue < originalMin) - { - inputValue = originalMin; - } - if (inputValue > originalMax) - { - inputValue = originalMax; - } - - // Zero Refference the values - OriginalRange = originalMax - originalMin; - - if (newEnd > newBegin) - { - NewRange = newEnd - newBegin; - } - else - { - NewRange = newBegin - newEnd; - invFlag = 1; - } - - zeroRefCurVal = inputValue - originalMin; - - // normalize to 0 - 1 float - normalizedCurVal = zeroRefCurVal / OriginalRange; - - // Check for originalMin > originalMax - the math for all other cases - // i.e. negative numbers seems to work out fine - if (originalMin > originalMax ) - { - return 0; - } - - if (invFlag == 0) - { - rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin; - } - else // invert the ranges - { - rangedValue = newBegin - (pow(normalizedCurVal, curve) * NewRange); - } - - return rangedValue; -} - - -/** - * @brief The Function will change frequency of Timer 0 - * - * This function accepts as input a divisor that will change - * the frequency of the Timer that controlls the PWM signal - * - * @param divisor -what we divide the timer frequency by - */ -void set_pwm_frequency(int divisor) -{ - byte mode; - switch(divisor) - { - case 1: - mode = 0x01; - break; - case 8: - mode = 0x02; - break; - case 64: - mode = 0x03; - break; - case 256: - mode = 0x04; - break; - case 1024: - mode = 0x05; - break; - default: - return; - } - - //set mode of timer 0 - TCCR0B = TCCR0B & 0b11111000 | mode; - - return; -} diff --git a/ros_ws/src/control/CMakeLists.txt b/ros_ws/src/control/CMakeLists.txt index 27616ba..fb2d6a0 100644 --- a/ros_ws/src/control/CMakeLists.txt +++ b/ros_ws/src/control/CMakeLists.txt @@ -6,8 +6,16 @@ find_package(catkin REQUIRED COMPONENTS roscpp std_msgs sensor_msgs + message_generation ) +################################################ +## Declare ROS messages, services and actions ## +################################################ +## Generate message in the 'msg' folder +add_message_files(FILES Encoder.msg) +generate_messages(DEPENDENCIES std_msgs) + ################################### ## catkin specific configuration ## ################################### @@ -16,6 +24,7 @@ catkin_package( LIBRARIES control CATKIN_DEPENDS geometry_msgs roscpp std_msgs sensor_msgs DEPENDS system_lib + CATKIN_DEPENDS message_runtime ) ########### diff --git a/ros_ws/src/control/msg/Encoder.msg b/ros_ws/src/control/msg/Encoder.msg new file mode 100644 index 0000000..af1b739 --- /dev/null +++ b/ros_ws/src/control/msg/Encoder.msg @@ -0,0 +1,2 @@ +Header header +uint8 ticks diff --git a/ros_ws/src/control/package.xml b/ros_ws/src/control/package.xml index f1af59c..87a9313 100644 --- a/ros_ws/src/control/package.xml +++ b/ros_ws/src/control/package.xml @@ -18,9 +18,11 @@ roscpp std_msgs sensor_msgs + message_generation geometry_msgs roscpp std_msgs sensor_msgs + message_runtime - \ No newline at end of file +