# 🖲️ 传感器 机器人需要传感器来观察周围的世界。 在 Genesis 中,传感器从场景中提取信息,使用场景的状态计算值,但不影响场景本身。 传感器可以使用 `scene.add_sensor(sensor_options)` 创建,并使用 `sensor.read()` 或 `sensor.read_ground_truth()` 读取。 ```python scene = ... # 1. 向场景添加传感器 sensor = scene.add_sensor( gs.sensors.Contact( ..., draw_debug=True, # 在场景查看器中可视化传感器数据 ) ) # 2. 构建场景 scene.build() for _ in range(1000): scene.step() # 3. 从传感器读取数据 measured_data = sensor.read() ground_truth_data = sensor.read_ground_truth() ``` 当前支持的传感器: - `IMU`(加速度计和陀螺仪) - `Contact`(每个刚体连杆的布尔值) - `ContactForce`(每个刚体连杆的 xyz 力) - `Raycaster` - `Lidar` - `DepthCamera` 传感器的示例用法可以在 `examples/sensors/` 下找到。 ## IMU 示例 在本教程中,我们将介绍如何在机械臂的末端执行器上设置惯性测量单元(IMU)传感器。IMU 将在机器人沿圆形路径运动时测量线性加速度和角速度,我们将使用真实的噪声参数实时可视化数据。 完整的示例脚本可在 `examples/sensors/imu_franka.py` 获取。 ### 场景设置 首先,让我们创建仿真场景并加载机械臂: ```python import genesis as gs import numpy as np gs.init(backend=gs.gpu) ########################## create a scene ########################## scene = gs.Scene( viewer_options=gs.options.ViewerOptions( camera_pos=(3.5, 0.0, 2.5), camera_lookat=(0.0, 0.0, 0.5), camera_fov=40, ), sim_options=gs.options.SimOptions( dt=0.01, ), show_viewer=True, ) ########################## entities ########################## scene.add_entity(gs.morphs.Plane()) franka = scene.add_entity( gs.morphs.MJCF(file="xml/franka_emika_panda/panda.xml"), ) end_effector = franka.get_link("hand") motors_dof = (0, 1, 2, 3, 4, 5, 6) ``` 这里我们设置了一个基本场景,有一个 Franka 机械臂。相机位置让我们可以很好地观察机器人的工作空间,我们确定了末端执行器连杆,我们的 IMU 传感器将连接在那里。 ### 添加 IMU 传感器 我们通过指定 `entity_idx` 和 `link_idx_local` 将 IMU 传感器"连接"到末端执行器上的实体。 ```python imu = scene.add_sensor( gs.sensors.IMU( entity_idx=franka.idx, link_idx_local=end_effector.idx_local, pos_offset=(0.0, 0.0, 0.15), # 传感器特性 acc_cross_axis_coupling=(0.0, 0.01, 0.02), gyro_cross_axis_coupling=(0.03, 0.04, 0.05), acc_noise=(0.01, 0.01, 0.01), gyro_noise=(0.01, 0.01, 0.01), acc_random_walk=(0.001, 0.001, 0.001), gyro_random_walk=(0.001, 0.001, 0.001), delay=0.01, jitter=0.01, interpolate=True, draw_debug=True, ) ) ``` `gs.sensors.IMU` 构造函数有以下选项来配置传感器特性: - `pos_offset` 指定传感器相对于连杆坐标系的位置 - `acc_cross_axis_coupling` 和 `gyro_cross_axis_coupling` 仿真传感器错位 - `acc_noise` 和 `gyro_noise` 为测量添加高斯噪声 - `acc_random_walk` 和 `gyro_random_walk` 仿真随时间逐渐产生的传感器漂移 - `delay` 和 `jitter` 引入时序真实感 - `interpolate` 平滑延迟的测量值 - `draw_debug` 在查看器中可视化传感器坐标系 ### 运动控制与仿真 现在让我们构建场景并创建圆形运动以生成有趣的 IMU 读数: ```python ########################## build and control ########################## scene.build() franka.set_dofs_kp(np.array([4500, 4500, 3500, 3500, 2000, 2000, 2000, 100, 100])) franka.set_dofs_kv(np.array([450, 450, 350, 350, 200, 200, 200, 10, 10])) # 为末端执行器创建要跟随的圆形路径 circle_center = np.array([0.4, 0.0, 0.5]) circle_radius = 0.15 rate = np.deg2rad(2.0) # 每步的角速度,弧度 def control_franka_circle_path(i): pos = circle_center + np.array([np.cos(i * rate), np.sin(i * rate), 0]) * circle_radius qpos = franka.inverse_kinematics( link=end_effector, pos=pos, quat=np.array([0, 1, 0, 0]), # 保持方向固定 ) franka.control_dofs_position(qpos[:-2], motors_dof) scene.draw_debug_sphere(pos, radius=0.01, color=(1.0, 0.0, 0.0, 0.5)) # 可视化目标 # 运行仿真 for i in range(1000): scene.step() control_franka_circle_path(i) ``` 机器人在保持固定方向的同时描绘水平圆。圆形运动产生 IMU 将检测到的向心加速度,以及基于传感器方向的任何重力效应。 构建场景后,你可以访问测量值和真实 IMU 数据: ```python # 访问传感器读数 print("Ground truth data:") print(imu.read_ground_truth()) print("Measured data:") print(imu.read()) ``` IMU 以**命名元组**的形式返回数据,字段包括: - `lin_acc`:线性加速度,单位为 m/s²(3D 向量) - `ang_vel`:角速度,单位为 rad/s(3D 向量) ## 接触传感器 接触传感器从刚体求解器检索每个刚体连杆的接触信息。 `Contact` 传感器将返回布尔值,`ContactForce` 返回相关刚体连杆局部坐标系中的净力向量。 完整的示例脚本可在 `examples/sensors/contact_force_go2.py` 获取(添加标志 `--force` 以使用力传感器)。 ```{figure} ../../_static/images/contact_force_sensor.png ``` ## 光线投射传感器:激光雷达和深度相机 `Raycaster` 传感器通过向场景投射光线并检测与几何体的交点来测量距离。 光线数量和光线方向可以使用 `RaycastPattern` 指定。 `SphericalPattern` 支持类似激光雷达的视场和角分辨率规范,`GridPattern` 从平面投射光线。`DepthCamera` 传感器提供 `read_image()` 函数,将光线投射信息格式化为深度图像。有关可用选项的详细信息,请参阅 API 参考。 ```python lidar = scene.add_sensor( gs.sensors.Lidar( pattern=gs.sensors.Spherical(), entity_idx=robot.idx, # 连接到刚体实体 pos_offset=(0.3, 0.0, 0.1) # 相对于连接实体的偏移 return_world_frame=True, # 是否以世界坐标系或局部坐标系返回点 ) ) depth_camera = scene.add_sensor( gs.sensors.DepthCamera( pattern=gs.sensors.DepthCameraPattern( res=(480, 360), # 图像分辨率(宽,高) fov_horizontal=90, # 水平视场角度 fov_vertical=40, ), ) ) ... lidar.read() # 返回包含点和距离的 NamedTuple depth_camera.read_image() # 返回形状为(高,宽)的距离张量 ``` 演示安装在机器人上的光线投射传感器的示例脚本可在 `examples/sensors/lidar_teleop.py` 获取。 将标志 `--pattern` 设置为 `spherical` 以获得类似激光雷达的模式,`grid` 获得平面网格模式,`depth` 获得深度相机。 以下是运行 `python examples/sensors/lidar_teleop.py --pattern depth` 的效果: