Isaac Sim + ROS2联合仿真

学习如何将Isaac Sim与ROS2深度集成,实现传感器数据发布、控制指令订阅和多节点协同仿真。

Isaac Sim ROS2 联合仿真 传感器 控制
Isaac Sim + ROS2联合仿真

Isaac Sim + ROS2联合仿真

Isaac Sim与ROS2的深度集成是其核心优势之一。本文讲解如何搭建联合仿真环境,实现传感器数据发布和控制指令订阅。

配置ROS2桥接

from isaacsim.core import World
from isaacsim.ros2 import ROS2Bridge

world = World()

# 初始化ROS2桥接
ros_bridge = ROS2Bridge(
    world=world,
    node_name="isaac_sim_ros2"
)

# 启动桥接
ros_bridge.start()

发布传感器数据

相机图像发布

from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import rclpy

class CameraPublisher:
    def __init__(self, ros_bridge, camera):
        self.camera = camera
        self.bridge = CvBridge()

        # 创建发布者
        self.publisher = ros_bridge.create_publisher(
            Image, "/camera/color/image_raw", 10
        )

        # 创建深度图发布者
        self.depth_publisher = ros_bridge.create_publisher(
            Image, "/camera/depth/image_rect", 10
        )

    def publish(self):
        """发布相机数据"""
        # RGB图像
        rgb = self.camera.get_rgb()
        rgb_msg = self.bridge.cv2_to_imgmsg(rgb, encoding="rgb8")
        self.publisher.publish(rgb_msg)

        # 深度图
        depth = self.camera.get_depth()
        depth_msg = self.bridge.cv2_to_imgmsg(depth, encoding="32FC1")
        self.depth_publisher.publish(depth_msg)

# 使用
camera_pub = CameraPublisher(ros_bridge, camera)

while simulation_app.is_running():
    world.step(render=True)
    camera_pub.publish()

激光雷达发布

from sensor_msgs.msg import LaserScan, PointCloud2

class LidarPublisher:
    def __init__(self, ros_bridge, lidar):
        self.lidar = lidar
        self.scan_pub = ros_bridge.create_publisher(
            LaserScan, "/scan", 10
        )
        self.cloud_pub = ros_bridge.create_publisher(
            PointCloud2, "/point_cloud", 10
        )

    def publish(self):
        # 获取点云
        points = self.lidar.get_point_cloud()

        # 转换为LaserScan(2D切片)
        scan_msg = self._points_to_laserscan(points)
        self.scan_pub.publish(scan_msg)

        # 发布完整点云
        cloud_msg = self._points_to_pointcloud2(points)
        self.cloud_pub.publish(cloud_msg)

    def _points_to_laserscan(self, points):
        """将3D点云转换为2D激光扫描"""
        msg = LaserScan()
        msg.header.frame_id = "lidar_frame"

        # 提取Z=0附近的点
        z_mask = np.abs(points[:, 2]) < 0.1
        points_2d = points[z_mask, :2]

        # 转换为极坐标
        ranges = np.linalg.norm(points_2d, axis=1)
        angles = np.arctan2(points_2d[:, 1], points_2d[:, 0])

        # 排序
        sorted_indices = np.argsort(angles)
        msg.ranges = ranges[sorted_indices].tolist()
        msg.angle_min = float(angles[sorted_indices[0]])
        msg.angle_max = float(angles[sorted_indices[-1]])
        msg.angle_increment = float(np.mean(np.diff(angles[sorted_indices])))

        return msg

订阅控制指令

from geometry_msgs.msg import Twist
from std_msgs.msg import Float64MultiArray

class ControlSubscriber:
    def __init__(self, ros_bridge, robot):
        self.robot = robot

        # 订阅速度指令(差速驱动)
        self.cmd_sub = ros_bridge.create_subscription(
            Twist, "/cmd_vel", self.cmd_vel_callback, 10
        )

        # 订阅关节指令(机械臂)
        self.joint_sub = ros_bridge.create_subscription(
            Float64MultiArray, "/joint_commands",
            self.joint_callback, 10
        )

    def cmd_vel_callback(self, msg):
        """处理速度指令"""
        linear_vel = msg.linear.x
        angular_vel = msg.angular.z

        # 转换为轮子速度
        left_wheel_vel = linear_vel - angular_vel * 0.1
        right_wheel_vel = linear_vel + angular_vel * 0.1

        self.robot.set_wheel_velocities(left_wheel_vel, right_wheel_vel)

    def joint_callback(self, msg):
        """处理关节指令"""
        joint_positions = np.array(msg.data)
        self.robot.set_joint_positions(joint_positions)

# 使用
control_sub = ControlSubscriber(ros_bridge, robot)

完整的联合仿真示例

def main():
    # 初始化
    world = World()
    setup_scene(world)

    # 初始化ROS2
    rclpy.init()
    ros_bridge = ROS2Bridge(world, "isaac_sim_node")
    ros_bridge.start()

    # 创建发布者和订阅者
    camera_pub = CameraPublisher(ros_bridge, camera)
    lidar_pub = LidarPublisher(ros_bridge, lidar)
    control_sub = ControlSubscriber(ros_bridge, robot)

    # 仿真循环
    while simulation_app.is_running():
        world.step(render=True)

        # 发布传感器数据
        camera_pub.publish()
        lidar_pub.publish()

        # 处理ROS回调
        rclpy.spin_once(ros_bridge.node, timeout_sec=0)

    # 清理
    ros_bridge.stop()
    rclpy.shutdown()
    simulation_app.close()

if __name__ == "__main__":
    main()

在另一个终端运行ROS2节点

# 启动ROS2导航节点
ros2 launch nav2_bringup navigation_launch.py

# 查看话题
ros2 topic list

# 发布速度指令
ros2 topic pub /cmd_vel geometry_msgs/Twist \
  "{linear: {x: 0.5}, angular: {z: 0.0}}"

FAQ

Isaac Sim和ROS2版本如何匹配?

Isaac Sim 4.2支持ROS2 Humble。确保使用匹配的ROS2版本。

传感器数据发布频率低怎么办?

增加仿真步长或减少渲染分辨率。也可以在单独的线程中发布传感器数据。

如何调试ROS2话题?

使用ros2 topic echo /topic_name查看数据,或用RViz2可视化。