Skip to content
Published on

机器人 & AI 完全指南: 从 ROS2 到人形机器人

分享
Authors

1. 机器人基础

自由度 (DOF: Degrees of Freedom)

机器人的自由度指的是机器人能够独立运动的轴数。6-DOF 机械臂拥有在 3D 空间中表达任意位置与姿态所需的最小自由度。包含手指的全身人形机器人可以拥有 50-DOF 以上的自由度。

自由度可表达的运动示例
1-DOF单轴旋转/平移传送带
3-DOF3D 位置Delta 机器人
6-DOF位置 + 姿态完全控制工业机械臂
7-DOF含冗余自由度KUKA iiwa、Franka Panda

坐标系变换: 齐次变换矩阵

为了在 3D 空间中表达刚体的位置与方向,机器人学中使用齐次变换矩阵(Homogeneous Transformation Matrix)。用一个 4x4 矩阵同时表达旋转(R)与平移(t)。

T=[R3×3t3×101×31]T = \begin{bmatrix} R_{3\times3} & t_{3\times1} \\ 0_{1\times3} & 1 \end{bmatrix}

多个坐标系之间的变换可以通过矩阵乘法链式应用。

Tbaseend=Tbase1T12Tn1endT_{base \to end} = T_{base \to 1} \cdot T_{1 \to 2} \cdots T_{n-1 \to end}

正运动学 (Forward Kinematics)

正运动学是在给定各关节角度(joint angles)的情况下,计算末端执行器(end-effector)位置与姿态的问题。通过将各关节对应的变换矩阵相乘求得。

import numpy as np

def dh_transform(a, d, alpha, theta):
    """Denavit-Hartenberg 变换矩阵计算"""
    ct, st = np.cos(theta), np.sin(theta)
    ca, sa = np.cos(alpha), np.sin(alpha)
    return np.array([
        [ct,    -st*ca,  st*sa,  a*ct],
        [st,     ct*ca, -ct*sa,  a*st],
        [0,      sa,     ca,     d   ],
        [0,      0,      0,      1   ]
    ])

def forward_kinematics(joint_angles, dh_params):
    """
    正运动学: 关节角度 -> 末端执行器位置/姿态
    dh_params: [(a, d, alpha, theta_offset), ...]
    """
    T = np.eye(4)
    for i, theta in enumerate(joint_angles):
        a, d, alpha, theta_off = dh_params[i]
        T = T @ dh_transform(a, d, alpha, theta + theta_off)
    return T

# 2-DOF 平面机械臂示例 (每段连杆长 1.0m)
dh_params_2dof = [
    (1.0, 0, 0, 0),
    (1.0, 0, 0, 0),
]
joint_angles = [np.pi/4, np.pi/4]
T_end = forward_kinematics(joint_angles, dh_params_2dof)
print("末端执行器位置:", T_end[:3, 3])

逆运动学 (Inverse Kinematics)

逆运动学是在给定末端执行器所需的位置/姿态时,计算所需关节角度的问题。有解析方法(Analytical IK)与数值方法(Numerical IK,基于雅可比矩阵)两种。

数值逆运动学的核心是利用雅可比矩阵(Jacobian) JJ 进行的迭代计算。

Δθ=J+Δx\Delta \theta = J^+ \cdot \Delta x

这里的 J+J^+ 是雅可比矩阵的伪逆(pseudoinverse)。


2. ROS2 (Robot Operating System 2)

ROS1 vs ROS2 核心差异

为了克服 ROS1 的缺点,ROS2 基于 DDS(Data Distribution Service) 通信重新设计而成。

项目ROS1ROS2
通信中间件自研 TCP/UDPDDS (OMG 标准)
实时支持不足支持 (rclcpp rt)
Python 版本Python 2/3仅 Python 3
安全性SROS2 (TLS/DDS)
多机器人困难借助命名空间轻松实现
生命周期节点支持 LifecycleNode

核心概念: 节点、话题、服务、动作

  • 节点(Node): 独立的执行单元。可以是一个进程,也可以包含多个节点
  • 话题(Topic): 发布(publish)/订阅(subscribe)的异步消息通信
  • 服务(Service): 请求/响应(request/response)的同步通信
  • 动作(Action): 用于长时间运行任务的异步通信(含反馈)

ROS2 Python 发布者/订阅者示例

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class TalkerNode(Node):
    def __init__(self):
        super().__init__('talker')
        self.publisher = self.create_publisher(String, 'chatter', 10)
        self.timer = self.create_timer(0.5, self.publish_msg)
        self.count = 0

    def publish_msg(self):
        msg = String()
        msg.data = f'Hello ROS2: {self.count}'
        self.publisher.publish(msg)
        self.get_logger().info(f'Published: {msg.data}')
        self.count += 1

class ListenerNode(Node):
    def __init__(self):
        super().__init__('listener')
        self.subscription = self.create_subscription(
            String, 'chatter', self.callback, 10
        )

    def callback(self, msg):
        self.get_logger().info(f'Received: {msg.data}')

def main():
    rclpy.init()
    talker = TalkerNode()
    rclpy.spin(talker)
    rclpy.shutdown()

启动文件示例

# launch/my_robot.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='my_robot',
            executable='talker',
            name='talker_node',
            output='screen',
            parameters=[{'use_sim_time': False}],
        ),
        Node(
            package='my_robot',
            executable='listener',
            name='listener_node',
            output='screen',
        ),
    ])

执行: ros2 launch my_robot my_robot.launch.py


3. 传感器处理

LiDAR: 点云处理

LiDAR(Light Detection And Ranging)利用激光生成 3D 点云,对自动驾驶和 3D 建图至关重要。

import open3d as o3d
import numpy as np

def process_pointcloud(pcd_path):
    # 加载点云
    pcd = o3d.io.read_point_cloud(pcd_path)

    # 统计离群点去除
    pcd_clean, _ = pcd.remove_statistical_outlier(
        nb_neighbors=20, std_ratio=2.0
    )

    # 体素下采样 (降低分辨率)
    pcd_down = pcd_clean.voxel_down_sample(voxel_size=0.05)

    # 法线估计
    pcd_down.estimate_normals(
        search_param=o3d.geometry.KDTreeSearchParamHybrid(
            radius=0.1, max_nn=30
        )
    )

    # RANSAC 平面分割 (去除地面)
    plane_model, inliers = pcd_down.segment_plane(
        distance_threshold=0.01,
        ransac_n=3,
        num_iterations=1000
    )
    [a, b, c, d] = plane_model
    print(f"地面平面方程: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0")

    # 只提取排除地面后的物体
    objects_pcd = pcd_down.select_by_index(inliers, invert=True)
    return objects_pcd

IMU 与卡尔曼滤波器

IMU(惯性测量单元)包含加速度计与陀螺仪,用于估计机器人的方向与速度。利用卡尔曼滤波器降低噪声,进行精确的状态估计。

卡尔曼滤波器的两个阶段:

  1. 预测(Predict): 根据先前状态与运动模型预测当前状态
  2. 更新(Update): 用传感器测量值校正预测值

x^kk=x^kk1+Kk(zkHx^kk1)\hat{x}_{k|k} = \hat{x}_{k|k-1} + K_k(z_k - H\hat{x}_{k|k-1})

这里的 KkK_k 是卡尔曼增益(Kalman Gain),zkz_k 是测量值。


4. SLAM (Simultaneous Localization and Mapping)

什么是 SLAM?

SLAM 是指机器人在未知环境中,一边构建地图一边估计自身位置的技术,是机器人探索与自动驾驶的核心问题。

SLAM 的核心课题:

  • 数据关联(Data Association): 判断当前观测对应于哪个地标(landmark)
  • 回环检测(Loop Closure): 识别曾经到过的地点,以修正累积误差
  • 地图表示(Map Representation): 特征点地图、栅格地图、拓扑地图等

EKF-SLAM

利用扩展卡尔曼滤波器(EKF)实现的 SLAM。将非线性的机器人运动/观测模型通过一阶泰勒展开线性化。

状态向量: 机器人位姿 + 所有地标位置

x=[xr,yr,θr,xl1,yl1,xl2,yl2,]Tx = [x_r, y_r, \theta_r, x_{l1}, y_{l1}, x_{l2}, y_{l2}, \ldots]^T

由于计算复杂度相对于地标数量 nnO(n2)O(n^2),在大规模环境中存在局限性。

实际的 SLAM 库

方法传感器特点
CartographerGraph SLAMLiDAR、IMUGoogle 开发,支持 ROS2
ORB-SLAM3基于特征相机、IMU实时单目/双目
LIO-SAM紧耦合LiDAR + IMU户外大规模环境
RTAB-MapGraph SLAMRGB-D、LiDAR实时 3D 建图
# 安装并运行 Cartographer ROS2
sudo apt install ros-humble-cartographer-ros

# 运行 Cartographer (以 TurtleBot3 为例)
ros2 launch turtlebot3_cartographer cartographer.launch.py

5. 运动规划

路径规划算法概览

运动规划是在避开障碍物的同时,寻找从起点到目标点路径的问题。

  • 基于网格(Grid-based): A*、Dijkstra - 在栅格地图中求最优路径
  • 基于采样(Sampling-based): RRT、PRM - 在高维构型空间中高效
  • 基于优化(Optimization-based): CHOMP、STOMP - 优化代价函数

RRT* 实现

RRT*(Rapidly-exploring Random Tree Star) 是一种能保证渐近最优性的基于采样的算法。

import numpy as np

def rrt_star(start, goal, obstacles, max_iter=1000, step=0.5, goal_radius=0.5):
    """
    简单的 2D RRT* 实现
    start, goal: np.array([x, y])
    obstacles: [(cx, cy, radius), ...]
    """
    nodes = [start]
    parents = [-1]
    costs = [0.0]

    def is_collision(p1, p2):
        """检查线段 p1-p2 是否与障碍物碰撞"""
        for (cx, cy, r) in obstacles:
            center = np.array([cx, cy])
            d = p2 - p1
            t = np.clip(np.dot(center - p1, d) / (np.dot(d, d) + 1e-9), 0, 1)
            closest = p1 + t * d
            if np.linalg.norm(closest - center) < r:
                return True
        return False

    for _ in range(max_iter):
        rand = goal if np.random.random() < 0.05 else np.random.uniform(-10, 10, 2)

        dists = [np.linalg.norm(np.array(n) - rand) for n in nodes]
        nearest_idx = int(np.argmin(dists))
        nearest = np.array(nodes[nearest_idx])

        direction = rand - nearest
        norm = np.linalg.norm(direction)
        if norm < 1e-9:
            continue
        new_node = nearest + step * direction / norm

        if is_collision(nearest, new_node):
            continue

        nodes.append(new_node)
        parents.append(nearest_idx)
        costs.append(costs[nearest_idx] + step)

        if np.linalg.norm(new_node - goal) < goal_radius:
            print(f"目标到达! 总节点数: {len(nodes)}")
            return nodes, parents

    return nodes, parents

# 执行示例
start = np.array([0.0, 0.0])
goal = np.array([9.0, 9.0])
obstacles = [(5.0, 5.0, 1.5), (3.0, 7.0, 1.0)]
nodes, parents = rrt_star(start, goal, obstacles)

MoveIt2 与机械臂运动规划

MoveIt2 是 ROS2 中用于机械臂运动规划的标准框架。

# MoveIt2 Python API 示例
import rclpy
from moveit.planning import MoveItPy
from moveit.core.robot_state import RobotState

rclpy.init()
moveit = MoveItPy(node_name='moveit_py_planning')
panda_arm = moveit.get_planning_component('panda_arm')

# 设置目标位姿
from geometry_msgs.msg import PoseStamped
goal_pose = PoseStamped()
goal_pose.header.frame_id = 'panda_link0'
goal_pose.pose.position.x = 0.5
goal_pose.pose.position.y = 0.0
goal_pose.pose.position.z = 0.5
goal_pose.pose.orientation.w = 1.0

panda_arm.set_goal_state(pose_stamped_msg=goal_pose, pose_link='panda_hand')

# 路径规划与执行
plan_result = panda_arm.plan()
if plan_result:
    moveit.execute(plan_result.trajectory)

6. 计算机视觉与机器人

物体检测 (Object Detection)

为了让机器人识别环境,YOLO(You Only Look Once) 系列模型被广泛用于实时物体检测。

from ultralytics import YOLO
import cv2

# 加载 YOLOv11
model = YOLO('yolo11n.pt')

def detect_objects(frame):
    """从相机帧中检测物体"""
    results = model(frame, conf=0.5)
    annotated = results[0].plot()

    for box in results[0].boxes:
        cls_id = int(box.cls[0])
        label = model.names[cls_id]
        conf = float(box.conf[0])
        x1, y1, x2, y2 = map(int, box.xyxy[0])
        print(f"{label}: {conf:.2f} at ({x1},{y1})-({x2},{y2})")

    return annotated

# 网络摄像头实时检测
cap = cv2.VideoCapture(0)
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    annotated_frame = detect_objects(frame)
    cv2.imshow('Robot Vision', annotated_frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()

6D 位姿估计

为了让机器人抓取(grasping)物体,需要估计物体的 6D 位姿(3D 位置 + 3D 方向)。使用 PnP(Perspective-n-Point) 算法。

import cv2
import numpy as np

def estimate_pose(object_points, image_points, camera_matrix, dist_coeffs):
    """
    利用 PnP 算法进行 6D 位姿估计
    object_points: 3D 点 (物体坐标系)
    image_points: 2D 点 (图像坐标)
    """
    success, rvec, tvec = cv2.solvePnP(
        object_points, image_points,
        camera_matrix, dist_coeffs,
        flags=cv2.SOLVEPNP_ITERATIVE
    )
    if success:
        R, _ = cv2.Rodrigues(rvec)
        print(f"位置: {tvec.flatten()}")
        print(f"旋转矩阵:\n{R}")
        return R, tvec
    return None, None

7. 基于强化学习的机器人控制

机器人强化学习环境

模拟器开发方特点用途
MuJoCoGoogle DeepMind精确物理仿真,速度快连续控制研究
PyBulletErwin Coumans开源,对 Python 友好原型开发
Isaac GymNVIDIAGPU 并行仿真大规模 RL 训练
CARLAIntel/UPV专注自动驾驶AD 研究

用 SAC 进行连续控制学习

SAC(Soft Actor-Critic)是一种通过熵正则化,在探索与利用之间保持平衡的离策略(off-policy)强化学习算法。

import gymnasium as gym
from stable_baselines3 import SAC
from stable_baselines3.common.callbacks import EvalCallback
import os

# 在 MuJoCo Ant 环境中训练 SAC
env = gym.make('Ant-v4', render_mode=None)
eval_env = gym.make('Ant-v4', render_mode=None)

# 保存最优模型的回调
eval_callback = EvalCallback(
    eval_env,
    best_model_save_path='./logs/best_model',
    log_path='./logs/',
    eval_freq=10000,
    deterministic=True,
    render=False
)

# 创建 SAC 模型
model = SAC(
    'MlpPolicy',
    env,
    verbose=1,
    learning_rate=3e-4,
    buffer_size=1_000_000,
    batch_size=256,
    ent_coef='auto',
    gamma=0.99,
    tau=0.005,
    tensorboard_log='./tb_logs/'
)

# 训练
model.learn(total_timesteps=1_000_000, callback=eval_callback)
model.save('ant_sac_final')

# 推理 (部署)
obs, _ = env.reset()
total_reward = 0
for _ in range(1000):
    action, _ = model.predict(obs, deterministic=True)
    obs, reward, done, truncated, info = env.step(action)
    total_reward += reward
    if done or truncated:
        print(f"回合结束,总奖励: {total_reward:.2f}")
        obs, _ = env.reset()
        total_reward = 0

Sim-to-Real 迁移

将在仿真中学到的策略应用于真实机器人时,会产生 Reality Gap 问题,以下是解决该问题的技巧:

  1. 域随机化(Domain Randomization): 在仿真中随机改变物理参数(摩擦、质量、关节刚度),训练出对真实环境变化具有鲁棒性的策略
  2. 域适应(Domain Adaptation): 用真实环境数据微调策略
  3. 系统辨识(System Identification): 精确测量真实机器人的物理参数并反映到模拟器中

8. 自动驾驶

SAE 自动驾驶等级

等级名称说明示例
L0无自动化驾驶员完全控制普通车辆
L1驾驶员辅助单一功能辅助ACC、LKAS
L2部分自动化复合功能Tesla Autopilot
L3有条件自动化特定条件下自动驾驶Mercedes Drive Pilot
L4高度自动化指定区域内完全自动驾驶Waymo One
L5完全自动化所有条件下自动驾驶(尚未实现)

感知-预测-规划-控制流水线

class AutonomousDrivingSystem:
    """自动驾驶系统的基本流水线"""

    def __init__(self, perception, prediction, planning, control):
        self.perception = perception    # 感知模块
        self.prediction = prediction    # 预测模块
        self.planning = planning        # 规划模块
        self.control = control          # 控制模块

    def run_step(self, sensor_data):
        # 1. 感知: 从传感器数据中检测并跟踪物体
        detected_objects = self.perception.detect(sensor_data)
        ego_state = self.perception.localize(sensor_data)

        # 2. 预测: 预测周围车辆/行人的未来轨迹
        predicted_trajectories = self.prediction.predict(
            detected_objects, horizon=3.0  # 预测 3 秒
        )

        # 3. 规划: 规划通往目标的安全路径
        trajectory = self.planning.plan(
            ego_state, predicted_trajectories, goal=self.goal
        )

        # 4. 控制: 跟踪已规划的路径
        steering, throttle, brake = self.control.compute(
            ego_state, trajectory
        )

        return steering, throttle, brake

9. 人形机器人 2026

2026 年,人形机器人市场正在快速增长。我们来看看各主要厂商的最新动态。

主要人形机器人对比

机器人公司身高/体重主要特点
Atlas (电动)Boston Dynamics1.5m / 89kg电动执行器,先进的移动能力
Optimus Gen 3Tesla1.73m / 57kg手部 22 自由度,搭载 FSD AI
Figure 02Figure AI1.7m / 60kg与 OpenAI 合作,语音理解
Unitree H1/G1Unitree Robotics1.8m/1.3m高性价比,对开源友好
1X NEO Beta1X Technologies1.65m / 30kg轻量化,软体执行器

机器人基础模型

RT-2(Robotics Transformer 2)是 Google DeepMind 开发的视觉-语言-动作模型,将从网络数据中学到的知识直接应用于机器人控制。它能理解自然语言指令,并可泛化到新物体上。

OpenVLA 是一个开源的 VLA(Vision-Language-Action)模型,研究者可以用自己的机器人数据对其进行微调后使用。

# OpenVLA 推理示例 (概念代码)
from transformers import AutoModelForVision2Seq, AutoProcessor
import torch

model_name = "openvla/openvla-7b"
processor = AutoProcessor.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForVision2Seq.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map='auto',
    trust_remote_code=True
)

# 通过视觉-语言指令生成动作
image = get_camera_image()  # 机器人相机图像
instruction = "pick up the red block and place it in the box"

inputs = processor(instruction, image).to('cuda:0', dtype=torch.bfloat16)
action = model.predict_action(**inputs, unnorm_key='bridge_orig', do_sample=False)
# action: [dx, dy, dz, droll, dpitch, dyaw, gripper]
robot.execute_action(action)

嵌入式 AI 与端侧推理

人形机器人的「大脑」必须在不依赖云端的情况下实时运行。NVIDIA Jetson Orin、Qualcomm Robotics RB5 这类边缘 AI 芯片是关键。

  • NVIDIA Jetson AGX Orin: 275 TOPS,机器人 AI 推理的标准配置
  • 量化(Quantization): 用 INT8/FP16 实现模型轻量化
  • TensorRT: NVIDIA GPU 推理优化引擎

10. 测验: 机器人 & AI 核心概念

Q1. 为什么 Loop Closure 在 SLAM 中很重要?

答案: 因为它能识别机器人曾经到过的地点,从而修正累积的位置估计误差。

解析: 在长时间探索过程中,微小误差会不断累积,产生较大的位置误差。一旦检测到 Loop Closure,就会通过图优化(例如 g2o、iSAM2)修正整条轨迹的误差。没有这一过程,地图会逐渐失真。

Q2. ROS2 使用 DDS 的主要优势是什么?

答案: 标准化的中间件、实时支持、多机器人环境下的网络灵活性,以及安全性(SROS2)支持。

解析: ROS1 依赖单一的 roscore,存在单点故障。基于 DDS 的 ROS2 采用分布式架构,无需 roscore 即可实现节点间直接通信,并可通过 QoS(Quality of Service)策略精细控制通信可靠性。

Q3. SAC 算法中熵项(entropy term)的作用是什么?

答案: 把策略的随机性(探索水平)纳入奖励的一部分,使智能体不至于过早收敛到过度确定性的行为,从而保持行为的多样性。

解析: SAC 的目标函数是期望奖励与策略熵之和。熵系数(alpha)越大,越强调探索;设为 auto 时,会自动调整以匹配目标熵。这样可以防止陷入局部最优解的陷阱。

Q4. 为什么逆运动学(IK)比正运动学(FK)更难,其根本原因是什么?

答案: 因为 IK 是一个可能无解、也可能存在多个解(而非唯一解)的非线性方程组。

解析: FK 是一个单向函数 — 给定关节角度就能计算出唯一的末端执行器位姿。而 IK 是从目标位姿反算的过程,对于 7-DOF 以上的冗余自由度机器人,可能存在无穷多个解(redundancy),而在工作空间边界处则可能无解。在奇异点(singularity)附近,雅可比矩阵会变得奇异,导致数值不稳定。

Q5. 为什么域随机化在 Sim-to-Real 迁移中是有效的?

答案: 因为通过在多样化的仿真环境中训练,可以学到对真实环境的不确定性与变化具有鲁棒性(robust)的策略。

解析: 如果只用单一的仿真参数训练,由于真实环境中摩擦、执行器延迟、传感器噪声等方面的差异,性能会急剧下降。域随机化在训练过程中随机改变这些参数,强制策略适应各种物理条件。OpenAI 的 Dextrous Hand 研究就是这种方法的代表性成功案例。


参考资料