How to calculate RPM correctly using encoders?
I have optical encoders that read the ticks and publishes the tick count everytime when there is a new tick:
def tick_callback(channel):
global tick
global pub
tick += 1
pub.publish(tick)
class EncoderNodeLeft:
def __init__(self,encoder):
self.tick = 0
self.encoder = encoder
self.rate = rospy.Rate(100)
GPIO.setmode(GPIO.ASUS)
GPIO.setwarnings(False)
GPIO.setup(encoder, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def run(self):
global tick
GPIO.add_event_detect(self.encoder, GPIO.RISING, callback=tick_callback)
while not rospy.is_shutdown():
self.rate.sleep()
But the problem arises when I need to count the RPM and in order to do that, I need a current tick, the last tick and the time difference. The difference between current and last tick is always 1:
void MotorRPM::encoderCallbackRight(const std_msgs::Int64::ConstPtr &msg) {
std_msgs::Float64 rpm_msg;
// get the time difference
ros::Time now = ros::Time::now();
ros::Duration time = now - lastMessageTime;
int rightDifference = msg->data - lastRightEncoder.data;
//ROS_INFO("The tick: %d, the lastData: %d", msg->data, lastRightEncoder.data);
rpm_msg.data = rpmFromEncoderCount(rightDifference, time.toSec());
// publish all the time
rpmPubRight.publish(rpm_msg);
lastMessageTime = now;
lastRightRPM.data = rpm_msg.data;
lastRightEncoder.data = msg->data;
}
Am I doing something wrong? Because I always have a difference of 1 I get incorrect RPM
Asked by stevemartin on 2019-02-06 06:01:34 UTC
Answers
Hi stevemartin.
A potentially large source of error in this method is using ros::Time::now() to get a time stamp for the pulse/count. Rather, get the timestamp from the message and use it as 'now'. But this is still not guaranteed to be accuracy since ROS is quite abstracted from the hardware, the timestamp can be off by a substantial margin and it can be random since the process load of the CPU varies. If the pulses per second are relatively low, it shouldn't matter too much.
Be that as it may, all you need to calculate RPM are the timestamps if this is called at every tick, the count difference shouldn't matter (since it is 1).You can do it with the following equation.
RPM = 60*(1/(time*ticks_per_rotation))
because,
time_per_rotation = time*ticks_per_rotation
RPS = 1/time_per_rotation
Note, that this is the RPM of the encoder. If there is a gearbox involved you'll have to multiply it by the gearbox factor.
Since ROS timestamps aren't incredibly accurate, a more robust way would be to measure the number of pulses in the last second and extrapolate the RPM from there. You can use a moving window filter to publish an RPM message at the same rate as the pulses (if needed). If you need a more sensitive measure, you can reduce the window time frame
Asked by Tyrone Nowell on 2019-02-06 08:53:06 UTC
Comments
@Tyrone Nowell I did not understand your formula, the time in this case is the message's timestamp?
Asked by stevemartin on 2019-02-07 05:09:35 UTC
If so, there is no header in std_msgs::Int64
Asked by stevemartin on 2019-02-07 05:17:08 UTC
Sorry for the slow response. 'time' in the formula is the variable from your code, so the time difference between current and previous message timestamps.
Asked by Tyrone Nowell on 2019-02-11 05:08:44 UTC
Comments