Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in /var/www/html/plugins/system/falangdriver/falangdriver.php on line 534

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/plugins/system/t4/src/t4/MVC/Router/T4.php on line 388
自动驾驶竞赛车 - bluetooth蓝牙技术
Support us and view this ad

可选:点击以支持我们的网站

免费文章

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/components/com_comprofiler/plugin/user/plug_cbjdownloads/cbjdownloads.php on line 49

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/components/com_comprofiler/plugin/user/plug_cbblogs/cbblogs.php on line 48

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/components/com_comprofiler/plugin/user/plug_cbarticles/cbarticles.php on line 47

  • 开发平台: NXP
  • 许可协议: GNU General Public License, version 3 or later (GPL3+)
  项目中使用的工具物品 硬件 FRDM-K64F 带驱动器的齿轮步进电机 微伺服器 Alamak 车模 定制PCB 激光接近传感器 许多不同的SMD和非SMD组件(您可以在本文的底部找到下载列表) 4N25 光耦合器   软件、App和在线服务 MCUXpresso (MCUXpresso-IDE) Processing   手工工具和制造器械 3D 打印机 螺丝刀 焊接工具(最好是空气焊接工具)   故事 我们已经从上一个赛车比赛中获得了一些知识,所以我们决定做一些更困难的事情。所以我们将会制作一个新的主板,伺服电机上的摄像头,蓝牙通讯等等。   摄像头 这是整个车辆中最重要的部件。摄像机用于车辆在两条轨道边缘线之间的导航。甚至在法规中都规定摄像机应该是汽车导航的主要用途。我们使用的是单色线扫描相机,这意味着我们的相机只能看到一行黑色,灰色和白色像素,因为单色线扫描相机的分辨率为128x1。这种低分辨率可以通过尽可能的高的光谱速率和良好的光敏性来补偿。 在一年前的比赛中,我们发现了两个问题。首先,光线不仅来自相机的正前方,也来自相机的后方。这是因为相机芯片被放置在薄薄的PCB板上,让光线通过。所以为了解决这个问题,我们3D打印了黑色相机外壳,现在它看起来更酷炫了! 第二个问题是,我们把相机上下移动了很多次来找到正确的位置,使其在捕捉正确的转弯的同时不捕捉汽车的车身。我们在移动的过程中同时发现,不同类型的轨道的最佳位置不同。 下一个问题也是这样,如果我们不小心撞到相机,我们重新移动来找到正确的位置。所以我们增加了齿轮比为1:2的步进电机,使我们的相机能更快的移动。同时,可以在图中看到,我们添加了end Switch,这是因为伺服电机系统本身并不提供任何关于其初始位置的信息。当我们的汽车启动时,我们必须手动把它移到触碰末端开关的位置。从那一刻起我们就知道相机的摄影区间。 在这里,你可以看到所有3D打印部分的3D渲染(图中只有黑色细杆不是3D打印的) 接下来,让我们详细看一下硬件和软件。我们的目标是在一秒内捕获尽可能多图像的同时保持良好的对比度。因为如果对比度不合适,则会出现要么图像太暗看不到图片上的任何东西的情况,要么是图像太亮同样也看不到任何东西,但在这种情况对比度不合适的情况下,所有的物体都是白色的。此外,我们不能让我们的曝光时间太长,因为这会限制我们的帧率。帧率的计算公式为: 帧率= 1 /曝光 通过归一化值可以部分地解决这些问题。假设我们有值为0的黑色和值为1的白色。我们取过饱和图像,其最低值为0.8,最高值为1,这给出了<0.8,1>的值范围,我们要将其映射为<0,1>。这样我们的颜色从0.8变为0;0.9~0.5;以及从1到1。通过这个非常简单的过程,我们得到了没有饱和度的正常图像,但是这也会给图像带来大量的噪声,使得搜索线条变得非常困难。如果我们在场景中添加25%的光照,那么捕获的图像将进入<1,1>范围,我们将无法从中提取任何数据,甚至包括噪声数据。 那么,让我们回到如何正确的设置曝光这一步骤。我们的目标是实现最大的对比度。对比度的计算公式如下: 对比度=浅颜色-深颜色 当最浅的颜色是纯白,而最深的颜色是纯黑时,我们获得了最大的对比度。我们的相机是线性传感器,所以这意味着如果我们增加50%的曝光,那么最暗值和最亮值都应该增加50%。我们的目标是得到平均颜色0.5。考虑下面的例子<0,0.1>,这些值的平均值太低(10x),所以我们需要将曝光量除以0.1;<0,1>平均值是0.5,这完全没问题,我们什么都不用变;<1,1>平均值太高(2x),所以我们将曝光除以2。在此基础上,我们可以建立以下公式来调整曝光。 新曝光率 = 旧曝光率 / (最浅色 + 最深色) 这样调整之后,一切就绪。可以实现。 无论你的车去任何地方,色彩看起来都挺不错。最低限度是当你进入一个不是靠太阳而是靠荧光灯或其他东西照明的房间之前。你会开始看到一个巨大的闪烁,即使当灯光像往常一样照耀你的眼睛。为什么会这样呢? 我们的灯是由交流电供电的,它跟随50赫兹频率的正弦。如下所示(x轴以秒为单位): 您可以看到灯是在50 Hz下运行,但峰值在100 Hz。同样,对于光来说,真正重要的不是电压,而是功率,它看起来是这样的:  //原文中缺一张图片 我们现在可以非常清楚地看到实际是100赫兹。那么如何修复这个问题呢?我们需要使采样频率与电源频率同步,因此只有100,50,25,...Hz才是可行的频率。但是我们希望保持在尽可能高的频率,因为这些线条真的很重要,如果我们不高频拍照,我们很容易错过它们。但较低的频率(如50 Hz)在弱光情况下是有用的,在弱光水平下,我们需要曝光超过10ms。 所以让我们看看如何实现这个功能。我们将需要采取帧每10ms的速率曝光。但随之,我们在这里遇到了新的问题。帧捕获的开始和结束连接在一起成为单个事件。  //原文缺一张图片 如图所示,我们发送SI脉冲,并通过发送CLK脉冲逐个读取像素数据。但正如您在图片中看到的,在读取图像的18个像素后,将开始新的积分(图像捕获),我们希望这个步骤在更长时间后进行(前一幅图像的积分耗时6ms,我们希望保持同步在10ms,因此我们像多等待4ms)。我们可以通过虚拟读取清除CMOS来解决这个问题(忽略内容,并尽可能快地进行读取)。最终结果如下:我们对图像进行6ms积分,然后在新积分开始后读取并处理图像,因此在4ms后读取传感器,但忽略数据。在这之后,真实图像的长度为6ms的新的积分开始了。 当然,也有可能用更好的灯光照亮赛道。有有电容器些灯可以减少这种闪烁,或者他们可以储存一段时间一些能量的热和光。那么光动力就会是这样的:  //缺一张图片 或者它可以只是在阳光下,所以它将是完美的线条。当我们像这样得到足够稳定的光输出时,我们可以移除代码来修复闪烁,并尝试得到100fps以上。但是当赛程出现在我们不知道的环境中,我们应该一直围绕着这段代码。 让我们看看如何编程。K64F有四个可供选择的凹面,凹面代表可编程中断控制器。这是一个非常简单的设备,可以设置时间段选择触发中断。中断通常是一个小函数,它不被其他代码执行,而是由中断控制器执行。凹面是一种能够中断当前代码的执行,保存当前代码的状态,并在某些事件发生时执行一些中断功能的设备,如我们将凹面等待时间设置为等待时间。 我们的照相机需要两个凹面。一个用于10ms清除间隔,另一个用于设置积分间隔。 让我们看一下中断处理程序的示例代码: extern "C" { void PIT_CHANNEL_0_IRQHANDLER(void) { if(PIT->CHANNEL[0].TFLG & PIT_TFLG_TIF_MASK) { PIT->CHANNEL[0].TFLG = PIT_TFLG_TIF_MASK; // interrupt code goes here } } 首先我们需要编写的是外部的“C”,因为C++不遵循传统的低级代码实践,这会使我们很难找到中断函数。将其还原为旧的C模式可以使一切正常工作。然后是函数声明,它总是必须具有void参数的void返回类型,因为此函数不返回任何值,也不接受任何值。通道设置使用哪个凹点,在本例中我们使用的是PIT0。该函数做的第一件事是检查它是否被中断执行,如果不是,则退出。如果是,则清除中断标志。此标志使中断控制器触发此中断,如果该中断不被清除,则将以无休止的中断循环结束。后面是我们真正的中断代码。 现在我们需要看看如何从得到的图像中检测线条。我们正在寻找旁边有白色区域的黑线。我首先考虑了阈值和搜索区域,但是效果不是很好。我发现的最好的方法是使用派生,派生当黑白变化时给出峰值。派生效果不错,但要实现最佳检测质量,我们需要使用平均值。一开始有人可能会想到,这样做的方法是对相邻的导数求平均,但这是行不通的。原因如下: Derivation = (Der1 + Der2 + Der3)/3 此等式等于下一等式: Derivation = ((P1-P2)+(P2-P3)+(P3-P4))/3 当我们简化它时,可得到: Derivation = (P1-P4)/3 这并不能让我们摆脱噪音。所以我决定尝试不同的方法。对第一组像素进行平均,然后对第二组相邻像素进行平均,并对这些组的平均值进行推导: Derivation = (P1+P2+P3)/3 - (P4+P5+P6)/3 如果结果接近0,那么就意味着不可行。如果结果远离零,那么就意味着某处有线。基于派生是正还是负,我们就可以检测它是从白变黑还是从黑变白。 步进电机 步进电机采用4路信号A,B,C,D控制。每一个步进电机控制一个线圈。我们使用的是电机28BYJ-48,这是最好的半步工作模式,你可以在下面的图片上看到。也有全步模式。尝试之后感觉全步模式比半步模式表现更差。半步模式由8种不同的ABCD组合组成(您可以看到图像上的所有内容在8次分割后重复),而半步模式仅使用4种组合。电机通过从左向右发送脉冲而向前移动,通过从右向左发送脉冲而向相反方向移动。 当汽车启动时,电机的位置取决于汽车停机前电机的位置。但是用这种方法得到位置精确,因为有人可能会用手对电机施加足够大的力使其移动到不同的位置,而且这种位置节省会慢慢地破坏控制器的闪存。因此,最好的方法是在汽车行李箱处向下移动相机的方向发送这些脉冲,并且在每次移动时检查相机是否击中了末端开关(即安装在相机下面的小开关,它定义了相机的最低位置,也必须知道相机击中开关的角度,这样我们就可以进行所需的所有计算)。当相机按下结束开关后,开关闭合并向外微控制器发送信号,步进器停止移动,并表示我们现在处于位置0,也就是开始位置。    保险杠 我们从一年前学到的经验是保险杠非常重要。我们曾多次与墙壁和各种物体相撞,这几乎完全撞坏了我们的车。所以这一次我们增加了一个软管保险杠,软管和巨大的3D打印块相连。我们也把软管当作激光保持器。软管能够吸收部分碰撞,其余部分通过巨大的块转移到我们的车,所以碰撞不是仅靠车的边缘来处理的。 这里是保险杠的3D渲染,你可以看到它真的很巨型,它还包括激光支架。 激光 避障是一个必须的功能。为闪避放入轨道的白色立方体。我们的计划用激光找到这个障碍物,激光会用伺服电机绕着白色立方体旋转。这第一步,会准确的发出障碍物靠近时的信息,但是不能给出完美的位置。在我们找到障碍物后,我们将使用相机和步进电机正确地扫描它的位置。 为了使该部分正常工作,需要直接连接到电池以获得最大电压。此外,还需要光耦合器,因为该激光传感器输出的电压接近电池电压,约为7.2V。但K64F最大可处理5.5V电压。...

继续阅读完整内容

支持我们的网站,请点击查看下方广告

正在加载广告...
带驱动器的齿轮步进电机
微伺服器
Alamak 车模
定制PCB
激光接近传感器
许多不同的SMD和非SMD组件(您可以在本文的底部找到下载列表)
4N25 光耦合器
 

软件、App和在线服务

MCUXpresso (MCUXpresso-IDE)
 

手工工具和制造器械

3D 打印机
螺丝刀
焊接工具(最好是空气焊接工具)
 

故事

我们已经从上一个赛车比赛中获得了一些知识,所以我们决定做一些更困难的事情。所以我们将会制作一个新的主板,伺服电机上的摄像头,蓝牙通讯等等。

 

摄像头

这是整个车辆中最重要的部件。摄像机用于车辆在两条轨道边缘线之间的导航。甚至在法规中都规定摄像机应该是汽车导航的主要用途。我们使用的是单色线扫描相机,这意味着我们的相机只能看到一行黑色,灰色和白色像素,因为单色线扫描相机的分辨率为128x1。这种低分辨率可以通过尽可能的高的光谱速率和良好的光敏性来补偿。

在一年前的比赛中,我们发现了两个问题。首先,光线不仅来自相机的正前方,也来自相机的后方。这是因为相机芯片被放置在薄薄的PCB板上,让光线通过。所以为了解决这个问题,我们3D打印了黑色相机外壳,现在它看起来更酷炫了!

第二个问题是,我们把相机上下移动了很多次来找到正确的位置,使其在捕捉正确的转弯的同时不捕捉汽车的车身。我们在移动的过程中同时发现,不同类型的轨道的最佳位置不同。

下一个问题也是这样,如果我们不小心撞到相机,我们重新移动来找到正确的位置。所以我们增加了齿轮比为1:2的步进电机,使我们的相机能更快的移动。同时,可以在图中看到,我们添加了end Switch,这是因为伺服电机系统本身并不提供任何关于其初始位置的信息。当我们的汽车启动时,我们必须手动把它移到触碰末端开关的位置。从那一刻起我们就知道相机的摄影区间。

在这里,你可以看到所有3D打印部分的3D渲染(图中只有黑色细杆不是3D打印的)

接下来,让我们详细看一下硬件和软件。我们的目标是在一秒内捕获尽可能多图像的同时保持良好的对比度。因为如果对比度不合适,则会出现要么图像太暗看不到图片上的任何东西的情况,要么是图像太亮同样也看不到任何东西,但在这种情况对比度不合适的情况下,所有的物体都是白色的。此外,我们不能让我们的曝光时间太长,因为这会限制我们的帧率。帧率的计算公式为:

  • 帧率= 1 /曝光

通过归一化值可以部分地解决这些问题。假设我们有值为0的黑色和值为1的白色。我们取过饱和图像,其最低值为0.8,最高值为1,这给出了<0.8,1>的值范围,我们要将其映射为<0,1>。这样我们的颜色从0.8变为0;0.9~0.5;以及从1到1。通过这个非常简单的过程,我们得到了没有饱和度的正常图像,但是这也会给图像带来大量的噪声,使得搜索线条变得非常困难。如果我们在场景中添加25%的光照,那么捕获的图像将进入<1,1>范围,我们将无法从中提取任何数据,甚至包括噪声数据。

那么,让我们回到如何正确的设置曝光这一步骤。我们的目标是实现最大的对比度。对比度的计算公式如下:

  • 对比度=浅颜色-深颜色

当最浅的颜色是纯白,而最深的颜色是纯黑时,我们获得了最大的对比度。我们的相机是线性传感器,所以这意味着如果我们增加50%的曝光,那么最暗值和最亮值都应该增加50%。我们的目标是得到平均颜色0.5。考虑下面的例子<0,0.1>,这些值的平均值太低(10x),所以我们需要将曝光量除以0.1;<0,1>平均值是0.5,这完全没问题,我们什么都不用变;<1,1>平均值太高(2x),所以我们将曝光除以2。在此基础上,我们可以建立以下公式来调整曝光。

  • 新曝光率 = 旧曝光率 / (最浅色 + 最深色)

这样调整之后,一切就绪。可以实现。

无论你的车去任何地方,色彩看起来都挺不错。最低限度是当你进入一个不是靠太阳而是靠荧光灯或其他东西照明的房间之前。你会开始看到一个巨大的闪烁,即使当灯光像往常一样照耀你的眼睛。为什么会这样呢?

我们的灯是由交流电供电的,它跟随50赫兹频率的正弦。如下所示(x轴以秒为单位):

您可以看到灯是在50 Hz下运行,但峰值在100 Hz。同样,对于光来说,真正重要的不是电压,而是功率,它看起来是这样的:

 //原文中缺一张图片

我们现在可以非常清楚地看到实际是100赫兹。那么如何修复这个问题呢?我们需要使采样频率与电源频率同步,因此只有100,50,25,...Hz才是可行的频率。但是我们希望保持在尽可能高的频率,因为这些线条真的很重要,如果我们不高频拍照,我们很容易错过它们。但较低的频率(如50 Hz)在弱光情况下是有用的,在弱光水平下,我们需要曝光超过10ms。

所以让我们看看如何实现这个功能。我们将需要采取帧每10ms的速率曝光。但随之,我们在这里遇到了新的问题。帧捕获的开始和结束连接在一起成为单个事件。

 //原文缺一张图片

如图所示,我们发送SI脉冲,并通过发送CLK脉冲逐个读取像素数据。但正如您在图片中看到的,在读取图像的18个像素后,将开始新的积分(图像捕获),我们希望这个步骤在更长时间后进行(前一幅图像的积分耗时6ms,我们希望保持同步在10ms,因此我们像多等待4ms)。我们可以通过虚拟读取清除CMOS来解决这个问题(忽略内容,并尽可能快地进行读取)。最终结果如下:我们对图像进行6ms积分,然后在新积分开始后读取并处理图像,因此在4ms后读取传感器,但忽略数据。在这之后,真实图像的长度为6ms的新的积分开始了。

当然,也有可能用更好的灯光照亮赛道。有有电容器些灯可以减少这种闪烁,或者他们可以储存一段时间一些能量的热和光。那么光动力就会是这样的:

 //缺一张图片

或者它可以只是在阳光下,所以它将是完美的线条。当我们像这样得到足够稳定的光输出时,我们可以移除代码来修复闪烁,并尝试得到100fps以上。但是当赛程出现在我们不知道的环境中,我们应该一直围绕着这段代码。

让我们看看如何编程。K64F有四个可供选择的凹面,凹面代表可编程中断控制器。这是一个非常简单的设备,可以设置时间段选择触发中断。中断通常是一个小函数,它不被其他代码执行,而是由中断控制器执行。凹面是一种能够中断当前代码的执行,保存当前代码的状态,并在某些事件发生时执行一些中断功能的设备,如我们将凹面等待时间设置为等待时间。

我们的照相机需要两个凹面。一个用于10ms清除间隔,另一个用于设置积分间隔。

让我们看一下中断处理程序的示例代码:

extern "C" {
void PIT_CHANNEL_0_IRQHANDLER(void) {
if(PIT->CHANNEL[0].TFLG & PIT_TFLG_TIF_MASK) {
PIT->CHANNEL[0].TFLG = PIT_TFLG_TIF_MASK;
// interrupt code goes here
}
}

首先我们需要编写的是外部的“C”,因为C++不遵循传统的低级代码实践,这会使我们很难找到中断函数。将其还原为旧的C模式可以使一切正常工作。然后是函数声明,它总是必须具有void参数的void返回类型,因为此函数不返回任何值,也不接受任何值。通道设置使用哪个凹点,在本例中我们使用的是PIT0。该函数做的第一件事是检查它是否被中断执行,如果不是,则退出。如果是,则清除中断标志。此标志使中断控制器触发此中断,如果该中断不被清除,则将以无休止的中断循环结束。后面是我们真正的中断代码。

现在我们需要看看如何从得到的图像中检测线条。我们正在寻找旁边有白色区域的黑线。我首先考虑了阈值和搜索区域,但是效果不是很好。我发现的最好的方法是使用派生,派生当黑白变化时给出峰值。派生效果不错,但要实现最佳检测质量,我们需要使用平均值。一开始有人可能会想到,这样做的方法是对相邻的导数求平均,但这是行不通的。原因如下:

  • Derivation = (Der1 + Der2 + Der3)/3

此等式等于下一等式:

  • Derivation = ((P1-P2)+(P2-P3)+(P3-P4))/3

当我们简化它时,可得到:

  • Derivation = (P1-P4)/3

这并不能让我们摆脱噪音。所以我决定尝试不同的方法。对第一组像素进行平均,然后对第二组相邻像素进行平均,并对这些组的平均值进行推导:

  • Derivation = (P1+P2+P3)/3 - (P4+P5+P6)/3

如果结果接近0,那么就意味着不可行。如果结果远离零,那么就意味着某处有线。基于派生是正还是负,我们就可以检测它是从白变黑还是从黑变白。

步进电机

步进电机采用4路信号A,B,C,D控制。每一个步进电机控制一个线圈。我们使用的是电机28BYJ-48,这是最好的半步工作模式,你可以在下面的图片上看到。也有全步模式。尝试之后感觉全步模式比半步模式表现更差。半步模式由8种不同的ABCD组合组成(您可以看到图像上的所有内容在8次分割后重复),而半步模式仅使用4种组合。电机通过从左向右发送脉冲而向前移动,通过从右向左发送脉冲而向相反方向移动。

当汽车启动时,电机的位置取决于汽车停机前电机的位置。但是用这种方法得到位置精确,因为有人可能会用手对电机施加足够大的力使其移动到不同的位置,而且这种位置节省会慢慢地破坏控制器的闪存。因此,最好的方法是在汽车行李箱处向下移动相机的方向发送这些脉冲,并且在每次移动时检查相机是否击中了末端开关(即安装在相机下面的小开关,它定义了相机的最低位置,也必须知道相机击中开关的角度,这样我们就可以进行所需的所有计算)。当相机按下结束开关后,开关闭合并向外微控制器发送信号,步进器停止移动,并表示我们现在处于位置0,也就是开始位置。

  autoracing car

保险杠

我们从一年前学到的经验是保险杠非常重要。我们曾多次与墙壁和各种物体相撞,这几乎完全撞坏了我们的车。所以这一次我们增加了一个软管保险杠,软管和巨大的3D打印块相连。我们也把软管当作激光保持器。软管能够吸收部分碰撞,其余部分通过巨大的块转移到我们的车,所以碰撞不是仅靠车的边缘来处理的。

这里是保险杠的3D渲染,你可以看到它真的很巨型,它还包括激光支架。

激光

避障是一个必须的功能。为闪避放入轨道的白色立方体。我们的计划用激光找到这个障碍物,激光会用伺服电机绕着白色立方体旋转。这第一步,会准确的发出障碍物靠近时的信息,但是不能给出完美的位置。在我们找到障碍物后,我们将使用相机和步进电机正确地扫描它的位置。

为了使该部分正常工作,需要直接连接到电池以获得最大电压。此外,还需要光耦合器,因为该激光传感器输出的电压接近电池电压,约为7.2V。但K64F最大可处理5.5V电压。因此,我们用此电压为光耦合器二极管供电,其电流受电阻限制,因此性能良好。二极管点亮晶体管,晶体管打开。该晶体管采用3.3V供电,这是K64F的优良电压。

此简单电路的原理图如下:

 Schematics for autoracing car

伺服装置

激光和转向都需要伺服传动装置。伺服使用频率为50 Hz的 PWM控制。伺服电机的位置是由占空比改变的,通常最小为1ms,最大为2ms。但时,最好是分别标定各个伺服机构,找出它们的中心位置和占空比变化的步长。

 pwm for autoracing car

有人可能会认为,使用伺服装置来上下移动相机而不是步移会更好。但我却不那么认为,伺服可能更快并且更容易控制,但这是要付出“精确”作为代价。伺服电机实际上是由直流电机构成的,就像那些小型RC汽车或其他儿童玩具和电位器中的一样。电机转动,通过齿轮箱输出电机的转动,齿轮箱上装有电位器,用于检测位置。问题所在是电位器中可能存在的细小灰尘,电阻元件的非线性精度,控制器的低精度等。当试图缓慢移动电机或电机根本不移动时,这可能导致突然的无例外的跳跃。它不能仅仅用于精确位置的设定(至少那些便宜的伺服不能),所以我选择了步进器,它速度慢但更精确。

编码器

我们从前一年学到,对车速的了解是十分有用且必要的,车速可以用来调节我们供给马达的能量。主要是在起步时,我们想要传递最大的能量来使到汽车达到所需的速度。与此同时,这是一个非常好的挑战,在那里我们想要走得非常慢。如果我们转弯,会有传统的摩擦力使我们的车停下来,并永远保持住它。所以如果编码器告诉我们,我们被卡住了,那么它可以把额外的动力投入到马达中,这样我们就会再次开始移动。

编码器产生两个信号称为A相和B相,我们很幸运,我们的控制器已经包含了正交解码器(编码器数据读取)的硬件模块,这使我们能够在0%的CPU占用下高速读取编码器。

解码非常简单,如果信号导通在A相上,那么编码器是向前移动的。

如果信号导联在B相上,则编码器向后移动。

它实际上是通过在任一相位上存在边沿时递增/递减来工作的。如果您打算使用控制器而不使用硬件解码器,并且需要编写自己的软件,则此实现非常重要。您可以在第1072页的K64F文档中找到八种可能的状态,也可以从下图中提取它们:

在硬件和软件方面,它的工作非常简单。我们向电机输入PWM信号,该信号调节有多少功率可以进入电机。但令人悲伤的是,能量与速度不成正比。因此,我们得到我们的当前速度使用编码器,通过这一点,我们可以计算误差从期望的速度。然后我们把这个误差传递到PID调节器(这代表比例,积分和微分调节器),它调节应该进入电机的功率以达到我们期望的速度。

同样,为了使工作一切正确,那么我们将需要使用凹面,就像相机。我选择使用PIT3,它将每秒执行20次,每次它被执行时,它将读取编码器从上次读取移动了多远,将此值传递到PID调节器并更新电机速度。我们使用这个凹面,因为如果我们只是读取并将数值相加,那么我们就会得到距离。为了计算速度,我们可以使用这个小学方程:

  1. 速度=距离/时间

在我们的情况下,时间将始终为50ms,距离将是编码器的位置在上次读取时改变的量。

显示

我们使用的是工具包附带的显示器SD1306,通过SPI控制。被用于不同的菜单和诊断信息,使控制汽车更方便快捷。为了控制显示,我们使用u8g2库,它很容易移植到其他设备,这一系列控制是为arduino制作的,为了简单地有效地处理基本文本和位图。 

通讯

在比赛的过程中,给汽车附加任何电脑或无线设备都是违反规则的。所以竞赛时,就用上面介绍的OLED显示器配合按钮来控制汽车。但是当我们不是在比赛,而是在调试的时候,那么我们要用两个东西来调试。

首先是通过USB连接器。它使我们能够对微控制器进行编程,也使我们能够与设备进行稳定,快速的通信。其次是蓝牙。当我们在赛道上测试赛车时我们使用蓝牙,因为USB连接线太短,我们不能保持USB电缆连接状态,很容易绞入车轮或者失联。

单MCU架构还是多MCU架构?

这个问题可能有点困难,并且取决于很多东西和正确的架构设计。但首先我们应该知道MCU到底是什么。MCU代表微控制器单元。它包含可以运行代码的芯片,处理数据等,它也包含不同功能的引脚(大多数知道的功能是输入和输出)。让我们看看每种可能性的好处。

单MCU:

  • 价格低
  • 无需等待数据延迟和CPU通信开销
  • 小空间

多MCU:

  • 更高的处理能力
  • 更多 I/O

我已经为我的项目选择了单MCU架构,我来解释为什么。

第一个原因很简单。我用我手中现成的东西。我有能力使用KL25Z微控制器或K64F微控制器。(我认为这些都是比赛中最常见的MCU,因此本小指南也可以帮助其他人选择正确的MCU.)我有性能简洁的K64F,也能在任何元件失灵的条件下有更多的MCU替换选项。而且K64F比KL25Z更强大,具有2.5倍以上的CPU速度,8倍以上的闪存,16倍以上的RAM。此外,K64F还包含用于除法,浮点计算和更快的中断执行的指令,因此CPU实际上比2.5倍更快。K64F还有一些其他的优势,比如编码器的硬件支持,这将在KL25Z占用大量的CPU。 

既然KL25Z有了这样的处理能力,没必要同时使用两个。但让我们更详细地研究一下,考虑一下我们所做的以及其他一些事情。 

这里是双MCU架构的示例图像:

理想很丰满,现实很骨感,当现实中实际应用的时候,问题就会出现。

MCU1的目标是从加速度计,陀螺仪和摄像机中获取数据,从而进行所有所需的计算,以知道每台电机的正确速度、伺服机构的位置等。MCU2还可以获得激光,因此在需要时MCU2可以设置汽车在什么轨道上行驶,并在此基础上忽略激光,当它看到障碍物或躲闪障碍物时将速度设置为零。
 

MCU2将使用所有这些数据来设置正确的伺服位置,从编码器获得数据,并根据MCU1的期望速度设置电机功率。它还可以控制步进器移动摄像机。此外,它将包括所有的通信蓝牙和创建用户界面显示和按钮。

所有的东西看起来都很好地分开了,处理也应该很好地分开,但是让我们看看硬件的预见性,看看它是如何工作的。

这两个器件之间最快的通信方式是通过SPI总线。K64F上SPI总线频率的硬件限制为30 Mhz,KL25Z上为12 Mhz。这意味着3.75Mbps或1.5Mbps。这些速度并不差,但这些处理器以480Mbps或192Mbps的速度与寄存器通信,因此差异是巨大的。

假设MCU1决定改变电机的速度。它需要检查SPI总线是否空闲,或者等待它空闲,这会消耗CPU。然后它将以较慢的速度通过SPI总线传输+它需要包含控制字节,因此我们知道这些值的含义。之后,MCU2将需要检查它是否收到任何东西,它的意思是什么,然后最后设置电机速度。主要的好处是在二级MCU上计算PID,因此卸载MCU1用于相机数据处理,但它只需要大约5个FPU的计算,这是K64F本地支持的,他们真的很快。电机PWM的设置是在480Mbps的速率下,两个4字节写入存储器的问题。PWM由硬件产生,因此它不给CPU带来任何负载。

如果MCU1本身有电机和编码器,那么它可以自己进行FPU计算,然后对存储器进行简单的写操作来设置电机的速度,这将花费大约相同的处理器时间将这些数据传送到MCU2。

当我们谈到电机时,我们可以关注一下编码器。它实际上没有使用任何CPU,因为K64F有硬件支持,所以无论它是多快的旋转,它将仍然使用没有CPU。但是如果你打算用KL25Z构建它,那么它可能会很有用,因为它没有对编码器的硬件支持。由于没有这种支持,它必须使用中断来添加这种支持,因此这意味着至少30个中断周期,每个磁盘孔4个中断,每个磁盘10个孔,每秒100个轮子旋转。当然,这些只是样本数字,它们肯定可以更高或更低。但是他们估计4800万个循环中有12万个循环是可行的,这些数字听起来并不是很高,但是把它们放到MCU2中可能会很有趣。但这不是我的情况,因为我使用的是K64F。

伺服系统和马达是一样的。它们也依赖于PWM,PWM只需两次写入存储器,而且卸载PID计算的可用性也有所降低,因此这实际上更加无用。

让我们看看显示和按钮。起初,将它们卸载到次级MCU中听起来也很酷,因为这将再次为处理相机数据提供更大的处理能力。但遗憾的是,这又一次变得无用了,因为我们不需要那些按钮和显示器,当汽车行驶时,也就是处理相机数据的时刻。所以它总是运行,要么是带有按钮的dipslay,要么是带有传感器的摄像头。当然,当我们想调试我们的汽车时,我们希望看到显示器显示结果从摄像头,所以两者都应该运行,但在这种情况下,我们并不关心下降到20fps,因为它真的不重要,我们的眼睛,并会给我们足够好的印象。

接下来就是蓝牙了。如果它仅用于读取电机速度,编码器数据和伺服数据,那么它是有用的,因为所有这些数据都可以缓存在MCU2中,并且只需发送到另一个蓝牙设备,而无需与MCU1通话。但是如果你决定通过蓝牙发送其他东西,比如原始的相机数据,那么它就没有多大用处了,因为MCU2通常无法访问相机数据,需要向MCU1请求。这需要通过SPI将数据发送到MCU2,但如果蓝牙模块将直接挂接到MCU1并通过UART将数据发送到蓝牙模块,则没有太大的区别。

最后是步进电机。它的卸载可以是有用的,如果我们想移动它的位置更大的ammount,如果我们正在驾驶。但这也是需要的情况下,我们不会关心什么是当前的位置,电机,这是不经常发生的,当我们开车,至少不是在我们的情况。否则,一步移动电机只需在内存中写两次,我们就能准确地知道电机在哪里,没有延迟。

所以我们现在能够看到整体的结果。有点偏负到中性,但对KL25Z来说有点偏正。但是翻倍也是增加成本,而使用单人K64F就更好了。同时需要同时对两个芯片进行编程和调试,这就增加了测试的复杂度。所以现在你可以明白为什么我选择单人K64F了。

但是正如我所说的,双MCU在更多的I/O方面具有冒险作用,而我遇到了单MCU构建的问题。K64F的I/O引脚比KL25Z少,这在一定程度上也造成了这个问题,所以让我们来看看我的架构。

我能够连接所有组件,这需要高速通信直接到MCU。在MCU上,我还通过I2C连接了16口I/O扩展器,与陀螺仪和加速度计共享,这样就不需要在MCU中使用更多的引脚。它只连接一些元件,这些元件对检测率要求很低,如开关和按钮,而且它们也不需要在汽车行驶中进行检测,因此I2C总线将完全免费用于陀螺仪和加速度计。还有蓝牙模块的连接状态和使能引脚,只需在汽车启动时使用一次模块配置。在所有这些之后,我留下了接下来的5个未使用的引脚,我焊接头,并可能使用他们的外部元件。

3D部件生产过程

在现代,3D打印机已经成为爱好者非常有用的工具。如果你想做一些家庭项目,几乎有必要拥有一个。但当你有一台3D打印机时,打印是制造过程中最简单的部分,因为在你开始打印之前,你必须先制作一个数字3D模型。我家里有两台3D打印机,显然是市场上最便宜的,因为我是一个收入很低的学生。但这只是意味着,即使用很少的钱,你也能做出大东西。如果你想设计自己的零件并打印它们,你需要以下三件东西:

  • 3D 建模软件
  • 3D 切片机
  • 3D 打印机

你应该使用哪种3D建模软件取决于你想要制作什么东西。例如,如果你想要制作一个雕像,你可能不应该使用工程软件,而应该使用一个有适合你将要执行的操作的工具的软件。我最喜欢的创建数字模型的程序是FreeCAD。它功能强大,源代码开放,易于使用。这部分制作的最终产品是一个。stl文件,其中包含您的数字模型。实际上,如果您想复制这个项目并调整您的恩智浦汽车,您可以跳过这一部分,因为这些。stl模型实际上是我制作的,所以您可以下载它们。制造过程中的下一个部分是3D切片器,在该切片器中,您的数字模型可以被切片成薄层。如果你想找一个专业的切片机与吨的选择,我可以推荐你库拉。您可以在此指定打印过程并选择最终产品的属性。选择的打印速度,温度和其他“细节”是很重要的,如果你搞砸了它,就会毁了你的结果。有时候它只需要耐心和从错误中学习。当你的模型准备好制作,切片器将生成一个G代码文件,这基本上是一个有限程序为你的3D打印机。

定制部件装配说明

  • 在开始之前,请确保您的NXP汽车(Alamak汽车套件)的型号完全相同,否则这些部件将无法安装
  • 下载 stl 文件
  • 打开你的3D模型切片器,选择合适的打印属性
  • 打印出来(材质不重要,但是我用了PLA和ABS)
  • 正确组装。让这些图片指引你走向成功。

你可以在以下链接检查所有的型号 https://www.thingiverse.com/thing:4319473

 

主板

当我们计划好所有其他硬件时,我们意识到我们遇到了问题。默认主板提供的汽车套件有不同的引脚排列比我们需要的,并没有支持我们所有的硬件。此外,我们希望最大的计算速度为这张卡,所以我们决定使用K64F而不是KL25Z控制器。而且这些控制器有不同的引脚排列,所以我们连我们汽车的大脑都不能装进主板,所以我们开始设计我们的新主板。

我使用 EasyEda ,一个非常简单的工具,用于制作原理图,并把它们变成PCB布局。

我们增加了旧板上的一些重要部件,如6V小型电源,为伺服电机供电。或者按钮,但是我们对它们进行了修改,使它们不像单个模拟元件那样工作,这就有点难以获得按压,所以我们不能使用经典的去抖,还有一个问题是可以按下多个键,这使得提取真实值有点困难,而且这将需要使用我们用于相机的A/D转换器,所以它可以降低最大fps。

然后我们添加了一些新的部分,例如外部I2C以便于添加I2C模块,陀螺仪以便于更好地计算汽车运动,开关和适当的显示器安装。因为我们已经添加了所有这些东西,并且我们使用了更少引脚的控制器,我们还需要添加I/O扩展器来增加可用I/O端口的数量。

PCB的布局设计将适合现有的电机板和其他部分,如SD1306显示器和K64F控制器。

这是5件新的作品,它们刚刚从中国运来:

残酷的现实

遗憾的是,我们订购的板子和组件太晚了。当我们准备焊接的时候,学校因为COVID-19(新冠病毒疫情)关闭了,而我家里没有正确的SMD焊接设备。所以为了临时解决这个问题,我用了很多电线和胶带,然后把所有东西都放在一起。你可以自己看看我的结果。

但是这个状态应该足够测试了。我们有马达,伺服,步进器,编码器和K64F工作,但这将是很好的一切在更稳定的形式。现在,连接有时会出现问题,所以伺服系统有时会随机移动,无一例外的情况会发生。我们还缺少一些功能,如陀螺仪和按钮。

与残酷的现实抗争

我已经等了一段时间,如果学校终于开学,我将能够继续我的工作。但似乎还要关闭一段时间。所以我决定去我的学校,挑选一些我放在我的储物柜里的零件,并决定试着把它全部焊接在我自己的地方。我没有空气焊锡站,液体焊剂,也没有镊子。但我设法焊接了所有的部件,并主要检查他们是否工作。

这是焊接开始时的图像。但是已经有很多工作完成了。你可能一眼就能看到按钮,但如果你看得更详细,你会看到一些电容焊接,如果你看得更详细,你会看到电阻。它们的尺寸是0402 (0.4x0.2mm),看起来就像一个小黑点。
 
你还可以看到没有元件的黄色滴物。这是我对缺失的液体焊剂的解。在我焊接任何元件之前,我加热我的烙铁到200度。这个温度是理想的,因为它使焊剂变成液体,但不会蒸发或冒烟。我做的第一件事是我插入我的烙铁到固态焊剂,然后把它移动到上面的板。在这一刻,应该有足够的流量在尖端。然后我用尖端接触板,把焊剂转到板上。
 

这种添加助焊剂的工艺是必要的,因为如果您试图在焊接温度(我使用350°)下获得助焊剂,那么助焊剂不会停留在尖端,因此无法到达电路板和元件上。

当焊剂到达所有要焊接当前组件的点后,您可以将烙铁调至焊接温度。待烙铁加热后,将元件放置在电路板上。如果您要焊接电容或电阻,则将元件放置在助焊剂旁边,然后将烙铁的尖端放置在元件的侧面,使其最大可能接触到元件,并将元件推向助焊剂。这将把热量通过组件传递到熔剂上,这样熔剂就会熔化,组件就会在熔剂内。极短时间后,助焊剂将激活并清除电路板和元件焊盘。仍然用这种方式握住烙铁,并用锡接触两个元件焊盘。在这之后,应该有你的部件与熔化的锡在它的位置,然后你应该移除烙铁。

这需要一些练习,因为元件粘在烙铁上,或者在焊盘上旋转,甚至站立(它们旋转90度,就像它们在单个焊盘上指向和站立的方式)。这通常是由于只有一个焊盘上有锡造成的。此外,您还应记住,连接到接地层的焊盘能够吸收大量热量。所以你应该加热他们更长的时间,甚至像10秒,这样组件正确地粘在一起。你应该能看到锡球的小变化,因为它正确地贴在地平面上(这种变化真的很快,需要半秒)。

如果你能使用液体焊剂或空气焊锡站,那么一定要使用它。它比这种方式容易得多。

这里我焊接了所有的按钮和开关,还有大部分的电容和电阻。

我现在将解释为什么我先焊接像开关和按钮这样的较大部件,然后再焊接较小的部件,即使先焊接小部件是更好的做法。

原因很简单。我希望我的董事会测试尽可能好,并正在工作的第一次尝试。我主要是在焊接I/O扩展器之前焊接那些组件,因为这会使测试更加困难。我通过触摸I/O扩展器上正确的焊盘(属于被测试按钮或开关)和其他3.3V或接地来测试这些组件。当第二个焊盘接地时,电阻率应为10 kΩ。如果电阻率为零,则说明接地电阻短路;如果电阻率为无穷大,则说明某部分连接不良。当第二个焊盘为3.3 V时,电阻率应为无穷大,但按下按钮时,电阻率应变为1 kΩ。如果没有发生这种情况,那么您的电路中又出现了一些问题。开关的测试方式与按钮相同。

你还应该检查所有的电容。大部分都在某个电源轨上。电池有四个电源轨,分别为6V,5V和3.3V。当你焊接电容器,然后检查它属于什么轨道,并测量那里的容量。应随焊接电容的大小而增大。大多数情况下,所有电容都是100nF,因此应该增加到这个值。如果这是焊接到轨上的第一个电容,则容量应为100nF。当你测量了钢轨的能力后,你应该得到正确的值。如果万用表不能测量容量,那就意味着你短路了。如果它测量的是不变的容量,那么就意味着电容和板卡之间存在不良的连接。

此外,我建议在电阻或任何其他元件焊接到轨之前先焊接电容,因为这样您将无法测量之后的容量。当你在测量电阻时,那么你应该在它们连接的地方找到两个正确的焊盘,并检查是否存在短路或连接不良。

第二次与残酷的现实抗争

现在我可以告诉你:在有很多支腿的小部件上涂上尽可能少的锡。

如果你会拿很多锡放在腿上,那么它们就会连接在一起,你将有一个很大的问题,去除它。当你尝试焊接像这样的组件,然后采取尽可能多的少量锡,并尝试焊接它。如果量不够,就慢慢加锡。当我焊接它的时候,我以为我只是添加了理想的数量,但它太多了。第二个问题是我试了很长一段时间去锡,直到一些焊盘从板上脱出。我可以再次说,如果这发生在你,然后老学校锡提取器工作刚刚好,并尝试芬兰你的去除尽快。

我也会给出几个字,我是如何修复那些与电路板断开的引脚的。我发现最好检查工具是强光灯,你应该把PCB放在灯前面,它会透过,这样你就能很好地看到连接的痕迹或缺失的痕迹。我很幸运,所有的痕迹仍然是连接的,它们只是从原来的位置移动,触摸其他垫。解决这个问题的最简单方法是将烙铁加热到更高的温度,然后移动轨道,这样它们就不会相互接触,甚至可以将它们从电路板上拿起来放在组件的腿上。

我撒了点谎。有一条线从板上断开了。它是从I/O扩展器的中断引脚跟踪的。我原计划通过将中断引脚连接到板上正确位置来修复它,但我决定不修复此引脚,并保持断开状态,因为我还不需要此IC的任何中断。但是将来如果我决定将某些东西连接到GPIO报头,需要迭代的话,修复它可能是很重要的。

在焊接几乎所有元件后,您可以看到以下结果:

只有缺少连接K64F的头,因为这是我需要做的最后一件事,也是最高的组件。但在我焊接它之前,我通过底部的连接器将这块板连接到电机板。这允许我检查电池和6V轨是否工作。当两个蓝色LED亮起时,它被检测得非常快。如果发光二极管点亮,没有元件爆炸,可能一切都正常工作。现在也是带螺丝刀和万用表的好时机。将万用表连接到6V轨并测量电压。它可能会有点偏离6V。现在拿螺丝刀,用板上的蓝色电位器慢慢转动,直到你把6V轨的电压调到6V。

现在焊接那些剩余的头部和连接K64F。现在是时候希望一切都焊接正确,你可以测试出来。上传一些显示的程序,看看它是否亮起来,然后伺服,相机等。我希望它都能为你工作。对我来说,幸运的是,第一次尝试就成功了。

好用吗?

我们已经建好了自己的跑道,我们的车正在开动!它还不是完美的,但它是可行的,我们将继续努力,直到比赛来临,赢得它。

未来的计划

我们可能还将使用超声波传感器与激光一号一起获得更精确的结果,当搜索轨道上的立方体。此外,我们需要完成我们的代码,使我们的赛车正常运行,但我们有很多时间,因为新冠肺炎推迟了比赛,并没有新的日期。

如何制作自己的测试轨道

如果你有兴趣像我一样做你自己的轨道,你将需要一大卷纸。我不知道具体大小,但你可以看到它很大。你必须把那些巨大的纸放在一起,然后用透明胶带,这样它才能粘在地上。你应该用铅笔画出你的轨迹,因为如果你画错了,很容易修复这个问题。当你确定你已经正确地设计了你的轨道,然后使用黑色的电子胶带使所有这些边缘。

这个BOM包含了所有需要的部件,除了超高的头部,因为BOM是从原理图自动生成的,并且没有正确大小的超高头部,所以你需要手动将它们分成更小的部分。链接如下:

https://lcsc.com/product-detail/Pin-Header-Female-Header_BOOMELE-Boom-Precision-Elec-C51353_C51353.html

BOM

新的主板

 

CAD, 外壳和定制部件

包括以下文件 (全部下载):

Back shield.stl 2020-09-04 11:59 36K  
Bumper.stl 2020-09-04 11:59 51K  
Camera case holder.stl 2020-09-04 11:59 24K  
Camera large gear.stl 2020-09-04 11:59 801K  
Camera mount part 1.stl 2020-09-04 11:59 38K  
Camera mount part 2.stl 2020-09-04 11:59 37K  
Camera small gear.stl 2020-09-04 11:59 79K  
Camera stepper endstop platform.stl 2020-09-04 11:59 13K  
Case for camera.stl 2020-09-04 11:59 42K  
Encoder enclosure.stl 2020-09-04 11:59 67K  
Front block.stl 2020-09-04 11:59 98K  
In gear rivet.stl 2020-09-04 11:59 15K  
New main board - gerber board outline.GKO 2020-09-04 11:59 1.2K  
New main board - gerber bottom layer.GBL 2020-09-04 11:59 392K  
New main board - gerber bottom silk layer.GBO 2020-09-04 11:59 129K  
New main board - gerber bottom solder mask layer.GBS 2020-09-04 11:59 6.0K  
New main board - gerber drill NPTH.DRL 2020-09-04 11:59 445  
New main board - gerber drill PTH.DRL 2020-09-04 11:59 4.3K  
New main board - gerber top layer.GTL 2020-09-04 11:59 629K  
New main board - gerber top paste mask layer.GTP 2020-09-04 11:59 5.4K  
New main board - gerber top silk layer.GTO 2020-09-04 11:59 140K  
New main board - gerber top solder mask layer.GTS 2020-09-04 11:59 28K  
Slope platform.stl 2020-09-04 11:59 37K
 

代码

主要控制代码

这个文件控制servos和马达如何根据所有输入参数工作

mainControlCode.txt

设置和控制外围设备

这段代码提供所有功能从主代码来控制所有外设,获得所有传感器的信息。

 
FaLang translation system by Faboba

登陆


Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/components/com_comprofiler/plugin/user/plug_cbjdownloads/cbjdownloads.php on line 49

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/components/com_comprofiler/plugin/user/plug_cbblogs/cbblogs.php on line 48

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/components/com_comprofiler/plugin/user/plug_cbarticles/cbarticles.php on line 47

Deprecated: Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/cbPluginHandler.php on line 323

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/src/Menu/AbstractMenu.php on line 164

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/LegacyFoundationFunctions.php on line 217

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/LegacyFoundationFunctions.php on line 219

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/LegacyFoundationFunctions.php on line 227

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/LegacyFoundationFunctions.php on line 231

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/LegacyFoundationFunctions.php on line 234

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/LegacyFoundationFunctions.php on line 237

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /var/www/html/libraries/CBLib/CB/Legacy/LegacyFoundationFunctions.php on line 239

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in /var/www/html/plugins/system/falangdriver/falangdriver.php on line 100

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in /var/www/html/plugins/system/falangdriver/falangdriver.php on line 100
mysqli object is already closed (500 Whoops, looks like something went wrong.)

Error

HTTP 500 Whoops, looks like something went wrong.

mysqli object is already closed

Exception

Error

  1. */
  2. public function disconnect()
  3. {
  4. // Close the connection.
  5. if (\is_callable([$this->connection, 'close'])) {
  6. $this->connection->close();
  7. }
  8. parent::disconnect();
  9. }
  1. */
  2. public function disconnect()
  3. {
  4. // Close the connection.
  5. if (\is_callable([$this->connection, 'close'])) {
  6. $this->connection->close();
  7. }
  8. parent::disconnect();
  9. }
  1. *
  2. * @since 2.0.0
  3. */
  4. public function __destruct()
  5. {
  6. $this->disconnect();
  7. }
  8. /**
  9. * Alter database's character set.
  10. *
DatabaseDriver->__destruct()

Stack Trace

Error
Error:
mysqli object is already closed

  at /var/www/html/libraries/vendor/joomla/database/src/Mysqli/MysqliDriver.php:318
  at mysqli->close()
     (/var/www/html/libraries/vendor/joomla/database/src/Mysqli/MysqliDriver.php:318)
  at Joomla\Database\Mysqli\MysqliDriver->disconnect()
     (/var/www/html/libraries/vendor/joomla/database/src/DatabaseDriver.php:496)
  at Joomla\Database\DatabaseDriver->__destruct()                

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in /var/www/html/plugins/system/falangdriver/falangdriver.php on line 100

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in /var/www/html/plugins/system/falangdriver/falangdriver.php on line 100