当无人机在天空中执行复杂任务时,地面站就是操作者的眼睛和双手。想象一下,你正在参加电子设计竞赛,无人机需要完成货物盘点、坐标追踪等任务,而你需要一个直观的界面来监控和控制这一切。本文将带你从零开始,用树莓派和淘晶驰串口屏打造一个功能完备的无人机地面站交互系统。
在这个项目中,我们需要以下硬件设备:
硬件连接示意图:
code复制树莓派4B ←TTL转USB→ 淘晶驰串口屏
↓
USB
↓
Arduino UNO → LED指示灯
树莓派需要安装以下软件环境:
bash复制# 安装必要的Python库
sudo apt-get update
sudo apt-get install python3-pip
pip3 install pyserial rospy
对于串口屏开发,需要下载淘晶驰的专用开发工具USART HMI:
根据无人机地面站的功能需求,我们设计四个主要界面:
关键控件类型及用途:
| 控件类型 | 命名规则 | 用途 | 示例 |
|---|---|---|---|
| 数字控件 | n0-n23 | 显示货物编号 | n0.val=12 |
| 文本控件 | t0-t5 | 显示文字信息 | t0.txt="A1" |
| 按钮控件 | b0-b25 | 触发操作指令 | b0按下事件 |
| 图片控件 | p0-p23 | 显示图标标记 | pic 9,11,37 |
主界面初始化事件:
javascript复制// 前初始化事件代码
if(n24.val>=1) {
if(n24.val==n0.val) { pic 9,11,37 }
if(n24.val==n1.val) { pic 9,45,37 }
// ...其他23个位置的判断
}
任务按钮按下事件:
javascript复制// 任务1按钮按下事件
prints "renwu1",0
// 任务2按钮按下事件
prints "renwu2",0
页面切换逻辑:
javascript复制// 查询按钮按下事件
page page1
// 返回按钮按下事件
page page0
在树莓派上首先确认串口设备:
bash复制ls -l /dev/tty*
典型的输出可能是/dev/ttyUSB0或/dev/ttyACM0,取决于使用的转换模块。
向串口屏发送数据:
python复制import serial
def send_to_screen(control, value):
try:
ser = serial.Serial("/dev/ttyUSB0", 115200, timeout=0.5)
command = f"{control}.val={value}".encode("GB2312")
ser.write(command)
ser.write(b'\xff\xff\xff') # 结束符
ser.close()
except Exception as e:
print(f"发送失败: {e}")
# 示例:设置n0控件的值为20
send_to_screen("n0", 20)
接收串口屏数据:
python复制import serial
def read_from_screen():
ser = serial.Serial("/dev/ttyUSB0", 115200, timeout=1)
while True:
if ser.in_waiting > 0:
data = ser.readline().decode("GB2312").strip()
if data == "renwu1":
handle_task1()
elif data == "renwu2":
handle_task2()
def handle_task1():
print("收到任务1指令")
# 触发无人机执行任务1
def handle_task2():
print("收到任务2指令")
# 触发无人机执行任务2
地面站与无人机通过ROS进行通信,需要配置主从机环境:
地面站发布任务指令:
python复制#!/usr/bin/env python3
import rospy
from std_msgs.msg import String
import serial
def serial_to_ros_bridge():
rospy.init_node('ground_station')
pub = rospy.Publisher('mission_cmd', String, queue_size=10)
ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
while not rospy.is_shutdown():
if ser.in_waiting > 0:
cmd = ser.readline().decode('GB2312').strip()
if cmd in ["renwu1", "renwu2"]:
pub.publish(cmd)
无人机接收任务指令:
python复制#!/usr/bin/env python3
import rospy
from std_msgs.msg import String
def mission_callback(data):
if data.data == "renwu1":
execute_task1()
elif data.data == "renwu2":
execute_task2()
def execute_task1():
# 执行任务1的代码
pass
def execute_task2():
# 执行任务2的代码
pass
rospy.init_node('drone_control')
rospy.Subscriber("mission_cmd", String, mission_callback)
rospy.spin()
无人机发送的货物数据格式示例:
python复制{
'A1': None,
'A2': 12,
'A3': 8,
# ...其他货位数据
}
地面站处理数据的Python代码:
python复制import json
def process_drone_data(raw_data):
try:
data = json.loads(raw_data)
# 将None转换为0
processed = {k: 0 if v is None else v for k, v in data.items()}
return processed
except json.JSONDecodeError:
print("数据解析错误")
return None
def update_screen(data):
# 映射关系:货位到控件编号
position_map = {
'A1': 0, 'A2': 1, 'A3': 2,
# ...其他货位映射
}
for pos, val in data.items():
ctrl_num = position_map.get(pos)
if ctrl_num is not None:
send_to_screen(f"n{ctrl_num}", val)
通过Arduino控制LED指示灯,树莓派发送控制命令:
python复制import serial
def control_led(state):
arduino = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
arduino.write(b'1' if state else b'0')
arduino.close()
Arduino端代码:
cpp复制int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
char cmd = Serial.read();
if (cmd == '1') {
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
}
}
}
python复制def safe_serial_send(command, max_retries=3):
for attempt in range(max_retries):
try:
ser = serial.Serial("/dev/ttyUSB0", 115200, timeout=0.5)
ser.write(command.encode("GB2312"))
ser.write(b'\xff\xff\xff')
ser.close()
return True
except:
time.sleep(0.5)
return False
python复制def validate_data(data):
required_keys = ['A1', 'A2', ..., 'D6'] # 所有货位键
return all(k in data for k in required_keys)
在串口屏设计时,可以使用"页面预加载"技术减少切换延迟:
javascript复制// 在page0的前初始化事件中预加载其他页面
page preload page1
page preload page2
page preload page3
当通信不正常时,可以按照以下步骤排查:
bash复制screen /dev/ttyUSB0 115200
bash复制sudo usermod -a -G dialout pi
常见显示问题及解决方法:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 控件无显示 | 控件未正确命名 | 检查控件名称与代码一致 |
| 触摸无响应 | 触摸区域设置不当 | 重新校准触摸屏 |
| 文字乱码 | 编码不一致 | 确保使用GB2312编码 |
| 图片不显示 | 未导入图库 | 重新导入图片资源 |
减少串口通信量:
优化ROS通信:
界面响应优化:
扩展系统以支持多架无人机:
结合摄像头实现AR功能:
将地面站数据同步到云端:
python复制import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client = mqtt.Client()
client.on_connect = on_connect
client.connect("mqtt.example.com", 1883, 60)
client.loop_start()
def upload_data(data):
client.publish("drone/ground_station", json.dumps(data))
在实际项目中,我发现最关键的挑战是确保串口通信的稳定性。特别是在长时间运行时,偶尔会出现数据丢失的情况。通过添加重试机制和心跳检测,最终实现了99%以上的通信可靠性。另一个实用技巧是在串口屏设计时,为每个控件添加调试用的临时显示区域,可以快速定位问题所在。