让小车进行循迹最重要的是获取车相对于循迹线的位置,关于循迹模块的原理我已经在之前的一篇博客上讲过,如果大家没看过的话可以点此链接:PID循迹模块原理。
大家最关心的整车资料链接(百度云):整车资料链接
提取码:wki1
视频演示链接:视频演示链接
PID循迹的原理我们大致可以用常识性思维去思考下,如图1所示为循迹车寻线行走图示,现在小车要按照中间那条黑线循迹行走,如果车头往左偏则需要控制方向盘让车往右转,车头往又偏则需要让车往左转。但是仅仅只有左转和右转的话,车一到左边就向右转,一到右边就向左转,则车会来回在线左右晃动,速度一快就会偏离航向。这种循迹方法是最简单的方法,市场上常用的循迹传感器控制方法也就是这种,因为这种循迹传感器只能输出0或1,如图2所示。
所以这种传感器控制效果很差,速度慢了还好,速度稍微一块就会很容易偏离循迹线。如果我们需要实现更为精确的控制就需要引入比例P控制,也就是我们需要通过考验获取到线性输出的循迹传感器,获取到小车偏离黑线的程度,偏离的越远就向黑线方向转的程度就越大,偏离的越近就向黑线方向转的程度就越小,这就是引入比例P控制。但是转的时候又不能使小车转的过快,这就需要引入微分d控制,也就是PI转向控制器即可控制小车的循迹转向。小车在循迹的的时候有时也需要控制小车的循迹速度,控制小车的循迹速度需要PI速度控制器,前提当然我们也需要一个获取小车速度的传感器,也就是编码器,下面开始介绍下PID控制器原理。
PID控制器是一种线性控制方法,控制原理如图3-1所示,它根据给定值r(t)与实际输出值y(t)构成控制偏差e(t),即e(t)=r(t)-y(t)。对偏差进行比例、积分、微分运算,将三种运算结果相加,即可得到PID控制器的控制输出u(t)。在连续时间域中,PID控制器算法表达式如下:
式中:kp为比例系数,Ti为积分时间常数;Td为微分时间常数。
在循迹控制中,只需要PD控制器即可,公式中输入偏差e(t)为循迹传感器输出值,经过PD控制器后输出u(t)直接控制小车转向。在循迹控制中我们也需要控制循迹车的前进的速度,控制循迹车前进速度需要通过PI控制器来实现。循迹小车的整个控制原理系统框图如图所示。
PID控制器实现代码如下:
//定义pid结构体
struct PID
{
float kp;
float ki;
float kd;
};
typedef struct PID _PID;
//定义转向pid参数结构体
_PID TurnPdate=
{
.kp=100, //赋值比例值60
.ki=0, //赋值积分值
.kd=-34 //赋值微分值-45
};
//定义速度pid参数结构体
_PID SpdPdate=
{
.kp=-50, //赋值比例值-80
.ki=-6, //赋值积分值-10
.kd=0 //赋值微分值
};
/*@brief: 位置式PID控制器
* @param:
* [in] float deviation: 和目标值得偏差
* [in] _PID pid: 位置式pid的参数
* @return: 调节占空比的一个整形数
*/
int PositionPID(float deviation,_PID pid)
{
float Position_KP=pid.kp,Position_KI=pid.ki,Position_KD=pid.kd;
static float Bias,Pwm,Integral_bias,Last_Bias;
Bias=deviation; //计算偏差
Integral_bias+=Bias; //求出偏差的积分
Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias); //位置式PID控制器
Last_Bias=Bias; //保存上一次偏差
return Pwm;
}
/*@brief: 为速度pid使用位置式PID控制器,对积分值进行限幅
* @param:
* [in] float deviation: 和目标值得偏差
* [in] _PID pid: 位置式pid的参数
* @return: 调节占空比的一个整形数
*/
int PositionPIDToSpd(float deviation,_PID pid)
{
float Position_KP=pid.kp,Position_KI=pid.ki,Position_KD=pid.kd;
static float Bias,Pwm,Integral_bias,Last_Bias,pwmKI=0;
Bias=deviation; //计算偏差
Integral_bias+=Bias; //求出偏差的积分
pwmKI=Position_KI*Integral_bias;
if(pwmKI>MAX_MOTOR_PWM) Integral_bias=MAX_MOTOR_PWM/Position_KI;
Pwm=Position_KP*Bias+pwmKI+Position_KD*(Bias-Last_Bias); //位置式PID控制器
Last_Bias=Bias; //保存上一次偏差
return Pwm;
}
代码中PositionPID(float deviation,_PID pid)中函数deviation偏差参数输入循迹传感器输出值,pid参数需要根据自己车型和循迹场地调试设置。
通过PD转向控制器获取到输出数据后,需要控制舵机转向,一般循迹车有两种车型,分别为舵机转向型和差速转向型。
如图5所示为差速转向车型,这种车转向方法是通过控制车轮的差速。我们可以想到如果让小车往右转,则需左轮往前转,然后右轮往后转,且差速越大转的越快。同理,让小车往左转的话需要右轮往前转,左轮往后转。实现小车向前走的话就是两个轮子同时往前转即可,控制小车循迹速度的话要映入PI速度控制器,同时需要传感器测量出小车的速度,这是就需要大家使用编码器电机了。
代码实现如下:
/*@brief:根据循迹传感器pid调节小车转向使小车处于黑线中间
* @param:
* [in]int TraceDate: 循迹传感器输出的值
* [in]float TarSpdL:右边电机目标速度,最大速度越1.27m/s
* @return: 返回调节电机速度的转向pwm
*/
int ChangeTraceTurn(int TraceDate)
{
int pwm=0;
int bias;
bias=TraceDate;
pwm=PositionPID(bias,TraceTurnPdate);
if(pwm>5000) pwm=5000;//限幅
else if(pwm<-5000) pwm=-5000;
return pwm;
}
/*@brief:根据pid调节左边电机到目标速度
* @param:
* [in]int EncodeSpdL: 当前左电机编码器测速值
* [in]float TarSpdL:左边电机目标速度,最大速度越1.19m/s
* @return: 返回左边电机计算后的pwm占空比
*/
int ChangeSpeedMotorL(int NowEncodeSpdL,float TarSpdL)
{
int pwm=0;
int bias;
int TarEncodeSpdL;
TarEncodeSpdL=(int)((TarSpdL*ACircleEncoder)/(WheelOneCircleDis*100)+0.5f);//根据目标速度求出目标编码器速度
bias=NowEncodeSpdL-TarEncodeSpdL;
pwm=PositionPIDToSpd(bias,SpdPdate);
if(pwm>5000) pwm=5000;//限幅
else if(pwm<-5000) pwm=-5000;
return pwm;
}
/*@brief:根据pid调节右边电机到目标速度
* @param:
* [in]int EncodeSpdL: 当前右电机编码器测速值
* [in]float TarSpdL:右边电机目标速度,最大速度越1.27m/s
* @return: 返回右边电机计算后的pwm占空比
*/
int ChangeSpeedMotorR(int NowEncodeSpdR,float TarSpdR)
{
int pwm=0;
int bias;
int TarEncodeSpdR;
TarEncodeSpdR=(int)((TarSpdR*ACircleEncoder)/(WheelOneCircleDis*100)+0.5f);//根据目标速度求出目标编码器速度
bias=NowEncodeSpdR-TarEncodeSpdR;
pwm=PositionPIDToSpd(bias,SpdPdate);
if(pwm>5000) pwm=5000;//限幅
else if(pwm<-5000) pwm=-5000;
return pwm;
}
/*@brief: 让小车根据循迹黑线走
*@param:
* [in]TraceDate: 循迹传感器输出的值
* [in]TarSpeed:循迹的目标速度
*@return: 到达目标点返回1,否则返回0
*/
void TraceMove(int TraceDate,float TarSpeed)
{
int turnpwm=0;
int spdpwml=0,spdpwmr=0;
int pwml=0,pwmr=0;
turnpwm=ChangeTraceTurn(TraceDate);
spdpwml=ChangeSpeedMotorL(Encode_Left,TarSpeed);
spdpwmr=ChangeSpeedMotorR(Encode_Right,TarSpeed);
pwmr=turnpwm+spdpwmr;
if(pwmr>5000) pwmr=5000;//限幅
else if(pwmr<-5000) pwmr=-5000;
pwml=-turnpwm+spdpwml;
if(pwml>5000) pwml=5000;//限幅
else if(pwml<-5000) pwml=-5000;
Set_Pwm_Motor1(pwmr); //设置电机1占空比
Set_Pwm_Motor2(pwml); //设置电机2占空比
}
舵机转向车结构如图6所示,控制舵机转向原理和差速转向相似,不同点是控制转向的输出最后作用于舵机上,将循迹传感器通过PD控制后的输出控制舵机即可,大家可以参考差速转向写舵机转向的代码,同样舵机转向也需要控制车的行驶速度。
关于小车的PID循迹控制,最重要的点是需要一个能够获取线性输出的循迹传感器,然后通过PD控制器控制舵机循迹转向,其次我们在循迹的同时如果需要控制小车的行驶速度需要引入PI速度控制器,两者共同作用即可实现小车循迹的PID控制。
文章浏览阅读10w+次,点赞121次,收藏777次。BS架构与CS架构的区别引言特点C/S系统结构B/S系统结构CS与BS的比较C/S 与 B/S 区别:现状与趋势(转自知乎)引言C/S结构,即Client/Server(客户机/服务器)结构,是大家熟知的软件系统体系结构,通过将任务合理分配到Client端和Server端,降低了系统的通讯开销,可以充分利用两端硬件环境的优势。早期的软件系统多以此作为首选设计标准。B/S结构,即Browse..._bs架构
文章浏览阅读1.1k次。主成分分析是对于原先提出的所有变量,将重复的变量(关系紧密的变量)删去多余,建立尽可能少的新变量,使得这些新变量是两两不相关的,而且这些新变量在反映课题的信息方面尽可能保持原有的信息。灰色预测是就灰色系统所做的预测。将相同本质的变量归入一个因子,可减少变量的数目,还可检验变量间关系的假设。它的基本原理是:为了从总体上把握两组指标之间的相关关系,分别在两组变量中提取有代表性的两个综合变量U1和V1(分别为两个变量组中各变量的线性组合),利用这两个综合变量之间的相关关系来反映两组指标之间的整体相关性。_2023深圳杯d题
文章浏览阅读197次。【举例】:A去消费,花了1千元,A的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了1千元,就在这时,A又花了1千元买了一个机械键盘,即新增INSERT了一条消费记录,并提交。【举例】:A给B发1000块钱,手一抖打了10000,这个钱已经打到B的户口,但是事务还没有提交,这时B查下卡,发现多了9000,兴奋坏了,但是A及时发现,马上回滚差点提交的事务,将数字改回1000再提交。(2)流图G的环形复杂度V(G)=E-N+2,其中,E是流图中边的条数,N是结点数。_a给b发1000块钱,手一抖打了10000,这个钱已经打到b的户口,但是事务还没有提交,
文章浏览阅读337次。什么是UUID?UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符。UUID具有以下涵义:经由一定的算法机器生成为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUI..._时间戳生成uuid
文章浏览阅读57次。2018年Q4 MaxCompute发布了一系列新功能。 本文对主要新功能和增强功能进行了概述。SQL功能增强:Grouping Sets 多维聚合分析 (CUBEand ROLLUP)GROUPING 和 GROUPING_ID 函数UDF和外表功能增强SQL语言支持:数据集交集和补集Java UDX类型系统增强SQL性能优化..._maxcompute grouping sets 限制
文章浏览阅读1k次。【代码】linux中用vim编辑文件不能鼠标右键粘贴复制问题。_linux 不能右键
文章浏览阅读970次,点赞20次,收藏25次。大家好,小编来为大家解答以下问题,利用python爬取简单网页数据步骤,怎么用python爬取网站上的数据,今天让我们一起来看看吧!下面就按这个路线逐一讲讲各部分的内容;HTTP协议是一个应用层面向对象协议,也叫超文本传输协议。是基于TCP协议的可靠传输,采用客户端/服务器端模式,指定了客户端可能发送给服务器什么样的消息,以及服务端给出什么样的响应。HTTP协议请求由状态行、请求头和请求正文三部分组成;请求端的HTTP报文叫做请求报文,响应端的叫做响应报文,通常,并不一定要有报文主体。_python爬贴吧数据
文章浏览阅读138次。在编程中,使用jQuery的DataGrid插件可以方便地展示和管理数据。然而,默认情况下,DataGrid会显示一个滚动条,以便在数据超出可见区域时进行滚动查看。为了去除DataGrid的滚动条,我们需要通过CSS样式来修改它的外观。接下来,我们需要初始化DataGrid并应用上述的CSS样式。通过以上步骤,你已经成功去除了jQuery中DataGrid的滚动条。现在,你可以根据自己的需求进行进一步的定制和样式修改。如果需要进一步配置DataGrid,你可以在初始化代码中添加适当的选项。_jqgrid滚动条
文章浏览阅读738次。基于空洞卷积DCNN与长短期时间记忆模型LSTM的dcnn-lstm的回归预测模型_dcnn-lstm
文章浏览阅读92次。一、配置防火墙,开启80端口、3306端口CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙。1、关闭firewall:systemctl stop firewalld.service #停止firewallsystemctl disable firewalld.service #禁止firewall开机启动2、安装iptables防火墙...
文章浏览阅读25次。Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图 Bing Maps Silverlight Control虽然为我们提供了简洁、方面的开发模型,但也有许多不足之处,比如我们想实现一个迷你小地图功能,Bing Map Silverlight Control就没有这样的内置控件,要想实现这一功能就需要我们自己想办法。...
文章浏览阅读84次。一.页面内的锚点定位;锚点定位中,就算采用overflow:hidden将滚动条隐藏,但是它依然可以发生锚点定位,实现定点跳转;1.单向定位: 锚定定位实在页面必须要发生滚动的情况下,不滚动也能发生,效果不明显;他的语法见下面代码:<a href="#2">锚点2</a>....文本省略<h2><a name="2" id="2"&g..._标签范围比较大,如何定位到一个点的位置