1
0
forked from zbw/yiliao2026

修复了雷达扫描范围的bug,修复了里程计数据的发布者冲突的问题。目前存在问题:odom坐标系在运动过程中还是会飘

This commit is contained in:
2026-06-13 17:03:06 +08:00
parent 4e576888f5
commit 51ea5bd52e
12 changed files with 1624 additions and 72 deletions

Binary file not shown.

View File

@@ -1134,7 +1134,7 @@ namespace lslidar_driver
{ {
auto scan = sensor_msgs::msg::LaserScan::UniquePtr(new sensor_msgs::msg::LaserScan()); auto scan = sensor_msgs::msg::LaserScan::UniquePtr(new sensor_msgs::msg::LaserScan());
//int scan_num = ceil((angle_able_max - angle_able_min) / 360 * count_num) + 1; //int scan_num = ceil((angle_able_max - angle_able_min) / 360 * count_num) + 1;
int scan_num = fixed_array_length;//cyy_addcyy_add int scan_num = ceil((angle_able_max - angle_able_min) / 360 * count_num) + 1;
std::vector<ScanPoint> points; std::vector<ScanPoint> points;
rclcpp::Time start_time; rclcpp::Time start_time;
@@ -1160,7 +1160,7 @@ namespace lslidar_driver
scan->angle_min = 2 * M_PI * angle_able_min / 360; scan->angle_min = 2 * M_PI * angle_able_min / 360;
scan->angle_max = 2 * M_PI * angle_able_max / 360; scan->angle_max = 2 * M_PI * angle_able_max / 360;
} }
scan->angle_increment = 2 * M_PI / (double)(fixed_array_length - 1); scan->angle_increment = 2 * M_PI / (double)(scan_num - 1);
scan->range_min = min_range; scan->range_min = min_range;
scan->range_max = max_range; scan->range_max = max_range;
@@ -1169,7 +1169,7 @@ namespace lslidar_driver
scan->intensities.reserve(scan_num); scan->intensities.reserve(scan_num);
scan->intensities.assign(scan_num, std::numeric_limits<float>::infinity()); scan->intensities.assign(scan_num, std::numeric_limits<float>::infinity());
scan->scan_time = 0.1; scan->scan_time = 0.1;
scan->time_increment = 0.1 / (double)(fixed_array_length - 1); scan->time_increment = 0.1 / (double)(scan_num - 1);
int start_num = floor(angle_able_min * count_num / 360); int start_num = floor(angle_able_min * count_num / 360);
int end_num = floor(angle_able_max * count_num / 360); int end_num = floor(angle_able_max * count_num / 360);

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
# ============================================================================ # ============================================================================
# gc_nav2_with_slam_online_real.launch.py # gc_nav2_with_slam_online_real.launch.py
# 功能:真机实时建图 — slam_toolbox 从零建图 + Nav2 导航 # 功能:真机实时建图 — slam_toolbox 从零建图 + Nav2 导航
# 依赖:需先启动 origincar_bringup底盘驱动 + EKF
# ============================================================================ # ============================================================================
import os import os
@@ -29,7 +30,7 @@ def generate_launch_description():
pkg_dir, 'params', 'gc_navigation_slam.yaml')) pkg_dir, 'params', 'gc_navigation_slam.yaml'))
slam_params_file = os.path.join(pkg_dir, 'config', 'slam_toolbox_mapping.yaml') slam_params_file = os.path.join(pkg_dir, 'config', 'slam_toolbox_mapping.yaml')
# === 3. slam_toolbox实时建图 → 直接发布到 /map === # === 3. slam_toolbox实时建图 → 发布 map→odom ===
slam_toolbox_node = Node( slam_toolbox_node = Node(
package='slam_toolbox', package='slam_toolbox',
executable='async_slam_toolbox_node', executable='async_slam_toolbox_node',
@@ -49,7 +50,7 @@ def generate_launch_description():
}.items(), }.items(),
) )
# === 5. 机器人模型 + TF === # === 5. 机器人模型URDF 已定义 base_footprint→base_link无需 static TF===
model = DeclareLaunchArgument( model = DeclareLaunchArgument(
name='model', name='model',
default_value=os.path.join(origincar_urdf_dir, 'urdf', 'origincar.urdf')) default_value=os.path.join(origincar_urdf_dir, 'urdf', 'origincar.urdf'))
@@ -63,17 +64,9 @@ def generate_launch_description():
'use_sim_time': use_sim_time, 'publish_frequency': 30.0}], 'use_sim_time': use_sim_time, 'publish_frequency': 30.0}],
) )
base_footprint_tf = Node(
package='tf2_ros',
executable='static_transform_publisher',
name='base_footprint_to_base_link',
arguments=['0', '0', '0', '0', '0', '0', 'base_footprint', 'base_link'],
)
# === 6. 组装 === # === 6. 组装 ===
ld.add_action(model) ld.add_action(model)
ld.add_action(robot_state_publisher) ld.add_action(robot_state_publisher)
ld.add_action(base_footprint_tf)
ld.add_action(slam_toolbox_node) ld.add_action(slam_toolbox_node)
ld.add_action(navigation_launch) ld.add_action(navigation_launch)

View File

@@ -13,7 +13,7 @@ ekf_filter_node:
map_frame: map map_frame: map
odom_frame: odom odom_frame: odom
base_link_frame: base_link base_link_frame: base_footprint
world_frame: odom world_frame: odom
odom0: odom odom0: odom

View File

@@ -212,6 +212,7 @@ private:
rclcpp::Subscription<std_msgs::msg::Int32>::SharedPtr Sign_Switch_Sub; rclcpp::Subscription<std_msgs::msg::Int32>::SharedPtr Sign_Switch_Sub;
string usart_port_name, robot_frame_id, gyro_frame_id, odom_frame_id, akm_cmd_vel, test; string usart_port_name, robot_frame_id, gyro_frame_id, odom_frame_id, akm_cmd_vel, test;
bool publish_tf_;
std::string cmd_vel; std::string cmd_vel;
int serial_baud_rate; int serial_baud_rate;
RECEIVE_DATA Receive_Data; RECEIVE_DATA Receive_Data;

View File

@@ -37,12 +37,6 @@ def generate_launch_description():
) )
base_to_link = launch_ros.actions.Node(
package='tf2_ros',
executable='static_transform_publisher',
name='base_to_link',
arguments=['0', '0', '0','0', '0','0','base_footprint','base_link'],#arguments=['0.41', '0.12', '0','0', '0','0','base_footprint','base_link'],
)
base_to_gyro = launch_ros.actions.Node( base_to_gyro = launch_ros.actions.Node(
@@ -73,10 +67,21 @@ def generate_launch_description():
remappings=[("odometry/filtered", "odom_combined")] remappings=[("odometry/filtered", "odom_combined")]
) )
# 从 URDF 生成 robot_description供 joint_state_publisher 和 robot_state_publisher 共用
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command
robot_description = ParameterValue(
Command(['xacro ', os.path.join(
get_package_share_directory('origincar_description'),
'urdf', 'origincar.urdf')]),
value_type=str)
joint_state_publisher_node = launch_ros.actions.Node( joint_state_publisher_node = launch_ros.actions.Node(
package='joint_state_publisher', package='joint_state_publisher',
executable='joint_state_publisher', executable='joint_state_publisher',
name='joint_state_publisher', name='joint_state_publisher',
parameters=[{'robot_description': robot_description}],
) )
ld = LaunchDescription() ld = LaunchDescription()
@@ -84,7 +89,6 @@ def generate_launch_description():
ld.add_action(carto_slam_dec) ld.add_action(carto_slam_dec)
ld.add_action(akmcar_dec) ld.add_action(akmcar_dec)
ld.add_action(origincar_base) ld.add_action(origincar_base)
ld.add_action(base_to_link)
ld.add_action(base_to_gyro) ld.add_action(base_to_gyro)
ld.add_action(joint_state_publisher_node) ld.add_action(joint_state_publisher_node)
ld.add_action(choose_car) ld.add_action(choose_car)

View File

@@ -1,24 +1,27 @@
import os import os
from pathlib import Path
import launch_ros.actions
import launch
from ament_index_python.packages import get_package_share_directory from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription from launch import LaunchDescription
from launch.actions import (DeclareLaunchArgument, GroupAction,LogInfo, from launch_ros.actions import Node
IncludeLaunchDescription, SetEnvironmentVariable) from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import LaunchConfiguration from launch.substitutions import Command
def generate_launch_description(): def generate_launch_description():
origincar_description = GroupAction([ urdf_path = os.path.join(
launch_ros.actions.Node( get_package_share_directory('origincar_description'),
'urdf', 'origincar.urdf')
robot_description = ParameterValue(
Command(['xacro ', urdf_path]), value_type=str)
robot_state_publisher = Node(
package='robot_state_publisher', package='robot_state_publisher',
executable='robot_state_publisher', executable='robot_state_publisher',
name='robot_state_publisher', name='robot_state_publisher',
arguments=[os.path.join(get_package_share_directory('origincar_description'),'urdf','origincar.urdf')] parameters=[{'robot_description': robot_description,
'publish_frequency': 30.0}],
) )
])
ld = LaunchDescription() ld = LaunchDescription()
ld.add_action(robot_state_publisher)
ld.add_action(origincar_description)
return ld return ld

View File

View File

@@ -225,7 +225,9 @@ void origincar_base::Publish_Odom()
t.transform.translation.z = 0.0; t.transform.translation.z = 0.0;
t.transform.rotation = odom_quat; t.transform.rotation = odom_quat;
if (publish_tf_) {
tf_broadcaster_->sendTransform(t); tf_broadcaster_->sendTransform(t);
}
odom_publisher->publish(odom); odom_publisher->publish(odom);
robotpose_publisher->publish(robotpose); robotpose_publisher->publish(robotpose);
robotvel_publisher->publish(robotvel); robotvel_publisher->publish(robotvel);
@@ -264,7 +266,11 @@ bool origincar_base::Get_Sensor_Data()
{ {
short transition_16 = 0, j = 0, Header_Pos = 0, Tail_Pos = 0; short transition_16 = 0, j = 0, Header_Pos = 0, Tail_Pos = 0;
uint8_t Receive_Data_Pr[RECEIVE_DATA_SIZE] = {0}; uint8_t Receive_Data_Pr[RECEIVE_DATA_SIZE] = {0};
try {
Stm32_Serial.read(Receive_Data_Pr,sizeof (Receive_Data_Pr)); Stm32_Serial.read(Receive_Data_Pr,sizeof (Receive_Data_Pr));
} catch (const serial::SerialException& e) {
return false;
}
for (j = 0; j < 24; j++) { for (j = 0; j < 24; j++) {
if (Receive_Data_Pr[j] == FRAME_HEADER) if (Receive_Data_Pr[j] == FRAME_HEADER)
Header_Pos=j; Header_Pos=j;
@@ -375,6 +381,7 @@ origincar_base::origincar_base()
this->declare_parameter<std::string>("odom_frame_id", "odom"); this->declare_parameter<std::string>("odom_frame_id", "odom");
this->declare_parameter<std::string>("robot_frame_id", "base_link"); this->declare_parameter<std::string>("robot_frame_id", "base_link");
this->declare_parameter<std::string>("gyro_frame_id", "gyro_link"); this->declare_parameter<std::string>("gyro_frame_id", "gyro_link");
this->declare_parameter<bool>("publish_tf", false);
this->get_parameter("serial_baud_rate", serial_baud_rate); this->get_parameter("serial_baud_rate", serial_baud_rate);
this->get_parameter("usart_port_name", usart_port_name); this->get_parameter("usart_port_name", usart_port_name);
@@ -383,6 +390,7 @@ origincar_base::origincar_base()
this->get_parameter("odom_frame_id", odom_frame_id); this->get_parameter("odom_frame_id", odom_frame_id);
this->get_parameter("robot_frame_id", robot_frame_id); this->get_parameter("robot_frame_id", robot_frame_id);
this->get_parameter("gyro_frame_id", gyro_frame_id); this->get_parameter("gyro_frame_id", gyro_frame_id);
this->get_parameter("publish_tf", publish_tf_);
odom_publisher = create_publisher<nav_msgs::msg::Odometry>("odom", 10); odom_publisher = create_publisher<nav_msgs::msg::Odometry>("odom", 10);

View File

@@ -4,12 +4,6 @@
<link name="base_footprint"> <link name="base_footprint">
<origin xyz="0 0 0" rpy="0 0 0" /> <origin xyz="0 0 0" rpy="0 0 0" />
</link> </link>
<<<<<<< HEAD
<!-- 基础底盘 -->
=======
>>>>>>> mo_new
<link name="base_link"> <link name="base_link">
</link> </link>
<joint name="base_joint" type="fixed"> <joint name="base_joint" type="fixed">
@@ -89,11 +83,7 @@
</inertial> </inertial>
</link> </link>
<joint name="fr_left_steer_joint" type="revolute"> <joint name="fr_left_steer_joint" type="revolute">
<<<<<<< HEAD
<origin xyz="0.0841 0.0945 -0.03" rpy="0 0 0" />
=======
<origin xyz="0.0715 0.0945 -0.03" rpy="0 0 0" /> <origin xyz="0.0715 0.0945 -0.03" rpy="0 0 0" />
>>>>>>> mo_new
<parent link="chassis_link" /> <parent link="chassis_link" />
<child link="fr_left_steer_link" /> <child link="fr_left_steer_link" />
<axis xyz="0 0 1" /> <axis xyz="0 0 1" />
@@ -146,11 +136,7 @@
</inertial> </inertial>
</link> </link>
<joint name="fr_right_steer_joint" type="revolute"> <joint name="fr_right_steer_joint" type="revolute">
<<<<<<< HEAD
<origin xyz="0.0841 -0.0945 -0.03" rpy="0 0 0" />
=======
<origin xyz="0.0715 -0.0945 -0.03" rpy="0 0 0" /> <origin xyz="0.0715 -0.0945 -0.03" rpy="0 0 0" />
>>>>>>> mo_new
<parent link="chassis_link" /> <parent link="chassis_link" />
<child link="fr_right_steer_link" /> <child link="fr_right_steer_link" />
<axis xyz="0 0 1" /> <axis xyz="0 0 1" />
@@ -219,11 +205,7 @@
</collision> </collision>
</link> </link>
<joint name="re_left_wheel_joint" type="continuous"> <joint name="re_left_wheel_joint" type="continuous">
<<<<<<< HEAD
<origin xyz="-0.0841 0.0945 -0.03" rpy="1.5708 0 0" />
=======
<origin xyz="-0.0715 0.0945 -0.03" rpy="1.5708 0 0" /> <origin xyz="-0.0715 0.0945 -0.03" rpy="1.5708 0 0" />
>>>>>>> mo_new
<parent link="chassis_link" /> <parent link="chassis_link" />
<child link="re_left_wheel_link" /> <child link="re_left_wheel_link" />
<axis xyz="0 0 -1" /> <axis xyz="0 0 -1" />
@@ -257,11 +239,7 @@
</collision> </collision>
</link> </link>
<joint name="re_right_wheel_joint" type="continuous"> <joint name="re_right_wheel_joint" type="continuous">
<<<<<<< HEAD
<origin xyz="-0.0841 -0.0945 -0.03" rpy="-1.5708 0 0" />
=======
<origin xyz="-0.0715 -0.0945 -0.03" rpy="-1.5708 0 0" /> <origin xyz="-0.0715 -0.0945 -0.03" rpy="-1.5708 0 0" />
>>>>>>> mo_new
<parent link="chassis_link" /> <parent link="chassis_link" />
<child link="re_right_wheel_link" /> <child link="re_right_wheel_link" />
<axis xyz="0 0 1" /> <axis xyz="0 0 1" />
@@ -287,11 +265,7 @@
</inertial> </inertial>
</link> </link>
<joint name="virtual_steering_wheel_joint" type="revolute"> <joint name="virtual_steering_wheel_joint" type="revolute">
<<<<<<< HEAD
<origin xyz="0.0841 0.0 0.0" rpy="0.0 0.0 0.0" />
=======
<origin xyz="0.0715 0.0 0.0" rpy="0.0 0.0 0.0" /> <origin xyz="0.0715 0.0 0.0" rpy="0.0 0.0 0.0" />
>>>>>>> mo_new
<parent link="chassis_link" /> <parent link="chassis_link" />
<child link="virtual_steer_link" /> <child link="virtual_steer_link" />
<axis xyz="-1 0 0" /> <axis xyz="-1 0 0" />
@@ -336,10 +310,6 @@
<origin rpy="0 0 0" xyz="0 0 0.045" /> <origin rpy="0 0 0" xyz="0 0 0.045" />
</joint> </joint>
<<<<<<< HEAD
=======
>>>>>>> mo_new
<!-- ackerman --> <!-- ackerman -->
<gazebo> <gazebo>

View File

@@ -17,3 +17,153 @@ Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
16:48 16:48
雷达串口问题解决原因是type-c转接线的芯片问题换了一条转接线后雷达串口设备正常显示为ttyACM0和ttyACM1了。 雷达串口问题解决原因是type-c转接线的芯片问题换了一条转接线后雷达串口设备正常显示为ttyACM0和ttyACM1了。
============================================================
2026/6/11 — TF树修复 & base驱动稳定性修复
============================================================
## 问题1: SLAM Toolbox Message Filter 丢帧
**现象**: `Message Filter dropping message: frame laser_link ... reason discarding message because the queue is full`
**原因**: `slam_toolbox_mapping.yaml` 中 `scan_queue_size` 偏小,激光雷达 10Hz、minimum_time_interval 0.5s,队列缓冲不足
**修复**: 将 `scan_queue_size` 适当调大即可(属配置调优项,未做硬编码修改)
## 问题2: SLAM Toolbox Message Filter 时间戳过期
**现象**: `Message Filter dropping message: frame laser_link ... reason the timestamp on the message is earlier than all the data in the transform cache`
**原因**: 激光帧时间戳早于 TF 缓存最早数据,通常刚启动时 TF 尚未就绪或处理积压
**修复**: 随问题3 TF树修复后缓解
## 问题3: TF 树断连 — bt_navigator 找不到 map→base_footprint (核心问题)
**现象**: `Could not find a connection between map and base_footprint because they are not part of the same tree. Tf has two or more unconnected trees.`
**根因**: 两个节点同时发布 `odom→base_link` TF:
- base 驱动 (origincar_base.cpp:228) — 原始轮式里程计
- EKF (ekf.yaml: base_link_frame=base_link, publish_tf=true) — 融合 odom+IMU
两节点争夺同一条 TF导致树撕裂
**修复** (4处改动):
1. ekf.yaml: base_link_frame 从 base_link 改为 base_footprint
→ EKF 发布 odom→base_footprint不再与 base 驱动争抢
2. origincar_base.cpp: 新增 publish_tf 参数,默认 false
→ base 驱动默认不再广播 TF
3. gc_nav2_with_slam_online_real.launch.py: 移除重复的 base_footprint→base_link static TF
→ URDF 已定义该关节 (z=-0.09)static TF 提供 identity (z=0) 造成冲突
4. origincar_bringup.launch.py: 移除 base_to_link static TF同上
**修复后 TF 树**:
map → odom → base_footprint → base_link → chassis_link → borad_link → laser_link
↑ ↑ ↑ ↑
SLAM EKF融合 URDF URDF
## 问题4: cmd_vel_to_ackermann_drive.py 找不到
**现象**: `executable cmd_vel_to_ackermann_drive.py not found on the libexec directory`
**原因**: 源文件缺执行权限 (644)--symlink-install 时安装路径继承源文件权限
**修复**: chmod +x scripts/cmd_vel_to_ackermann_drive.py
## 问题5: base 驱动串口异常崩溃
**现象**: `terminate called after throwing serial::SerialException: device reports readiness to read but returned no data`
**原因**: Get_Sensor_Data() 中 Stm32_Serial.read() 抛异常未捕获
**修复**: origincar_base.cpp 中 Stm32_Serial.read() 包裹 try-catch捕获 SerialException 后 return false
## 问题6: URDF 文件 git 冲突未解决
**现象**: xacro 解析失败 — XML parsing error: not well-formed (invalid token)
**原因**: origincar.urdf 存在 8 处 git merge 冲突标记 (HEAD vs mo_new),涉及轮距参数 (0.0841 vs 0.0715)
**修复**: 清除所有冲突标记,采用 mo_new 分支轮距值
## 问题7: joint_state_publisher 找不到 robot_description
**现象**: `Waiting for robot_description to be published on the robot_description topic...`
**原因**: robot_mode_description.launch.py 使用旧式命令行参数传 URDF未发布到 topic
**修复**: 改为参数方式传递 robot_description同时传给 joint_state_publisher
## 编译记录
- 命令: colcon build --packages-select origincar_base --symlink-install
- 结果: 通过
================================================================================
日期: 2026-06-13
问题: LSLiDAR /scan 可视化中出现空扇形面
================================================================================
【现象】
在 rviz/foxglove 中查看 /scan 话题时显示中凭空出现一个约60°的扇形区域
没有任何点inf物体轮廓在该边界处像是被"切断"一般。但实际雷达能够检测
到360°所有方向的物体。
【根因分析】
驱动代码 lslidar_driver.cc 中N10 雷达走的是 else 分支Path 2
第1137行被硬编码为:
int scan_num = fixed_array_length; // 450
而原始正确公式(被注释掉)为:
//int scan_num = ceil((angle_able_max - angle_able_min) / 360 * count_num) + 1;
问题机制:
- LiDAR 硬件每圈输出约374个数据点 (分辨率 ~0.96°/点)完整覆盖360°
- 但 fixed_array_length=450 强制分配了450个bin (分辨率 0.80°/点)
- 索引映射 point_idx = round((360-degree) * count_num / 360) 最大只能到373
- 所以 indices 374-449 (78个bin, ~60°) 永远是 inf
- 所有真实数据被"压缩"到 0-299° 范围内显示
同时 angle_increment 和 time_increment 也使用了 fixed_array_length
导致与实际数据不匹配。
【修复内容】
文件: src/lslidar_driver/src/lslidar_driver.cc
修改1 (line 1137):
- 修复前: int scan_num = fixed_array_length;//cyy_addcyy_add
+ 修复后: int scan_num = ceil((angle_able_max - angle_able_min) / 360 * count_num) + 1;
修改2 (line 1163):
- 修复前: scan->angle_increment = 2 * M_PI / (double)(fixed_array_length - 1);
+ 修复后: scan->angle_increment = 2 * M_PI / (double)(scan_num - 1);
修改3 (line 1172):
- 修复前: scan->time_increment = 0.1 / (double)(fixed_array_length - 1);
+ 修复后: scan->time_increment = 0.1 / (double)(scan_num - 1);
【验证】
编译通过。需要重启 lslidar_driver_node 使修复生效:
ros2 lifecycle set lslidar_driver_node shutdown
然后重新启动 launch 文件。
================================================================================
日期: 2026-06-13
问题: LSLiDAR /scan 可视化中出现空扇形面
================================================================================
【现象】
在 rviz/foxglove 中查看 /scan 话题时显示中凭空出现一个约60°的扇形区域
没有任何点inf物体轮廓在该边界处像是被切断一般。但实际雷达能够检测
到360°所有方向的物体。
【根因分析】
驱动代码 lslidar_driver.cc 中N10 雷达走的是 else 分支Path 2
第1137行被硬编码为:
int scan_num = fixed_array_length; // 450
而原始正确公式(被注释掉)为:
//int scan_num = ceil((angle_able_max - angle_able_min) / 360 * count_num) + 1;
问题机制:
- LiDAR 硬件每圈输出约374个数据点 (分辨率 ~0.96°/点)完整覆盖360°
- 但 fixed_array_length=450 强制分配了450个bin (分辨率 0.80°/点)
- 索引映射 point_idx = round((360-degree) * count_num / 360) 最大只能到373
- 所以 indices 374-449 (78个bin, ~60°) 永远是 inf
- 所有真实数据被压缩到 0-299° 范围内显示
同时 angle_increment 和 time_increment 也使用了 fixed_array_length
导致与实际数据不匹配。
【修复内容】
文件: src/lslidar_driver/src/lslidar_driver.cc
修改1 (line 1137):
- 修复前: int scan_num = fixed_array_length;
+ 修复后: int scan_num = ceil((angle_able_max - angle_able_min) / 360 * count_num) + 1;
修改2 (line 1163):
- 修复前: scan->angle_increment = 2 * M_PI / (double)(fixed_array_length - 1);
+ 修复后: scan->angle_increment = 2 * M_PI / (double)(scan_num - 1);
修改3 (line 1172):
- 修复前: scan->time_increment = 0.1 / (double)(fixed_array_length - 1);
+ 修复后: scan->time_increment = 0.1 / (double)(scan_num - 1);
【验证】
编译通过。需要重启 lslidar_driver_node 使修复生效。