1. 项目概述:从“玩具”到“生产力工具”的蜕变
“履带加摄像头小车”,听起来是不是像小时候玩的遥控车?但当你真正动手把它做出来,你会发现它远不止于此。这玩意儿,本质上是一个移动机器人平台,它把“看得见”和“走得稳”这两件事结合在了一起。我最初做这个,就是想解决一个很实际的问题:远程检查一些不方便人进去的地方,比如设备柜的角落、仓库的货架底部,或者院子里某个犄角旮旯。它不像轮式小车那么容易打滑或卡住,也不像固定摄像头那样视野受限。
这个项目,核心就是“移动”与“视觉”的融合。履带负责在各种复杂地形(地毯、门槛、小坡、碎石)上提供稳定的通过性;摄像头则充当了你的“眼睛”,让你能实时看到小车前方的景象。听起来简单,对吧?但当你开始选型、组装、写代码、调试时,你会发现每一个环节都有门道。从底盘电机的扭矩匹配,到摄像头视频流的低延迟传输,再到远程控制的稳定性,每一步都考验着你对硬件和软件的综合理解。
无论你是电子爱好者、机器人初学者,还是想搞点自动化巡检的极客,这个项目都是一个绝佳的起点。它能带你完整地走一遍嵌入式开发、无线通信、图像处理(哪怕是简单的视频传输)的流程。接下来,我就把我从零开始搭建这台小车的全过程,包括踩过的坑、省钱的技巧、以及如何让它更“聪明”一点的经验,毫无保留地分享给你。
2. 核心设计思路与方案选型
做东西最怕一开始思路就错了,后面全是坑。对于这台小车,我的核心设计目标是:稳定可靠、延迟可控、易于扩展。稳定可靠是基础,我可不想它跑着跑着就“趴窝”;延迟可控是关键,毕竟你看着一秒前的画面去遥控,撞墙是分分钟的事;易于扩展则是为未来留余地,比如加个机械臂、装个激光雷达什么的。
2.1 底盘与动力系统:为什么是履带?
首先,为什么选履带而不是轮子?这得看应用场景。轮子适合平坦路面,效率高、速度快。但我们的目标场景往往不那么友好。比如家里有地毯、有门槛,或者想在室外不平整的地面走走。履带的优势就出来了:接地面积大、压强小、越障能力强。一个简单的类比:穿平底鞋走沙地容易陷进去,但穿雪地靴或者滑雪板就不容易,履带就是这个原理。
市面上有现成的履带底盘套件卖,从几十块的玩具级到上千块的工业级都有。我的建议是,别买最便宜的玩具底盘。那种底盘用的通常是130电机(玩具四驱车那种),扭矩小,塑料齿轮易打滑,履带也是光面塑料,抓地力差。我一开始图便宜买了一个,结果连个2厘米高的门槛都上不去,电机还因为负载过大发热严重。
我最终选择的是一个基于N20减速电机的金属履带底盘。N20电机体积小,但搭配减速箱后扭矩可观。金属履带内侧有橡胶齿,与驱动轮咬合紧密,不易打滑。底盘也自带金属结构,承重和耐用性都好很多。这套底盘自重约500克,载重能力标称2公斤,对于搭载树莓派、摄像头和一些传感器来说绰绰有余。
注意:选购底盘时,一定要关注电机的额定电压和减速比。常见的是6V或12V。减速比决定了速度和扭矩,比如1:100的减速比,转速慢但劲大,适合爬坡;1:30的则速度快些,但扭矩小。对于履带小车,建议选择减速比在1:90到1:150之间的,以保证足够的牵引力。
2.2 控制核心:树莓派 vs 单片机
这是第二个关键决策:用什么来做小车的大脑?主流选择有两个:树莓派(或类似开发板)和单片机(如STM32、Arduino)。
- 单片机方案:优点是实时性强、功耗低、编程相对简单(对于控制电机而言)。但缺点也很明显:处理视频流能力非常弱。你需要额外为摄像头搭配一个视频传输模块(比如Wi-Fi图传),这样系统就变成了“单片机控制运动 + 独立图传”,整合度低,延迟和同步可能成问题。
- 树莓派方案:优点是“全能”。它本身就是一个微型电脑,运行Linux系统,有强大的处理能力和丰富的接口(USB、CSI、GPIO)。你可以用Python等高级语言轻松编程,用一个树莓派同时完成电机控制、摄像头图像采集、视频编码、网络传输所有任务。系统整合度高,软件生态丰富,后期想加AI识别(比如用OpenCV识别人脸或物体)也非常方便。
我毫不犹豫地选择了树莓派4B作为主控。虽然它比单片机贵,功耗也高一些,但它带来的开发便利性和功能扩展潜力是巨大的。你完全可以把这辆小车看作一个“会跑的微型电脑”。
2.3 视觉系统:摄像头选型与视频流
摄像头是项目的“眼睛”。树莓派官方有CSI接口的摄像头模块,也有USB摄像头可选。
- CSI摄像头:直接通过排线连接到树莓派的CSI接口。优点是延迟极低、占用CPU资源少,因为图像数据直接通过芯片级总线传输。官方摄像头模块画质不错,社区支持也好。
- USB摄像头:即插即用,选择多,有些还带自动对焦、广角。缺点是延迟相对CSI摄像头略高,且占用USB总线带宽。
对于实时遥控小车来说,低延迟比高画质更重要。你肯定不希望看到卡顿的马赛克画面。因此,我推荐使用树莓派官方CSI摄像头模块。它的分辨率支持到1080p,对于小车视野完全够用,关键是延迟可以控制在100毫秒以内,这对于遥控体验是质的提升。
视频流传输,我选择了MJPG-Streamer这个开源方案。它轻量、高效,可以直接从摄像头获取数据,压缩成MJPEG流,通过HTTP协议推出来。你在电脑或手机浏览器上输入树莓派的IP地址,就能看到一个实时视频页面。相比更复杂的RTSP或HLS方案,MJPG-Streamer配置简单,延迟表现更好。
2.4 动力驱动与电源管理
树莓派的GPIO引脚不能直接驱动电机,因为电流太小。我们需要一个电机驱动板作为“中介”。最常用的是L298N或TB6612FNG驱动模块。我选用的是TB6612FNG,因为它效率更高、发热更小,支持双路电机控制,正好驱动左右两条履带,实现差速转向(像坦克一样)。
电源是另一个容易忽视但至关重要的部分。整个系统需要两种电压:
- 树莓派供电:需要稳定的5V/3A。我用了一个大容量18650锂电池组(两串或三串),搭配一个高效的DC-DC降压模块,将电池电压(如7.4V或11.1V)稳定降到5V给树莓派供电。
- 电机供电:电机驱动板需要另一个电源,电压要与电机额定电压匹配(如6V或12V)。这里我用了独立的电池组为电机供电。非常重要:电机和树莓派的电源一定要隔离!因为电机启动和急停时会产生巨大的电流波动和反向电动势,如果共用电源,会严重干扰树莓派,导致其重启或损坏。我的方案是使用两组电池,或者使用一个带有独立输出的电池管理板。
3. 硬件组装与电路连接详解
思路清晰了,就可以动手组装了。这个过程就像搭积木,但每一步的牢固和正确都关系到后续的稳定。
3.1 材料清单与工具准备
这里是我最终使用的核心部件清单,你可以根据预算调整:
- 主控:树莓派4B 2GB版本(性价比之选)
- 摄像头:树莓派官方CSI摄像头模块 V2
- 底盘:N20金属减速电机履带底盘(6V,减速比1:120)
- 电机驱动:TB6612FNG双路电机驱动模块
- 电源:
- 树莓派供电:2节18650电池盒(串联,7.4V) + 5V/3A DC-DC降压模块
- 电机供电:另1组2节18650电池盒(串联,7.4V)
- 其他:面包板、杜邦线(公对公、公对母)、开关、扎带、螺丝包、移动电源外壳(可选,用于封装电池)。
工具需要螺丝刀、电烙铁、焊锡、热熔胶枪、万用表。万用表在调试阶段非常有用。
3.2 机械结构组装
首先,按照说明书把履带底盘组装好。通常包括安装电机、连接履带、固定上层板。注意拧紧螺丝,但也不要过度用力导致塑料件开裂。安装好后,用手转动履带,检查是否顺畅,有无卡顿。可以将底盘拿起来,通电测试一下左右电机是否都能正常正反转。
接下来,规划各模块在底盘上层板的布局。我的原则是:重心低、走线整齐、散热好。
- 将树莓派放在中心靠后位置,用铜柱垫高,便于散热。
- 电机驱动板放在树莓派旁边,靠近底盘边缘,方便连接电机线。
- 电池组因为最重,放在最底层或底盘前部/后部,以降低重心,防止小车翻车。
- DC-DC降压模块固定在电池组附近。
- 所有模块都用螺丝或尼龙扎带固定牢固,避免行驶中震动导致松动或短路。
3.3 电路连接实战
连接电路是硬件部分的核心,务必仔细。下图是接线示意图,后面有详细说明:
(此处应为接线示意图,用文字描述替代:)
[电机电源正极] ---> TB6612FNG的 VM引脚 [电机电源负极] ---> TB6612FNG的 GND引脚 [树莓派5V输出] ---> TB6612FNG的 VCC引脚 (给驱动板逻辑部分供电) [树莓派GND] ---> TB6612FNG的 GND引脚 (共地!) TB6612FNG的 AIN1, AIN2, BIN1, BIN2 ---> 树莓派GPIO (例如GPIO17, 18, 22, 23) TB6612FNG的 PWMA, PWMB ---> 树莓派PWM引脚 (例如GPIO12, 13) TB6612FNG的 A01, A02 ---> 左侧电机两根线 TB6612FNG的 B01, B02 ---> 右侧电机两根线 [电池组正极] ---> DC-DC降压模块输入+ [电池组负极] ---> DC-DC降压模块输入- 及 树莓派电源地 [DC-DC输出5V+] ---> 树莓派5V引脚 (注意不是USB口) [DC-DC输出5V-] ---> 树莓派GND引脚详细步骤与要点:
电源隔离与共地:这是最关键的一步。我使用两组独立的18650电池。一组接DC-DC降压模块给树莓派供电(切记:从树莓派的5V和GND引脚取电,不要用Micro USB口,否则会绕过保护电路)。另一组直接给TB6612FNG的VM(电机电源)引脚供电。然后,必须用一根杜邦线,将树莓派的GND与TB6612FNG的GND连接起来,这叫“共地”。没有共地,控制信号就无法形成回路,电机驱动板无法理解树莓派的指令。
控制信号连接:
- TB6612FNG的AIN1/AIN2控制左电机方向,BIN1/BIN2控制右电机方向。将它们连接到树莓派的任意GPIO口(如17, 18, 22, 23)。
- PWMA和PWMB是PWM信号引脚,控制电机速度。连接到树莓派支持硬件PWM的引脚(如GPIO12, 13)。软件PWM也可以,但可能不够平滑。
- STBY(待机)引脚接高电平(可以接到树莓派的3.3V或5V),让驱动板处于工作状态。
电机连接:将底盘左电机的两根线接到驱动板的A01和A02,右电机接到B01和B02。如果发现电机转向与预期相反,只需将这两根线对调即可。
摄像头连接:找到树莓派上那个窄长的CSI接口,抬起卡扣,将摄像头排线金属面朝向网口方向插入,然后按下卡扣锁紧。非常脆弱,动作要轻。
通电前检查:务必用万用表通断档检查所有电源线(特别是电池正负极)没有短路。确认树莓派5V输入极性正确。一切无误后,先单独给树莓派上电,看能否正常启动(观察指示灯)。然后再连接电机电源。
4. 软件环境搭建与核心代码实现
硬件是躯体,软件是灵魂。这部分我们将让小车真正“活”起来。
4.1 树莓派系统与基础配置
首先,使用Raspberry Pi Imager工具,将最新的Raspberry Pi OS(Bullseye或Bookworm)烧录到SD卡。建议选择Lite版本(无桌面),以节省资源。烧录时,记得在“设置”(齿轮图标)中预先开启SSH,并设置Wi-Fi和国家,这样开机就能直接联网。
将SD卡插入树莓派,上电。通过路由器管理页面或手机APP找到树莓派的IP地址,使用SSH客户端(如PuTTY)连接。用户名pi,密码是你设置的。
基础优化:
sudo raspi-config:扩展文件系统、设置时区、修改密码。sudo apt update && sudo apt upgrade -y:更新系统。sudo apt install vim git python3-pip:安装常用工具。
4.2 电机控制与PWM调速
我们使用Python的RPi.GPIO库来控制GPIO。首先安装:sudo pip3 install RPi.GPIO
创建一个Python脚本,比如motor_control.py:
import RPi.GPIO as GPIO import time # 引脚定义 (根据你的实际接线修改) AIN1, AIN2 = 17, 18 # 左电机方向 BIN1, BIN2 = 22, 23 # 右电机方向 PWMA, PWMB = 12, 13 # 左、右电机PWM STBY = 5 # 待机引脚 # 初始化 GPIO.setmode(GPIO.BCM) GPIO.setup([AIN1, AIN2, BIN1, BIN2, PWMA, PWMB, STBY], GPIO.OUT) # 初始化PWM,频率设为1000Hz pwm_left = GPIO.PWM(PWMA, 1000) pwm_right = GPIO.PWM(PWMB, 1000) pwm_left.start(0) # 启动,初始占空比0% pwm_right.start(0) # 将STBY置高,使能驱动板 GPIO.output(STBY, GPIO.HIGH) def motor_stop(): """停止所有电机""" GPIO.output(AIN1, GPIO.LOW) GPIO.output(AIN2, GPIO.LOW) GPIO.output(BIN1, GPIO.LOW) GPIO.output(BIN2, GPIO.LOW) pwm_left.ChangeDutyCycle(0) pwm_right.ChangeDutyCycle(0) def left_motor(direction, speed): """控制左电机 direction: 'forward' 或 'backward' speed: 0-100 """ if direction == 'forward': GPIO.output(AIN1, GPIO.HIGH) GPIO.output(AIN2, GPIO.LOW) elif direction == 'backward': GPIO.output(AIN1, GPIO.LOW) GPIO.output(AIN2, GPIO.HIGH) pwm_left.ChangeDutyCycle(speed) def right_motor(direction, speed): """控制右电机""" if direction == 'forward': GPIO.output(BIN1, GPIO.HIGH) GPIO.output(BIN2, GPIO.LOW) elif direction == 'backward': GPIO.output(BIN1, GPIO.LOW) GPIO.output(BIN2, GPIO.HIGH) pwm_right.ChangeDutyCycle(speed) def car_forward(speed=50): left_motor('forward', speed) right_motor('forward', speed) def car_backward(speed=50): left_motor('backward', speed) right_motor('backward', speed) def car_turn_left(speed=50): """原地左转:左轮后退,右轮前进""" left_motor('backward', speed) right_motor('forward', speed) def car_turn_right(speed=50): """原地右转""" left_motor('forward', speed) right_motor('backward', speed) def car_stop(): motor_stop() # 测试代码 if __name__ == '__main__': try: print("前进2秒") car_forward(60) time.sleep(2) car_stop() time.sleep(1) print("原地右转1秒") car_turn_right(50) time.sleep(1) car_stop() except KeyboardInterrupt: pass finally: car_stop() GPIO.cleanup() # 清理GPIO资源实操心得:PWM频率不宜过高。我试过10kHz,电机噪音小但有时启动不畅。1kHz是一个比较均衡的选择。另外,电机在低速时(占空比低于20%)可能抖动或不转,这是电机的死区特性,可以在软件中做映射,让低速指令对应一个较高的基础占空比。
4.3 视频流服务器搭建
我们使用MJPG-Streamer。它已经不在默认仓库里,需要从GitHub编译安装。
# 安装依赖 sudo apt install cmake libjpeg-dev libv4l-dev # 下载源码(使用一个活跃的分支) git clone https://github.com/jacksonliam/mjpg-streamer.git cd mjpg-streamer/mjpg-streamer-experimental # 编译 make sudo make install # 运行测试 (假设摄像头设备是 /dev/video0, CSI摄像头通常是这个) ./mjpg_streamer -i "input_uvc.so -d /dev/video0 -r 1280x720 -f 30" -o "output_http.so -p 8080 -w ./www"如果一切正常,你会看到输出信息。然后在同一网络下的电脑浏览器打开http://[树莓派IP]:8080,就能看到视频流页面了。为了更稳定,我们可以创建一个系统服务。
# 创建服务文件 sudo vim /etc/systemd/system/mjpg-streamer.service写入以下内容:
[Unit] Description=MJPG-Streamer Service After=network.target [Service] ExecStart=/usr/local/bin/mjpg_streamer -i "input_uvc.so -d /dev/video0 -r 1280x720 -f 15" -o "output_http.so -p 8080 -w /usr/local/share/mjpg-streamer/www" Restart=on-failure User=pi Group=pi [Install] WantedBy=multi-user.target注意:
-f 15表示帧率15fps,提高帧率会增加延迟和CPU占用,15-20fps对于遥控观察足够。分辨率1280x720(720p)也是平衡画质和带宽的好选择。
保存后,启用并启动服务:
sudo systemctl daemon-reload sudo systemctl enable mjpg-streamer.service sudo systemctl start mjpg-streamer.service sudo systemctl status mjpg-streamer.service # 查看状态4.4 远程控制Web界面开发
现在有了视频流和电机控制,我们需要一个网页来发送控制指令。我们用Python的Flask框架快速搭建一个后端,前端用简单的HTML+JavaScript。
首先安装Flask:sudo pip3 install flask
创建项目目录,比如web_controller,在里面创建app.py:
from flask import Flask, render_template, request, jsonify import subprocess import threading app = Flask(__name__) # 这里简化处理,实际应该调用你的电机控制函数 # 假设我们有一个控制脚本的接口 def send_car_command(cmd): """向小车控制进程发送命令(示例)""" # 这里可以是一个socket通信,或者调用另一个模块的函数 # 为了简单,我们打印出来 print(f"执行命令: {cmd}") # 例如: subprocess.run(['python3', 'motor_command.py', cmd]) @app.route('/') def index(): return render_template('index.html') # 渲染控制页面 @app.route('/api/control', methods=['POST']) def control(): data = request.json command = data.get('cmd') speed = data.get('speed', 50) if command in ['forward', 'backward', 'left', 'right', 'stop']: # 在实际项目中,这里应该触发真正的电机控制 send_car_command(f"{command}:{speed}") return jsonify({'status': 'success', 'command': command}) else: return jsonify({'status': 'error', 'message': '未知命令'}), 400 if __name__ == '__main__': # 注意:生产环境不要用debug=True,并使用WSGI服务器如gunicorn app.run(host='0.0.0.0', port=5000, debug=False, threaded=True)然后创建模板目录templates,在里面创建index.html:
<!DOCTYPE html> <html> <head> <title>履带小车遥控器</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { font-family: sans-serif; text-align: center; margin: 20px; } #video-container { margin: 20px auto; } #video-stream { width: 90%; max-width: 640px; border: 2px solid #333; } .control-panel { margin: 30px; } .control-btn { padding: 20px 30px; font-size: 18px; margin: 10px; border: none; border-radius: 8px; cursor: pointer; background-color: #4CAF50; color: white; } .control-btn:active { background-color: #367c39; } #btn-stop { background-color: #f44336; } #btn-stop:active { background-color: #d32f2f; } .speed-control { margin-top: 20px; } #speed-value { font-weight: bold; } </style> </head> <body> <h1>履带小车遥控平台</h1> <div id="video-container"> <img id="video-stream" src="http://YOUR_PI_IP:8080/?action=stream" alt="视频流"> <!-- 将 YOUR_PI_IP 替换为树莓派实际的IP --> </div> <div class="control-panel"> <div> <button class="control-btn" id="btn-forward" ontouchstart="sendCommand('forward')" ontouchend="sendCommand('stop')" onmousedown="sendCommand('forward')" onmouseup="sendCommand('stop')" onmouseleave="sendCommand('stop')">前进</button> </div> <div> <button class="control-btn" id="btn-left" ontouchstart="sendCommand('left')" ontouchend="sendCommand('stop')" onmousedown="sendCommand('left')" onmouseup="sendCommand('stop')">左转</button> <button class="control-btn" id="btn-stop" onclick="sendCommand('stop')">停止</button> <button class="control-btn" id="btn-right" ontouchstart="sendCommand('right')" ontouchend="sendCommand('stop')" onmousedown="sendCommand('right')" onmouseup="sendCommand('stop')">右转</button> </div> <div> <button class="control-btn" id="btn-backward" ontouchstart="sendCommand('backward')" ontouchend="sendCommand('stop')" onmousedown="sendCommand('backward')" onmouseup="sendCommand('stop')">后退</button> </div> <div class="speed-control"> <label>速度: <span id="speed-value">50</span>%</label><br> <input type="range" id="speed-slider" min="20" max="100" value="50" oninput="updateSpeed(this.value)"> </div> </div> <script> let currentSpeed = 50; let currentCmd = ''; function updateSpeed(val) { currentSpeed = val; document.getElementById('speed-value').textContent = val; // 如果当前有持续指令(如按住前进),更新速度 if (currentCmd && currentCmd !== 'stop') { sendCommand(currentCmd); } } function sendCommand(cmd) { currentCmd = cmd; fetch('/api/control', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ cmd: cmd, speed: currentSpeed }) }) .then(response => response.json()) .then(data => console.log('控制响应:', data)) .catch(err => console.error('控制错误:', err)); } // 键盘控制支持 document.addEventListener('keydown', (e) => { switch(e.key) { case 'w': case 'ArrowUp': sendCommand('forward'); break; case 's': case 'ArrowDown': sendCommand('backward'); break; case 'a': case 'ArrowLeft': sendCommand('left'); break; case 'd': case 'ArrowRight': sendCommand('right'); break; case ' ': sendCommand('stop'); break; } }); document.addEventListener('keyup', (e) => { if (['w', 's', 'a', 'd', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) { sendCommand('stop'); } }); </script> </body> </html>这个界面很简单,但很实用。它包含了视频流显示、按钮控制(支持触摸和鼠标)、键盘控制(WASD/方向键)和速度滑块。前端通过Fetch API与后端的/api/control接口通信。
如何整合电机控制:上面的send_car_command函数是示意。更优雅的做法是,将之前写的motor_control.py改成一个模块,在Flask应用中导入并直接调用其控制函数。或者,运行一个独立的电机控制守护进程,Flask应用通过进程间通信(如Socket、Redis、MQTT)向其发送指令。这样可以避免GPIO操作在多线程Flask中的潜在问题。
最后,运行Flask应用:python3 app.py。然后在电脑或手机浏览器访问http://[树莓派IP]:5000,就能看到控制界面了。记得把HTML里的YOUR_PI_IP替换成树莓派运行MJPG-Streamer的IP。
5. 调试、优化与功能扩展
项目做到这里,基本功能已经实现了。但要让小车更好用、更稳定,还需要一番调试和优化。
5.1 常见问题与排查实录
问题:上电后树莓派无法启动,或频繁重启。
- 排查:首先怀疑电源。用万用表测量给树莓派供电的5V电压,在电机启动瞬间是否跌落到4.5V以下?如果是,说明DC-DC模块功率不足或电池电量已低。确保使用持续输出电流3A以上的降压模块,并使用动力型18650电池(如索尼VTC6、三星25R),普通容量型电池放电能力不够。
- 解决:更换大电流降压模块和动力电池。确保电源线足够粗(建议18AWG或更粗)。
问题:视频流卡顿、延迟高。
- 排查:打开树莓派终端,运行
htop命令查看CPU占用率。如果MJPG-Streamer进程占用超过50%,可能是分辨率或帧率设高了。同时检查网络信号强度。 - 解决:降低视频流参数。在MJPG-Streamer启动命令中,将
-r 1280x720改为-r 800x600或-r 640x480,将-f 30改为-f 15。确保树莓派和遥控设备连接在同一个5GHz Wi-Fi网络下(干扰少,带宽大)。如果条件允许,使用网线连接树莓派是最稳定的。
- 排查:打开树莓派终端,运行
问题:电机响应迟缓或不按指令动作。
- 排查:首先检查GPIO接线是否松动。然后写一个简单的测试脚本,只控制一个电机正反转,看是否正常。如果正常,问题可能出在Flask后端与电机控制模块的通信上。
- 解决:确保“共地”连接可靠。检查电机驱动板的VCC(逻辑电源)是否供电(通常接树莓派5V)。如果使用PWM,确认频率是否合适(500Hz-2kHz)。对于通信问题,可以考虑使用消息队列(如
pika库连接RabbitMQ)来解耦Web服务和电机控制服务,提高可靠性。
问题:小车直线跑偏。
- 原因:这是最常见的问题,因为两个电机的实际转速不可能完全一致,履带摩擦力也有差别。
- 解决:软件校准。在
motor_control.py中,为左右电机设置一个速度修正系数。例如,如果小车总是向右偏,说明左电机快了,可以left_speed = desired_speed * 0.95。通过实地测试,微调这个系数,直到小车能基本走直线。
5.2 性能与稳定性优化
- 固定IP地址:在路由器中为树莓派的MAC地址分配静态IP,这样每次访问的地址都不会变。
- 开机自启动:将Flask应用和电机控制服务也设置为systemd服务,实现开机全自动运行。
- 降低CPU占用:可以尝试使用
libcamera替代旧的raspistill/raspivid管线,并与MJPG-Streamer配合,据说效率更高。或者使用更轻量的uv4l驱动。 - 电池监控:通过ADS1115等ADC模块读取电池电压,在Web页面上显示电量,低压报警。
5.3 功能扩展思路
基础平台搭建好后,想象力就可以放飞了:
- 自动驾驶巡航:加装一个超声波传感器(如HC-SR04)在车前,用Python写一个简单的避障逻辑(前进,遇到障碍物则转向),就可以实现自动巡逻。
- 第一人称视角(FPV):买一个廉价的FPV图传发射机和接收机,将摄像头视频信号直接发射,用FPV眼镜接收,实现超低延迟的沉浸式驾驶体验(注意法规)。
- 机械臂抓取:在上层板加装一个舵机驱动的机械臂,通过Web页面增加控制按钮,实现远程抓取小物件。
- AI视觉识别:利用树莓派上的OpenCV或TensorFlow Lite,实现人脸跟踪(小车自动转向面对人)、颜色跟踪(跟随一个红色小球)、或物体识别(发现猫狗就停下)。
- 4G远程控制:插入一个USB 4G上网卡,搭配内网穿透工具(如frp),即使小车在外面,你也可以通过互联网控制它,实现真正的远程巡检。
这个“履带加摄像头小车”项目,就像一颗种子。你从最基本的移动和视觉功能做起,每一步都会遇到问题,解决问题,然后获得新的能力。它带给你的不仅仅是这辆小车本身,更是一整套关于嵌入式系统、网络通信、实时控制乃至简单AI的实践经验。这些经验,远比单纯买一个成品玩具要有价值得多。我最享受的时刻,就是看着它按照我编写的指令,稳稳地越过一个小坎,将前方的画面清晰地传回我的屏幕——那一刻,感觉整个世界都在掌控之中。