本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

有趣的项目—移动远程监控摄像机器人

发布时间:2022-09-17
分享到:

有趣的项目—移动远程监控摄像机器人

发布时间:2022-09-17
分享到:

这个有趣但复杂的项目将涵盖从设计构建机器人到 linux (raspberry pi) 中的高级配置,再到构建 Android 应用程序和控制机器人。

之所以说它是个有趣的项目,是因为它比一般的项目更有野心,而且我认为通过在这里理解一些想法甚至复制整个项目也并不能完全复现整个项目,你会有很多东西需要学习。

首先,我们将使用有机玻璃、塑料板、带变速箱的直流电机和各种电子元件来构建机器人。该设备将能够独立移动两个前轮,并且能够使用它的前照灯。然后我们将设置为机器人供电的树莓派,并配置项目并安装各种依赖项。然后我们将构建并安装一个 android 应用程序,并使用它通过摄像头和 wifi 连接远程控制机器人。

所需技术和概念:

  • 开发平台:Arduino、Raspberry pi、Android
  • 电子:H桥,使用晶体管驱动大负载,红外传感器
  • Linux:使用 docker、docker compose、使用 systemctl 配置服务、视频流
  • 编程:Android应用程序、python、arduino语言、串口通信、MQTT

第 1 步:需要的东西

材料:

1.有机玻璃板

2.塑料板(这里也可以用有机玻璃板)

3.胶水

4.轮胎+直流电机带变速箱+支架。小螺母和螺栓,六角金属垫片

6. 2 x 任意方向的车轮轮胎

7.小LED手电筒(会变成头灯)

8. Arduino pro mini 328p

9. 2 x 红外障碍物传感器

10.印刷电路板

11.NPN晶体管(驱动手电筒)

12. L7805CV 5V稳压器

13. L298 H桥

14. Rezistor 220 欧姆

15. 公头 USB 连接器

16. 公微型 USB 连接器

17.各种电线

18. 3 v调节器(用于arduino和树莓派之间的通信)

19. 公母PCB连接器

20.开/关开关

21. XT-60 母 LiPo 连接器

22. 2S 1300 mAh 锂聚合物电池,带 XT-60 连接器

23. 5v电池组

24.树莓派 3

25.树莓派卡

26.树莓派案例

27.树莓派相机

工具:

1. USB 到串行 FTDI 适配器 FT232RL 对 arduino pro mini 进行编程

2. Arduino IDE

3. 钻孔

4.细锯片

5. 螺丝刀

6.烙铁

7. 剪线钳

技能:

1.焊接

2.基本的arduino编程

3.Linux服务配置、包安装

第 2 步:构建机器人平台
我们要建造的机器人将具有以下规格:

- 两个单独的直流电机将在前轮上产生牵引力

- 后轮应该能够向任何方向移动 360 度

- 方向将由前轮的变速控制,因此不需要单独的方向机构,机器人也可以原地旋转

- 顶部会有灯

- 它应该有足够的空间放置电子设备、电池和带摄像头的树莓派外壳

- 需要几厘米的离地间隙来克服小障碍物

注意:不要忘记检查图像以获取重要细节和构建提示。

我们要用有机玻璃或硬塑料制造机器人,我都用过,但你可以选择任何你想要的。

底板上的底板为 18 x 13 厘米,直流发动机将用金属支架螺母和螺栓连接。H 桥将安装在面向地板的板中间。后轮将使用 2 cm 六角形金属垫片(一侧为公头,一侧为母头)安装

H 桥附近需要一个大孔来连接顶部的电子设备。

机器人的顶部由两个“L”形的板组成,一个是 12 x 13 厘米,另一个是 6.5 x 13 厘米。塑料板将粘在一起。这些板将为电子设备提供盖板、安装前照灯的位置和树莓派外壳的支撑。顶部将使用 6 cm 六角形金属垫片从底部连接

第 3 步:构建电子设备

引脚分配(arduino):

  • LED手电筒:D3
  • 左电机:PWM (D5), EN1, EN2(A4, A5)
  • 右电机:PWM(D6)、EN1、EN2(A3、A2)
  • 红外传感器:前(A0),后(A1)
  • 树莓派通信引脚:Tx:D11,Rx:D10

构建PCB,组装
1.在最后一步中,我们已经在机器人的地板侧安装了 H 桥。我们还需要安装两个红外传感器,一个在前面,一个在后面。我们将使用一块小金属板将它们安装在底盘上。金属板将呈“L”形,并有两个孔。我们将使用螺母和螺栓将其安装在底盘上。传感器将位于底盘的中间,一个在前面,一个在后面。

2.接下来是大灯部分,我为此使用了一个 5 伏的 LED 手电筒。我已经剪掉了头灯,只暴露了“头部”部分,并焊接了两根电线为其供电。然后我将头灯粘在机器人顶部的中间,并在头灯附近钻了一个孔,将电缆穿过孔并焊接一个小的母头两线连接器。

3.组装树莓派外壳。您将需要一个树莓派、一个 pi 相机、至少 4 GB 的存储卡、一个 pi 相机连接器。插入已安装最新Raspian的卡,然后将pi相机连接器小心地插入树莓派上,然后将其插入相机,然后关闭外壳。

4.用主要电子元件搭建PCB 。

焊接步骤:

a、切断母 PCB 连接器,微控制器有两个 12 针连接器和两个 5 针连接器,红外传感器有两个 3 针连接器,H 桥有一个六针连接器,树莓派通信有一个针连接器(接地,TX,RX)

b、切断所有连接器后,必须在 PCB 背面焊接

c、焊接 KF301-2P 连接器

d、将 NPN 晶体管和相应的电阻器焊接到它的基极

e、焊接 L7805CV 5V 稳压器

f、将 arduino 上的3.3 伏稳压器焊接到 raspeberry pi TX 线上

g、将公针焊接到 arduino pro mini

h、根据fritzig原理图焊接所有红色(+)、黑色(-)和白色(信号)细线

5. 连接器:

a、建立一个从 5V USB 电池组到树莓派和 arduino 的连接器,您将需要一个 A 型公 USB 连接器、一个公微型 USB 连接器黑色和红色线、热缩管和一个母到母面包板连接器。首先将母对母连接器一分为二,这些部分将进入 arduino 负极和正极公针。A 型 USB 连接器将使用微型 USB 连接器向 arduino 和树莓派供电。检查图像以焊接 USB 提示。

b、将 LiPo 电池的连接器连接到电子板,您将需要一个 XT-60 母 LiPo 连接器、红线和黑线、热缩管和一个能够处理 10 A 的小开关。黑线将直接从XT-60到电子板(KF301-2P插入式螺丝连接器),红线将通过小开关连接

c、使用母 - 公面包板连接器将机器人的两个 IR 传感器连接到 PCB 上相应的母连接器

d、使用公母面包板连接器将 H 桥连接到 PCB 的 6 针母连接器

e.、将电机正极和负极端子连接到 H 桥

F、将 H 桥主电源连接到 PCB 上的 KF301-2P 插入式螺钉连接器

6.在将 arduino 放置到 PCB 之前,使用放大镜和万用表仔细检查所有内容

第 4 步:Arduino 代码
首先我需要回答一个重要的问题:为什么必须存在一个中间 arduino 层而不是直接将 Pi 连接到电子设备?

  • 1.更加模块化,你可以在没有PI的情况下在另一个项目中重复使用arduino机器人
  • 2.为了安全起见,更换 3 美元的 arduino pro mini 比更换 Pi(35 美元)便宜
  • 3. arduino 不会像 pi 那样受到操作系统的干扰,因此为电机实现 PWM 控制会更有效,每秒轮询前后传感器几次
  • 4.如果python脚本中可能发生错误,机器人可能会永远运行,耗尽电池并可能损坏它或着火,如果​​没有监督,在arduino草图中它更可靠,因为它不依赖于操作系统
  • 5.解耦系统更容易调试

好的,所以我已经涵盖了为什么部分,我将稍微解释一下 arduino 草图。它基本上做了两件事:

1.它接收来自串行线的电机和灯光命令并驱动电机或切换灯光

例如:

  • * " M:-25:16; " 表示(-25 左)和(16 功率),它将转换为左电机 17% 和右电机 32%,并且方向向前 * " M:44:19; " 表示(向右 44)和(19 功率)它将转换为:左电机 38%,右电机 5% 和前进方向
  • * “ L:1; ”表示灯亮,“ L:0 ”灯灭

2.它从机器人前后轮询红外传感器,并通过串行线发送有关距离的数据

首先,您需要下载并安装库,主要代码位于 github 存储库中,你可以从本文下方复制粘贴它。

使用 FTDI 适配器将代码上传到 arduino。现在您可以向机器人发出命令以查看它的工作情况,为此只需连接第二条串行线并通过它发送电机或光。一种方法是使用 HC-05 之类的蓝牙模块,并使用蓝牙应用程序将其连接到手机。然后给它像“L:1”这样的串行命令

// source for TextMotorCommandsInterpretter: "https://github.com/danionescu0/arduino/tree/master/libraries/TextMotorCommandsInterpretter" 
#include <SoftwareSerial.h>  
#include <TextMotorCommandsInterpretter.h> 
const char MOTOR_COMMAND = 'M'; 
const char LIGHT_COMMAND = 'L'; 
/** 
* how long the motor command will take effect in ms 
* an incomming motor command will last for maxDurationForMottorCommand 
* if it's not going to be resetted by another motor command 
*/ 
const long maxDurationForMottorCommand = 300; 
// adjust this value to limit robot speed 
const byte maxPwmValue = 230; 
// How long between successive distance transmissions in ms 
const long transmitingInterval = 500; 
const int maxObstacleDetection = 1000; // analog read max detection value 
const int minObstacleDetection = 500; // analog read min detection value 
const byte FLASH_PIN = 3; 
const byte RIGHT_MOTOR_PWM_PIN = 5; 
const byte RIGHT_MOTOR_EN1_PIN = A4; 
const byte RIGHT_MOTOR_EN2_PIN = A5; 
const byte LEFT_MOTOR_PWM_PIN = 6; 
const byte LEFT_MOTOR_EN1_PIN = A3; 
const byte LEFT_MOTOR_EN2_PIN = A2; 
const byte FRONT_DISTANCE_SENSOR = A0; 
const byte BACK_DISTANCE_SENSOR = A1; 
SoftwareSerial masterComm(11, 10); // RX, TX 
TextMotorCommandsInterpretter motorCommandsInterpretter(-50, 50, -50, 50); 
String currentCommand; 
long lastCheckedTime; 
long lastTransmitTime; 
boolean inMotion = false; 
void setup()  

   Serial.begin(9600); 
   masterComm.begin(9600); 
   masterComm.setTimeout(10);   
   pinMode(FLASH_PIN, OUTPUT); 
   pinMode(LEFT_MOTOR_PWM_PIN, OUTPUT); 
   pinMode(LEFT_MOTOR_EN1_PIN, OUTPUT); 
   pinMode(LEFT_MOTOR_EN2_PIN, OUTPUT); 
   pinMode(RIGHT_MOTOR_PWM_PIN, OUTPUT); 
   pinMode(RIGHT_MOTOR_EN1_PIN, OUTPUT); 
   pinMode(RIGHT_MOTOR_EN2_PIN, OUTPUT); 
   lastCheckedTime = millis(); 
   lastTransmitTime = millis(); 

void loop()  

   if (masterComm.available() > 0) {    
       currentCommand = masterComm.readString(); 
       processCommand(); 
   } 
   if (inMotion && millis() - lastCheckedTime > maxDurationForMottorCommand) { 
       stopMotors(); 
   } 
   if (millis() - lastTransmitTime > transmitingInterval) { 
       lastTransmitTime = millis(); 
       masterComm.print(getObstacleData()); 
       Serial.print(analogRead(BACK_DISTANCE_SENSOR));Serial.print("---"); 
       Serial.println(getObstacleData()); 
   } 
   /* FOR DEBUG 
   motorCommandsInterpretter.analizeText("M:-14:40;"); 
   Serial.write("Left==");Serial.println(motorCommandsInterpretter.getPercentLeft()); 
   Serial.write("Right==");Serial.println(motorCommandsInterpretter.getPercentRight());    
   delay(10000);*/ 

String getObstacleData() 

   int frontDistance = analogRead(FRONT_DISTANCE_SENSOR); 
   int backDistace = analogRead(BACK_DISTANCE_SENSOR); 
   frontDistance = map(frontDistance, maxObstacleDetection, minObstacleDetection, 0, 10); 
   backDistace = map(backDistace, maxObstacleDetection, minObstacleDetection, 0, 10); 
   return String("F=" + String(frontDistance) + ":B=" + String(backDistace) + ";"); 

void processCommand()  

   switch (currentCommand.charAt(0)) { 
       case (MOTOR_COMMAND): 
           steerCar(); 
           break; 
       case (LIGHT_COMMAND): 
           toggleLight(currentCommand.charAt(2)); 
           break; 
   } 

void steerCar()  

   motorCommandsInterpretter.analizeText(currentCommand); 
   float percentLeftMotor = motorCommandsInterpretter.getPercentLeft(); 
   float percentRightMotor = motorCommandsInterpretter.getPercentRight(); 
   Serial.write("Left=");Serial.println(percentLeftMotor); 
   Serial.write("Right=");Serial.println(percentRightMotor); 
   setMotorsDirection(motorCommandsInterpretter.getDirection()); 
   analogWrite(LEFT_MOTOR_PWM_PIN, percentLeftMotor * maxPwmValue); 
   analogWrite(RIGHT_MOTOR_PWM_PIN, percentRightMotor * maxPwmValue);     
   inMotion = true; 
   lastCheckedTime = millis(); 

void setMotorsDirection(boolean forward) 

   if (forward) { 
       digitalWrite(LEFT_MOTOR_EN1_PIN, HIGH); 
       digitalWrite(LEFT_MOTOR_EN2_PIN, LOW); 
       digitalWrite(RIGHT_MOTOR_EN1_PIN, HIGH); 
       digitalWrite(RIGHT_MOTOR_EN2_PIN, LOW); 
   } else { 
       digitalWrite(LEFT_MOTOR_EN1_PIN, LOW); 
       digitalWrite(LEFT_MOTOR_EN2_PIN, HIGH); 
       digitalWrite(RIGHT_MOTOR_EN1_PIN, LOW); 
       digitalWrite(RIGHT_MOTOR_EN2_PIN, HIGH); 
   } 

void stopMotors() 

   Serial.println("Stopping motors"); 
   analogWrite(LEFT_MOTOR_PWM_PIN, 0); 
   analogWrite(RIGHT_MOTOR_PWM_PIN, 0); 
   inMotion = false; 

void toggleLight(char command) 

   Serial.println("Toggle light"); 
   if (command == '1') { 
       digitalWrite(FLASH_PIN, HIGH); 
   } else { 
       digitalWrite(FLASH_PIN, LOW); 
   } 
}  
第 5 步:安装和配置 Raspberry Pi 项目和依赖项
它是如何工作的:

a、android 应用程序在 web 视图中显示 uv4l 流。uv4l 进程在树莓派上运行,从摄像头捕获视频输入并将其流式传输。这是一个很棒的工具,具有许多功能

b、使用 android 应用程序中的控件将灯和引擎命令发送到 MQTT 服务器

c、树莓派上 docker 容器内的 python 服务器侦听 MQTT 命令并使用串行接口将它们传递给 arduino。arduino 板控制电机和灯。

d、arduino 感知机器人前后的距离,并通过串行接口将数据发送到 python 服务器,python 将它们转发到 MQTT,它们被 android 接口拾取并显示给用户

首先需要在树莓派上完全安装和配置 Raspbian,并且相机需要进行心理连接和配置。此外,所有配置都将使用 ssh 完成,因此配置它是一件便利的功能。

如果您希望在 wifi 外部使用 android 应用程序控制您的机器人,您应该考虑在您的 wifi 路由器上进行端口转发,否则您将被限制在 wifi 内使用您的本地 IP 地址。

要在树莓派上找到您的本地 IP 地址,请使用“ifconfig”:

ifconfig 
......... 
eth0       
Link encap:Ethernet  HWaddr b8:27:eb:16:e7:ff   
         inet6 addr: fe80::ff00:f22f:9258:b92b/64 Scope:Link 
         UP BROADCAST MULTICAST  MTU:1500  Metric:1 
         RX packets:0 errors:0 dropped:0 overruns:0 frame:0 
         TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 
         collisions:0 txqueuelen:1000  
         RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B) 
........ 
wlan0     Link encap:Ethernet  HWaddr 00:c1:41:00:10:f6   
         inet addr:192.168.0.102  Bcast:192.168.0.255  Mask:255.255.255.0 
         inet6 addr: fe80::e1f4:5112:9cb2:3839/64 Scope:Link 
         UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1 
         RX packets:1678396 errors:0 dropped:0 overruns:0 frame:0 
         TX packets:381859 errors:0 dropped:0 overruns:0 carrier:0 
         collisions:0 txqueuelen:1000  
         RX bytes:259428527 (247.4 MiB)  TX bytes:187573084 (178.8 MiB) 
.....  

在我们的例子中是“192.168.0.102”

要转发的端口(默认)是:9090 用于 uv4l,1883 用于 mosquitto。如果这些端口被 Internet 提供商防火墙或其他端口禁止,您可以将此端口转发到相同的输出端口。

端口转发在每个路由器上都有不同的做法,这里有一些教程:this,您也可以尝试在 google 上搜索“ port forwarding your_router_model ”以查看更多相关结果。

文件夹位置很重要,因为在 docker-compose.yml 中,该位置被硬编码为:/home/pi/robot-camera-platform:/root/debug 如果您需要更改位置,请更改 docker-compose 中的值也

git clone  https://github.com/danionescu0/robot-camera-platform.git 

安装 Uv4l 流媒体:

chmod +x uv4l/install.sh 
chmod +x uv4l/start.sh 
sh ./uv4l/install.sh  

第 6 步:配置和构建 Android 应用程序
我们已经完成了大部分工作,在这一步中,我们将安装 android 应用程序。这些都是先决条件:

1.克隆github项目:

git clone  https://github.com/danionescu0/android-robot-camera  

接下来的步骤涉及设置您的环境,我将列举它们并提供指向专门教程的链接,以防您不知道如何操作。

2.在您的安卓手机上启用开发者选项。您可以在此处了解更多信息:https://developer.android.com/studio/debug/dev-opt...

3.下载并安装Android studio:https://developer.android.com/studio/index.html?ut...这个https://www.javaworld.com/article/3095406/android/...

4.导入项目:https://developer.android.com/studio/intro/migrate...

现在我们要配置流和 MQTT 凭据:

5.编辑./app/src/main/values/passwords.xml并配置 MQTT 和流式传输

MQTT 主机应该类似于:1883

流媒体主机应该是这样的:

6.上传并运行应用程序

第 7 步:使用机器人和调试

使用应用程序
该应用程序只有一个主屏幕,在屏幕左侧显示流媒体图像,在右侧有控制界面。主控是方向盘,按您希望机器人移动的方向触摸方向盘。方向盘下方有一个大灯按钮,轻触可切换灯光。

在右上角有一个文本,如:“- Batt Connected”。

  • *第一个破折号表示没有障碍物,如果机器人前面或后面有障碍物,它将用一个指向前面或后面的小箭头发出信号。
  • * “Batt”状态尚未实现。
  • * "Connected" 表示 MQTT 服务器已连接,因此机器人可以使用,其他可能的值为 "Disconnected"

可以在多个层上进行调试:
1.在arduino层

- 将 FTDI 适配器连接到笔记本电脑的第二条串行线(RX 连接到引脚 11,TX 连接到引脚 10)并发出电机命令和灯光命令以查看机器人是否响应这些命令

- 仔细检查连接,如果电机向后移动,则将两条电机线反向,如果一个电机向后移动,则将其线反向

- 检查 arduino 是否正确连接到 H 桥

2. 在树莓派层上

- 检查 docker 是否正在运行两个容器(mosquitto 和 python 服务器)

pi@raspberrypi:~ $ docker ps 
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                    NAMES 
473a56da2230        dockercontainer_python-server   "python /root/debu..."   9 months ago        Up 4 hours                                   dockercontainer_python-server_1 
3e0b1933d310        robot-camera-mosquitto          "/usr/bin/entry.sh..."   9 months ago        Up 4 hours          0.0.0.0:1883->1883/tcp   dockercontainer_mosquitto_1  

- 检查进程是否在指定端口上运行,您应该寻找 9090和 1883

pi@raspberrypi:~ $ netstat -nltp 
(Not all processes could be identified, non-owned process info 
will not be shown, you would have to be root to see it all.) 
Active Internet connections (only servers) 
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name 
tcp        0      0 0.0.0.0:9090            0.0.0.0:*               LISTEN      -                
tcp        0      0 0.0.0.0:5900            0.0.0.0:*               LISTEN      -                
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      -                
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                
tcp6       0      0 :::1883                 :::*                    LISTEN      -                
tcp6       0      0 :::5900                 :::*                    LISTEN      -                
tcp6       0      0 :::22                   :::*                    LISTEN      - 

- 检查串口是否存在并且它在项目config.py中指定

pi@raspberrypi:~ $ ls -l /dev/ttyS0 
crw-rw---- 1 root dialout 4, 64 Jan 14 19:59 /dev/ttyS0 

- 停止 docker 进程并使用picocom手动连接到串口

然后直接发出电机和灯光指令,看机器人是否响应

sudo systemctl start robot-camera.service 
picocom -b 9600 /dev/ttyS0 
# now issue motor and light commands to test the robot 

- 检查树莓派上的串行控制台是否已停用

- 检查流媒体和 MQTT 是否可以在树莓派外部访问,起诉 mosquitto 客户端(用于 MQTT)并在 Web 浏览器中检查流媒体)

3.Android应用层

- 检查所有必要步骤以使手机进入调试模式

- 确保您已在 passwords.xml 中正确设置密码和端点

- 检查流媒体和 MQTT 是否可以在树莓派外部访问,起诉 mosquitto 客户端(用于 MQTT)并在 Web 浏览器中检查流媒体)

- 查看应用程序的右上角并检查“已连接”

- 使用 android 调试器检查错误

本文中所用到的一些代码

如果您对此项目有任何想法、意见或问题,请在下方留言。

以上内容翻译自网络,原作者:danionescu,如涉及侵权,可联系删除。

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论