Ask Your Question
0

How to calculate RPM correctly using encoders?

asked 2019-02-06 05:01:34 -0500

stevemartin gravatar image

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

edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
0

answered 2019-02-06 07:53:06 -0500

Tyrone Nowell gravatar image

updated 2019-02-06 07:57:55 -0500

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

edit flag offensive delete link more

Comments

@Tyrone Nowell I did not understand your formula, the time in this case is the message's timestamp?

stevemartin gravatar image stevemartin  ( 2019-02-07 04:09:35 -0500 )edit

If so, there is no header in std_msgs::Int64

stevemartin gravatar image stevemartin  ( 2019-02-07 04:17:08 -0500 )edit

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.

Tyrone Nowell gravatar image Tyrone Nowell  ( 2019-02-11 04:08:44 -0500 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2019-02-06 05:01:34 -0500

Seen: 428 times

Last updated: Feb 06 '19