本文主要是介绍树莓派4B+ROS+RealSense T265室内定位替代光流开源方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
硬件机载端:树莓派4B 4/8G 、T265、RPLIDAR-A2
配套开发环境:
树莓派系统Ubuntu 20.04.3 LTS
RealSense SDK 2.0
ROS系统版本:noetic+realsense-ros
多平台远程登陆软件:nomachine(win10、mac、安卓、ubuntu等)
有两种方案实现t265位姿数据,本方案通过修改宏定义T265_STATE_CAPTURE_ENABLE实现
1、可直接利用SDK函数中的rs-pose获取位姿数据,参考链接如下:
rs-pose (intelrealsense.com)
2、利用官方提供的realsense-ros包中自带的demo启动roslaunch realsense2_camera rs_t265.launch,通过订阅节点发布的"/camera/odom/sample"话题获取位姿数据
传输浮点数据需有用到的数据转换函数(大小端通用)
#include <iostream>
#include "datatf.h"
using namespace std;
union
{
unsigned char floatByte[4];
float floatValue;
}FloatUnion;
/***************************************************************************************
@函数名:void Float2Byte(float *FloatValue, unsigned char *Byte, unsigned char Subscript)
@入口参数:FloatValue:float值
Byte:数组
Subscript:指定从数组第几个元素开始写入
@出口参数:无
功能描述:将float数据转成4字节数据并存入指定地址
@作者:无名小哥
@日期:2020年01月17日
****************************************************************************************/
void Float2Byte(float *FloatValue, unsigned char *Byte, unsigned char Subscript)
{
FloatUnion.floatValue = (float)2;
if(FloatUnion.floatByte[0] == 0)//小端模式
{
FloatUnion.floatValue = *FloatValue;
Byte[Subscript] = FloatUnion.floatByte[0];
Byte[Subscript + 1] = FloatUnion.floatByte[1];
Byte[Subscript + 2] = FloatUnion.floatByte[2];
Byte[Subscript + 3] = FloatUnion.floatByte[3];
}
else//大端模式
{
FloatUnion.floatValue = *FloatValue;
Byte[Subscript] = FloatUnion.floatByte[3];
Byte[Subscript + 1] = FloatUnion.floatByte[2];
Byte[Subscript + 2] = FloatUnion.floatByte[1];
Byte[Subscript + 3] = FloatUnion.floatByte[0];
}
}
/***************************************************************************************
@函数名:void Byte2Float(unsigned char *Byte, unsigned char Subscript, float *FloatValue)
@入口参数:Byte:数组
Subscript:指定从数组第几个元素开始写入
FloatValue:float值
@出口参数:无
功能描述:从指定地址将4字节数据转成float数据
@作者:无名小哥
@日期:2020年01月17日
****************************************************************************************/
void Byte2Float(unsigned char *Byte, unsigned char Subscript, float *FloatValue)
{
FloatUnion.floatByte[0]=Byte[Subscript];
FloatUnion.floatByte[1]=Byte[Subscript + 1];
FloatUnion.floatByte[2]=Byte[Subscript + 2];
FloatUnion.floatByte[3]=Byte[Subscript + 3];
*FloatValue=FloatUnion.floatValue;
}
传输浮点数据需有用到的数据转换函数(大小端通用)
#include <iostream>
#include "datatf.h"
using namespace std;
union
{
unsigned char floatByte[4];
float floatValue;
}FloatUnion;
/***************************************************************************************
@函数名:void Float2Byte(float *FloatValue, unsigned char *Byte, unsigned char Subscript)
@入口参数:FloatValue:float值
Byte:数组
Subscript:指定从数组第几个元素开始写入
@出口参数:无
功能描述:将float数据转成4字节数据并存入指定地址
@作者:无名小哥
@日期:2020年01月17日
****************************************************************************************/
void Float2Byte(float *FloatValue, unsigned char *Byte, unsigned char Subscript)
{
FloatUnion.floatValue = (float)2;
if(FloatUnion.floatByte[0] == 0)//小端模式
{
FloatUnion.floatValue = *FloatValue;
Byte[Subscript] = FloatUnion.floatByte[0];
Byte[Subscript + 1] = FloatUnion.floatByte[1];
Byte[Subscript + 2] = FloatUnion.floatByte[2];
Byte[Subscript + 3] = FloatUnion.floatByte[3];
}
else//大端模式
{
FloatUnion.floatValue = *FloatValue;
Byte[Subscript] = FloatUnion.floatByte[3];
Byte[Subscript + 1] = FloatUnion.floatByte[2];
Byte[Subscript + 2] = FloatUnion.floatByte[1];
Byte[Subscript + 3] = FloatUnion.floatByte[0];
}
}
/***************************************************************************************
@函数名:void Byte2Float(unsigned char *Byte, unsigned char Subscript, float *FloatValue)
@入口参数:Byte:数组
Subscript:指定从数组第几个元素开始写入
FloatValue:float值
@出口参数:无
功能描述:从指定地址将4字节数据转成float数据
@作者:无名小哥
@日期:2020年01月17日
****************************************************************************************/
void Byte2Float(unsigned char *Byte, unsigned char Subscript, float *FloatValue)
{
FloatUnion.floatByte[0]=Byte[Subscript];
FloatUnion.floatByte[1]=Byte[Subscript + 1];
FloatUnion.floatByte[2]=Byte[Subscript + 2];
FloatUnion.floatByte[3]=Byte[Subscript + 3];
*FloatValue=FloatUnion.floatValue;
}
机载端代码如下:
#include <ros/ros.h>
#include <serial/serial.h>
#include <std_msgs/String.h>
#include <std_msgs/Bool.h>
#include <std_msgs/Empty.h>
#include <geometry_msgs/Pose.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/Vector3.h>
#include <nav_msgs/Odometry.h>
#include <tf2/LinearMath/Quaternion.h>
#include <librealsense2/rs.hpp>
#include "datatf.h"
#define T265_STATE_CAPTURE_ENABLE 1
static uint8_t NCLink_Head[2]={0xFC,0xFF};//数据帧头
static uint8_t NCLink_End[2] ={0xA1,0xA2};//数据帧尾
uint8_t nclink_databuf[100];//待发送数据缓冲区
serial::Serial com;
geometry_msgs::Pose current_position;
geometry_msgs::Pose target_position;
geometry_msgs::Vector3 currrent_velocity;
std_msgs::Bool current_position_update_flag;
std_msgs::Bool target_position_update_flag;
void NCLink_Send_Currrent_State(float userdata1 ,float userdata2,
float userdata3 ,float userdata4,
float userdata5 ,float userdata6,
float userdata7 ,float userdata8,
float userdata9 ,float userdata10,
float quality ,
uint8_t byte0,
uint8_t byte1,
uint8_t byte2,
uint8_t byte3,
uint8_t byte4,
uint8_t byte5,
uint8_t byte6,
uint8_t byte7,
uint8_t update_flag,uint8_t msg_id)
{
uint8_t sum=0,_cnt=0,i=0;
nclink_databuf[_cnt++]=NCLink_Head[0];
nclink_databuf[_cnt++]=NCLink_Head[1];
nclink_databuf[_cnt++]=msg_id;
nclink_databuf[_cnt++]=0;
Float2Byte(&userdata1,nclink_databuf,_cnt);//position.x
_cnt+=4;
Float2Byte(&userdata2,nclink_databuf,_cnt);//position.y
_cnt+=4;
Float2Byte(&userdata3,nclink_databuf,_cnt);//position.z
_cnt+=4;
Float2Byte(&userdata4,nclink_databuf,_cnt);//velocity.x
_cnt+=4;
Float2Byte(&userdata5,nclink_databuf,_cnt);//velocity.y
_cnt+=4;
Float2Byte(&userdata6,nclink_databuf,_cnt);//velocity.z
_cnt+=4;
Float2Byte(&userdata7,nclink_databuf,_cnt);//quaternion.x
_cnt+=4;
Float2Byte(&userdata8,nclink_databuf,_cnt);//quaternion.y
_cnt+=4;
Float2Byte(&userdata9,nclink_databuf,_cnt);//quaternion.z
_cnt+=4;//28
Float2Byte(&userdata10,nclink_databuf,_cnt);//quaternion.w
_cnt+=4;//32
Float2Byte(&quality,nclink_databuf,_cnt);//quality
_cnt+=4;//32
nclink_databuf[_cnt++]=byte0;
nclink_databuf[_cnt++]=byte1;
nclink_databuf[_cnt++]=byte2;
nclink_databuf[_cnt++]=byte3;
nclink_databuf[_cnt++]=byte4;
nclink_databuf[_cnt++]=byte5;
nclink_databuf[_cnt++]=byte6;
nclink_databuf[_cnt++]=byte7;
nclink_databuf[_cnt++]=update_flag;
nclink_databuf[3] = _cnt-4;
for(i=0;i<_cnt;i++) sum ^= nclink_databuf[i];
nclink_databuf[_cnt++]=sum;
nclink_databuf[_cnt++]=NCLink_End[0];
nclink_databuf[_cnt++]=NCLink_End[1];
com.write(nclink_databuf,_cnt);
//ROS_INFO("%d",_cnt);
}
void serial_write_callback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO_STREAM("writing to serial port"<<msg->data);
com.write(msg->data);
}
#ifdef T265_STATE_CAPTURE_ENABLE
void t265_callback_internal(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
ROS_INFO_STREAM("t265_callback_internal");
NCLink_Send_Currrent_State(msg->pose.position.x,
msg->pose.position.y,
msg->pose.position.z,
currrent_velocity.x,
currrent_velocity.y,
currrent_velocity.z,
msg->pose.orientation.x,
msg->pose.orientation.y,
msg->pose.orientation.z,
msg->pose.orientation.w,
100.0f,0x01,
0,1,2,3,4,5,6,7,
0x0D);
}
#endif
void t265_callback_external(const nav_msgs::Odometry::ConstPtr& msg)
{
ROS_INFO_STREAM("t265_callback_external");
NCLink_Send_Currrent_State(msg->pose.pose.position.y,
msg->pose.pose.position.x,
msg->pose.pose.position.z,
msg->twist.twist.linear.y,
msg->twist.twist.linear.x,
msg->twist.twist.linear.z,
-msg->pose.pose.orientation.y,
msg->pose.pose.orientation.z,
-msg->pose.pose.orientation.x,
msg->pose.pose.orientation.w,
100.0f,0x01,
0,1,2,3,4,5,6,7,
0x0D);
}
int main(int argc,char** argv)
{
ros::init(argc,argv,"serialport");
ros::NodeHandle n;
ros::Subscriber serial_write_sub=n.subscribe("serial_write",1000,serial_write_callback);
ros::Publisher serial_read_pub=n.advertise<std_msgs::String>("serial_write",1000);
ros::Subscriber t265_sub_external=n.subscribe("/camera/odom/sample",1000,t265_callback_external);
#ifdef T265_STATE_CAPTURE_ENABLE
ros::Subscriber t265_sub_innernal=n.subscribe("/t265/odom/sample",1000,t265_callback_internal);
ros::Publisher t265_pub=n.advertise<geometry_msgs::PoseStamped>("/t265/odom/sample",1000);
rs2::pipeline pipe;
rs2::config cfg;
cfg.enable_stream(RS2_STREAM_POSE,RS2_FORMAT_6DOF);
pipe.start(cfg);
#endif
try
{
com.setPort("/dev/serial0");//("/dev/ttyUSB0");("/dev/ttyAMA0");
com.setBaudrate(921600);
serial::Timeout to=serial::Timeout::simpleTimeout(1000);
com.setTimeout(to);
com.open();
}
catch (serial::IOException& e)
{
ROS_ERROR_STREAM("unable to open port");
return -1;
}
if(com.isOpen())
{
ROS_INFO_STREAM("serial port is initialized");
}
else return -1;
ros::Rate loop_rate(100);
while(ros::ok())
{
if(com.available())
{
ROS_INFO_STREAM("read from serial port\n");
std_msgs::String result;
result.data=com.read(com.available());
ROS_INFO_STREAM("read: "<<result.data);
serial_read_pub.publish(result);
}
ros::spinOnce();
loop_rate.sleep();
#ifdef T265_STATE_CAPTURE_ENABLE
auto frames=pipe.wait_for_frames();
auto f=frames.first_or_default(RS2_STREAM_POSE);
auto pose_data=f.as<rs2::pose_frame>().get_pose_data();
//rs2_pose pose_data;
geometry_msgs::PoseStamped pmsg;
pmsg.pose.position.x=current_position.position.x=-pose_data.translation.x;
pmsg.pose.position.y=current_position.position.y=-pose_data.translation.z;
pmsg.pose.position.z=current_position.position.z=pose_data.translation.y;
currrent_velocity.x=-pose_data.velocity.x;
currrent_velocity.y=-pose_data.velocity.z;
currrent_velocity.z=pose_data.velocity.y;
pmsg.pose.orientation.x=pose_data.rotation.x;
pmsg.pose.orientation.y=pose_data.rotation.y;
pmsg.pose.orientation.z=pose_data.rotation.z;
pmsg.pose.orientation.w=pose_data.rotation.w;
t265_pub.publish(pmsg);
ROS_INFO("translation:%f %f %f",
pose_data.translation.x,
pose_data.translation.y,
pose_data.translation.z);
ROS_INFO("velocity:%f %f %f",
pose_data.velocity.x,
pose_data.velocity.y,
pose_data.velocity.z);
ROS_INFO("quad:%f %f %f %f",
pose_data.rotation.x,
pose_data.rotation.y,
pose_data.rotation.z,
pose_data.rotation.w);
#endif
}
}
飞控端解析代码如下:
void NCLink_Data_Prase_Process_Lite(uint8_t *data_buf,uint8_t num)//飞控数据解析进程
{
uint8_t sum = 0;
for(uint8_t i=0;i<(num-3);i++) sum ^= *(data_buf+i);
if(!(sum==*(data_buf+num-3))) return;//判断sum
if(!(*(data_buf)==NCLink_Head[1]&&*(data_buf+1)==NCLink_Head[0])) return;//判断帧头
if(!(*(data_buf+num-2)==NCLink_End[0]&&*(data_buf+num-1)==NCLink_End[1])) return;//帧尾校验
if(*(data_buf+2)==0X0A) //地面站控制移动数据
{
ngs_sdk.move_mode=*(data_buf+4),
ngs_sdk.mode_order=*(data_buf+5);
ngs_sdk.move_distance=(uint16_t)(*(data_buf+6)<<8)|*(data_buf+7);;
ngs_sdk.update_flag=true;
ngs_sdk.move_flag.sdk_front_flag=false;
ngs_sdk.move_flag.sdk_behind_flag=false;
ngs_sdk.move_flag.sdk_left_flag=false;
ngs_sdk.move_flag.sdk_right_flag=false;
ngs_sdk.move_flag.sdk_up_flag=false;
ngs_sdk.move_flag.sdk_down_flag=false;
if(*(data_buf+4)==SDK_FRONT)
{
ngs_sdk.move_flag.sdk_front_flag=true;
ngs_sdk.f_distance=ngs_sdk.move_distance;
}
else if(*(data_buf+4)==SDK_BEHIND)
{
ngs_sdk.move_flag.sdk_behind_flag=true;
ngs_sdk.f_distance=-ngs_sdk.move_distance;
}
else if(*(data_buf+4)==SDK_LEFT)
{
ngs_sdk.move_flag.sdk_left_flag=true;
ngs_sdk.f_distance=-ngs_sdk.move_distance;
}
else if(*(data_buf+4)==SDK_RIGHT)
{
ngs_sdk.move_flag.sdk_right_flag=true;
ngs_sdk.f_distance=ngs_sdk.move_distance;
}
else if(*(data_buf+4)==SDK_UP)
{
ngs_sdk.move_flag.sdk_up_flag=true;
ngs_sdk.f_distance=ngs_sdk.move_distance;
}
else if(*(data_buf+4)==SDK_DOWN)
{
ngs_sdk.move_flag.sdk_down_flag=true;
ngs_sdk.f_distance=-ngs_sdk.move_distance;
}
NCLink_Send_Check_Flag[8]=1;
Pilot_Status_Tick();
}
else if(*(data_buf+2)==0X0C)
{
Guide_Flight_Lng =((int32_t)(*(data_buf+4)<<24)|(*(data_buf+5)<<16)|(*(data_buf+6)<<8)|*(data_buf+7));
Guide_Flight_Lat =((int32_t)(*(data_buf+8)<<24)|(*(data_buf+9)<<16)|(*(data_buf+10)<<8)|*(data_buf+11));
Guide_Flight_Cnt++;;
Guide_Flight_Flag=1;
Pilot_Status_Tick();
}
else if(*(data_buf+2)==0X0D)
{
Byte2Float(data_buf,4,¤t_state.position_x);
Byte2Float(data_buf,8,¤t_state.position_y);
Byte2Float(data_buf,12,¤t_state.position_z);
Byte2Float(data_buf,16,¤t_state.velocity_x);
Byte2Float(data_buf,20,¤t_state.velocity_y);
Byte2Float(data_buf,24,¤t_state.velocity_z);
current_state.position_x*=100.0f;
current_state.position_y*=100.0f;
current_state.position_z*=100.0f;
current_state.velocity_x*=100.0f;
current_state.velocity_y*=100.0f;
current_state.velocity_z*=100.0f;
Byte2Float(data_buf,28,¤t_state.q[0]);
Byte2Float(data_buf,32,¤t_state.q[1]);
Byte2Float(data_buf,36,¤t_state.q[2]);
Byte2Float(data_buf,40,¤t_state.q[3]);
float _q[4];
_q[0]=current_state.q[3]*(1.0f);
_q[1]=current_state.q[0]*(1.0f);
_q[2]=current_state.q[2]*(-1.0f);
_q[3]=current_state.q[1]*(1.0f);
current_state.q[0]=_q[0];
current_state.q[1]=_q[1];
current_state.q[2]=_q[2];
current_state.q[3]=_q[3];
quad_getangle(current_state.q,current_state.rpy);
if(current_state.rpy[2]<0.0f) current_state.rpy[2]+=360.0f;
if(current_state.rpy[2]>360.0f) current_state.rpy[2]-=360.0f;
Byte2Float(data_buf,44,¤t_state.quality);
current_state.update_flag=*(data_buf+48);
current_state.byte[0]=*(data_buf+49);
current_state.byte[1]=*(data_buf+50);
current_state.byte[2]=*(data_buf+51);
current_state.byte[3]=*(data_buf+52);
current_state.byte[4]=*(data_buf+53);
current_state.byte[5]=*(data_buf+54);
current_state.byte[6]=*(data_buf+55);
current_state.byte[7]=*(data_buf+56);
Pilot_Status_Tick();
Get_Systime(&nclink_t);
}
}
飞控IMU与VIO融合代码如下:
uint16_t VIO_Sync_Cnt=5;
float K_ACC_VIO=1.0f,K_VEL_VIO=5.0f,K_POS_VIO=1.0f;
void VIO_SLAM_INS_CF(void)
{
Vector2f speed_delta={0};
float obs_err[2];
if(current_state.update_flag==1)//存在数据光流更新时,20ms一次
{
opt_data.valid=1;
current_state.update_flag=0;
OpticalFlow_Position.x=current_state.position_x;
OpticalFlow_Position.y=current_state.position_y;
OpticalFlow_Speed.x =current_state.velocity_x;
OpticalFlow_Speed.y =current_state.velocity_y;
OpticalFlow_Speed_Err.x=OpticalFlow_Speed.x-VIO_SINS.Speed[_EAST];
OpticalFlow_Speed_Err.y=OpticalFlow_Speed.y-VIO_SINS.Speed[_NORTH];
OpticalFlow_Position_Err.x=OpticalFlow_Position.x-VIO_SINS.Pos_Backups[_EAST][VIO_Sync_Cnt];
OpticalFlow_Position_Err.y=OpticalFlow_Position.y-VIO_SINS.Pos_Backups[_NORTH][VIO_Sync_Cnt];
OpticalFlow_Speed_Err.x=OpticalFlow_Speed.x-VIO_SINS.Vel_Backups[_EAST][VIO_Sync_Cnt];
OpticalFlow_Speed_Err.y=OpticalFlow_Speed.y-VIO_SINS.Vel_Backups[_NORTH][VIO_Sync_Cnt];
OpticalFlow_Speed_Err.x=constrain_float(OpticalFlow_Speed_Err.x,-250,250);
OpticalFlow_Speed_Err.y=constrain_float(OpticalFlow_Speed_Err.y,-250,250);
}
obs_err[0]=OpticalFlow_Speed_Err.x;
obs_err[1]=OpticalFlow_Speed_Err.y;
//三路反馈量修正惯导
correct[0].acc +=obs_err[0]* K_ACC_VIO*0.005f;//加速度矫正量
correct[1].acc +=obs_err[1]* K_ACC_VIO*0.005f;//加速度矫正
correct[0].acc=constrain_float(correct[0].acc,-50,50);
correct[1].acc=constrain_float(correct[1].acc,-50,50);
correct[0].vel =obs_err[0]* K_VEL_VIO*0.005f;//速度矫正量
correct[1].vel =obs_err[1]* K_VEL_VIO*0.005f;//速度矫正量
correct[0].pos =obs_err[0]* K_POS_VIO*0.005f;//位置矫正量
correct[1].pos =obs_err[1]* K_POS_VIO*0.005f;//位置矫正量
acc.x=-Origion_NamelessQuad._Acceleration[_EAST];//惯导加速度沿载体横滚,机头左侧为正
acc.y= Origion_NamelessQuad._Acceleration[_NORTH];//惯导加速度沿载体机头,机头前向为正
VIO_SINS.Acceleration[_EAST] = acc.x+correct[0].acc;//惯导加速度沿载体横滚,机头左侧为正
VIO_SINS.Acceleration[_NORTH]= acc.y+correct[1].acc;//惯导加速度沿载体机头,机头前向为正
speed_delta.x=(VIO_SINS.Acceleration[_EAST])*0.005f;
speed_delta.y=(VIO_SINS.Acceleration[_NORTH])*0.005f;
VIO_SINS.Position[_EAST]+=VIO_SINS.Speed[_EAST]*0.005f+0.5f*speed_delta.x*0.005f+pos.x+correct[0].pos;
VIO_SINS.Position[_NORTH] +=VIO_SINS.Speed[_NORTH] *0.005f+0.5f*speed_delta.y*0.005f+pos.y+correct[1].pos;
VIO_SINS.Speed[_EAST]+=speed_delta.x+correct[0].vel;
VIO_SINS.Speed[_NORTH] +=speed_delta.y+correct[1].vel;
for(uint16_t i=Num-1;i>0;i--)
{
VIO_SINS.Pos_Backups[_NORTH][i]=VIO_SINS.Pos_Backups[_NORTH][i-1];
VIO_SINS.Pos_Backups[_EAST][i]=VIO_SINS.Pos_Backups[_EAST][i-1];
VIO_SINS.Vel_Backups[_NORTH][i]=VIO_SINS.Vel_Backups[_NORTH][i-1];
VIO_SINS.Vel_Backups[_EAST][i]=VIO_SINS.Vel_Backups[_EAST][i-1];
}
VIO_SINS.Pos_Backups[_NORTH][0]=VIO_SINS.Position[_NORTH];
VIO_SINS.Pos_Backups[_EAST][0]=VIO_SINS.Position[_EAST];
VIO_SINS.Vel_Backups[_NORTH][0]=VIO_SINS.Speed[_NORTH];
VIO_SINS.Vel_Backups[_EAST][0]=VIO_SINS.Speed[_EAST];
from_vio_to_body_frame(VIO_SINS.Position[_EAST],VIO_SINS.Position[_NORTH],
&OpticalFlow_SINS.Position[_EAST],&OpticalFlow_SINS.Position[_NORTH],
current_state.rpy[2]);
from_vio_to_body_frame(VIO_SINS.Speed[_EAST],VIO_SINS.Speed[_NORTH],
&OpticalFlow_SINS.Speed[_EAST],&OpticalFlow_SINS.Speed[_NORTH],
current_state.rpy[2]);
}
作者:无名创新 https://www.bilibili.com/read/cv14270245?spm_id_from=333.999.0.0 出处:bilibili
这篇关于树莓派4B+ROS+RealSense T265室内定位替代光流开源方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!