上一节介绍了里程计Odometry传感数据的订阅和发布。

本节会介绍陀螺仪Imu数据的发布和订阅。陀螺仪在cartographer中主要用于前端位置预估和后端优化。

目录

1:sensor_msgs/Imu消息类型

2:发布Imu消息

3:订阅Imu消息


1:sensor_msgs/Imu消息类型

在终端查看消息数据结构:

rosmsg show sensor_msgs/Imu

Odometry消息类型数据结构如下:

Header header
geometry_msgs/Quaternion orientation
float64[9] orientation_covariance // Row major about x, y, z axes
geometry_msgs/Vector3 angular_velocity
float64[9] angular_velocity_covariance // Row major about x, y, z axes
geometry_msgs/Vector3 linear_acceleration
float64[9] linear_acceleration_covariance // Row major x, y z

其中linear_acceleration表示线加速度,angular_velocity表示角速度,orientation表示姿态,使用四元数表示。covariance表示对应协方差,体现各个数据的误差


2:发布Imu消息

陀螺仪用的是LPMS-IG1 RS232,这个陀螺仪同时能提供角速度 ,线加速度,和欧拉角。

#include <ros/ros.h>
#include <tf/tf.h>
#include <sensor_msgs/Imu.h> using namespace std; unsigned int step = 0;
unsigned int data_i = 0;
unsigned int data_len = 0;
unsigned char handle_buf[2048]; float acc[3];
float gyo[3];
float eular[3]; void DataReceivedCallback(std::vector<unsigned char> &data)
{
unsigned char datasingle1;
for (size_t k = 0; k < data.size(); k++)
{
datasingle1 = data[k];
switch (step)
{
case 0:
{
if (datasingle1 == 0x3A)
{
step = 1;
data_i = 0;
memset(handle_buf, 0, 2048);
}
break;
}
case 1: // sensor id low
{
handle_buf[0] = datasingle1;
step = 2;
break;
}
case 2: // sensor id high
{
handle_buf[1] = datasingle1;
step = 3;
break;
}
case 3: //指令号 low
{
handle_buf[2] = datasingle1;
step = 4;
break;
}
case 4: //指令号 high
{
handle_buf[3] = datasingle1;
step = 5;
break;
}
case 5: //数据长度 low
{
handle_buf[4] = datasingle1;
data_len = datasingle1;
step = 6;
break;
}
case 6: //数据长度 high
{
handle_buf[5] = datasingle1;
data_len += (uint16_t)handle_buf[5] * 256;
if (data_len > 512)
{
step = 0;
cout << " data_len error : " << hex << datasingle1 << ", " << data_len << std::endl;
}
else
{
if (data_len > 0)
{
data_i = 0;
step = 7;
}
else
{
step = 0;
cout << " data_len error : " << hex << datasingle1 << ", " << data_len << std::endl;
}
}
break;
}
case 7:
{
handle_buf[data_i + 6] = datasingle1;
data_i++;
if (data_i >= data_len + 4) //完整一帧
{
//判断包尾
if ((handle_buf[data_len + 8] != 0x0D) && (handle_buf[data_len + 9] != 0x0A))
{
step = 0;
cout << " tail error : " << hex << handle_buf[data_len + 8] << ", " << hex << handle_buf[data_len + 9] << std::endl;
break;
} uint16_t lrc = ((uint16_t)handle_buf[data_len + 7] * 256) + (uint16_t)handle_buf[data_len + 6]; //判断lrc
uint16_t sum_lrc = 0;
for (unsigned int i = 0; i < (6 + data_len); i++)
{
sum_lrc += handle_buf[i];
} if (lrc != sum_lrc)
{
step = 0;
cout << " crc error : " << lrc << ", " << sum_lrc << std::endl;
break;
} //线加速度(含重力)
acc[0] = *((float *)&handle_buf[22]);
acc[1] = *((float *)&handle_buf[26]);
acc[2] = *((float *)&handle_buf[30]); //角速度(陀螺仪I的输出)
gyo[0] = *((float *)&handle_buf[82]);
gyo[1] = *((float *)&handle_buf[86]);
gyo[2] = *((float *)&handle_buf[90]); //欧拉角
eular[0] = *((float *)&handle_buf[146]);
eular[1] = *((float *)&handle_buf[150]);
eular[2] = *((float *)&handle_buf[154]); step = 0;
}
break;
}
default:
break;
}
}
} int main(int argc, char **argv)
{
ros::init(argc, argv, "Imu_publisher"); ros::NodeHandle n;
ros::Publisher imu_pub = n.advertise<sensor_msgs::Imu>("imu", 50); string device = "/dev/ttyUSB0";
int baud_rate = 921600;
int data_bits = 8;
int stop_bits = 0;
string parity = "n"; boost::shared_ptr<SerialPort> serialPort;
serialPort.reset(new SerialPort(device, baud_rate, data_bits, stop_bits, parity));
auto binding = bind(&DataReceivedCallback, this, std::placeholders::_1);
serialPort->setcallback(binding);
if (serialPort->Open())
{
serialPort->LoadConfig();
cout << "Init serial open success";
}
else
cout << "Init serial open false"; ros::Rate r(1.0);
while (n.ok())
{ ros::spinOnce(); sensor_msgs::Imu imu;
imu.header.stamp = ros::Time::now();
imu.header.frame_id = "base_link";
imu.linear_acceleration.x = acc[0] * (-9.8);
imu.linear_acceleration.y = acc[1] * (-9.8);
imu.linear_acceleration.z = acc[2] * (-9.8);
imu.angular_velocity.x = gyo[0] * 3.1415926 / 180.0;
imu.angular_velocity.y = gyo[1] * 3.1415926 / 180.0;
imu.angular_velocity.z = gyo[2] * 3.1415926 / 180.0;
imu.orientation = tf::createQuaternionMsgFromRollPitchYaw(eular[0], eular[1], eular[2]); //发布Imu消息
imu_pub.publish(imu); last_time = current_time;
r.sleep();
}
}

SerialPort是自定义的串口通信类,附上代码:

SerialPort.h

SerialPort.cpp


3:订阅Imu消息

(1) 通过rosbag订阅

rostopic echo /imu

(2) 通过rviz查看

打开rviz

rosrun rviz rviz

Fixed Frame修改为base_link,添加Imu并将Topic设为/imu

(3) 编写程序打印

#include "ros/ros.h"
#include "sensor_msgs/Imu.h" void imuCallback(const sensor_msgs::Imu::ConstPtr &msg)
{
ROS_INFO("imu: %f, %f, %f, %f, %f, %f, %f, %f, %f, %f", msg->linear_acceleration.x, msg->linear_acceleration.y, msg->linear_acceleration.z,
msg->angular_velocity.x, msg->angular_velocity.y, msg->angular_velocity.z,
msg->orientation.w, msg->orientation.x, msg->orientation.y, msg->orientation.z);
} int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle node;
ros::Subscriber subimu = node.subscribe("imu", 1000, imuCallback);
ros::spin();
return 0;
}

【完】


下一节会介绍路标Landmark数据的发布和订阅。

【cartographer_ros】五: 发布和订阅陀螺仪Imu信息的更多相关文章

  1. 【cartographer_ros】六: 发布和订阅路标landmark信息

    上一节介绍了陀螺仪Imu传感数据的订阅和发布. 本节会介绍路标Landmark数据的发布和订阅.Landmark在cartographer中作为定位的修正补充,避免定位丢失. 这里着重解释一下Land ...

  2. StackExchange.Redis学习笔记(五) 发布和订阅

    Redis命令中的Pub/Sub Redis在 2.0之后的版本中 实现了 事件推送的  发布订阅命令 以下是Redis关于发布和订阅提供的相关命令 SUBSCRIBE channel [channe ...

  3. 【cartogarpher_ros】三: 发布和订阅雷达scan信息

    上一节介绍和测试了cartographer的官方demo. 本节会编写ros系统中,最常用的激光雷达LaserScan传感数据的订阅和发布,方便在cartographer中加入自己的数据进行建图与定位 ...

  4. 【cartographer_ros】四: 发布和订阅里程计odom信息

    上一节介绍了激光雷达Scan传感数据的订阅和发布. 本节会介绍里程计Odom数据的发布和订阅.里程计在cartographer中主要用于前端位置预估和后端优化. 官方文档: http://wiki.r ...

  5. 八十五:redis之redis的事物、发布和订阅操作 (2019-11-18 22:54)

    redis事物可以一次执行多个命令,事物具有以下特征1.隔离操作:事物中的所有命令都会序列化.按顺序执行,不会被其他命令打扰2.原子操作:事物中的命令要么全部被执行,要么全部都不执行 开启一个事物,以 ...

  6. Redis的高级应用-事务处理、持久化、发布与订阅消息、虚拟内存使用

    三.事务处理 Redis的事务处理比较简单.只能保证client发起的事务中的命令可以连续的执行,而且不会插入其他的client命令,当一个client在连接 中发出multi命令时,这个连接就进入一 ...

  7. Dubbo系列之 (五)服务订阅(2)

    辅助链接 Dubbo系列之 (一)SPI扩展 Dubbo系列之 (二)Registry注册中心-注册(1) Dubbo系列之 (三)Registry注册中心-注册(2) Dubbo系列之 (四)服务订 ...

  8. (转)SqlServer 数据库同步的两种方式 (发布、订阅),主从数据库之间的同步

    最近在琢磨主从数据库之间的同步,公司正好也需要,在园子里找了一下,看到这篇博文比较详细,比较简单,本人亲自按步骤来过,现在分享给大家. 在这里要提醒大家的是(为了更好的理解,以下是本人自己理解,如有错 ...

  9. (原)3.2 Zookeeper应用 - 数据的发布与订阅

    本文为原创文章,转载请注明出处,谢谢 数据的发布与订阅 1.应用 服务端监听数据改变,客户端创建/更新节点数据,客户端提供数据,服务端处理 2.原理 客户端监控节点数据改变事件(例如配置信息,下图的c ...

随机推荐

  1. Vue_基础功能循环、计算、绑定、事件处理、组件

    1 <!DOCTYPE html> 2 <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xht ...

  2. python学习-Day34

    目录 今日内容详细 作业讲解 客户端 服务端 UDP协议 实现 服务器端 客户端 基于UDP实现简易版本的qq 客户端 服务器端 操作系统的发展史 第一代计算机(1940~1955) 第二代就算机(1 ...

  3. FreeRTOS --(15)信号量之概述

    转载自 https://blog.csdn.net/zhoutaopower/article/details/107359095 在裸机编程中这样使用过一个变量:用于标记某个事件是否发生,或者标志一下 ...

  4. Mysql 连续时间分组

    该方案:不限于本例的时间连续,也可适用于其他按连续分组. 连续条件 分组这问题困扰了很久,之前觉得在SQL上很难处理,都是在程序上做处理.后面实在有太多这需求了,所以只能想办法在SQL上处理了. 如下 ...

  5. Docker容器网络-基础篇

    开源Linux 一个执着于技术的公众号 Docker的技术依赖于Linux内核的虚拟化技术的发展,Docker使用到的网络技术有Network Namespace.Veth设备对.Iptables/N ...

  6. 【mq】从零开始实现 mq-05-实现优雅停机

    前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ...

  7. go convert slice to struct

    Question: in golang how to convert slice to struct scene 1:use reflect convert slice to struct func ...

  8. Python模块 | EasyGui

    (Python模块 | EasyGui | 2021/04/08) 目录 什么是 EasyGUI? [EasyGui中的函数] msbox | 使用示例 ynbox | 使用示例 ccbox | 使用 ...

  9. c++ :STL

    基础知识 容器 容器就是一些模板类的集合,不同之处就是容器中封装的是数据结构 1.序列容器 主要有vector向量容器.list列表容器.deque双端队列容器 元素在容器中是无序的 2.排序容器 包 ...

  10. 6.Docker网络

    什么是 Docker网络 docker 不启动,默认网络情况 ens33 lo virbr0 在 CentOS7 的安装过程中如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的 ...