无人驾驶之--记录自己的无人驾驶只旅
无人驾驶的传感器分类
五一过后自己要开始全身心投入到紧张的研发中来了。
前言。本人作为一个初创公司的无人驾驶扫地车公司的技术人员,从大学开始一直做关于机器人相关的项目和比赛,了解一点嵌入式硬件,嵌入式软件,包括但不限于Cortex-M到cortext-A系列和一些FPGA等。1.先主要做的工作是使用双目视觉-深度学习、语义分割等纯视觉方案(视觉方案是其他小伙伴主攻的方向,本人只做视觉应用,标注,训练,选择模型调参等不做),只使用该方案做了纯视觉在公园狭窄的道路上行驶的一个demo,有时间我将此demo视频上传至我的B站。2.激光雷达,目前接触的激光雷达不多,大致有欢创的雷达,思岚的A1、2、3、S1,还有希克的单线,多线雷达只接触了一下镭神的16线,其他没真正使用过,就不多说了;我是用单线激光雷达一般是在室内跑建图和导航,目前比较好点的是cartography,hector建图也还行吧,但是机器人速度可可能不能很快,做应用可能不行。3.毫米波雷达,目前只用过24Ghz的毫米波雷达硬件应用解决方案,(⊙﹏⊙)还没有实际应用或者结合其他传感器使用,所以讨论的不多,等我接触一下TI的77G的一个方案,等使用该方案做出一些小demo后我再详细介绍该传感器,本人觉得毫米波将来也许可以取代激光雷达哦。4.超声波,超声波这个东西吧用途广,但我感觉缺陷明显,实际使用场景和FOV角等因素,收发一体的超声波和收发分离的超声波还有很大区别,后面再做细细详细叙述。5.GPS,卫星定位导航一般不单独使用,一般结合其他传感器使用,这个也是我重点使用的传感器之一,因为他和多线激光雷达是互补的关系,在室外的时候,如果是有丰富的特征点多线雷达可以结合GPS进行很好的定位效果,如果是在类似草原大广场等,激光雷达直接废掉,但是这种场景一般不是很多。6.UWB,这个传感器比较尴尬,一般不做单独使用,如果单独使用可以做的有跟随、智能车钥匙、或者防丢啥的,如果组网的话确实可以仅仅靠该传感器做编队,高精度定位等,但是要有好的算法支撑,不然数据差别很难用。7.IMU,这个怎么说呢,一般市面上的IMU大多是做测量车辆的角度,经过滤波算法作为一个捷联惯导给车辆提供航向,真的惯导如果长时间在复杂的环境中单独使用稳定性还需要进一步提高,一般使用是配合GPS,当定位精度低于厘米及的时候由于信号被遮挡,可提供短暂的厘米级定位。配合激光或者视觉都可以进行建图和导航。8.正交码盘,这个是我们自己实验室的叫法,其实他就是一个9轴IMU和一个高精度防滑的正交编码器,在地面比较好的环境中直接提供机器人实时坐标和航向,就是ROS中的里程计一样的一个单独传感器。
关于如何设计或者选型合适的无人驾驶硬件
我遵循的原则是好用方便的原则,不会刻意去使用某个大的平台或者方法,一般是自己去发现问题,然后再去用自己的逻辑方法去解决问题;比如关于无人驾驶一般的厂家在电机FOC矢量控制方面可能刻意去找带有CAN芯片的电机驱动系统,说是因为CAN的优秀的抗干扰和可扩展,我则是去找几款不同的,比如有PWM控制的,RS232、RS485等,结合使用的方便性和自己的实际情况我会最终选择485而不是流行的CAN,关于上下位机的芯片选型,个人觉得够用就行,我不会刻意最求性能,比如如果使用视觉,那么优先选择带有NPU的SOC等,只有我跑一些一般的芯片负载比较吃力的算法,如使用多线激光雷达跑室外大场景建图,carto3D或者loam的时候我才会使用X86CPU的工控,不然一般使用arm的SOC,如树莓派、瑞芯微的soc就行了,比如思岚的mapping就是使用RK3288结合单线雷达和IMU做的一个建图器;后面我会开源我的硬件设计架构和部分软件架构,现在针对每种传感器我基本上做了一些小demo,接下来开始多传感器进行融合调试了,我会每天把自己调试的过程和一些心得体会分享出来,给大家踩踩坑。
双目视觉传感器
去年我们无人驾驶的team完成了纯视觉的部分训练道路的测试:
- 我把测试的其中一个demo上传供大家参考
神经网络测试demo
激光雷达传感器
激光雷达传感器呢我有的做法是使用两种,第一种使用高斯地球坐标系,使用单线激光雷达进行cartography平面建图,这里使用ROS中开源的建图和导航框架,具体如何使用cartography我就不介绍了,假设大家已经在上位机上建图完毕,通过如下代码把机器人当前坐标和角度输出出来,给下位机,当然了这个坐标肯定跟高斯地球坐标不一致,需要对数据进行对齐,那些我是放到下位机上做去了,因为我导航算法是自己精简了一下按照自己需求,有时间我也会对其进行详细叙述的。
#include "ros/ros.h" //ros需要的头文件
#include <tf/tfMessage.h>
#include <tf/transform_broadcaster.h>
#include <nav_msgs/Odometry.h>
#include "std_msgs/String.h"
//以下为串口通讯需要的头文件
#include <string>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <math.h>
#include "serial/serial.h"
/****************************************************************************/
using std::string;
using std::exception;
using std::cout;
using std::cerr;
using std::endl;
using std::vector;
/*****************************************************************************/
float ratio = 1000.0f ; //转速转换比例,执行速度调整比例
float D = 0.2680859f ; //两轮间距,单位是m
float linear_temp=0,angular_temp=0;//暂存的线速度和角速度
/****************************************************/
unsigned char data_terminal0=0x0d; //“/r"字符
unsigned char data_terminal1=0x0a; //“/n"字符
unsigned char speed_data[10]={1,2,3,4,5}; //要发给串口的数据
string rec_buffer; //串口数据接收变量
//发送给下位机的左右轮速度,里程计的坐标和方向
union floatData //union的作用为实现char数组和float之间的转换
{
float d;
unsigned char data[4];
}right_speed_data,left_speed_data,position_x,position_y,oriention,vel_linear,vel_angular;
/************************************************************/
void callback(const nav_msgs::Odometry& cmd_input)//订阅/cmd_vel主题回调函数
{
string port("/dev/ttyUSB0"); //小车串口号
unsigned long baud = 115200; //小车串口波特率d
serial::Serial my_serial(port, baud, serial::Timeout::simpleTimeout(1000)); //配置串口// angular_temp = cmd_input.angular.z ;//获取/cmd_vel的角速度,rad/s
if(cmd_input.pose.pose.position.x >= 0)
{
speed_data[2] = 0x01;
}
else
{
speed_data[2] = 0x00,cmd_input.pose.pose.position.x*-1;
}
if(cmd_input.pose.pose.position.y >= 0)
{
speed_data[5] = 0x01;
}
else
{
speed_data[5] = 0x00,cmd_input.pose.pose.position.y*-1;
}
ROS_INFO("position.x: [%f]",(float)cmd_input.pose.pose.position.x*100);
ROS_INFO("position.y: [%f]",(float)cmd_input.pose.pose.position.y*100);
ROS_INFO("position.z: [%f]",(float)cmd_input.pose.pose.orientation.w*180+180);
speed_data[0] = 0xff;
speed_data[1] = 0xfe;
speed_data[3] = (cmd_input.pose.pose.position.x*100)/256;
speed_data[4] = (cmd_input.pose.pose.position.x*100)-speed_data[3]*256;
speed_data[6] = (cmd_input.pose.pose.position.y*100)/256;
speed_data[7] = (cmd_input.pose.pose.position.y*100)-speed_data[6]*256;
my_serial.write(speed_data,10);
}
int main(int argc, char **argv)
{
string port("/dev/ttyUSB0");//小车串口号
unsigned long baud = 115200;//小车串口波特率
serial::Serial my_serial(port, baud, serial::Timeout::simpleTimeout(1000));//配置串口
ros::init(argc, argv, "base_controller");//初始化串口节点
ros::NodeHandle n("~"); //定义节点进程句柄
ros::Subscriber sub = n.subscribe("/slamware_ros_sdk_server_node/odom", 1000, callback); //订阅/cmd_vel主题
// ros::Publisher chatter_pub= n.advertise<std_msgs::String>("chatter", 200); //定义要发布/odom主题
static tf::TransformBroadcaster odom_broadcaster;//定义tf对象
geometry_msgs::TransformStamped odom_trans;//创建一个tf发布需要使用的TransformStamped类型消息
nav_msgs::Odometry odom;//定义里程计对象
ros::Rate loop_rate(10);//设置周期休眠时间
int count=0;
while(ros::ok())
{
//std_msgs::String msg;
//std::stringstream ss;
//ss << "hellow world" << count;
//msg.data = ss.str();
//chatter_pub.publish(msg);
// for(int i=0;i<4;i++) //将左右轮速度存入数组中发送给串口
// {
// speed_data[i]=right_speed_data.data[i];
// }
// my_serial.write(speed_data,10);
// ROS_INFO("Position.x: [%f]",(float)odom.pose.pose.position.x);
// ROS_INFO("Position.y: [%f]",(float)odom.pose.pose.position.x);
// ROS_INFO("Position.twist: [%f]",(float)odom.transform.transform.translation.x);
ros::spinOnce();
//++count;
// ROS_INFO("为啥距离不对啊啊: [%f]",msg->tf);
}
return 0;
}
GPS传感器
GPS传感器这部分比较重要,在我这里是凌驾于激光雷达和双目视觉之上的,因为什么呢,因为激光给我提供实时厘米级定位,GPS也是如此,而且航向和三维高度以及速度等都有提供。
我做了哪些工作呢,首先我使用了很多GPS芯片,什么M6,M8,F9P,什么726,704、803,我全用过,这个传感器的我理解的还是比较深的(自我感觉哈,废话不多说直接进入正题),我呢以前使用千寻的RTK网络差分服务,经过半年的各种测试发现,额(⊙﹏⊙)精度不是很理想,首先呢不稳定,其次是精度在分米到厘米波动大(受到各种因素影响,我觉得还是虚拟基站的影响最大)。SO!我就自己设置一个基站,然后通过自己服务器把差分数据分发出去,因为基站到无人车的距离很近(低速无人车),所以精度基本一直维持在厘米级,再加上我开启惯导融合算法,当GPS信号短暂不好的时候也能维持接近厘米级定位,而且我对定位数据使用了卡尔曼滤波,想必大家都知道这个数据有多稳了吧。
毫米波传感器
这个传感器暂时没有demo,后面等待TI有大进展了我再(●’◡’●)
(看好他)
超声波传感器
没啥好说的啊,就是8个一体收发超声波,近距离避障使用
正交码盘传感器
就是无刷电机上加装一个1024线的增量式光电编码器,通过运动学分解做了坐标转换输出里程计信息而已。
光电编码器运动学
Gamma公式展示
Γ
(
n
)
=
(
n
−
1
)
!
∀
n
∈
N
\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N
Γ(n)=(n−1)!∀n∈N 是通过 Euler integral
Γ
(
z
)
=
∫
0
∞
t
z
−
1
e
−
t
d
t
.
\Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.
Γ(z)=∫0∞tz−1e−tdt.
。有点晚了,写的比较乱,后面可能会整理一下