读取编码器的计数脉冲转化成ROS的odom数据

版权声明:转载请标明出处,谢谢! https://blog.csdn.net/SimileciWH/article/details/84976187

1,理论模型

1.1 原理

如下图所示,蓝色圆形区域为小车的地盘,左右轮之间的间距为wheel_dist注意算法中的所有的单位,全部是国际标准单位。移动机器人编码器,安装在左右轮上,机器人地底盘的坐标系为base_link坐标系,里程计坐标系为odom。

当移动机器人移动时,假设运动到如下图所示位置。图中的,V为移动小车中心点的运动速度方向,dxy_avg为移动小车中心点的运动距离。

根据编码器的差值,可知前一时刻与当前时刻编码器增量的计数脉冲,已知小车车轮行走一个周长的长度的编码器计数脉冲个数,可以推算出,一个计数脉冲的移动距离。代码实现如下:

# (4096 * 7.5) dright is right boot position
dright = (right_enc - enc_right) * pi * D / (4096 * 7.5)
# dleft is left boot positon
dleft = (left_enc - enc_left) * pi * D / (4096 * 7.5)

其中,dright 为右轮的移动距离,dleft 为左轮的移动距离right_enc - enc_right为右轮编码器脉冲个数的增量,left_enc - enc_left为左轮编码器脉冲个数的增量,参数pi * D / (4096 * 7.5)需要依据自己的小车测量出,编码器一个计数脉冲对应的行走距离(单位m)

1.2 向中心点换算

因为在使用里程计向上层的开发中,往往对于小车模型,通常会考率成中心点的运动模型,即大致认为时一个质点,这是不准确的。算法如下:

 dxy_ave = (dright + dleft) / 2.0
 dth = (dright - dleft) / wheel_track
 vxy = dxy_ave / dt
 vth = dth / dt

其中, dxy_ave为左右轮的移动距离均值,认为是中心点的移动距离;dth 为通过为中心点的转动角度,通过弧长公式 θ = l / R \theta = l/R ,中心点速度V=vxy 为位移对时间的微分 v = d x / d t v= dx/dt vth 为角速度,为转动角度对时间的倒数 v t h = θ / d t vth= \theta/dt

1.3 base_link到odom坐标系的转化

在1.2节中的换算数据都是在小车底盘坐标系base_link上进行的,但是我们最终需要将这些数据转化成odom坐标系上的数据发送出去。代码如下所示:

 dx = cos(dth) * dxy_ave
 dy = -sin(dth) * dxy_ave
 x += (cos(th) * dx - sin(th) * dy)
 y += (sin(th) * dx + cos(th) * dy)

其中,dxy_ave为中心点的在dt时间内的位移;dx&&dy分别为中心点出的位移在base_link坐标系xoy轴上的投影;然后通过两个坐标系的投影转换,即旋转矩阵求解odom坐标系上的x’轴和y’轴的位移;前一时刻的位移加当前位移增量,即为当前位移。
可以理解成,在base_link坐标系上的x轴和y轴投影的位移,需要投影到odom坐标系上的x’轴和y’轴上,所以, d x = d x c o s ( θ ) d y s i n ( θ ) dx'=dx*cos(\theta)-dy*sin(\theta) ,即在odom坐标系上的x’轴上的位移是由base_link坐标系上的x轴和y轴投影的位移的合成,至于为什么是“-”号,画出投影的位移分解图,就可以很清楚的看出来了。;同理可得 d y = d y s i n ( θ ) + d y c o s ( θ ) dy'=dy*sin(\theta)+dy*cos(\theta)

位移的分解图,如下所示

2, python源代码主要部分

下面包含了,发送odom的方式,看到这一步的大概都是理解ROS的原理了,就不细细的说明了。

now = rospy.Time.now()
            # record get encode left&right in first time, must initial enc_right&left
            get_encode_count += 1
            if get_encode_count == 1:
                enc_right = right_enc
                enc_left = left_enc
            dt = now - then
            then = now
            dt = dt.to_sec()

            # Calculate odometry
            if enc_left is None:
                dright = 0
                dleft = 0
            else:
                # (4096 * 7.5) dright is right boot position
                dright = (right_enc - enc_right) * pi * D / (4096 * 7.5)
                # dleft is left boot positon
                dleft = (left_enc - enc_left) * pi * D / (4096 * 7.5)

            enc_right = right_enc
            enc_left = left_enc

            dxy_ave = (dright + dleft) / 2.0
            dth = (dright - dleft) / wheel_track
            vxy = dxy_ave / dt
            vth = dth / dt
            rospy.loginfo('dxy_ave = %f', dxy_ave)
            if dxy_ave != 0:
                dx = cos(dth) * dxy_ave
                dy = -sin(dth) * dxy_ave
                x += (cos(th) * dx - sin(th) * dy)
                y += (sin(th) * dx + cos(th) * dy)

            if dth != 0:
                th += dth
            rospy.loginfo('x = %f, y = %f' % (x, y))
            quaternion = Quaternion()
            quaternion.x = 0.0
            quaternion.y = 0.0
            quaternion.z = sin(th / 2.0)
            quaternion.w = cos(th / 2.0)

            # Create the odometry transform frame broadcaster.
            odomBroadcaster.sendTransform(
                (x, y, 0),
                (quaternion.x, quaternion.y, quaternion.z, quaternion.w),
                rospy.Time.now(),
                base_frame,  # sub-ordi
                "odom"  # farthe-ordi
            )

            odom = Odometry()
            odom.header.frame_id = "odom"
            odom.child_frame_id = base_frame
            odom.header.stamp = now
            odom.pose.pose.position.x = x
            odom.pose.pose.position.y = y
            odom.pose.pose.position.z = 0
            odom.pose.pose.orientation = quaternion
            odom.twist.twist.linear.x = vxy
            odom.twist.twist.linear.y = 0
            odom.twist.twist.angular.z = vth

            odomPub.publish(odom)

猜你喜欢

转载自blog.csdn.net/SimileciWH/article/details/84976187
今日推荐