第二章 硬件平台
2.1 机器人底盘类型
2.1.1 差速底盘(Differential Drive)
差速底盘是最常见的移动机器人底盘,通过左右轮速差实现转向。
运动学特性:
- 左右轮独立驱动
- 转弯半径可变(0到无穷大)
- 不能横向移动
轮式编码器:
脉冲数(PPR): 通常为500-2000
轮距(L): 两轮中心距离
轮径(R): 驱动轮半径
线速度计算:
v = (v_left + v_right) / 2
角速度计算:
ω = (v_right - v_left) / L
编码器读取(Arduino):
// 编码器中断处理
#define ENCODER_LEFT_A 2
#define ENCODER_LEFT_B 3
#define ENCODER_RIGHT_A 18
#define ENCODER_RIGHT_B 19
volatile long encoder_left = 0;
volatile long encoder_right = 0;
void setup() {
pinMode(ENCODER_LEFT_A, INPUT_PULLUP);
pinMode(ENCODER_LEFT_B, INPUT_PULLUP);
pinMode(ENCODER_RIGHT_A, INPUT_PULLUP);
pinMode(ENCODER_RIGHT_B, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCODER_LEFT_A),
leftEncoderA, CHANGE);
attachInterrupt(digitalPinToInterrupt(ENCODER_RIGHT_A),
rightEncoderA, CHANGE);
}
void leftEncoderA() {
if (digitalRead(ENCODER_LEFT_A) == digitalRead(ENCODER_LEFT_B)) {
encoder_left++;
} else {
encoder_left--;
}
}
void rightEncoderA() {
if (digitalRead(ENCODER_RIGHT_A) == digitalRead(ENCODER_RIGHT_B)) {
encoder_right++;
} else {
encoder_right--;
}
}
// 计算速度
float getSpeed(long encoder_count, float dt) {
const float PPR = 1000.0; // 每转脉冲数
const float WHEEL_RADIUS = 0.05; // 轮半径(m)
float rps = encoder_count / PPR / dt; // 转/秒
return 2 * PI * WHEEL_RADIUS * rps; // m/s
}
2.1.2 全向轮底盘(Omni-directional)
全向轮可以沿任意方向移动,无需旋转车体。
三轮全向配置(120度分布):
运动学方程:
V1 = -0.5*Vx + 0.866*Vy + L*ω
V2 = -0.5*Vx - 0.866*Vy + L*ω
V3 = Vx + L*ω
其中:
Vx, Vy: 机器人x、y方向速度
ω: 角速度
L: 轮子到中心距离
四轮全向配置(90度分布):
// 运动学逆解
void omniWheelControl(float vx, float vy, float omega) {
const float L = 0.2; // 轮距的一半
const float R = 0.05; // 轮半径
// 四轮速度计算
float v1 = vx - vy - L * omega; // 左前
float v2 = vx + vy - L * omega; // 右前
float v3 = vx + vy + L * omega; // 右后
float v4 = vx - vy + L * omega; // 左后
// 转换为轮速(rad/s)
float w1 = v1 / R;
float w2 = v2 / R;
float w3 = v3 / R;
float w4 = v4 / R;
setMotorSpeed(1, w1);
setMotorSpeed(2, w2);
setMotorSpeed(3, w3);
setMotorSpeed(4, w4);
}
2.1.3 麦克纳姆轮(Mecanum Wheel)
麦轮通过斜向的辊子实现全向移动。
力学分析:
每个轮子的辊子与车体成45度角
运动学方程(X型配置):
V_left_front = Vx - Vy - L*ω
V_right_front = Vx + Vy + L*ω
V_left_rear = Vx + Vy - L*ω
V_right_rear = Vx - Vy + L*ω
逆解(已知轮速求车体速度):
Vx = (V_lf + V_rf + V_lr + V_rr) / 4
Vy = (-V_lf + V_rf + V_lr - V_rr) / 4
ω = (-V_lf + V_rf - V_lr + V_rr) / (4*L)
2.1.4 底盘对比表格
| 底盘类型 | 运动灵活性 | 承载能力 | 控制复杂度 | 成本 | 适用场景 |
|---|---|---|---|---|---|
| 差速 | 低 | 高 | 简单 | 低 | 室内导航、仓储 |
| 三轮全向 | 中 | 中 | 中等 | 中 | 狭窄空间、服务 |
| 四轮全向 | 高 | 中 | 中等 | 中 | 精确定位 |
| 麦轮 | 高 | 高 | 复杂 | 高 | 工业、竞赛 |
2.2 电机驱动系统
2.2.1 直流电机(DC Motor)
H桥驱动原理:
H桥允许电流双向流动,实现正反转
S1 S3
| |
+------+
| |
+--+ M +--+
| |
+------+
| |
S2 S4
正转: S1、S4导通
反转: S2、S3导通
制动: S1、S3或S2、S4导通
L298N驱动模块:
// L298N控制代码
#define IN1 7
#define IN2 6
#define ENA 5 // PWM引脚
void setup() {
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENA, OUTPUT);
}
// 电机控制函数
void motorControl(int speed) {
// speed范围: -255到255
if (speed > 0) {
// 正转
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(ENA, speed);
} else if (speed < 0) {
// 反转
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
analogWrite(ENA, -speed);
} else {
// 停止
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
analogWrite(ENA, 0);
}
}
// PWM调速
void loop() {
// 加速
for (int speed = 0; speed <= 255; speed += 5) {
motorControl(speed);
delay(50);
}
// 减速
for (int speed = 255; speed >= 0; speed -= 5) {
motorControl(speed);
delay(50);
}
}
TB6612驱动模块(更高效):
// TB6612控制
#define PWMA 5
#define AIN1 7
#define AIN2 6
#define STBY 8 // Standby引脚
void setup() {
pinMode(PWMA, OUTPUT);
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(STBY, OUTPUT);
digitalWrite(STBY, HIGH); // 使能
}
void motorDrive(int speed) {
if (speed > 0) {
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, LOW);
} else {
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, HIGH);
speed = -speed;
}
analogWrite(PWMA, speed);
}
2.2.2 步进电机(Stepper Motor)
细分驱动:
全步: 1.8度/步 (200步/转)
半步: 0.9度/步 (400步/转)
1/4步: 0.45度/步 (800步/转)
1/8步: 0.225度/步 (1600步/转)
1/16步: 0.1125度/步 (3200步/转)
A4988驱动器控制:
#define STEP_PIN 3
#define DIR_PIN 4
#define ENABLE_PIN 5
// 微步设置引脚
#define MS1 6
#define MS2 7
#define MS3 8
void setup() {
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
pinMode(ENABLE_PIN, OUTPUT);
pinMode(MS1, OUTPUT);
pinMode(MS2, OUTPUT);
pinMode(MS3, OUTPUT);
// 设置1/16细分
digitalWrite(MS1, HIGH);
digitalWrite(MS2, HIGH);
digitalWrite(MS3, HIGH);
digitalWrite(ENABLE_PIN, LOW); // 使能
}
// S型加减速曲线
void moveWithAccel(long steps, int max_speed, int accel_steps) {
digitalWrite(DIR_PIN, steps > 0 ? HIGH : LOW);
steps = abs(steps);
for (long i = 0; i < steps; i++) {
int delay_time;
if (i < accel_steps) {
// 加速阶段
float ratio = (float)i / accel_steps;
delay_time = 2000 - ratio * (2000 - max_speed);
} else if (i > steps - accel_steps) {
// 减速阶段
float ratio = (float)(steps - i) / accel_steps;
delay_time = 2000 - ratio * (2000 - max_speed);
} else {
// 匀速阶段
delay_time = max_speed;
}
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(delay_time);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(delay_time);
}
}
2.2.3 舵机(Servo Motor)
PWM角度控制:
标准舵机:
- PWM周期: 20ms (50Hz)
- 0度: 0.5ms高电平 (占空比2.5%)
- 90度: 1.5ms高电平 (占空比7.5%)
- 180度: 2.5ms高电平 (占空比12.5%)
Arduino舵机控制:
#include <Servo.h>
Servo myservo;
void setup() {
myservo.attach(9); // 连接到引脚9
}
void loop() {
// 扫描0-180度
for (int pos = 0; pos <= 180; pos += 1) {
myservo.write(pos);
delay(15);
}
for (int pos = 180; pos >= 0; pos -= 1) {
myservo.write(pos);
delay(15);
}
}
// 自定义PWM控制(更精确)
void setServoPulse(int pin, int angle) {
int pulse_width = map(angle, 0, 180, 500, 2500);
digitalWrite(pin, HIGH);
delayMicroseconds(pulse_width);
digitalWrite(pin, LOW);
delay(20 - pulse_width/1000);
}
2.3 传感器硬件
2.3.1 激光雷达
RPLIDAR A1规格:
扫描频率: 5.5Hz (330rpm)
采样频率: 8000次/秒
测距范围: 0.15-12m
角度分辨率: 1度
接口: UART (115200bps)
供电: 5V DC
Arduino串口读取:
#define RPLIDAR_MOTOR 3 // PWM控制电机
void setup() {
Serial.begin(115200); // 连接RPLIDAR
Serial1.begin(115200); // 调试输出
pinMode(RPLIDAR_MOTOR, OUTPUT);
analogWrite(RPLIDAR_MOTOR, 255); // 启动电机
}
void loop() {
if (Serial.available() >= 5) {
byte sync1 = Serial.read();
byte sync2 = Serial.read();
if (sync1 == 0xA5 && sync2 == 0x5A) {
// 读取数据包
byte quality = Serial.read();
byte angle_low = Serial.read();
byte angle_high = Serial.read();
byte distance_low = Serial.read();
byte distance_high = Serial.read();
float angle = ((angle_high << 8) | angle_low) / 64.0;
float distance = ((distance_high << 8) | distance_low) / 4.0;
Serial1.print("Angle: ");
Serial1.print(angle);
Serial1.print(" Distance: ");
Serial1.println(distance);
}
}
}
树莓派ROS集成:
#!/usr/bin/env python
import rospy
from sensor_msgs.msg import LaserScan
# 安装rplidar驱动
# sudo apt install ros-noetic-rplidar-ros
# launch文件配置
"""
<launch>
<node name="rplidar_node" pkg="rplidar_ros" type="rplidarNode" output="screen">
<param name="serial_port" type="string" value="/dev/ttyUSB0"/>
<param name="serial_baudrate" type="int" value="115200"/>
<param name="frame_id" type="string" value="laser"/>
<param name="inverted" type="bool" value="false"/>
<param name="angle_compensate" type="bool" value="true"/>
</node>
</launch>
"""
2.3.2 深度相机
Intel RealSense D435规格:
深度技术: 主动立体视觉
RGB分辨率: 1920x1080 @ 30fps
深度分辨率: 1280x720 @ 90fps
测距范围: 0.3-10m
视场角: 87°×58°
接口: USB 3.0
树莓派配置:
# 安装RealSense SDK
sudo apt install librealsense2-dkms librealsense2-utils
# 安装ROS包
sudo apt install ros-noetic-realsense2-camera
# 测试
realsense-viewer
ROS启动配置:
<launch>
<node name="realsense_node" pkg="realsense2_camera" type="realsense2_camera_node">
<param name="enable_depth" value="true"/>
<param name="enable_color" value="true"/>
<param name="enable_infra1" value="false"/>
<param name="enable_infra2" value="false"/>
<param name="depth_width" value="640"/>
<param name="depth_height" value="480"/>
<param name="depth_fps" value="30"/>
<param name="color_width" value="640"/>
<param name="color_height" value="480"/>
<param name="color_fps" value="30"/>
</node>
</launch>
2.3.3 IMU(惯性测量单元)
MPU6050规格:
加速度计: ±2g/±4g/±8g/±16g
陀螺仪: ±250/±500/±1000/±2000 °/s
接口: I2C
I2C地址: 0x68 (AD0=0) 或 0x69 (AD0=1)
供电: 3-5V
Arduino I2C读取:
#include <Wire.h>
const int MPU6050_ADDR = 0x68;
void setup() {
Serial.begin(9600);
Wire.begin();
// 唤醒MPU6050
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(0x6B); // PWR_MGMT_1寄存器
Wire.write(0); // 设置为0(唤醒)
Wire.endTransmission(true);
}
void loop() {
int16_t ax, ay, az, gx, gy, gz;
// 读取加速度计和陀螺仪
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(0x3B); // 从ACCEL_XOUT_H开始读
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_ADDR, 14, true);
ax = Wire.read() << 8 | Wire.read();
ay = Wire.read() << 8 | Wire.read();
az = Wire.read() << 8 | Wire.read();
Wire.read(); Wire.read(); // 跳过温度
gx = Wire.read() << 8 | Wire.read();
gy = Wire.read() << 8 | Wire.read();
gz = Wire.read() << 8 | Wire.read();
// 转换为实际值
float accel_x = ax / 16384.0; // ±2g
float accel_y = ay / 16384.0;
float accel_z = az / 16384.0;
float gyro_x = gx / 131.0; // ±250°/s
float gyro_y = gy / 131.0;
float gyro_z = gz / 131.0;
Serial.print("AccX: "); Serial.print(accel_x);
Serial.print(" AccY: "); Serial.print(accel_y);
Serial.print(" AccZ: "); Serial.print(accel_z);
Serial.print(" GyroX: "); Serial.print(gyro_x);
Serial.print(" GyroY: "); Serial.print(gyro_y);
Serial.print(" GyroZ: "); Serial.println(gyro_z);
delay(100);
}
BNO055(带磁力计和融合算法):
#include <Adafruit_BNO055.h>
Adafruit_BNO055 bno = Adafruit_BNO055(55);
void setup() {
Serial.begin(9600);
if (!bno.begin()) {
Serial.println("BNO055未检测到");
while (1);
}
bno.setExtCrystalUse(true);
}
void loop() {
// 获取欧拉角(内部融合算法)
sensors_event_t event;
bno.getEvent(&event);
Serial.print("Heading: "); Serial.print(event.orientation.x);
Serial.print(" Roll: "); Serial.print(event.orientation.y);
Serial.print(" Pitch: "); Serial.println(event.orientation.z);
// 获取四元数
imu::Quaternion quat = bno.getQuat();
Serial.print("Quat w: "); Serial.print(quat.w());
Serial.print(" x: "); Serial.print(quat.x());
Serial.print(" y: "); Serial.print(quat.y());
Serial.print(" z: "); Serial.println(quat.z());
delay(100);
}
2.3.4 超声波传感器
HC-SR04规格:
测距范围: 2cm-400cm
精度: 3mm
工作频率: 40kHz
触发信号: 10us高电平
回响信号: 150us-25ms
Arduino测距代码:
#define TRIG_PIN 9
#define ECHO_PIN 10
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
}
float getDistance() {
// 发送触发信号
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// 测量回响时间
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 超时30ms
if (duration == 0) {
return -1; // 超时
}
// 计算距离(cm)
float distance = duration * 0.034 / 2;
return distance;
}
void loop() {
float dist = getDistance();
if (dist > 0) {
Serial.print("距离: ");
Serial.print(dist);
Serial.println(" cm");
} else {
Serial.println("测量失败");
}
delay(200);
}
2.4 单片机与开发板
2.4.1 Arduino平台
Arduino Mega 2560规格:
微控制器: ATmega2560
工作电压: 5V
数字I/O: 54个(15个PWM)
模拟输入: 16个
串口: 4个(UART)
Flash: 256KB
SRAM: 8KB
时钟频率: 16MHz
机器人底盘控制完整代码:
// 差速机器人控制
#define MOTOR_L1 7
#define MOTOR_L2 6
#define PWM_L 5
#define MOTOR_R1 4
#define MOTOR_R2 3
#define PWM_R 2
#define ENCODER_L_A 18
#define ENCODER_L_B 19
#define ENCODER_R_A 20
#define ENCODER_R_B 21
volatile long encoder_left = 0;
volatile long encoder_right = 0;
const float WHEEL_RADIUS = 0.05; // 轮半径(m)
const float WHEEL_BASE = 0.3; // 轮距(m)
const float PPR = 1000.0; // 编码器精度
// PID参数
float Kp = 1.0, Ki = 0.1, Kd = 0.05;
float error_left = 0, error_right = 0;
float integral_left = 0, integral_right = 0;
float last_error_left = 0, last_error_right = 0;
void setup() {
Serial.begin(115200);
// 电机引脚
pinMode(MOTOR_L1, OUTPUT);
pinMode(MOTOR_L2, OUTPUT);
pinMode(PWM_L, OUTPUT);
pinMode(MOTOR_R1, OUTPUT);
pinMode(MOTOR_R2, OUTPUT);
pinMode(PWM_R, OUTPUT);
// 编码器引脚
pinMode(ENCODER_L_A, INPUT_PULLUP);
pinMode(ENCODER_L_B, INPUT_PULLUP);
pinMode(ENCODER_R_A, INPUT_PULLUP);
pinMode(ENCODER_R_B, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCODER_L_A), leftEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(ENCODER_R_A), rightEncoder, CHANGE);
}
void leftEncoder() {
if (digitalRead(ENCODER_L_A) == digitalRead(ENCODER_L_B)) {
encoder_left++;
} else {
encoder_left--;
}
}
void rightEncoder() {
if (digitalRead(ENCODER_R_A) == digitalRead(ENCODER_R_B)) {
encoder_right++;
} else {
encoder_right--;
}
}
void setMotor(int motor, int speed) {
// motor: 0=左, 1=右
// speed: -255到255
int pin1, pin2, pwm_pin;
if (motor == 0) {
pin1 = MOTOR_L1; pin2 = MOTOR_L2; pwm_pin = PWM_L;
} else {
pin1 = MOTOR_R1; pin2 = MOTOR_R2; pwm_pin = PWM_R;
}
if (speed > 0) {
digitalWrite(pin1, HIGH);
digitalWrite(pin2, LOW);
analogWrite(pwm_pin, constrain(speed, 0, 255));
} else if (speed < 0) {
digitalWrite(pin1, LOW);
digitalWrite(pin2, HIGH);
analogWrite(pwm_pin, constrain(-speed, 0, 255));
} else {
digitalWrite(pin1, LOW);
digitalWrite(pin2, LOW);
analogWrite(pwm_pin, 0);
}
}
int pidControl(float target, float current, float &integral,
float &last_error) {
float error = target - current;
integral += error;
integral = constrain(integral, -100, 100); // 抗积分饱和
float derivative = error - last_error;
float output = Kp * error + Ki * integral + Kd * derivative;
last_error = error;
return constrain((int)output, -255, 255);
}
void setVelocity(float linear, float angular) {
// 运动学逆解
float v_left = linear - angular * WHEEL_BASE / 2;
float v_right = linear + angular * WHEEL_BASE / 2;
// 这里应该使用PID闭环控制
// 简化版直接映射
int pwm_left = (int)(v_left * 255 / 1.0); // 假设最大速度1m/s
int pwm_right = (int)(v_right * 255 / 1.0);
setMotor(0, pwm_left);
setMotor(1, pwm_right);
}
void loop() {
// 串口接收速度命令
if (Serial.available() >= 8) {
float linear, angular;
Serial.readBytes((char*)&linear, 4);
Serial.readBytes((char*)&angular, 4);
setVelocity(linear, angular);
}
// 发送里程计数据
static unsigned long last_time = 0;
unsigned long current_time = millis();
if (current_time - last_time >= 50) { // 20Hz
float dt = (current_time - last_time) / 1000.0;
// 计算速度
float v_left = (encoder_left / PPR) * (2 * PI * WHEEL_RADIUS) / dt;
float v_right = (encoder_right / PPR) * (2 * PI * WHEEL_RADIUS) / dt;
encoder_left = 0;
encoder_right = 0;
// 发送数据
Serial.print(v_left); Serial.print(",");
Serial.println(v_right);
last_time = current_time;
}
}
2.4.2 树莓派4B
规格:
处理器: Broadcom BCM2711 (4核 Cortex-A72 @ 1.5GHz)
内存: 2GB/4GB/8GB LPDDR4
网络: Gigabit Ethernet, WiFi 5, Bluetooth 5.0
GPIO: 40针
USB: 2x USB 3.0, 2x USB 2.0
供电: 5V 3A USB-C
操作系统: Ubuntu 20.04 + ROS Noetic
ROS安装:
# 安装ROS Noetic
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo apt update
sudo apt install ros-noetic-desktop
# 环境配置
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
GPIO控制(Python):
#!/usr/bin/env python3
import RPi.GPIO as GPIO
import time
# 电机控制
MOTOR_L1 = 17
MOTOR_L2 = 27
PWM_L = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(MOTOR_L1, GPIO.OUT)
GPIO.setup(MOTOR_L2, GPIO.OUT)
GPIO.setup(PWM_L, GPIO.OUT)
pwm = GPIO.PWM(PWM_L, 1000) # 1kHz
pwm.start(0)
def set_motor(speed):
"""speed: -100到100"""
if speed > 0:
GPIO.output(MOTOR_L1, GPIO.HIGH)
GPIO.output(MOTOR_L2, GPIO.LOW)
pwm.ChangeDutyCycle(speed)
elif speed < 0:
GPIO.output(MOTOR_L1, GPIO.LOW)
GPIO.output(MOTOR_L2, GPIO.HIGH)
pwm.ChangeDutyCycle(-speed)
else:
GPIO.output(MOTOR_L1, GPIO.LOW)
GPIO.output(MOTOR_L2, GPIO.LOW)
pwm.ChangeDutyCycle(0)
try:
while True:
# 加速
for speed in range(0, 101, 10):
set_motor(speed)
time.sleep(0.1)
# 减速
for speed in range(100, -1, -10):
set_motor(speed)
time.sleep(0.1)
except KeyboardInterrupt:
pwm.stop()
GPIO.cleanup()
串口通信(与Arduino通信):
#!/usr/bin/env python3
import serial
import struct
ser = serial.Serial('/dev/ttyACM0', 115200, timeout=1)
def send_velocity(linear, angular):
"""发送速度命令到Arduino"""
data = struct.pack('ff', linear, angular)
ser.write(data)
def read_odometry():
"""读取里程计数据"""
if ser.in_waiting:
line = ser.readline().decode('utf-8').strip()
if ',' in line:
v_left, v_right = map(float, line.split(','))
return v_left, v_right
return None, None
# 使用示例
send_velocity(0.5, 0.0) # 直行0.5m/s
time.sleep(1)
v_left, v_right = read_odometry()
print(f"Left: {v_left}, Right: {v_right}")
2.4.3 Jetson Nano/Xavier
Jetson Nano规格:
GPU: 128-core Maxwell
CPU: Quad-core ARM A57 @ 1.43GHz
内存: 4GB LPDDR4
存储: microSD
供电: 5V 4A / 5V 2A(低功耗模式)
功耗: 10W(最大)
CUDA核心: 128
适用: 深度学习推理
CUDA加速示例:
import cv2
import numpy as np
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
# YOLO TensorRT推理
class YOLOTensorRT:
def __init__(self, engine_path):
self.logger = trt.Logger(trt.Logger.WARNING)
with open(engine_path, 'rb') as f:
self.engine = trt.Runtime(self.logger).deserialize_cuda_engine(f.read())
self.context = self.engine.create_execution_context()
# 分配显存
self.inputs = []
self.outputs = []
self.bindings = []
for binding in self.engine:
size = trt.volume(self.engine.get_binding_shape(binding))
dtype = trt.nptype(self.engine.get_binding_dtype(binding))
host_mem = cuda.pagelocked_empty(size, dtype)
device_mem = cuda.mem_alloc(host_mem.nbytes)
self.bindings.append(int(device_mem))
if self.engine.binding_is_input(binding):
self.inputs.append({'host': host_mem, 'device': device_mem})
else:
self.outputs.append({'host': host_mem, 'device': device_mem})
def infer(self, image):
# 图像预处理
input_image = cv2.resize(image, (640, 640))
input_image = input_image.astype(np.float32) / 255.0
input_image = np.transpose(input_image, (2, 0, 1))
# 复制到GPU
np.copyto(self.inputs[0]['host'], input_image.ravel())
cuda.memcpy_htod(self.inputs[0]['device'], self.inputs[0]['host'])
# 推理
self.context.execute_v2(bindings=self.bindings)
# 复制回CPU
cuda.memcpy_dtoh(self.outputs[0]['host'], self.outputs[0]['device'])
return self.outputs[0]['host']
2.5 电源设计
2.5.1 电池选择
锂电池对比:
| 类型 | 电压 | 容量 | 重量 | 放电率 | 适用场景 |
|---|---|---|---|---|---|
| 18650 | 3.7V | 2000-3500mAh | 45g/节 | 1-3C | 通用 |
| 聚合物 | 3.7V | 定制 | 轻 | 10-30C | 高功率 |
| 磷酸铁锂 | 3.2V | 高 | 重 | 1-5C | 长寿命 |
电池组配置:
3S电池组:
3节串联 = 11.1V标称电压
满电: 12.6V
安全截止: 9V
功率计算:
电流 = 功率 / 电压
例: 50W电机在12V下
I = 50W / 12V = 4.17A
容量计算:
续航时间 = 电池容量 / 平均电流
例: 5000mAh电池,平均4A
续航 = 5Ah / 4A = 1.25小时
2.5.2 电源转换
降压模块(DC-DC Buck):
输入: 12V(电池)
输出1: 5V 3A (树莓派)
输出2: 5V 2A (传感器)
输出3: 12V (电机驱动)
推荐芯片:
LM2596: 3A输出
XL4015: 5A输出
电路原理图:
+12V Battery
|
|
[保护二极管]
|
+--- [DC-DC 1] ---> 5V 3A (树莓派)
|
+--- [DC-DC 2] ---> 5V 2A (传感器)
|
+--- [直通] -----> 12V (电机)
|
GND
2.5.3 电源保护电路
完整保护方案:
1. 过流保护: 自恢复保险丝
2. 反接保护: 肖特基二极管或MOSFET
3. 过压保护: 稳压管
4. 电池管理: BMS(充放电保护)
保护电路代码(电压监测):
#define BATTERY_PIN A0
#define VOLTAGE_DIVIDER_RATIO 4.0 // 分压比
float getBatteryVoltage() {
int raw = analogRead(BATTERY_PIN);
float voltage = (raw / 1023.0) * 5.0 * VOLTAGE_DIVIDER_RATIO;
return voltage;
}
void checkBattery() {
float voltage = getBatteryVoltage();
if (voltage < 9.0) {
// 电池电压过低,停止电机
Serial.println("警告: 电池电压过低!");
stopAllMotors();
digitalWrite(LED_PIN, HIGH); // 报警LED
} else if (voltage < 10.0) {
// 低电量警告
Serial.println("警告: 电池电量低");
}
}
2.6 完整硬件系统架构
2.6.1 系统电路图
[12V 锂电池组]
|
[电源开关]
|
+----------+----------+
| | |
[12V直通] [5V 3A] [5V 2A]
| | |
[电机驱动] [树莓派4B] [传感器]
L298N | |
/ \ [USB] [I2C/UART]
/ \ | |
[左电机] [右电机] [Arduino] [激光雷达]
| | Mega [IMU]
[编码器] [编码器] | [超声波]
|
[舵机控制]
2.6.2 BOM清单示例
| 序号 | 名称 | 型号 | 数量 | 单价 | 总价 | 备注 |
|---|---|---|---|---|---|---|
| 1 | 主控板 | 树莓派4B 4GB | 1 | 350 | 350 | 核心控制 |
| 2 | 单片机 | Arduino Mega 2560 | 1 | 50 | 50 | 底层控制 |
| 3 | 激光雷达 | RPLIDAR A1 | 1 | 500 | 500 | SLAM建图 |
| 4 | IMU | MPU6050 | 1 | 15 | 15 | 姿态测量 |
| 5 | 电机驱动 | L298N | 2 | 15 | 30 | 双路H桥 |
| 6 | 直流电机 | 12V 200RPM | 2 | 30 | 60 | 带编码器 |
| 7 | 轮子 | 橡胶轮65mm | 2 | 10 | 20 | 防滑 |
| 8 | 万向轮 | 小型万向轮 | 1 | 5 | 5 | 支撑 |
| 9 | 锂电池 | 3S 11.1V 5000mAh | 1 | 120 | 120 | 聚合物 |
| 10 | 电源模块 | LM2596降压模块 | 2 | 10 | 20 | 5V输出 |
| 11 | 超声波 | HC-SR04 | 4 | 5 | 20 | 避障 |
| 12 | 底盘 | 亚克力板 | 1 | 30 | 30 | 300x200mm |
| 13 | 连接线 | 杜邦线若干 | 1 | 20 | 20 | 公母/母母 |
| 14 | 开关 | 船型开关 | 1 | 3 | 3 | 电源开关 |
| 15 | 散热 | 散热片+风扇 | 1 | 15 | 15 | 树莓派散热 |
| 总计 | 1258 |
2.7 高频面试题
问题1: 直流电机的PWM调速原理是什么?如何提高控制精度?
答案:
PWM调速原理:
- PWM(脉宽调制)通过改变高电平占空比控制电机平均电压
- 占空比 = 高电平时间 / 周期时间
- 平均电压 = 占空比 × 供电电压
- 电机转速正比于平均电压
提高精度的方法:
提高PWM频率
- 常用频率: 1kHz-20kHz
- 频率过低: 电机抖动
- 频率过高: 开关损耗大
使用编码器闭环控制
float target_speed = 1.0; // m/s float current_speed = getSpeedFromEncoder(); float error = target_speed - current_speed; int pwm = pid_controller(error);加减速曲线
- 避免突然启停
- S型曲线更平滑
电压补偿
- 监测电池电压
- 根据电压调整PWM
死区补偿
- 电机启动存在最小电压
- PWM低于阈值时不转动
问题2: 如何选择合适的电机驱动芯片?L298N和TB6612有什么区别?
答案:
对比表格:
| 特性 | L298N | TB6612 |
|---|---|---|
| 工作电压 | 5-35V | 4.5-13.5V |
| 最大电流 | 2A(持续) | 1.2A(持续) |
| 效率 | 约50% | 约90% |
| 压降 | 3-4V | 1V |
| 发热 | 严重 | 较小 |
| 价格 | 便宜 | 稍贵 |
| 适用 | 低速大扭矩 | 高速小负载 |
选择建议:
L298N适用场景:
- 大功率电机(>1A)
- 电压充足(>12V)
- 成本敏感
- 不在意效率
TB6612适用场景:
- 小功率电机(<1A)
- 电池供电(效率重要)
- 发热受限
- 低压应用(5-12V)
其他选择:
- DRV8833: 小功率,低压
- BTS7960: 大功率(43A)
- A4950: 步进电机
问题3: 激光雷达和深度相机的区别?如何选择?
答案:
对比表格:
| 特性 | 激光雷达 | 深度相机 |
|---|---|---|
| 测距原理 | 激光飞行时间 | 立体视觉/结构光 |
| 精度 | 高(mm级) | 中(cm级) |
| 测距范围 | 远(>10m) | 近(0.3-10m) |
| 扫描角度 | 360度 | 有限(<90度) |
| 环境光影响 | 小 | 大 |
| 室外使用 | 好 | 差 |
| 获取信息 | 2D距离 | 3D点云+RGB |
| 价格 | 贵 | 便宜 |
| 数据量 | 小 | 大 |
选择建议:
选择激光雷达:
- 室外环境
- 2D导航建图
- 远距离测量
- 360度感知
选择深度相机:
- 室内环境
- 需要颜色信息
- 3D重建
- 目标识别
- 成本敏感
组合使用:
- 激光雷达: 全局导航
- 深度相机: 局部避障+识别
- 互补优势
问题4: 树莓派和Arduino在机器人系统中如何分工?
答案:
分工原则:
Arduino (底层控制):
- 实时性要求高的任务
- 硬件接口直接控制
- 高频率循环(>100Hz)
- 简单逻辑
树莓派 (高层决策):
- 复杂算法
- ROS节点运行
- 传感器数据处理
- 导航规划
典型分工方案:
| 任务 | Arduino | 树莓派 |
|---|---|---|
| 电机PWM控制 | 是 | |
| 编码器读取 | 是 | |
| PID速度控制 | 是 | |
| 超声波测距 | 是 | |
| 激光雷达读取 | 是 | |
| 图像处理 | 是 | |
| SLAM建图 | 是 | |
| 路径规划 | 是 | |
| ROS节点 | 是 |
通信方式:
# 树莓派通过串口发送速度命令
import serial
ser = serial.Serial('/dev/ttyACM0', 115200)
# 发送线速度和角速度
cmd = f"{linear_vel},{angular_vel}\n"
ser.write(cmd.encode())
# Arduino接收并控制电机
String data = Serial.readStringUntil('\n');
float linear = data.substring(0, data.indexOf(',')).toFloat();
float angular = data.substring(data.indexOf(',') + 1).toFloat();
优势:
- Arduino: 实时性好,稳定
- 树莓派: 算力强,生态丰富
- 分工明确,各司其职
问题5: 如何设计机器人的电源系统?需要考虑哪些因素?
答案:
设计步骤:
- 功耗评估
树莓派4B: 5V 3A = 15W
Arduino Mega: 5V 0.5A = 2.5W
电机(2个): 12V 2A×2 = 48W
激光雷达: 5V 1A = 5W
传感器: 5V 0.5A = 2.5W
-----------------------------------
总功耗: 约73W
峰值功耗(电机启动): 约100W
- 电池选择
选择12V(3S)锂电池
容量: 5000mAh
续航时间计算:
平均电流 = 73W / 12V = 6.1A
续航 = 5Ah / 6.1A = 0.82小时 ≈ 50分钟
建议选择8000mAh或更大
- 电源分配
12V主线 ──┬─→ [电机驱动] (12V直通)
│
├─→ [DC-DC 5V/3A] → 树莓派
│
├─→ [DC-DC 5V/2A] → Arduino + 传感器
│
└─→ [DC-DC 5V/1A] → 激光雷达(独立供电)
- 保护设计
// 电压监测
void batteryMonitor() {
float voltage = getBatteryVoltage();
if (voltage < 9.0) {
emergencyStop(); // 紧急停止
} else if (voltage < 10.0) {
reducePower(); // 降低功率
warning(); // 发出警告
}
}
// 功率管理
void powerManagement() {
if (isLowBattery()) {
disableNonCritical(); // 关闭非关键功能
enableReturnHome(); // 启动返航
}
}
- 安全考虑
- 自恢复保险丝(每路)
- 反接保护二极管
- 急停开关
- BMS保护板
- 过温监测
- 布线规范
- 粗线走大电流(≥18AWG)
- 信号线与动力线分离
- 使用屏蔽线(减少干扰)
- 所有地线连接到公共地
完整电源方案框图:
[3S锂电池 11.1V 5000mAh]
|
[BMS保护板]
|
[主开关]
|
[自恢复保险丝 10A]
|
[电压监测分压器]
|
+----+----+----+
| | | |
[12V] [5V] [5V] [5V]
| 3A 2A 1A
电机 树莓派 Arduino 雷达