feat(craic_localization): add package foundation, configs, and launch files
New ROS2 package for wheel odometry + LiDAR localization. Includes: AMCL config (omni model, likelihood_field), gmapping config (5cm grid, 30 particles), YDLiDAR TminiPro config, launch files for lidar bringup, SLAM mapping, AMCL localization, and sensor calibration, plus RViz config with Map/LaserScan/Odometry displays. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
56
ros2/src/craic_localization/config/amcl.yaml
Normal file
56
ros2/src/craic_localization/config/amcl.yaml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# P5 AMCL 定位参数 —— 改自 lzu_robot/maps/amcl_config.yaml
|
||||||
|
# 麦轮底盘用 omni 运动模型;帧与 chassis_odometry / 激光一致。
|
||||||
|
# 地图文件由 localization.launch.py 的 map:= 参数注入 map_server 的 yaml_filename。
|
||||||
|
|
||||||
|
map_server:
|
||||||
|
ros__parameters:
|
||||||
|
use_sim_time: false
|
||||||
|
yaml_filename: "" # 由 launch 的 map:= 覆盖
|
||||||
|
topic_name: "map"
|
||||||
|
frame_id: "map"
|
||||||
|
|
||||||
|
amcl:
|
||||||
|
ros__parameters:
|
||||||
|
use_sim_time: false
|
||||||
|
# ===== 帧 / 话题 =====
|
||||||
|
odom_model_type: "omni" # 麦轮全向;差速底盘改 "diff"
|
||||||
|
global_frame_id: "map"
|
||||||
|
base_frame_id: "base_footprint"
|
||||||
|
odom_frame_id: "odom"
|
||||||
|
scan_topic: "scan"
|
||||||
|
|
||||||
|
# ===== 运动模型噪声(按里程计实际精度调)=====
|
||||||
|
alpha1: 0.005
|
||||||
|
alpha2: 0.005
|
||||||
|
alpha3: 0.005
|
||||||
|
alpha4: 0.005
|
||||||
|
alpha5: 0.005
|
||||||
|
|
||||||
|
# ===== 粒子滤波 =====
|
||||||
|
min_particles: 500
|
||||||
|
max_particles: 1000
|
||||||
|
pf_err: 0.05
|
||||||
|
resample_interval: 5
|
||||||
|
|
||||||
|
# ===== 激光模型(匹配 TminiPro:12m)=====
|
||||||
|
laser_model_type: "likelihood_field"
|
||||||
|
laser_max_range: 12.0
|
||||||
|
laser_min_range: -1.0
|
||||||
|
max_beams: 500
|
||||||
|
sigma_hit: 0.05
|
||||||
|
z_hit: 0.5
|
||||||
|
z_rand: 0.03
|
||||||
|
laser_likelihood_max_dist: 2.0
|
||||||
|
|
||||||
|
# ===== 更新策略 =====
|
||||||
|
update_min_d: 0.05
|
||||||
|
update_min_a: 0.05
|
||||||
|
transform_tolerance: 0.5
|
||||||
|
|
||||||
|
# ===== 初始位姿(按机器人在场地中的起始点设置;可被 launch 覆盖)=====
|
||||||
|
set_initial_pose: true
|
||||||
|
initial_pose:
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
yaw: 0.0
|
||||||
46
ros2/src/craic_localization/config/gmapping.yaml
Normal file
46
ros2/src/craic_localization/config/gmapping.yaml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# gmapping(slam_gmapping) 建图参数 —— 改自 lzu_robot/src/slam_gmapping/params/slam_gmapping.yaml
|
||||||
|
# 帧:base_frame=base_footprint(与 chassis_odometry 一致),odom_frame=odom,map_frame=map
|
||||||
|
# 适配 385×385cm 小场地:maxUrange 4m 足够覆盖;delta 0.05 = 5cm 栅格
|
||||||
|
# 用 /** 通配键确保参数无论节点全限定名如何都能生效(lzu 用 /slam_gmapping 键,
|
||||||
|
# 其 TF 树恰好有 base_link 才掩盖了键不匹配、参数全默认的问题;我们没有 base_link 会直接报错)
|
||||||
|
/**:
|
||||||
|
ros__parameters:
|
||||||
|
base_frame: base_footprint
|
||||||
|
odom_frame: odom
|
||||||
|
map_frame: map
|
||||||
|
map_update_interval: 5.0
|
||||||
|
maxUrange: 4.0
|
||||||
|
maxRange: 6.0
|
||||||
|
delta: 0.05
|
||||||
|
particles: 30
|
||||||
|
linearUpdate: 0.3
|
||||||
|
angularUpdate: 0.5
|
||||||
|
temporalUpdate: 1.0
|
||||||
|
resampleThreshold: 0.5
|
||||||
|
minimum_score: 0.0
|
||||||
|
# 运动模型噪声
|
||||||
|
srr: 0.1
|
||||||
|
srt: 0.2
|
||||||
|
str: 0.1
|
||||||
|
stt: 0.2
|
||||||
|
# 扫描匹配
|
||||||
|
sigma: 0.05
|
||||||
|
kernelSize: 1
|
||||||
|
lstep: 0.05
|
||||||
|
astep: 0.05
|
||||||
|
iterations: 5
|
||||||
|
lsigma: 0.075
|
||||||
|
ogain: 3.0
|
||||||
|
lskip: 0
|
||||||
|
llsamplerange: 0.01
|
||||||
|
llsamplestep: 0.01
|
||||||
|
lasamplerange: 0.005
|
||||||
|
lasamplestep: 0.005
|
||||||
|
# 地图范围(米)
|
||||||
|
xmin: -10.0
|
||||||
|
xmax: 10.0
|
||||||
|
ymin: -10.0
|
||||||
|
ymax: 10.0
|
||||||
|
occ_thresh: 0.25
|
||||||
|
transform_publish_period: 0.05
|
||||||
|
use_sim_time: false
|
||||||
24
ros2/src/craic_localization/config/lidar.yaml
Normal file
24
ros2/src/craic_localization/config/lidar.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
ydlidar_ros2_driver_node:
|
||||||
|
ros__parameters:
|
||||||
|
port: /dev/ttylzulaser
|
||||||
|
frame_id: laser_frame
|
||||||
|
ignore_array: ""
|
||||||
|
baudrate: 230400
|
||||||
|
lidar_type: 1
|
||||||
|
device_type: 0
|
||||||
|
sample_rate: 4
|
||||||
|
intensity_bit: 8
|
||||||
|
abnormal_check_count: 4
|
||||||
|
fixed_resolution: true
|
||||||
|
reversion: true
|
||||||
|
inverted: true
|
||||||
|
auto_reconnect: true
|
||||||
|
isSingleChannel: false
|
||||||
|
intensity: true
|
||||||
|
support_motor_dtr: false
|
||||||
|
angle_max: 180.0
|
||||||
|
angle_min: -180.0
|
||||||
|
range_max: 12.0
|
||||||
|
range_min: 0.03
|
||||||
|
frequency: 10.0
|
||||||
|
invalid_range_is_inf: false
|
||||||
62
ros2/src/craic_localization/launch/bringup.launch.py
Normal file
62
ros2/src/craic_localization/launch/bringup.launch.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""定位栈总 bringup:轮式里程计 + 激光雷达 + (可选) rviz。
|
||||||
|
|
||||||
|
组成完整 TF 树:odom ──(chassis_odometry)──> base_footprint ──(静态外参)──> laser_frame
|
||||||
|
用于 P3 激光外参标定与传感器联调:在 rviz 里同时看 /odom 与 /scan,
|
||||||
|
调 lidar_yaw/lidar_x/lidar_y 直到激光轮廓与实际场地吻合。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
ros2 launch craic_localization bringup.launch.py
|
||||||
|
ros2 launch craic_localization bringup.launch.py use_rviz:=false
|
||||||
|
ros2 launch craic_localization bringup.launch.py lidar_yaw:=-1.5707963 intensity:=true
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||||
|
from launch.conditions import IfCondition
|
||||||
|
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||||
|
from launch.substitutions import LaunchConfiguration
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
pkg_share = get_package_share_directory('craic_localization')
|
||||||
|
lidar_launch = os.path.join(pkg_share, 'launch', 'lidar.launch.py')
|
||||||
|
default_rviz = os.path.join(pkg_share, 'rviz', 'localization.rviz')
|
||||||
|
|
||||||
|
use_rviz = LaunchConfiguration('use_rviz')
|
||||||
|
chassis_port = LaunchConfiguration('chassis_port')
|
||||||
|
rviz_config = LaunchConfiguration('rviz_config')
|
||||||
|
|
||||||
|
return LaunchDescription([
|
||||||
|
DeclareLaunchArgument('use_rviz', default_value='true'),
|
||||||
|
DeclareLaunchArgument('chassis_port', default_value='/dev/ttylzucar'),
|
||||||
|
DeclareLaunchArgument('rviz_config', default_value=default_rviz),
|
||||||
|
|
||||||
|
# 轮式里程计:odom -> base_footprint
|
||||||
|
Node(
|
||||||
|
package='craic_localization',
|
||||||
|
executable='chassis_odometry',
|
||||||
|
name='chassis_odometry',
|
||||||
|
output='screen',
|
||||||
|
parameters=[{'port': chassis_port}],
|
||||||
|
),
|
||||||
|
|
||||||
|
# 激光雷达 + base_footprint -> laser_frame 静态外参(参数见 lidar.launch.py)
|
||||||
|
IncludeLaunchDescription(
|
||||||
|
PythonLaunchDescriptionSource(lidar_launch),
|
||||||
|
),
|
||||||
|
|
||||||
|
# 可视化(标定 / 联调)
|
||||||
|
Node(
|
||||||
|
package='rviz2',
|
||||||
|
executable='rviz2',
|
||||||
|
name='rviz2',
|
||||||
|
arguments=['-d', rviz_config],
|
||||||
|
condition=IfCondition(use_rviz),
|
||||||
|
output='screen',
|
||||||
|
),
|
||||||
|
])
|
||||||
100
ros2/src/craic_localization/launch/lidar.launch.py
Normal file
100
ros2/src/craic_localization/launch/lidar.launch.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""激光雷达 bringup:启动 YDLiDAR TminiPro 驱动 + 发布 base_footprint→laser_frame 静态外参。
|
||||||
|
|
||||||
|
YDLiDAR 驱动节点(ydlidar_ros2_driver_node)内部是普通 rclcpp::Node,启动即在 `scan`
|
||||||
|
话题持续发布 sensor_msgs/LaserScan,无需 lifecycle configure/activate 转换。
|
||||||
|
|
||||||
|
静态外参为**占位值**(绕 z 轴 -90°,对齐 move_try;平移取 0),需在 P3 按 CRAIC 上
|
||||||
|
激光雷达的实际安装位置标定。可通过 launch 参数 lidar_x/lidar_y/lidar_z/lidar_yaw 覆盖。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
ros2 launch craic_localization lidar.launch.py
|
||||||
|
ros2 launch craic_localization lidar.launch.py params_file:=/abs/path/lidar.yaml lidar_yaw:=-1.5707963
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import DeclareLaunchArgument
|
||||||
|
from launch.substitutions import LaunchConfiguration
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
from launch_ros.descriptions import ParameterValue
|
||||||
|
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
pkg_share = get_package_share_directory('craic_localization')
|
||||||
|
default_params = os.path.join(pkg_share, 'config', 'lidar.yaml')
|
||||||
|
|
||||||
|
params_file = LaunchConfiguration('params_file')
|
||||||
|
lidar_x = LaunchConfiguration('lidar_x')
|
||||||
|
lidar_y = LaunchConfiguration('lidar_y')
|
||||||
|
lidar_z = LaunchConfiguration('lidar_z')
|
||||||
|
lidar_yaw = LaunchConfiguration('lidar_yaw')
|
||||||
|
base_frame = LaunchConfiguration('base_frame')
|
||||||
|
laser_frame = LaunchConfiguration('laser_frame')
|
||||||
|
# 决定数据包解析格式的关键参数,作为 launch 覆盖项暴露,便于排查 Check Sum 报错:
|
||||||
|
# 基础 Tmini 无强度 -> intensity:=false;TminiPro 有强度 -> intensity:=true
|
||||||
|
intensity = LaunchConfiguration('intensity')
|
||||||
|
sample_rate = LaunchConfiguration('sample_rate')
|
||||||
|
baudrate = LaunchConfiguration('baudrate')
|
||||||
|
|
||||||
|
return LaunchDescription([
|
||||||
|
DeclareLaunchArgument('params_file', default_value=default_params,
|
||||||
|
description='YDLiDAR 参数文件路径'),
|
||||||
|
DeclareLaunchArgument('base_frame', default_value='base_footprint'),
|
||||||
|
DeclareLaunchArgument('laser_frame', default_value='laser_frame'),
|
||||||
|
# 占位外参,P3 标定后覆盖
|
||||||
|
DeclareLaunchArgument('lidar_x', default_value='0.0'),
|
||||||
|
DeclareLaunchArgument('lidar_y', default_value='0.0'),
|
||||||
|
DeclareLaunchArgument('lidar_z', default_value='0.0'),
|
||||||
|
DeclareLaunchArgument('lidar_yaw', default_value='-3.14159',
|
||||||
|
description='激光相对底盘偏航角(rad)。-180°=让机体X轴对准底盘实际前进(W键)方向;'
|
||||||
|
'若实测前后颠倒,改 0.0'),
|
||||||
|
# 数据包格式覆盖项(排查 Check Sum 报错用)
|
||||||
|
DeclareLaunchArgument('intensity', default_value='true',
|
||||||
|
description='设备是否输出强度:基础 Tmini=false,TminiPro=true。不匹配会刷 Check Sum 报错'),
|
||||||
|
DeclareLaunchArgument('sample_rate', default_value='4',
|
||||||
|
description='采样率(K),Tmini 系列为 4'),
|
||||||
|
DeclareLaunchArgument('baudrate', default_value='230400'),
|
||||||
|
|
||||||
|
Node(
|
||||||
|
package='ydlidar_ros2_driver',
|
||||||
|
executable='ydlidar_ros2_driver_node',
|
||||||
|
name='ydlidar_ros2_driver_node',
|
||||||
|
output='screen',
|
||||||
|
emulate_tty=True,
|
||||||
|
parameters=[
|
||||||
|
params_file,
|
||||||
|
{
|
||||||
|
'intensity': ParameterValue(intensity, value_type=bool),
|
||||||
|
'sample_rate': ParameterValue(sample_rate, value_type=int),
|
||||||
|
'baudrate': ParameterValue(baudrate, value_type=int),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
namespace='/',
|
||||||
|
),
|
||||||
|
Node(
|
||||||
|
package='tf2_ros',
|
||||||
|
executable='static_transform_publisher',
|
||||||
|
name='static_tf_base_to_laser',
|
||||||
|
arguments=[
|
||||||
|
'--x', lidar_x, '--y', lidar_y, '--z', lidar_z,
|
||||||
|
'--yaw', lidar_yaw, '--pitch', '0.0', '--roll', '0.0',
|
||||||
|
'--frame-id', base_frame, '--child-frame-id', laser_frame,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
# base_footprint -> base_link 恒等变换。
|
||||||
|
# slam_gmapping(本 build)不从参数读 base_frame,硬编码默认 base_link;
|
||||||
|
# 提供该帧让其默认值可解析。本工程 base_footprint 即底盘基准,两者重合。
|
||||||
|
Node(
|
||||||
|
package='tf2_ros',
|
||||||
|
executable='static_transform_publisher',
|
||||||
|
name='static_tf_base_link',
|
||||||
|
arguments=[
|
||||||
|
'--x', '0.0', '--y', '0.0', '--z', '0.0',
|
||||||
|
'--yaw', '0.0', '--pitch', '0.0', '--roll', '0.0',
|
||||||
|
'--frame-id', base_frame, '--child-frame-id', 'base_link',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
])
|
||||||
128
ros2/src/craic_localization/launch/localization.launch.py
Normal file
128
ros2/src/craic_localization/launch/localization.launch.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""P5 精准定位:激光 + rf2o 激光里程计 + 已知地图 + AMCL。
|
||||||
|
|
||||||
|
完整 TF 树:map ─(amcl)→ odom ─(rf2o)→ base_footprint ─(静态)→ laser_frame
|
||||||
|
odom→base_footprint 由 rf2o 从 /scan 连续提供(不依赖底盘里程计板),AMCL 用它做运动模型、
|
||||||
|
激光匹配已知地图做校正,输出绝对、无漂移位姿。需要 P4 建好并存盘的地图。
|
||||||
|
|
||||||
|
说明:底盘轮式里程计板上电后仅短时上报,暂不参与 odom→base;待其连续上报问题解决后,
|
||||||
|
可用 robot_localization EKF 融合轮速 + rf2o 再喂 AMCL 提升鲁棒性。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
ros2 launch craic_localization localization.launch.py map:=/abs/path/maps/craic.yaml
|
||||||
|
# 起点不在地图原点时,rviz 用 "2D Pose Estimate" 给初值,或:
|
||||||
|
ros2 launch craic_localization localization.launch.py map:=... init_x:=0.3 init_y:=0.2 init_yaw:=1.57
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||||
|
from launch.conditions import IfCondition
|
||||||
|
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||||
|
from launch.substitutions import LaunchConfiguration
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
from launch_ros.descriptions import ParameterValue
|
||||||
|
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
pkg_share = get_package_share_directory('craic_localization')
|
||||||
|
lidar_launch = os.path.join(pkg_share, 'launch', 'lidar.launch.py')
|
||||||
|
amcl_params = os.path.join(pkg_share, 'config', 'amcl.yaml')
|
||||||
|
default_rviz = os.path.join(pkg_share, 'rviz', 'localization.rviz')
|
||||||
|
default_map = os.path.join(pkg_share, 'maps', 'craic.yaml')
|
||||||
|
|
||||||
|
use_rviz = LaunchConfiguration('use_rviz')
|
||||||
|
map_yaml = LaunchConfiguration('map')
|
||||||
|
init_x = LaunchConfiguration('init_x')
|
||||||
|
init_y = LaunchConfiguration('init_y')
|
||||||
|
init_yaw = LaunchConfiguration('init_yaw')
|
||||||
|
show_points = LaunchConfiguration('show_points')
|
||||||
|
points_file = LaunchConfiguration('points_file')
|
||||||
|
|
||||||
|
return LaunchDescription([
|
||||||
|
DeclareLaunchArgument('use_rviz', default_value='true'),
|
||||||
|
DeclareLaunchArgument('map', default_value=default_map,
|
||||||
|
description='地图 yaml 路径(P4 由 map_saver 存盘得到)'),
|
||||||
|
DeclareLaunchArgument('init_x', default_value='0.0'),
|
||||||
|
DeclareLaunchArgument('init_y', default_value='0.0'),
|
||||||
|
DeclareLaunchArgument('init_yaw', default_value='0.0'),
|
||||||
|
DeclareLaunchArgument('show_points', default_value='true'),
|
||||||
|
DeclareLaunchArgument('points_file',
|
||||||
|
default_value=os.path.join(pkg_share, 'config', 'taught_points.yaml'),
|
||||||
|
description='示教点位文件(rviz 显示);想实时看示教就指向你的 taught_points.yaml'),
|
||||||
|
|
||||||
|
# ===== 传感器 + 激光里程计(odom -> base_footprint) =====
|
||||||
|
IncludeLaunchDescription(PythonLaunchDescriptionSource(lidar_launch)),
|
||||||
|
Node(
|
||||||
|
package='rf2o_laser_odometry',
|
||||||
|
executable='rf2o_laser_odometry_node',
|
||||||
|
name='rf2o_laser_odometry',
|
||||||
|
output='screen',
|
||||||
|
parameters=[{
|
||||||
|
'laser_scan_topic': '/scan',
|
||||||
|
'odom_topic': '/odom',
|
||||||
|
'publish_tf': True,
|
||||||
|
'base_frame_id': 'base_footprint',
|
||||||
|
'odom_frame_id': 'odom',
|
||||||
|
'init_pose_from_topic': '',
|
||||||
|
'freq': 10.0,
|
||||||
|
}],
|
||||||
|
),
|
||||||
|
|
||||||
|
# ===== 地图服务 =====
|
||||||
|
Node(
|
||||||
|
package='nav2_map_server',
|
||||||
|
executable='map_server',
|
||||||
|
name='map_server',
|
||||||
|
output='screen',
|
||||||
|
parameters=[amcl_params, {'yaml_filename': map_yaml}],
|
||||||
|
),
|
||||||
|
|
||||||
|
# ===== AMCL 定位 =====
|
||||||
|
Node(
|
||||||
|
package='nav2_amcl',
|
||||||
|
executable='amcl',
|
||||||
|
name='amcl',
|
||||||
|
output='screen',
|
||||||
|
parameters=[amcl_params, {
|
||||||
|
'set_initial_pose': True,
|
||||||
|
'initial_pose.x': ParameterValue(init_x, value_type=float),
|
||||||
|
'initial_pose.y': ParameterValue(init_y, value_type=float),
|
||||||
|
'initial_pose.yaw': ParameterValue(init_yaw, value_type=float),
|
||||||
|
}],
|
||||||
|
),
|
||||||
|
|
||||||
|
# ===== 生命周期管理:自动 configure+activate map_server 与 amcl =====
|
||||||
|
Node(
|
||||||
|
package='nav2_lifecycle_manager',
|
||||||
|
executable='lifecycle_manager',
|
||||||
|
name='lifecycle_manager_localization',
|
||||||
|
output='screen',
|
||||||
|
parameters=[{
|
||||||
|
'use_sim_time': False,
|
||||||
|
'autostart': True,
|
||||||
|
'node_names': ['map_server', 'amcl'],
|
||||||
|
}],
|
||||||
|
),
|
||||||
|
|
||||||
|
# ===== 示教点位可视化 =====
|
||||||
|
Node(
|
||||||
|
package='craic_localization',
|
||||||
|
executable='show_points',
|
||||||
|
name='show_points',
|
||||||
|
output='screen',
|
||||||
|
parameters=[{'points_file': points_file}],
|
||||||
|
condition=IfCondition(show_points),
|
||||||
|
),
|
||||||
|
|
||||||
|
Node(
|
||||||
|
package='rviz2',
|
||||||
|
executable='rviz2',
|
||||||
|
name='rviz2',
|
||||||
|
arguments=['-d', default_rviz],
|
||||||
|
condition=IfCondition(use_rviz),
|
||||||
|
output='screen',
|
||||||
|
),
|
||||||
|
])
|
||||||
73
ros2/src/craic_localization/launch/mapping.launch.py
Normal file
73
ros2/src/craic_localization/launch/mapping.launch.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""P4 建图:激光 + rf2o 激光里程计 + slam_gmapping(SLAM)。
|
||||||
|
|
||||||
|
odom→base_footprint 由 rf2o(从连续 /scan 推算)提供,**不依赖底盘里程计板** ——
|
||||||
|
该板上电后仅短时上报,会让 odom TF 冻结、gmapping 报 "queue is full" 丢弃所有 scan。
|
||||||
|
本管线对齐 lzu_robot 可正常建图的做法(rf2o 提供 odom→base,gmapping 出图)。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
ros2 launch craic_localization mapping.launch.py
|
||||||
|
# 另开终端遥控走图;满意后存盘:
|
||||||
|
ros2 run nav2_map_server map_saver_cli -f <pkg>/maps/craic --ros-args -p save_map_timeout:=10000.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||||
|
from launch.conditions import IfCondition
|
||||||
|
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||||
|
from launch.substitutions import LaunchConfiguration
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
pkg_share = get_package_share_directory('craic_localization')
|
||||||
|
lidar_launch = os.path.join(pkg_share, 'launch', 'lidar.launch.py')
|
||||||
|
gmapping_params = os.path.join(pkg_share, 'config', 'gmapping.yaml')
|
||||||
|
default_rviz = os.path.join(pkg_share, 'rviz', 'localization.rviz')
|
||||||
|
|
||||||
|
use_rviz = LaunchConfiguration('use_rviz')
|
||||||
|
|
||||||
|
return LaunchDescription([
|
||||||
|
DeclareLaunchArgument('use_rviz', default_value='true'),
|
||||||
|
|
||||||
|
# 激光 + 静态外参(base_footprint->laser_frame, base_footprint->base_link)
|
||||||
|
IncludeLaunchDescription(PythonLaunchDescriptionSource(lidar_launch)),
|
||||||
|
|
||||||
|
# 激光里程计:从 /scan 连续输出 odom -> base_footprint(建图运动来源)
|
||||||
|
Node(
|
||||||
|
package='rf2o_laser_odometry',
|
||||||
|
executable='rf2o_laser_odometry_node',
|
||||||
|
name='rf2o_laser_odometry',
|
||||||
|
output='screen',
|
||||||
|
parameters=[{
|
||||||
|
'laser_scan_topic': '/scan',
|
||||||
|
'odom_topic': '/odom',
|
||||||
|
'publish_tf': True,
|
||||||
|
'base_frame_id': 'base_footprint',
|
||||||
|
'odom_frame_id': 'odom',
|
||||||
|
'init_pose_from_topic': '',
|
||||||
|
'freq': 10.0,
|
||||||
|
}],
|
||||||
|
),
|
||||||
|
|
||||||
|
# SLAM:发布 /map 与 TF map -> odom
|
||||||
|
Node(
|
||||||
|
package='slam_gmapping',
|
||||||
|
executable='slam_gmapping',
|
||||||
|
name='slam_gmapping',
|
||||||
|
output='screen',
|
||||||
|
parameters=[gmapping_params],
|
||||||
|
),
|
||||||
|
|
||||||
|
Node(
|
||||||
|
package='rviz2',
|
||||||
|
executable='rviz2',
|
||||||
|
name='rviz2',
|
||||||
|
arguments=['-d', default_rviz],
|
||||||
|
condition=IfCondition(use_rviz),
|
||||||
|
output='screen',
|
||||||
|
),
|
||||||
|
])
|
||||||
26
ros2/src/craic_localization/package.xml
Normal file
26
ros2/src/craic_localization/package.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>craic_localization</name>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
<description>Wheel odometry + LiDAR localization for the CRAIC mobile base</description>
|
||||||
|
<maintainer email="fallensigh@gmail.com">fallensigh</maintainer>
|
||||||
|
<license>MIT</license>
|
||||||
|
|
||||||
|
<depend>rclpy</depend>
|
||||||
|
<depend>nav_msgs</depend>
|
||||||
|
<depend>geometry_msgs</depend>
|
||||||
|
<depend>std_msgs</depend>
|
||||||
|
<depend>tf2_ros</depend>
|
||||||
|
<depend>visualization_msgs</depend>
|
||||||
|
<exec_depend>python3-yaml</exec_depend>
|
||||||
|
|
||||||
|
<test_depend>ament_copyright</test_depend>
|
||||||
|
<test_depend>ament_flake8</test_depend>
|
||||||
|
<test_depend>ament_pep257</test_depend>
|
||||||
|
<test_depend>python3-pytest</test_depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_python</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
130
ros2/src/craic_localization/rviz/localization.rviz
Normal file
130
ros2/src/craic_localization/rviz/localization.rviz
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
Panels:
|
||||||
|
- Class: rviz_common/Displays
|
||||||
|
Name: Displays
|
||||||
|
Property Tree Widget:
|
||||||
|
Expanded:
|
||||||
|
- /TF1
|
||||||
|
- /LaserScan1
|
||||||
|
- /LaserScan1/Topic1
|
||||||
|
- /Odometry1
|
||||||
|
Splitter Ratio: 0.5
|
||||||
|
- Class: rviz_common/Views
|
||||||
|
Name: Views
|
||||||
|
Visualization Manager:
|
||||||
|
Class: ""
|
||||||
|
Displays:
|
||||||
|
- Class: rviz_default_plugins/Grid
|
||||||
|
Name: Grid
|
||||||
|
Enabled: true
|
||||||
|
Reference Frame: <Fixed Frame>
|
||||||
|
Plane Cell Count: 20
|
||||||
|
Cell Size: 0.5
|
||||||
|
Color: 160; 160; 164
|
||||||
|
Line Style:
|
||||||
|
Line Width: 0.03
|
||||||
|
Value: Lines
|
||||||
|
Value: true
|
||||||
|
- Class: rviz_default_plugins/TF
|
||||||
|
Name: TF
|
||||||
|
Enabled: true
|
||||||
|
Show Names: true
|
||||||
|
Show Axes: true
|
||||||
|
Show Arrows: true
|
||||||
|
Marker Scale: 0.5
|
||||||
|
Update Interval: 0
|
||||||
|
Frame Timeout: 15
|
||||||
|
Value: true
|
||||||
|
- Class: rviz_default_plugins/Map
|
||||||
|
Name: Map
|
||||||
|
Enabled: true
|
||||||
|
Topic:
|
||||||
|
Value: /map
|
||||||
|
Depth: 1
|
||||||
|
Durability Policy: Transient Local
|
||||||
|
History Policy: Keep Last
|
||||||
|
Reliability Policy: Reliable
|
||||||
|
Color Scheme: map
|
||||||
|
Draw Behind: true
|
||||||
|
Alpha: 0.7
|
||||||
|
Value: true
|
||||||
|
- Class: rviz_default_plugins/LaserScan
|
||||||
|
Name: LaserScan
|
||||||
|
Enabled: true
|
||||||
|
Topic:
|
||||||
|
Value: /scan
|
||||||
|
Depth: 5
|
||||||
|
Durability Policy: Volatile
|
||||||
|
History Policy: Keep Last
|
||||||
|
Reliability Policy: Best Effort
|
||||||
|
Filter size: 10
|
||||||
|
Style: Points
|
||||||
|
Size (m): 0.02
|
||||||
|
Size (Pixels): 3
|
||||||
|
Color Transformer: FlatColor
|
||||||
|
Color: 255; 30; 30
|
||||||
|
Decay Time: 0
|
||||||
|
Position Transformer: XYZ
|
||||||
|
Value: true
|
||||||
|
- Class: rviz_default_plugins/Odometry
|
||||||
|
Name: Odometry
|
||||||
|
Enabled: true
|
||||||
|
Topic:
|
||||||
|
Value: /odom
|
||||||
|
Depth: 10
|
||||||
|
Durability Policy: Volatile
|
||||||
|
History Policy: Keep Last
|
||||||
|
Reliability Policy: Reliable
|
||||||
|
Keep: 1
|
||||||
|
Position Tolerance: 0.05
|
||||||
|
Angle Tolerance: 0.05
|
||||||
|
Shape:
|
||||||
|
Value: Arrow
|
||||||
|
Color: 30; 255; 30
|
||||||
|
Shaft Length: 0.3
|
||||||
|
Shaft Radius: 0.03
|
||||||
|
Head Length: 0.1
|
||||||
|
Head Radius: 0.06
|
||||||
|
Alpha: 1
|
||||||
|
Covariance:
|
||||||
|
Value: false
|
||||||
|
Value: true
|
||||||
|
- Class: rviz_default_plugins/MarkerArray
|
||||||
|
Name: TaughtPoints
|
||||||
|
Enabled: true
|
||||||
|
Topic:
|
||||||
|
Value: /taught_points
|
||||||
|
Depth: 5
|
||||||
|
Durability Policy: Volatile
|
||||||
|
History Policy: Keep Last
|
||||||
|
Reliability Policy: Reliable
|
||||||
|
Namespaces: {}
|
||||||
|
Value: true
|
||||||
|
Global Options:
|
||||||
|
Fixed Frame: odom
|
||||||
|
Background Color: 48; 48; 48
|
||||||
|
Frame Rate: 30
|
||||||
|
Tools:
|
||||||
|
- Class: rviz_default_plugins/MoveCamera
|
||||||
|
- Class: rviz_default_plugins/Select
|
||||||
|
- Class: rviz_default_plugins/FocusCamera
|
||||||
|
- Class: rviz_default_plugins/Measure
|
||||||
|
Views:
|
||||||
|
Current:
|
||||||
|
Class: rviz_default_plugins/Orbit
|
||||||
|
Name: Current View
|
||||||
|
Distance: 8
|
||||||
|
Focal Point:
|
||||||
|
X: 0
|
||||||
|
Y: 0
|
||||||
|
Z: 0
|
||||||
|
Pitch: 1.2
|
||||||
|
Yaw: 3.14
|
||||||
|
Target Frame: <Fixed Frame>
|
||||||
|
Saved: ~
|
||||||
|
Window Geometry:
|
||||||
|
Height: 800
|
||||||
|
Width: 1200
|
||||||
|
Displays:
|
||||||
|
collapsed: false
|
||||||
|
Views:
|
||||||
|
collapsed: false
|
||||||
4
ros2/src/craic_localization/setup.cfg
Normal file
4
ros2/src/craic_localization/setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[develop]
|
||||||
|
script_dir=$base/lib/craic_localization
|
||||||
|
[install]
|
||||||
|
install_scripts=$base/lib/craic_localization
|
||||||
46
ros2/src/craic_localization/setup.py
Normal file
46
ros2/src/craic_localization/setup.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import os
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
package_name = 'craic_localization'
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name=package_name,
|
||||||
|
version='0.0.0',
|
||||||
|
packages=find_packages(exclude=['test']),
|
||||||
|
data_files=[
|
||||||
|
('share/ament_index/resource_index/packages',
|
||||||
|
['resource/' + package_name]),
|
||||||
|
('share/' + package_name, ['package.xml']),
|
||||||
|
(os.path.join('share', package_name, 'config'),
|
||||||
|
glob('config/*.yaml')),
|
||||||
|
(os.path.join('share', package_name, 'launch'),
|
||||||
|
glob('launch/*.launch.py')),
|
||||||
|
(os.path.join('share', package_name, 'rviz'),
|
||||||
|
glob('rviz/*.rviz')),
|
||||||
|
(os.path.join('share', package_name, 'urdf'),
|
||||||
|
glob('urdf/*')),
|
||||||
|
(os.path.join('share', package_name, 'maps'),
|
||||||
|
glob('maps/*')),
|
||||||
|
],
|
||||||
|
install_requires=['setuptools'],
|
||||||
|
zip_safe=True,
|
||||||
|
maintainer='fallensigh',
|
||||||
|
maintainer_email='fallensigh@gmail.com',
|
||||||
|
description='Wheel odometry + LiDAR localization for the CRAIC mobile base',
|
||||||
|
license='MIT',
|
||||||
|
extras_require={
|
||||||
|
'test': [
|
||||||
|
'pytest',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'chassis_odometry = craic_localization.chassis_odometry:main',
|
||||||
|
'teach_points = craic_localization.teach_points:main',
|
||||||
|
'navigate_to_point = craic_localization.navigate_to_point:main',
|
||||||
|
'show_points = craic_localization.show_points:main',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user