制作第一个机器人
从零开始制作一个机器人是学习机器人技术的最佳方式。本文提供完整的实战教程,从需求分析、硬件选型、软件配置到控制实现,带你一步步制作出第一个可以行走、避障、交互的机器人。
一、项目规划
1.1 机器人类型选择
入门级机器人类型:
| 类型 | 难度 | 成本 | 时间 | 技能要求 |
|---|---|---|---|---|
| 轮式小车 | ⭐⭐ | ¥500-1000 | 1-2 周 | 基础编程 |
| 四足机器狗 | ⭐⭐⭐⭐ | ¥3000-8000 | 1-2 月 | 控制理论 |
| 人形机器人 | ⭐⭐⭐⭐⭐ | ¥10000-50000 | 3-6 月 | 多学科知识 |
| 机械臂 | ⭐⭐⭐ | ¥2000-5000 | 2-4 周 | 运动学 |
推荐入门:轮式小车 → 机械臂 → 四足 → 人形
1.2 本教程项目
项目:智能轮式机器人
项目规格
功能:
├── 自主导航(SLAM)
├── 避障(激光/视觉)
├── 语音交互
└── 远程遥控
性能:
├── 速度:0-1 m/s
├── 续航:2 小时
├── 负载:2 kg
└── 尺寸:30×30×20 cm
预算:¥3000-5000
时间:2-4 周
难度:⭐⭐⭐(适合入门)
二、硬件选型
2.1 核心组件
完整硬件清单:
硬件清单与预算
┌─────────────────────────────────────────┐
│ 1. 计算平台(¥800-2000) │
├─────────────────────────────────────────┤
│ 选项 A:树莓派 4B(4GB) │
│ - 价格:¥500 │
│ - CPU: 4 核 ARM Cortex-A72 │
│ - RAM: 4GB │
│ - 适用:入门学习 │
│ │
│ 选项 B:Jetson Nano │
│ - 价格:¥800 │
│ - GPU: 128 核 CUDA │
│ - 适用:视觉 AI │
│ │
│ 选项 C:Jetson Orin Nano │
│ - 价格:¥2000 │
│ - GPU: 1024 核 CUDA │
│ - 适用:高性能 AI │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 2. 底盘与电机(¥500-1000) │
├─────────────────────────────────────────┤
│ 底盘:铝合金/亚克力(¥200-400) │
│ - 尺寸:30×30 cm │
│ - 层数:2-3 层 │
│ │
│ 电机:直流减速电机 ×2(¥150-300) │
│ - 电压:12V │
│ - 转速:100-200 RPM │
│ - 扭矩:5-10 kg·cm │
│ │
│ 电机驱动:L298N/TB6612(¥50-100) │
│ │
│ 轮子:万向轮 + 驱动轮(¥100-200) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 3. 传感器(¥800-1500) │
├─────────────────────────────────────────┤
│ 激光雷达(可选): │
│ - RPLIDAR A1: ¥800 │
│ - Slamtec R2: ¥1500 │
│ │
│ 深度相机(可选): │
│ - Intel RealSense D435i: ¥1500 │
│ - 奥比中光 Femto: ¥800 │
│ │
│ IMU:MPU6050(¥30) │
│ │
│ 超声波:HC-SR04 ×4(¥40) │
│ │
│ 电池电压检测:(¥50) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 4. 电源系统(¥300-500) │
├─────────────────────────────────────────┤
│ 电池:18650 锂电池组(¥200-300) │
│ - 容量:5000-10000 mAh │
│ - 电压:12V(3 串) │
│ │
│ BMS 保护板:(¥50) │
│ │
│ DC-DC 转换器:12V→5V(¥50) │
│ │
│ 开关、保险丝:(¥50) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 5. 其他配件(¥200-400) │
├─────────────────────────────────────────┤
│ 螺丝螺母套装:(¥50) │
│ │
│ 杜邦线、排线:(¥50) │
│ │
│ 3D 打印件:(¥100-200) │
│ │
│ 开关、LED 指示灯:(¥50) │
│ │
│ 工具:螺丝刀、剥线钳(已有可省) │
└─────────────────────────────────────────┘
总计:¥2600-5400
建议预算:¥4000
2.2 采购渠道
推荐采购渠道
国内:
├── 淘宝/天猫
│ ├── 树莓派:官方授权店
│ ├── 电机:嘉立创、DFRobot
│ └── 传感器:各种专卖店
│
├── 京东
│ └── 物流快、售后好
│
├── 立创商城
│ └── 电子元器件
│
└── 1688
└── 批量采购便宜
国外:
├── Adafruit
├── SparkFun
├── Seeed Studio
└── Amazon
二手:
└── 闲鱼(注意验货)
三、机械组装
3.1 底盘组装
底盘组装步骤
步骤 1:准备底盘
├── 亚克力/铝合金板 ×3(上层、中层、底层)
├── 铜柱 ×20(M3×20mm)
└── 螺丝螺母 ×40(M3)
步骤 2:安装电机
├── 将电机固定到中层板
├── 使用电机支架
└── 确保电机轴平行
步骤 3:安装轮子
├── 驱动轮压入电机轴
├── 万向轮安装到底层
└── 检查转动顺畅
步骤 4:层间连接
├── 使用铜柱连接三层板
├── 间距:5-8 cm
└── 确保平行
步骤 5:检查
├── 所有螺丝紧固
├── 轮子着地均匀
└── 结构稳固
3.2 电气连接
# 电气连接图
计算平台(树莓派)
├── GPIO 连接
│ ├── GPIO 17, 27 → 电机驱动 IN1, IN2(左)
│ ├── GPIO 22, 23 → 电机驱动 IN3, IN4(右)
│ ├── GPIO 5 → 电机驱动 ENA(左使能,PWM)
│ ├── GPIO 6 → 电机驱动 ENB(右使能,PWM)
│ ├── GPIO 2, 3 → I2C(IMU)
│ ├── GPIO 14, 15 → UART(激光雷达)
│ └── 5V, GND → 传感器供电
│
├── USB 连接
│ ├── USB 摄像头/深度相机
│ └── USB WiFi 适配器(如需要)
│
└── 电源连接
├── 电池 12V → 电机驱动
├── 12V→5V DC-DC → 树莓派
└── 5V → 传感器
# 接线注意事项
1. 电源先断开再接线
2. 电机驱动使能先接地
3. GPIO 不要接 12V
4. 使用万用表检查短路
3.3 3D 打印件
传感器支架:
# OpenSCAD 传感器支架设计
# 激光雷达支架
module lidar_mount() {
difference() {
cylinder(h=20, d=60);
cylinder(h=25, d=50);
}
// 螺丝孔
for(i=[0:3]) {
rotate([0, 0, i*90])
translate([30, 0, 10])
cylinder(h=20, d=3);
}
}
# 摄像头支架
module camera_mount() {
// 可调节角度
// 设计略
}
# 导出 STL
lidar_mount();
# render()
# export("lidar_mount.stl");
打印参数:
3D 打印参数
材料:PLA/PETG
层厚:0.2mm
填充:20-30%
壁厚:1.2mm
支撑:根据需要
后处理:去支撑、打磨
四、软件配置
4.1 系统安装
树莓派 Ubuntu 安装:
# 1. 下载 Ubuntu Server
# https://ubuntu.com/download/raspberry-pi
# 2. 写入 SD 卡
# 使用 Raspberry Pi Imager
# 3. 首次启动
# 插入 SD 卡,接通电源
# 4. 基础配置
sudo apt update
sudo apt upgrade -y
# 5. 启用接口
sudo raspi-config
# 启用:SSH, I2C, SPI, UART, Camera
# 6. 设置静态 IP(可选)
sudo nano /etc/netplan/00-installer-config.yaml
4.2 ROS2 安装
# 安装 ROS2 Humble
# 参考之前 ROS2 教程
# 创建工作空间
mkdir -p ~/robot_ws/src
cd ~/robot_ws
colcon build
source install/setup.bash
# 添加到.bashrc
echo "source ~/robot_ws/install/setup.bash" >> ~/.bashrc
4.3 电机控制
#!/usr/bin/env python3
# robot_base/motor_controller.py
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
import RPi.GPIO as GPIO
class MotorController(Node):
"""
电机控制器
"""
def __init__(self):
super().__init__('motor_controller')
# GPIO 设置
GPIO.setmode(GPIO.BCM)
# 左电机
self.left_in1 = 17
self.left_in2 = 27
self.left_en = 5
# 右电机
self.right_in1 = 22
self.right_in2 = 23
self.right_en = 6
# 设置 GPIO
pins = [self.left_in1, self.left_in2, self.left_en,
self.right_in1, self.right_in2, self.right_en]
for pin in pins:
GPIO.setup(pin, GPIO.OUT)
# PWM
self.left_pwm = GPIO.PWM(self.left_en, 1000)
self.right_pwm = GPIO.PWM(self.right_en, 1000)
self.left_pwm.start(0)
self.right_pwm.start(0)
# 订阅速度命令
self.subscription = self.create_subscription(
Twist,
'/cmd_vel',
self.cmd_vel_callback,
10
)
# 电机参数
self.wheel_base = 0.2 # 轮距 (m)
self.wheel_radius = 0.05 # 轮半径 (m)
self.max_speed = 1.0 # 最大速度 (m/s)
def cmd_vel_callback(self, msg):
"""
速度命令回调
"""
linear = msg.linear.x
angular = msg.angular.z
# 差速运动学
left_speed = linear - angular * self.wheel_base / 2
right_speed = linear + angular * self.wheel_base / 2
# 限制速度
left_speed = max(-self.max_speed, min(self.max_speed, left_speed))
right_speed = max(-self.max_speed, min(self.max_speed, right_speed))
# 设置电机
self.set_motor_speed('left', left_speed)
self.set_motor_speed('right', right_speed)
def set_motor_speed(self, side, speed):
"""
设置电机速度
speed: -1.0 ~ 1.0
"""
if side == 'left':
in1, in2, pwm = self.left_in1, self.left_in2, self.left_pwm
else:
in1, in2, pwm = self.right_in1, self.right_in2, self.right_pwm
# 方向控制
if speed >= 0:
GPIO.output(in1, GPIO.HIGH)
GPIO.output(in2, GPIO.LOW)
duty = speed * 100
else:
GPIO.output(in1, GPIO.LOW)
GPIO.output(in2, GPIO.HIGH)
duty = -speed * 100
# PWM 速度
pwm.ChangeDutyCycle(duty)
def stop(self):
"""
停止电机
"""
self.set_motor_speed('left', 0)
self.set_motor_speed('right', 0)
def destroy(self):
"""
清理
"""
self.stop()
GPIO.cleanup()
def main(args=None):
rclpy.init(args=args)
node = MotorController()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy()
rclpy.shutdown()
if __name__ == '__main__':
main()
4.4 传感器驱动
IMU 驱动(MPU6050):
#!/usr/bin/env python3
# imu_driver.py
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Imu
import smbus2
import math
class IMUDriver(Node):
"""
MPU6050 IMU 驱动
"""
def __init__(self):
super().__init__('imu_driver')
# I2C 总线
self.bus = smbus2.SMBus(1)
self.address = 0x68
# 初始化 MPU6050
self.bus.write_byte_data(self.address, 0x6B, 0) # 唤醒
self.bus.write_byte_data(self.address, 0x1B, 0x08) # 陀螺仪±500°/s
self.bus.write_byte_data(self.address, 0x1C, 0x08) # 加速度计±4g
# 发布器
self.publisher = self.create_publisher(Imu, '/imu/data', 10)
# 定时器(100Hz)
self.timer = self.create_timer(0.01, self.publish_imu)
def read_word(self, reg):
"""读取 16 位数据"""
high = self.bus.read_byte_data(self.address, reg)
low = self.bus.read_byte_data(self.address, reg + 1)
value = (high << 8) | low
if value > 32768:
value -= 65536
return value
def publish_imu(self):
"""发布 IMU 数据"""
# 读取加速度计
ax = self.read_word(0x3B) / 8192.0
ay = self.read_word(0x3D) / 8192.0
az = self.read_word(0x3F) / 8192.0
# 读取陀螺仪
gx = self.read_word(0x43) / 65.5
gy = self.read_word(0x45) / 65.5
gz = self.read_word(0x47) / 65.5
# 创建消息
msg = Imu()
msg.header.stamp = self.get_clock().now().to_msg()
msg.header.frame_id = 'imu_link'
# 角速度(rad/s)
msg.angular_velocity.x = math.radians(gx)
msg.angular_velocity.y = math.radians(gy)
msg.angular_velocity.z = math.radians(gz)
# 线加速度(m/s²)
msg.linear_acceleration.x = ax * 9.81
msg.linear_acceleration.y = ay * 9.81
msg.linear_acceleration.z = az * 9.81
self.publisher.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = IMUDriver()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
五、功能实现
5.1 遥控功能
#!/usr/bin/env python3
# teleop_keyboard.py
import rclpy
from geometry_msgs.msg import Twist
import sys, termios, tty
class TeleopKeyboard:
"""
键盘遥控
"""
def __init__(self):
self.node = rclpy.create_node('teleop_keyboard')
self.publisher = self.node.create_publisher(Twist, '/cmd_vel', 10)
self.speed = 0.5
self.turn = 0.5
print("""
机器人键盘遥控
--------------
控制:
w/s: 前进/后退
a/d: 左转/右转
q/e: 加速/减速
空格:停止
Ctrl+C: 退出
""")
def get_key(self):
"""获取键盘输入"""
tty.setraw(sys.stdin.fileno())
key = sys.stdin.read(1)
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings)
return key
def run(self):
"""运行"""
global settings
settings = termios.tcgetattr(sys.stdin)
try:
while True:
key = self.get_key()
twist = Twist()
if key == 'w':
twist.linear.x = self.speed
elif key == 's':
twist.linear.x = -self.speed
elif key == 'a':
twist.angular.z = self.turn
elif key == 'd':
twist.angular.z = -self.turn
elif key == 'q':
self.speed += 0.1
elif key == 'e':
self.speed -= 0.1
elif key == ' ':
twist = Twist()
elif key == '\x03': # Ctrl+C
break
self.publisher.publish(twist)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings)
if __name__ == '__main__':
teleop = TeleopKeyboard()
teleop.run()
5.2 避障功能
#!/usr/bin/env python3
# obstacle_avoidance.py
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
class ObstacleAvoidance(Node):
"""
激光雷达避障
"""
def __init__(self):
super().__init__('obstacle_avoidance')
# 发布器
self.cmd_pub = self.create_publisher(Twist, '/cmd_vel', 10)
# 订阅激光雷达
self.scan_sub = self.create_subscription(
LaserScan,
'/scan',
self.scan_callback,
10
)
# 参数
self.safe_distance = 0.5 # 安全距离 (m)
self.max_speed = 0.5 # 最大速度 (m/s)
self.obstacle_detected = False
def scan_callback(self, msg):
"""
激光雷达回调
"""
# 检查前方障碍
front_ranges = msg.ranges[len(msg.ranges)//2-10:len(msg.ranges)//2+10]
min_distance = min(front_ranges)
twist = Twist()
if min_distance < self.safe_distance:
# 检测到障碍,右转
self.get_logger().info(f'Obstacle detected: {min_distance:.2f}m')
twist.linear.x = 0.0
twist.angular.z = 0.5
else:
# 无障碍,前进
twist.linear.x = self.max_speed
twist.angular.z = 0.0
self.cmd_pub.publish(twist)
def main(args=None):
rclpy.init(args=args)
node = ObstacleAvoidance()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
5.3 SLAM 建图
# 使用 Cartographer SLAM
# 1. 安装
sudo apt install ros-humble-slam-toolbox
# 2. 启动 SLAM
ros2 launch slam_toolbox online_async_launch.py \
slam_toolbox::params_file:=slam_params.yaml
# 3. 遥控建图
ros2 run teleop_twist_keyboard teleop_twist_keyboard
# 4. 保存地图
ros2 run nav2_map_server map_saver_cli -f my_map
# 5. 查看
rviz2
# 添加 Map 显示
六、调试与优化
6.1 常见问题
常见问题与解决方案
问题 1:电机不转
├── 检查:电源连接
├── 检查:GPIO 接线
├── 检查:使能信号
└── 解决:重新接线
问题 2:走不直线
├── 原因:电机速度不一致
├── 解决:PID 调速
└── 解决:机械校准
问题 3:传感器数据异常
├── 检查:I2C/UART 连接
├── 检查:供电电压
├── 检查:驱动代码
└── 解决:更换传感器
问题 4:续航短
├── 原因:电池容量不足
├── 解决:更换大容量电池
└── 解决:优化功耗
问题 5:ROS2 节点崩溃
├── 检查:依赖安装
├── 检查:工作空间编译
├── 查看:日志文件
└── 解决:重新编译
6.2 性能优化
# PID 速度控制
class PIDController:
def __init__(self, kp, ki, kd):
self.kp = kp
self.ki = ki
self.kd = kd
self.integral = 0
self.prev_error = 0
def update(self, setpoint, measured, dt):
error = setpoint - measured
self.integral += error * dt
derivative = (error - self.prev_error) / dt
output = self.kp * error + self.ki * self.integral + self.kd * derivative
self.prev_error = error
return output
# 调参方法
# 1. 先调 kp,使系统响应快速
# 2. 再加 ki,消除稳态误差
# 3. 最后加 kd,抑制超调
七、扩展功能
7.1 语音交互
# 安装语音识别
pip install speechrecognition
pip install pyttsx3
# 简单语音控制
python3 voice_control.py
7.2 视觉跟随
# OpenCV 人脸检测 + 跟随
import cv2
import rclpy
from geometry_msgs.msg import Twist
class FaceFollower(Node):
def __init__(self):
super().__init__('face_follower')
# 加载人脸检测
self.face_cascade = cv2.CascadeClassifier(
'haarcascade_frontalface_default.xml'
)
# 摄像头
self.cap = cv2.VideoCapture(0)
# 发布器
self.cmd_pub = self.create_publisher(Twist, '/cmd_vel', 10)
def follow(self):
while True:
ret, frame = self.cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 人脸检测
faces = self.face_cascade.detectMultiScale(gray, 1.1, 5)
twist = Twist()
if len(faces) > 0:
# 计算人脸中心
x, y, w, h = faces[0]
center_x = x + w / 2
# 转向人脸
if center_x < 320:
twist.angular.z = 0.3
elif center_x > 320:
twist.angular.z = -0.3
else:
twist.linear.x = 0.2
self.cmd_pub.publish(twist)
7.3 手机 APP 控制
# 使用 ROS2 手机 APP
# 推荐:ROS2 Controller App
# 安装 APP
# Google Play / App Store 搜索"ROS2 Controller"
# 配置
# 输入树莓派 IP 地址
# 连接成功
八、总结
8.1 项目总结
完成的功能:
- ✅ 基础移动(前进、后退、转向)
- ✅ 键盘遥控
- ✅ 传感器数据采集
- ✅ 激光雷达避障
- ✅ SLAM 建图(可选)
- ✅ 视觉跟随(可选)
学到的技能:
- 机械组装
- 电气接线
- ROS2 编程
- 传感器驱动
- 控制算法
- 调试技巧
8.2 下一步
进阶项目:
- 自主导航(Nav2)
- 多机器人协作
- 机械臂集成
- 四足机器人
- 人形机器人
学习资源:
- ROS2 官方教程
- Coursera 机器人课程
- GitHub 开源项目
- 机器人论坛
制作第一个机器人是学习机器人技术的最佳方式。通过这个项目,你将掌握从硬件到软件的完整知识链。不要怕失败,每一次调试都是学习的机会。祝你成功!
参考资料:
- ROS2 官方文档
- 树莓派官方指南
- 开源机器人项目
- 各传感器数据手册