Jetson GPIO 控制与传感器数据采集实战
1. Jetson GPIO 接口概览
Jetson Orin NX 提供丰富的 GPIO 接口,可直接连接传感器和执行器:
Orin NX GPIO 资源:
├── GPIO 引脚:40-pin 扩展头
├── I2C 总线:7 路(I2C0-I2C6)
├── SPI 总线:4 路(SPI0-SPI3)
├── UART:3 路(UART0-UART2)
├── PWM:8 路
└── CAN:2 路
40-pin 引脚定义:
引脚编号(物理):
3.3V [1] [2] 5V
GPIO02 [3] [4] 5V
GPIO03 [5] [6] GND
GPIO04 [7] [8] GPIO14
GND [9] [10] GPIO15
GPIO17 [11] [12] GPIO18
GPIO27 [13] [14] GND
GPIO22 [15] [16] GPIO23
3.3V [17] [18] GPIO24
GPIO10 [19] [20] GND
GPIO09 [21] [22] GPIO25
GPIO11 [23] [24] GPIO08
GND [25] [26] GPIO07
GPIO00 [27] [28] GPIO01
GPIO05 [29] [30] GND
GPIO06 [31] [32] GPIO12
GPIO13 [33] [34] GND
GPIO19 [35] [36] GPIO16
GPIO26 [37] [38] GPIO20
GND [39] [40] GPIO21
2. GPIO 控制(Python)
2.1 安装 Jetson.GPIO
bash
# Jetson.GPIO 已预装在 JetPack 中
# 如需更新
sudo pip3 install Jetson.GPIO
# 设置用户组权限
sudo groupadd -f -r gpio
sudo usermod -aG gpio $USER
sudo cp /opt/nvidia/jetson-gpio/etc/99-gpio.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules && sudo udevadm trigger
# 需要重新登录才能生效
2.2 基础 GPIO 输出
python
#!/usr/bin/env python3
"""gpio_output.py - GPIO 输出控制 LED"""
import Jetson.GPIO as GPIO
import time
# 设置模式(BCM 编号)
GPIO.setmode(GPIO.BOARD) # 或 GPIO.BCM
# 设置引脚(BOARD 编号 = 物理引脚号)
LED_PIN = 7 # 物理引脚 7 = GPIO04
GPIO.setup(LED_PIN, GPIO.OUT)
try:
while True:
GPIO.output(LED_PIN, GPIO.HIGH) # LED 亮
print("LED ON")
time.sleep(1)
GPIO.output(LED_PIN, GPIO.LOW) # LED 灭
print("LED OFF")
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup() # 释放所有引脚
2.3 GPIO 输入(按钮检测)
python
#!/usr/bin/env python3
"""gpio_input.py - 按钮输入检测"""
import Jetson.GPIO as GPIO
import time
BUTTON_PIN = 11 # 物理引脚 11 = GPIO17
LED_PIN = 7 # 物理引脚 7 = GPIO04
GPIO.setmode(GPIO.BOARD)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # 上拉
GPIO.setup(LED_PIN, GPIO.OUT)
def button_callback(channel):
"""按钮按下回调"""
print("按钮按下!")
GPIO.output(LED_PIN, not GPIO.input(LED_PIN))
# 注册中断回调(边沿检测)
GPIO.add_event_detect(
BUTTON_PIN,
GPIO.FALLING,
callback=button_callback,
bouncetime=300 # 消抖 300ms
)
try:
print("等待按钮按下...")
while True:
time.sleep(0.1)
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
2.4 PWM 控制(舵机/电机)
python
#!/usr/bin/env python3
"""gpio_pwm.py - PWM 控制舵机"""
import Jetson.GPIO as GPIO
import time
SERVO_PIN = 32 # 物理引脚 32 = GPIO12
GPIO.setmode(GPIO.BOARD)
GPIO.setup(SERVO_PIN, GPIO.OUT)
# 创建 PWM 对象(50Hz 舵机标准频率)
pwm = GPIO.PWM(SERVO_PIN, 50)
pwm.start(0) # 初始占空比 0
def set_angle(angle):
"""设置舵机角度(0-180度)"""
# 角度 → 占空比映射
# 0° → 2.5%, 90° → 7.5%, 180° → 12.5%
duty = 2.5 + (angle / 180.0) * 10.0
pwm.ChangeDutyCycle(duty)
time.sleep(0.3) # 等待舵机到位
try:
print("舵机控制:输入角度 (0-180),输入 'q' 退出")
while True:
user_input = input("角度: ")
if user_input.lower() == 'q':
break
angle = int(user_input)
if 0 <= angle <= 180:
set_angle(angle)
print(f"舵机转到 {angle}°")
else:
print("角度范围:0-180")
except KeyboardInterrupt:
pass
finally:
pwm.stop()
GPIO.cleanup()
2.5 GPIO 事件检测(超声波测距)
python
#!/usr/bin/env python3
"""ultrasonic.py - HC-SR04 超声波测距"""
import Jetson.GPIO as GPIO
import time
TRIG_PIN = 16 # 物理引脚 16 = GPIO23
ECHO_PIN = 18 # 物理引脚 18 = GPIO24
GPIO.setmode(GPIO.BOARD)
GPIO.setup(TRIG_PIN, GPIO.OUT)
GPIO.setup(ECHO_PIN, GPIO.IN)
def measure_distance():
"""测量距离(cm)"""
# 发送 10us 触发脉冲
GPIO.output(TRIG_PIN, GPIO.HIGH)
time.sleep(0.00001)
GPIO.output(TRIG_PIN, GPIO.LOW)
# 等待回波信号
timeout = time.time() + 0.04 # 40ms 超时(约 6m)
pulse_start = time.time()
pulse_end = time.time()
while GPIO.input(ECHO_PIN) == 0:
pulse_start = time.time()
if pulse_start > timeout:
return -1
while GPIO.input(ECHO_PIN) == 1:
pulse_end = time.time()
if pulse_end > timeout:
return -1
# 计算距离
duration = pulse_end - pulse_start
distance = duration * 34300 / 2 # 声速 343m/s
return round(distance, 2)
try:
print("超声波测距(HC-SR04)")
print("按 Ctrl+C 退出\n")
while True:
dist = measure_distance()
if dist > 0:
print(f"距离: {dist} cm")
else:
print("测量超时")
time.sleep(0.5)
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
3. I2C 传感器
3.1 I2C 总线扫描
bash
# 安装 I2C 工具
sudo apt install -y i2c-tools
# 扫描 I2C 总线
i2cdetect -y -r 0 # 总线 0
i2cdetect -y -r 1 # 总线 1
i2cdetect -y -r 8 # 总线 8
# 输出示例:
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
# 00: -- -- -- -- -- -- -- -- -- -- -- -- --
# 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
# 70: -- -- -- -- -- -- -- --
# 0x40 = 温湿度传感器, 0x68 = MPU6050 陀螺仪
3.2 温湿度传感器(AHT20)
python
#!/usr/bin/env python3
"""aht20.py - AHT20 温湿度传感器"""
import smbus2
import time
class AHT20:
"""AHT20 I2C 温湿度传感器"""
def __init__(self, bus_number=1, address=0x38):
self.bus = smbus2.SMBus(bus_number)
self.address = address
self._initialize()
def _initialize(self):
"""初始化传感器"""
time.sleep(0.04) # 上电等待
# 检查是否需要初始化
self.bus.write_i2c_block_data(self.address, 0xBE, [0x08, 0x00])
time.sleep(0.01)
def read(self):
"""读取温湿度"""
# 发送测量命令
self.bus.write_i2c_block_data(self.address, 0xAC, [0x33, 0x00])
time.sleep(0.08) # 等待测量完成
# 读取 6 字节数据
data = self.bus.read_i2c_block_data(self.address, 0x00, 6)
# 解析湿度
humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4
humidity = humidity * 100.0 / 1048576.0
# 解析温度
temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]
temperature = temperature * 200.0 / 1048576.0 - 50.0
return {
"temperature": round(temperature, 2),
"humidity": round(humidity, 2)
}
if __name__ == "__main__":
sensor = AHT20(bus_number=1)
print("AHT20 温湿度传感器")
print("按 Ctrl+C 退出\n")
while True:
data = sensor.read()
print(f"温度: {data['temperature']}°C | 湿度: {data['humidity']}%")
time.sleep(1)
3.3 MPU6050 六轴陀螺仪
python
#!/usr/bin/env python3
"""mpu6050.py - MPU6050 六轴传感器"""
import smbus2
import time
import math
class MPU6050:
"""MPU6050 六轴 IMU"""
# 寄存器地址
PWR_MGMT_1 = 0x6B
SMPLRT_DIV = 0x19
CONFIG = 0x1A
GYRO_CONFIG = 0x1B
ACCEL_CONFIG = 0x1C
ACCEL_XOUT_H = 0x3B
TEMP_OUT_H = 0x41
GYRO_XOUT_H = 0x43
def __init__(self, bus_number=1, address=0x68):
self.bus = smbus2.SMBus(bus_number)
self.address = address
self._initialize()
def _initialize(self):
"""初始化"""
# 唤醒
self.bus.write_byte_data(self.address, self.PWR_MGMT_1, 0x00)
time.sleep(0.1)
# 设置采样率 1kHz
self.bus.write_byte_data(self.address, self.SMPLRT_DIV, 0x07)
# 设置低通滤波 5Hz
self.bus.write_byte_data(self.address, self.CONFIG, 0x06)
# 设置陀螺仪量程 ±500°/s
self.bus.write_byte_data(self.address, self.GYRO_CONFIG, 0x08)
# 设置加速度计量程 ±4g
self.bus.write_byte_data(self.address, self.ACCEL_CONFIG, 0x08)
def _read_raw(self, register):
"""读取 16 位原始数据"""
high = self.bus.read_byte_data(self.address, register)
low = self.bus.read_byte_data(self.address, register + 1)
value = (high << 8) | low
if value > 32767:
value -= 65536
return value
def read_accel(self):
"""读取加速度(g)"""
x = self._read_raw(self.ACCEL_XOUT_H) / 8192.0 # ±4g
y = self._read_raw(self.ACCEL_XOUT_H + 2) / 8192.0
z = self._read_raw(self.ACCEL_XOUT_H + 4) / 8192.0
return {"x": round(x, 4), "y": round(y, 4), "z": round(z, 4)}
def read_gyro(self):
"""读取角速度(°/s)"""
x = self._read_raw(self.GYRO_XOUT_H) / 65.5 # ±500°/s
y = self._read_raw(self.GYRO_XOUT_H + 2) / 65.5
z = self._read_raw(self.GYRO_XOUT_H + 4) / 65.5
return {"x": round(x, 4), "y": round(y, 4), "z": round(z, 4)}
def read_temp(self):
"""读取温度(°C)"""
raw = self._read_raw(self.TEMP_OUT_H)
temp = raw / 340.0 + 36.53
return round(temp, 2)
def get_angles(self):
"""计算俯仰角和横滚角"""
accel = self.read_accel()
pitch = math.atan2(accel["y"], math.sqrt(accel["x"]**2 + accel["z"]**2)) * 180 / math.pi
roll = math.atan2(-accel["x"], accel["z"]) * 180 / math.pi
return {"pitch": round(pitch, 2), "roll": round(roll, 2)}
if __name__ == "__main__":
imu = MPU6050(bus_number=1)
print("MPU6050 六轴传感器")
print("按 Ctrl+C 退出\n")
while True:
accel = imu.read_accel()
gyro = imu.read_gyro()
temp = imu.read_temp()
angles = imu.get_angles()
print(f"加速度(g): X={accel['x']}, Y={accel['y']}, Z={accel['z']}")
print(f"角速度(°/s): X={gyro['x']}, Y={gyro['y']}, Z={gyro['z']}")
print(f"角度: 俯仰={angles['pitch']}°, 横滚={angles['roll']}°")
print(f"温度: {temp}°C")
print("-" * 50)
time.sleep(0.1)
3.4 OLED 显示屏(SSD1306)
python
#!/usr/bin/env python3
"""oled_display.py - SSD1306 OLED 显示"""
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
from luma.core.render import canvas
from PIL import ImageFont, ImageDraw
import time
import psutil
import subprocess
class OLEDDisplay:
"""SSD1306 OLED 显示驱动"""
def __init__(self, bus_number=1, address=0x3C):
serial = i2c(port=bus_number, address=address)
self.device = ssd1306(serial, width=128, height=64)
self.font = ImageFont.load_default()
def show_text(self, lines):
"""显示多行文本"""
with canvas(self.device) as draw:
for i, line in enumerate(lines):
draw.text((0, i * 12), line, font=self.font, fill="white")
def show_system_info(self):
"""显示系统信息"""
cpu = psutil.cpu_percent(interval=0.1)
mem = psutil.virtual_memory()
# 获取温度
try:
with open("/sys/devices/virtual/thermal/thermal_zone0/temp") as f:
temp = float(f.read().strip()) / 1000
except:
temp = 0
# 获取 IP
try:
ip = subprocess.check_output(["hostname", "-I"]).decode().strip().split()[0]
except:
ip = "N/A"
self.show_text([
f"CPU: {cpu}%",
f"MEM: {mem.percent}%",
f"TEMP: {temp}°C",
f"IP: {ip}"
])
if __name__ == "__main__":
display = OLEDDisplay(bus_number=1)
print("OLED 系统监控显示")
while True:
display.show_system_info()
time.sleep(1)
4. SPI 传感器
4.1 SPI 通信基础
python
#!/usr/bin/env python3
"""spi_basic.py - SPI 通信示例"""
import spidev
import time
class SPIController:
"""SPI 控制器"""
def __init__(self, bus=0, device=0, speed=1000000):
self.spi = spidev.SpiDev()
self.spi.open(bus, device)
self.spi.max_speed_hz = speed
self.spi.mode = 0b00 # SPI Mode 0
def transfer(self, data):
"""SPI 数据传输"""
return self.spi.xfer2(data)
def read_adc(self, channel=0):
"""读取 MCP3008 ADC(8 通道 10 位)"""
# MCP3008 协议
cmd = [0x01, (0x08 | channel) << 4, 0x00]
result = self.transfer(cmd)
value = ((result[1] & 0x03) << 8) | result[2]
return value
def close(self):
self.spi.close()
if __name__ == "__main__":
spi = SPIController(bus=0, device=0)
print("MCP3008 ADC 读取")
while True:
for ch in range(8):
value = spi.read_adc(ch)
voltage = value * 3.3 / 1024
print(f"CH{ch}: {value} ({voltage:.2f}V) ", end="")
print()
time.sleep(0.5)
spi.close()
5. UART 串口通信
python
#!/usr/bin/env python3
"""uart_serial.py - UART 串口通信"""
import serial
import time
class UARTController:
"""UART 串口控制器"""
def __init__(self, port="/dev/ttyTHS1", baudrate=115200):
self.ser = serial.Serial(
port=port,
baudrate=baudrate,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1
)
def send(self, data):
"""发送数据"""
if isinstance(data, str):
data = data.encode()
self.ser.write(data)
def receive(self, size=1024):
"""接收数据"""
return self.ser.read(size)
def readline(self):
"""读取一行"""
return self.ser.readline().decode().strip()
def close(self):
self.ser.close()
# GPS 模块示例(NMEA 解析)
class GPSReader:
"""GPS NMEA 解析"""
def __init__(self, port="/dev/ttyTHS1", baudrate=9600):
self.uart = UARTController(port, baudrate)
def parse_gpgga(self, sentence):
"""解析 GPGGA 语句"""
parts = sentence.split(',')
if len(parts) < 15:
return None
return {
"time": parts[1],
"latitude": self._to_decimal(parts[2], parts[3]),
"longitude": self._to_decimal(parts[4], parts[5]),
"fix": int(parts[6]),
"satellites": int(parts[7]),
"altitude": float(parts[9]) if parts[9] else 0
}
def _to_decimal(self, value, direction):
"""NMEA 坐标转十进制"""
if not value:
return 0.0
degrees = int(value[:2]) if len(value) > 7 else int(value[:3])
minutes = float(value[2:]) if len(value) > 7 else float(value[3:])
decimal = degrees + minutes / 60
if direction in ('S', 'W'):
decimal = -decimal
return round(decimal, 6)
def read(self):
"""读取 GPS 数据"""
while True:
line = self.uart.readline()
if line.startswith('$GPGGA') or line.startswith('$GNGGA'):
return self.parse_gpgga(line)
if __name__ == "__main__":
gps = GPSReader(port="/dev/ttyTHS1", baudrate=9600)
print("GPS 定位")
while True:
data = gps.read()
if data:
print(f"纬度: {data['latitude']}, 经度: {data['longitude']}")
print(f"卫星: {data['satellites']}, 海拔: {data['altitude']}m")
time.sleep(1)
6. 多传感器融合系统
python
#!/usr/bin/env python3
"""sensor_fusion.py - 多传感器数据采集与融合"""
import threading
import time
import json
from datetime import datetime
from collections import deque
class SensorManager:
"""传感器管理器"""
def __init__(self):
self.sensors = {}
self.data = {}
self.locks = {}
def add_sensor(self, name, sensor_class, **kwargs):
"""添加传感器"""
try:
sensor = sensor_class(**kwargs)
self.sensors[name] = sensor
self.data[name] = deque(maxlen=100)
self.locks[name] = threading.Lock()
print(f"✅ 传感器 '{name}' 已添加")
except Exception as e:
print(f"❌ 传感器 '{name}' 初始化失败: {e}")
def _read_loop(self, name, interval=1.0):
"""持续读取循环"""
while True:
try:
value = self.sensors[name].read()
with self.locks[name]:
self.data[name].append({
"timestamp": datetime.now().isoformat(),
"value": value
})
except Exception as e:
print(f"传感器 '{name}' 读取错误: {e}")
time.sleep(interval)
def start(self):
"""启动所有传感器采集"""
self.threads = []
for name in self.sensors:
t = threading.Thread(
target=self._read_loop,
args=(name,),
daemon=True
)
t.start()
self.threads.append(t)
print(f"🚀 传感器 '{name}' 采集已启动")
def get_latest(self, name):
"""获取最新数据"""
with self.locks[name]:
if self.data[name]:
return self.data[name][-1]
return None
def get_all_latest(self):
"""获取所有传感器最新数据"""
result = {}
for name in self.sensors:
latest = self.get_latest(name)
if latest:
result[name] = latest
return result
def export_json(self, filepath):
"""导出数据为 JSON"""
export = {}
for name in self.sensors:
with self.locks[name]:
export[name] = list(self.data[name])
with open(filepath, 'w') as f:
json.dump(export, f, indent=2)
print(f"数据已导出: {filepath}")
# 使用示例
if __name__ == "__main__":
manager = SensorManager()
# 添加各种传感器
manager.add_sensor("temp_humidity", AHT20, bus_number=1, address=0x38)
manager.add_sensor("imu", MPU6050, bus_number=1, address=0x68)
manager.add_sensor("distance", UltrasonicSensor, trig_pin=16, echo_pin=18)
manager.start()
try:
while True:
all_data = manager.get_all_latest()
for name, data in all_data.items():
print(f"{name}: {data['value']}")
print("-" * 50)
time.sleep(2)
except KeyboardInterrupt:
manager.export_json("sensor_data.json")
总结
| 接口 | 典型传感器 | Python 库 | 通信速率 |
|---|---|---|---|
| GPIO | LED、按钮、超声波 | Jetson.GPIO | - |
| I2C | AHT20、MPU6050、OLED | smbus2 | 100-400kHz |
| SPI | MCP3008、MAX6675 | spidev | 1-10MHz |
| UART | GPS、蓝牙、STM32 | pyserial | 9600-115200 |
| PWM | 舵机、电机、蜂鸣器 | Jetson.GPIO | 50-1000Hz |
核心要点:
- GPIO 权限 :加入
gpio用户组,避免 sudo - I2C 扫描 :先用
i2cdetect确认设备地址 - 多传感器并行:每个传感器独立采集线程
- 数据持久化:JSON 导出 + SQLite 存储