246 lines
7.6 KiB
Python
246 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
M01激光测距模块测试脚本 - 修正版
|
||
基于文档中的完整命令示例
|
||
"""
|
||
|
||
from maix import uart, pinmap, time
|
||
import binascii
|
||
|
||
# ==================== 配置 ====================
|
||
UART_PORT = "/dev/ttyS1"
|
||
BAUDRATE = 9600
|
||
|
||
# 初始化串口
|
||
try:
|
||
pinmap.set_pin_function("A18", "UART1_RX")
|
||
pinmap.set_pin_function("A19", "UART1_TX")
|
||
laser_uart = uart.UART(UART_PORT, BAUDRATE)
|
||
print("✅ 硬件初始化完成")
|
||
except Exception as e:
|
||
print(f"❌ 初始化失败: {e}")
|
||
exit(1)
|
||
|
||
# ==================== 根据文档的完整命令集 ====================
|
||
# 1. 激光开关(文档2.3.10,已验证可用)
|
||
LASER_ON_CMD = bytes([0xAA, 0x00, 0x01, 0xBE, 0x00, 0x01, 0x00, 0x01, 0xC1])
|
||
LASER_OFF_CMD = bytes([0xAA, 0x00, 0x01, 0xBE, 0x00, 0x01, 0x00, 0x00, 0xC0])
|
||
|
||
# 2. 尝试不同的测距命令格式
|
||
TEST_COMMANDS = [
|
||
# 格式1:文档2.3.12的单次测量(您测试失败的)
|
||
{
|
||
"name": "单次测量 (0x0020)",
|
||
"cmd": bytes([0xAA, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x21]),
|
||
"desc": "文档2.3.12 示例命令"
|
||
},
|
||
# 格式2:文档2.3.7的读取测量结果
|
||
{
|
||
"name": "读取测量结果 (0x0022)",
|
||
"cmd": bytes([0xAA, 0x80, 0x00, 0x22, 0xA2]),
|
||
"desc": "文档2.3.7 读取测量结果"
|
||
},
|
||
# 格式3:文档2.3.13的快速测量
|
||
{
|
||
"name": "快速测量 (0x0022带数据)",
|
||
"cmd": bytes([0xAA, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x23]),
|
||
"desc": "文档2.3.13 快速测量"
|
||
},
|
||
# 格式4:连续测量模式
|
||
{
|
||
"name": "连续测量模式 (0x0021)",
|
||
"cmd": bytes([0xAA, 0x00, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x22]),
|
||
"desc": "文档2.3.14 连续测量"
|
||
}
|
||
]
|
||
|
||
def clear_buffer():
|
||
"""清空串口缓冲区"""
|
||
try:
|
||
data = laser_uart.read(-1)
|
||
if data:
|
||
print(f"清空: {len(data)}字节")
|
||
except:
|
||
pass
|
||
|
||
def send_and_wait(cmd, name, wait_time=2000):
|
||
"""发送命令并等待响应"""
|
||
print(f"\n📤 发送: {name}")
|
||
print(f" 命令: {cmd.hex()}")
|
||
|
||
clear_buffer()
|
||
|
||
try:
|
||
laser_uart.write(cmd)
|
||
print(f" 已发送 {len(cmd)} 字节")
|
||
except Exception as e:
|
||
print(f" ❌ 发送失败: {e}")
|
||
return None
|
||
|
||
# 等待响应
|
||
start_time = time.ticks_ms()
|
||
response = b""
|
||
|
||
while time.ticks_ms() - start_time < wait_time:
|
||
try:
|
||
chunk = laser_uart.read(1)
|
||
if chunk:
|
||
response += chunk
|
||
# 完整响应通常是9或13字节
|
||
if len(response) >= 9:
|
||
# 检查是否完整帧
|
||
if response[0] in [0xAA, 0xEE]:
|
||
if len(response) >= 13: # 测距完整响应
|
||
break
|
||
elif response[0] == 0xEE: # 错误响应
|
||
break
|
||
except:
|
||
break
|
||
|
||
time.sleep_ms(10)
|
||
|
||
if response:
|
||
print(f" 📥 响应: {response.hex()}")
|
||
print(f" 长度: {len(response)} 字节")
|
||
|
||
# 解析错误码
|
||
if response[0] == 0xEE and len(response) >= 9:
|
||
err_code = (response[7] << 8) | response[8]
|
||
error_mapping = {
|
||
0x0000: "无错误",
|
||
0x0001: "硬件错误",
|
||
0x0002: "无输出数据",
|
||
0x0003: "反射信号太弱",
|
||
0x0004: "反射信号太强",
|
||
0x0005: "温度太高(>40℃)",
|
||
0x0006: "温度太低(<-10℃)",
|
||
0x0007: "电源电压低(<2.5V)",
|
||
0x0008: "超出量程",
|
||
0x0009: "读通讯错误",
|
||
0x000A: "写通讯错误",
|
||
0x000B: "地址错误"
|
||
}
|
||
err_msg = error_mapping.get(err_code, f"未知错误: 0x{err_code:04X}")
|
||
print(f" ❌ 模块错误: {err_msg}")
|
||
else:
|
||
print(" ⚠️ 无响应")
|
||
|
||
return response
|
||
|
||
def parse_distance_data(response):
|
||
"""解析距离数据"""
|
||
if not response or len(response) < 13:
|
||
return None
|
||
|
||
if response[0] != 0xAA or response[3] not in [0x20, 0x21, 0x22]:
|
||
return None
|
||
|
||
# 解析4字节BCD码
|
||
bcd_bytes = response[6:10]
|
||
distance_int = 0
|
||
|
||
for byte in bcd_bytes:
|
||
high = (byte >> 4) & 0x0F
|
||
low = byte & 0x0F
|
||
|
||
if high > 9 or low > 9:
|
||
return None
|
||
|
||
distance_int = distance_int * 100 + high * 10 + low
|
||
|
||
distance_m = distance_int / 1000.0
|
||
|
||
# 信号质量
|
||
signal = 0
|
||
if len(response) >= 12:
|
||
signal = (response[10] << 8) | response[11]
|
||
|
||
return {
|
||
'meters': distance_m,
|
||
'millimeters': distance_m * 1000,
|
||
'signal': signal,
|
||
'raw': response.hex()
|
||
}
|
||
|
||
# ==================== 主测试 ====================
|
||
print("\n" + "="*50)
|
||
print("M01激光测距模块详细测试")
|
||
print("="*50)
|
||
|
||
try:
|
||
# 1. 测试基本连接
|
||
print("\n1. 测试模块连接...")
|
||
version_cmd = bytes([0xAA, 0x80, 0x00, 0x0A, 0x8A])
|
||
resp = send_and_wait(version_cmd, "读取硬件版本")
|
||
|
||
if resp and resp[0] == 0xAA and resp[3] == 0x0A:
|
||
print(f"✅ 模块正常,版本: {resp[6]:02X}{resp[7]:02X}")
|
||
else:
|
||
print("❌ 模块连接测试失败")
|
||
exit(1)
|
||
|
||
# 2. 开启激光
|
||
print("\n2. 开启激光...")
|
||
resp = send_and_wait(LASER_ON_CMD, "开启激光", 1000)
|
||
if resp and resp.hex() == "aa0001be00010001c1":
|
||
print("✅ 激光已开启")
|
||
|
||
print(" 等待激光稳定...")
|
||
time.sleep(2) # 重要等待时间
|
||
|
||
# 3. 尝试不同的测距命令
|
||
print("\n3. 测试不同测距命令...")
|
||
|
||
for i, test_cmd in enumerate(TEST_COMMANDS):
|
||
print(f"\n{'='*30}")
|
||
print(f"测试 {i+1}: {test_cmd['name']}")
|
||
print(f"{test_cmd['desc']}")
|
||
print(f"{'='*30}")
|
||
|
||
resp = send_and_wait(test_cmd['cmd'], test_cmd['name'], 3000)
|
||
|
||
if resp:
|
||
if resp[0] == 0xAA and len(resp) >= 13:
|
||
result = parse_distance_data(resp)
|
||
if result:
|
||
print(f"✅ 测距成功!")
|
||
print(f" 距离: {result['meters']:.3f} m")
|
||
print(f" 距离: {result['millimeters']:.1f} mm")
|
||
print(f" 信号质量: {result['signal']}")
|
||
break
|
||
else:
|
||
print("❌ 无法解析距离数据")
|
||
elif resp[0] == 0xEE:
|
||
print("❌ 命令执行错误")
|
||
else:
|
||
print("❌ 无效响应格式")
|
||
else:
|
||
print("❌ 无响应")
|
||
|
||
time.sleep(1) # 命令间间隔
|
||
|
||
# 4. 关闭激光
|
||
print("\n4. 关闭激光...")
|
||
send_and_wait(LASER_OFF_CMD, "关闭激光", 1000)
|
||
|
||
print("\n" + "="*50)
|
||
print("🏁 测试完成")
|
||
print("="*50)
|
||
|
||
print("\n📋 测试总结:")
|
||
print("1. 模块通信: ✅ 正常")
|
||
print("2. 激光控制: ✅ 正常")
|
||
print("3. 测距功能: ❌ 有问题")
|
||
print("\n建议:")
|
||
print("1. 检查激光是否实际发光(在暗处观察红点)")
|
||
print("2. 确保测量目标在有效范围内(0.2-60米)")
|
||
print("3. 确保目标有足够反射率(白色平面最佳)")
|
||
print("4. 如果所有测距命令都返回ERR_ADDR,可能是固件版本问题")
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n\n🛑 用户中断")
|
||
laser_uart.write(LASER_OFF_CMD)
|
||
print("✅ 已发送关闭指令")
|
||
except Exception as e:
|
||
print(f"\n❌ 测试出错: {e}") |