Skip to content
清晨的一缕阳光
返回

制作第一个机器人

制作第一个机器人

从零开始制作一个机器人是学习机器人技术的最佳方式。本文提供完整的实战教程,从需求分析、硬件选型、软件配置到控制实现,带你一步步制作出第一个可以行走、避障、交互的机器人。

一、项目规划

1.1 机器人类型选择

入门级机器人类型

类型难度成本时间技能要求
轮式小车⭐⭐¥500-10001-2 周基础编程
四足机器狗⭐⭐⭐⭐¥3000-80001-2 月控制理论
人形机器人⭐⭐⭐⭐⭐¥10000-500003-6 月多学科知识
机械臂⭐⭐⭐¥2000-50002-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, 3I2CIMU
│   ├── GPIO 14, 15UART(激光雷达)
│   └── 5V, GND → 传感器供电

├── USB 连接
│   ├── USB 摄像头/深度相机
│   └── USB WiFi 适配器(如需要)

└── 电源连接
    ├── 电池 12V → 电机驱动
    ├── 12V5V 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 项目总结

完成的功能

学到的技能

8.2 下一步

进阶项目

  1. 自主导航(Nav2)
  2. 多机器人协作
  3. 机械臂集成
  4. 四足机器人
  5. 人形机器人

学习资源

制作第一个机器人是学习机器人技术的最佳方式。通过这个项目,你将掌握从硬件到软件的完整知识链。不要怕失败,每一次调试都是学习的机会。祝你成功!


参考资料


分享这篇文章到:

上一篇文章
Sentinel 基础
下一篇文章
MySQL 主从复制模式