diff --git a/orange_bringup/config/motor_driver_params.yaml b/orange_bringup/config/motor_driver_params.yaml
index 3796ddb..e799121 100644
--- a/orange_bringup/config/motor_driver_params.yaml
+++ b/orange_bringup/config/motor_driver_params.yaml
@@ -7,7 +7,7 @@ motor_driver_node:
left_wheel_radius: 0.09767 # meter
right_wheel_radius: 0.09767 # meter
# For odometry computation
- computation_left_wheel_radius: 0.0970 # meter
+ computation_left_wheel_radius: 0.09755 # meter
computation_right_wheel_radius: 0.0973 # meter
# Encoder CPR(counts per revolution)
cpr: 16385
diff --git a/orange_bringup/launch/imu_data.launch.xml b/orange_bringup/launch/imu_data.launch.xml
new file mode 100644
index 0000000..2879952
--- /dev/null
+++ b/orange_bringup/launch/imu_data.launch.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/orange_bringup/launch/orange_robot.launch.xml b/orange_bringup/launch/orange_robot.launch.xml
index 4824862..841e94d 100644
--- a/orange_bringup/launch/orange_robot.launch.xml
+++ b/orange_bringup/launch/orange_robot.launch.xml
@@ -58,13 +58,42 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -81,14 +110,18 @@
-->
-
-
+
+
+
+
-
+
+
+
-
+
diff --git a/orange_bringup/orange_bringup/motor_driver_node.py b/orange_bringup/orange_bringup/motor_driver_node.py
index 4f4faea..1f6bdc3 100644
--- a/orange_bringup/orange_bringup/motor_driver_node.py
+++ b/orange_bringup/orange_bringup/motor_driver_node.py
@@ -492,6 +492,7 @@ def get_wheels_travelled(self):
) # unit in meter
return l_travelled, r_travelled
+ '''
def modbus_fail_read_handler(self, ADDR, WORD):
read_success = False
reg = [None] * WORD
@@ -506,6 +507,34 @@ def modbus_fail_read_handler(self, ADDR, WORD):
self.get_logger().error(e)
pass
return reg
+ '''
+ def modbus_fail_read_handler(self, ADDR, WORD, max_retry=5):
+ read_success = False
+ reg = [0] * WORD
+ retry_count = 0
+
+ while not read_success and retry_count < max_retry:
+ result = self.client.read_holding_registers(ADDR, WORD, slave=self.ID)
+ if result is None or not hasattr(result, "registers"):
+ self.get_logger().warn(f"Modbus read failed (try {retry_count+1}/{max_retry})")
+ time.sleep(0.2)
+ retry_count += 1
+ continue
+
+ try:
+ for i in range(WORD):
+ reg[i] = result.registers[i]
+ read_success = True
+ except Exception as e:
+ self.get_logger().error(f"Read error: {e}")
+ retry_count += 1
+ time.sleep(0.2)
+
+ if not read_success:
+ self.get_logger().error(f"Modbus read failed after {max_retry} retries. Stopping.")
+ raise RuntimeError("Modbus communication failed.")
+
+ return reg
def set_rpm_with_limit(self, left_rpm, right_rpm):
if self.left_rpm_lim < left_rpm:
diff --git a/orange_description/xacro/orange_robot.xacro b/orange_description/xacro/orange_robot.xacro
index 9e7afe4..ba8393f 100644
--- a/orange_description/xacro/orange_robot.xacro
+++ b/orange_description/xacro/orange_robot.xacro
@@ -7,17 +7,24 @@
+
-
+
+
+
+
+
+
+
diff --git a/orange_gnss/orange_gnss/GPSodom_correction.py b/orange_gnss/orange_gnss/GPSodom_correction.py
index 1ea3652..f397382 100644
--- a/orange_gnss/orange_gnss/GPSodom_correction.py
+++ b/orange_gnss/orange_gnss/GPSodom_correction.py
@@ -14,7 +14,7 @@ def __init__(self):
super().__init__('GPSodom_correction')
self.create_subscription(
- Odometry, "/odom/gps", self.odomgps_callback, 1)
+ Odometry, "/odom/UM982", self.odomgps_callback, 1)
self.create_subscription(
Imu, "/livox/imu", self.imu_callback, 1)
diff --git a/orange_gnss/orange_gnss/gnss_odom_movingbase_fix_publisher_ttyUSB.py b/orange_gnss/orange_gnss/gnss_odom_movingbase_fix_publisher_ttyUSB.py
index 9f8810c..5cc3f0b 100644
--- a/orange_gnss/orange_gnss/gnss_odom_movingbase_fix_publisher_ttyUSB.py
+++ b/orange_gnss/orange_gnss/gnss_odom_movingbase_fix_publisher_ttyUSB.py
@@ -16,24 +16,29 @@ class GPSData(Node):
def __init__(self):
super().__init__('gps_data_acquisition')
- # Parameters
self.declare_parameter('port', '/dev/sensors/GNSS_UM982')
self.declare_parameter('baud', 115200)
self.declare_parameter('country_id', 0)
self.declare_parameter('Position_magnification', 1.675)
self.declare_parameter('heading', 90.0)
+ self.declare_parameter('start_lat', 35.425952230280004) # tsukuba start point right 36.04974095972727, 140.04593633886364 , left 36.04976195993636, 140.04593755179093/nakaniwa 35.4257898377487,139.313807281254 /35.425952230280004, 139.31380123427
+ self.declare_parameter('start_lon', 139.31380123427)
self.dev_name = self.get_parameter('port').get_parameter_value().string_value
self.serial_baud = self.get_parameter('baud').get_parameter_value().integer_value
self.country_id = self.get_parameter('country_id').get_parameter_value().integer_value
self.Position_magnification = self.get_parameter('Position_magnification').get_parameter_value().double_value
- self.theta = self.get_parameter('heading').get_parameter_value().double_value
-
- #self.theta = 275.6 # tukuba param
- #self.theta = 180 #nakaniwa param
+ #self.theta = self.get_parameter('heading').get_parameter_value().double_value
+ self.tsukuba_theta= self.get_parameter('heading').get_parameter_value().double_value # nakaniwa 180 tsukuba 93
+ self.theta = self.tsukuba_theta
+
self.initial_coordinate = None
+ self.start_lat = self.get_parameter('start_lat').get_parameter_value().double_value
+ self.start_lon = self.get_parameter('start_lon').get_parameter_value().double_value
+ self.start_GPS_coordinate = [self.start_lat, self.start_lon]
self.fix_data = None
self.count = 0
+
self.initialized = False # 平均初期座標が取得できたかどうか
# Publishers
@@ -46,9 +51,12 @@ def __init__(self):
# service client
self.client = self.create_client(Avglatlon, 'send_avg_gps')
- while not self.client.wait_for_service(timeout_sec=1.0):
- self.get_logger().info("service not available...")
+ #while not self.client.wait_for_service(timeout_sec=1.0):
+ # self.get_logger().info("service not available...")
+ self.get_logger().info("Start get_lonlat quat node")
+ self.get_logger().info("-------------------------")
+
# Timers
self.timer = self.create_timer(1.0, self.timer_callback)
@@ -62,7 +70,7 @@ def __init__(self):
# tkinter GUI setup
self.root = tk.Tk()
self.root.title("GPS Data Acquisition")
- self.start_button = tk.Button(self.root, text="Start", command=self.start_gps_acquisition, width=20, height = 5)
+ self.start_button = tk.Button(self.root, text="Start GPS Acquisition", command=self.start_gps_acquisition, width=20, height = 5)
self.start_button.pack()
self.gps_acquisition_thread = None
@@ -73,7 +81,11 @@ def send_request(self):
request = Avglatlon.Request()
request.avg_lat = self.initial_coordinate[0] # ← average lat
request.avg_lon = self.initial_coordinate[1] # ← average lon
- request.theta = self.theta
+ request.current_lat = self.current_coordinate[0] # ← currennt lat
+ request.current_lon = self.current_coordinate[1] # ← currennt lon
+ #request.theta = self.theta
+ request.theta = self.tsukuba_theta # tsukuba start theta
+ request.current_theta = self.theta # for tsukuba
future = self.client.call_async(request)
future.add_done_callback(self.response_callback)
@@ -96,7 +108,7 @@ def timer_callback(self):
self.gps_data_cache = self.get_gps_quat(self.dev_name, self.country_id)
if self.gps_data_cache:
- fix_type, lat, lon, alt, _, heading = self.gps_data_cache
+ fix_type, lat, lon, alt, satelitecount_data, heading = self.gps_data_cache
if fix_type != 0:
self.publish_fix(self.gps_data_cache)
self.publish_odom(lat, lon, alt)
@@ -124,13 +136,19 @@ def acquire_gps_data(self):
if GPS_data and GPS_data[1] != 0 and GPS_data[2] != 0:
lat_sum += GPS_data[1]
lon_sum += GPS_data[2]
+ #heading_sum += float(GPS_data[5])
count += 1
time.sleep(0.1) # Slight delay to avoid overwhelming the GPS device
if count > 0:
- self.initial_coordinate = [lat_sum / count, lon_sum / count]
+ #self.initial_coordinate = [lat_sum / count, lon_sum / count] # calculate average
+ self.initial_coordinate = self.start_GPS_coordinate
+ self.current_coordinate = [lat_sum / count, lon_sum / count] # for tsukuba
+ #self.theta = (heading_sum / count) - 90
self.initialized = True
self.get_logger().info(f"Initial coordinate set to: {self.initial_coordinate}")
+ self.get_logger().info(f"current coordinate set to: {self.current_coordinate}")
+ self.get_logger().info(f"Initial theta set to: {self.tsukuba_theta}")
self.send_request()
self.is_acquiring = False
@@ -250,7 +268,7 @@ def quaternion_from_euler(self, roll, pitch, yaw):
def heading_to_quat(self ,real_heading):
- robotheading = real_heading + 90
+ robotheading = real_heading - 90
if robotheading >= 360:
robotheading -= 360
@@ -259,7 +277,8 @@ def heading_to_quat(self ,real_heading):
if self.count == 0:
self.get_logger().info(f"!!!----------robotheading: {robotheading} deg----------!!!")
- self.first_heading = robotheading
+ #self.first_heading = robotheading
+ self.first_heading = self.tsukuba_theta
self.count = 1
relative_heading = robotheading - self.first_heading
@@ -358,6 +377,8 @@ def publish_fix(self, gps):
self.lonlat_msg.latitude = gps[1]
self.lonlat_msg.longitude = gps[2]
self.lonlat_msg.altitude = gps[3]
+
+ self.lonlat_msg.status.service = gps[4] # satelitecount
self.lonlat_pub.publish(self.lonlat_msg)
# self.get_logger().info(f"Published GPS data: {lonlat}")
@@ -366,13 +387,13 @@ def publish_fix(self, gps):
def publish_movingbase(self, heading):
if heading is not None and heading != 0.0:
- robotheading = heading + 90.0
+ robotheading = heading - 90.0
if robotheading >= 360.0:
robotheading -= 360.0
if self.count == 0:
self.get_logger().info(f"!!!----------robotheading: {robotheading} deg----------!!!")
- self.first_heading = robotheading
+ self.first_heading = self.tsukuba_theta
self.count = 1
relative_heading = robotheading - self.first_heading
@@ -409,8 +430,7 @@ def publish_movingbase(self, heading):
self.get_logger().error("!!!!-not movingbase data-!!!!")
- def publish_odom(self, lat, lon, alt):
-
+ def publish_odom(self, lat, lon, alt):
#GPS_data = self.get_gps_quat(self.dev_name, self.country_id)
#gnggadata = (Fixtype_data,latitude_data,longitude_data,altitude_data,satelitecount_data,heading)
#if GPS_data and GPS_data[1] != 0 and GPS_data[2] != 0:
@@ -419,9 +439,9 @@ def publish_odom(self, lat, lon, alt):
self.satelite = GPS_data[4]
lonlat = [GPS_data[1], GPS_data[2]]
- if self.initial_coordinate is None:
- self.initial_coordinate = [GPS_data[1], GPS_data[2]]
- GPSxy = self.conversion(lonlat, self.initial_coordinate, self.theta)
+ #if self.initial_coordinate is None:
+ # self.initial_coordinate = [GPS_data[1], GPS_data[2]]
+ GPSxy = self.conversion(lonlat, self.start_GPS_coordinate, self.tsukuba_theta)
GPSquat = self.heading_to_quat(GPS_data[5])
self.odom_msg.header.stamp = self.get_clock().now().to_msg()
diff --git a/orange_gnss/orange_gnss/gnss_odom_publisher_ttyUSB.py b/orange_gnss/orange_gnss/gnss_odom_publisher_ttyUSB.py
index 7e2973d..9993e41 100644
--- a/orange_gnss/orange_gnss/gnss_odom_publisher_ttyUSB.py
+++ b/orange_gnss/orange_gnss/gnss_odom_publisher_ttyUSB.py
@@ -19,15 +19,21 @@ def __init__(self):
self.declare_parameter('baud', 115200)
self.declare_parameter('country_id', 0)
self.declare_parameter('Position_magnification', 1.675)
- self.declare_parameter('heading', 180.0)
+ self.declare_parameter('heading', 90.0)
+ self.declare_parameter('start_lat', 35.425952230280004) # tsukuba start point right 36.04974095972727, 140.04593633886364 , left 36.04976195993636, 140.04593755179093/nakaniwa 35.4257898377487,139.313807281254 /35.425952230280004, 139.31380123427
+ self.declare_parameter('start_lon', 139.31380123427)
self.dev_name = self.get_parameter('port').get_parameter_value().string_value
self.serial_baud = self.get_parameter('baud').get_parameter_value().integer_value
self.country_id = self.get_parameter('country_id').get_parameter_value().integer_value
self.Position_magnification = self.get_parameter('Position_magnification').get_parameter_value().double_value
#self.theta = self.get_parameter('heading').get_parameter_value().double_value
+ self.tsukuba_theta= self.get_parameter('heading').get_parameter_value().double_value # nakaniwa 180 tsukuba 93
self.initial_coordinate = None
+ self.start_lat = self.get_parameter('start_lat').get_parameter_value().double_value
+ self.start_lon = self.get_parameter('start_lon').get_parameter_value().double_value
+ self.start_GPS_coordinate = [self.start_lat, self.start_lon]
self.fix_data = None
self.count = 0
@@ -42,8 +48,8 @@ def __init__(self):
# service client
self.client = self.create_client(Avglatlon, 'send_avg_gps')
- while not self.client.wait_for_service(timeout_sec=1.0):
- self.get_logger().info("service not available...")
+ #while not self.client.wait_for_service(timeout_sec=1.0):
+ # self.get_logger().info("service not available...")
self.get_logger().info("Start get_lonlat quat node")
self.get_logger().info("-------------------------")
@@ -62,7 +68,11 @@ def send_request(self):
request = Avglatlon.Request()
request.avg_lat = self.initial_coordinate[0] # ← average lat
request.avg_lon = self.initial_coordinate[1] # ← average lon
- request.theta = self.theta
+ request.current_lat = self.current_coordinate[0] # ← current lat
+ request.current_lon = self.current_coordinate[1] # ← current lon
+ #request.theta = self.theta
+ request.theta = self.tsukuba_theta # tsukuba start theta
+ request.current_theta = self.theta # for tsukuba
future = self.client.call_async(request)
future.add_done_callback(self.response_callback)
@@ -104,10 +114,13 @@ def acquire_gps_data(self):
time.sleep(0.1) # Slight delay to avoid overwhelming the GPS device
if count > 0:
- self.initial_coordinate = [lat_sum / count, lon_sum / count]
+ #self.initial_coordinate = [lat_sum / count, lon_sum / count] # calculate average
+ self.initial_coordinate = self.start_GPS_coordinate
+ self.current_coordinate = [lat_sum / count, lon_sum / count] # for tsukuba
self.theta = (heading_sum / count) - 90
self.initialized = True
self.get_logger().info(f"Initial coordinate set to: {self.initial_coordinate}")
+ self.get_logger().info(f"current coordinate set to: {self.current_coordinate}")
self.get_logger().info(f"Initial theta set to: {self.theta}")
self.send_request()
self.is_acquiring = False
@@ -233,12 +246,13 @@ def heading_to_quat(self ,real_heading):
if robotheading >= 360:
robotheading -= 360
- self.get_logger().info(f"real_heading: {real_heading}")
- self.get_logger().info(f"robotheading: {robotheading}")
+ #self.get_logger().info(f"real_heading: {real_heading}")
+ #self.get_logger().info(f"robotheading: {robotheading}")
if self.count == 0:
self.get_logger().info(f"!!!----------robotheading: {robotheading} deg----------!!!")
- self.first_heading = robotheading
+ #self.first_heading = robotheading
+ self.first_heading = self.tsukuba_theta
self.count = 1
relative_heading = robotheading - self.first_heading
@@ -347,9 +361,9 @@ def publish_GPS_lonlat_quat(self):
#self.lonlat_pub.publish(self.lonlat_msg)
# self.get_logger().info(f"Published GPS data: {lonlat}")
- if self.initial_coordinate is None:
- self.initial_coordinate = [GPS_data[1], GPS_data[2]]
- GPSxy = self.conversion(lonlat, self.initial_coordinate, self.theta)
+ #if self.initial_coordinate is None:
+ # self.initial_coordinate = [GPS_data[1], GPS_data[2]]
+ GPSxy = self.conversion(lonlat, self.start_GPS_coordinate, self.tsukuba_theta)
GPSquat = self.heading_to_quat(GPS_data[5])
self.odom_msg.header.stamp = self.get_clock().now().to_msg()
diff --git a/orange_gnss/orange_gnss/odom_combination.py b/orange_gnss/orange_gnss/odom_combination.py
new file mode 100644
index 0000000..bab7625
--- /dev/null
+++ b/orange_gnss/orange_gnss/odom_combination.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+import rclpy
+import math
+import numpy as np
+from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy
+from nav_msgs.msg import Odometry
+from rclpy.node import Node
+from std_msgs.msg import Header
+import threading
+import time
+from my_msgs.srv import Avglatlon
+
+class Odom_Combination(Node):
+ def __init__(self):
+ super().__init__('odom_combination')
+
+ qos_profile = QoSProfile(
+ history=QoSHistoryPolicy.KEEP_LAST,
+ reliability=QoSReliabilityPolicy.RELIABLE,
+ durability=QoSDurabilityPolicy.VOLATILE,
+ depth = 1
+ )
+
+ # subscription
+ self.odom_sub = self.create_subscription(Odometry, '/odom/wheel_spimu', self.get_odom, qos_profile)
+ self.gps_odom_sub = self.create_subscription(Odometry, '/odom/UM982', self.get_gps_odom, qos_profile)
+
+ # publisher
+ self.odom_pub = self.create_publisher(Odometry, '/odom/combine', qos_profile)
+
+ self.position_x = 0.0
+ self.position_y = 0.0
+ self.theta_z = 0.0
+ self.theta = 0.0
+ self.initial_xy = None #(0.0, 0.0)
+ #self.Position_magnification = 1.0 # 必要なら調整
+
+ self.timer = self.create_timer(0.1, self.combine)
+
+ # /odom callback
+ def get_odom(self, msg):
+ self.position_x = msg.pose.pose.position.x
+ self.position_y = msg.pose.pose.position.y
+ x, y, z, w = msg.pose.pose.orientation.x, msg.pose.pose.orientation.y, msg.pose.pose.orientation.z, msg.pose.pose.orientation.w
+ roll, pitch, yaw = quaternion_to_euler(x, y, z, w)
+ self.theta_z = yaw # radian
+
+ # /odom/UM982 callback
+ def get_gps_odom(self, msg):
+ if self.initial_xy is None:
+ init_x = msg.pose.pose.position.x
+ init_y = msg.pose.pose.position.y
+ x, y, z, w = msg.pose.pose.orientation.x, msg.pose.pose.orientation.y, msg.pose.pose.orientation.z, msg.pose.pose.orientation.w
+ roll, pitch, yaw = quaternion_to_euler(x, y, z, w)
+ self.initial_xy = (init_x, init_y)
+ self.init_theta = yaw
+ #self.yaw_offset = self.init_theta - self.theta_z
+ self.get_logger().info(f"Initial /odom/UM982 position set to: x={init_x:.3f}, y={init_y:.3f}")
+
+ def yaw_to_orientation(self, yaw):
+ orientation_z = np.sin(yaw / 2.0)
+ orientation_w = np.cos(yaw / 2.0)
+ return orientation_z, orientation_w
+
+ def combine(self):
+ if self.initial_xy is None:
+ self.get_logger().warn("Waiting for initial /odom/UM982 position...")
+ return
+
+ pos_x = self.position_x
+ pos_y = self.position_y
+ #degree_to_radian = math.pi / 180
+ #pos_theta = self.theta * degree_to_radian # maybe -self.theta * degree_to_radian
+ pos_theta = self.theta_z + self.init_theta
+
+ # rotate init_theta for xy
+ cos_theta = math.cos(self.init_theta)
+ sin_theta = math.sin(self.init_theta)
+
+ rotated_x = cos_theta * pos_x - sin_theta * pos_y
+ rotated_y = sin_theta * pos_x + cos_theta * pos_y
+
+ # init xy + rotate xy
+ combined_x = self.initial_xy[0] + rotated_x
+ combined_y = self.initial_xy[1] + rotated_y
+
+ # quartanion z,w
+ odom_orientation = self.yaw_to_orientation(pos_theta)
+
+ # Odometryメッセージ作成
+ odom_msg = Odometry()
+ odom_msg.header.stamp = self.get_clock().now().to_msg()
+ odom_msg.header.frame_id = "odom"
+ odom_msg.pose.pose.position.x = combined_x
+ odom_msg.pose.pose.position.y = combined_y
+ odom_msg.pose.pose.position.z = 0.0
+ odom_msg.pose.pose.orientation.x = 0.0
+ odom_msg.pose.pose.orientation.y = 0.0
+ odom_msg.pose.pose.orientation.z = float(odom_orientation[0])
+ odom_msg.pose.pose.orientation.w = float(odom_orientation[1])
+
+ # publish
+ self.odom_pub.publish(odom_msg)
+
+def quaternion_to_euler(x, y, z, w):
+ t0 = +2.0 * (w * x + y * z)
+ t1 = +1.0 - 2.0 * (x * x + y * y)
+ roll = math.atan2(t0, t1)
+
+ t2 = +2.0 * (w * y - z * x)
+ t2 = +1.0 if t2 > +1.0 else t2
+ t2 = -1.0 if t2 < -1.0 else t2
+ pitch = math.asin(t2)
+
+ t3 = +2.0 * (w * z + x * y)
+ t4 = +1.0 - 2.0 * (y * y + z * z)
+ yaw = math.atan2(t3, t4)
+
+ return roll, pitch, yaw
+
+def main(args=None):
+ rclpy.init(args=args)
+ odom_combination = Odom_Combination()
+ rclpy.spin(odom_combination)
+
+if __name__ == '__main__':
+ main()
+
diff --git a/orange_gnss/setup.py b/orange_gnss/setup.py
index 891b69e..3ef7c4e 100644
--- a/orange_gnss/setup.py
+++ b/orange_gnss/setup.py
@@ -36,7 +36,8 @@
"gnss_odom_publisher_ttyUSB = orange_gnss.gnss_odom_publisher_ttyUSB:main",
"GPSodom_correction = orange_gnss.GPSodom_correction:main",
"lonlat_to_odom = orange_gnss.lonlat_to_odom:main",
- "ekf_myself_noGPS = orange_gnss.ekf_myself_noGPS:main"
+ "ekf_myself_noGPS = orange_gnss.ekf_myself_noGPS:main",
+ "odom_combination = orange_gnss.odom_combination:main",
],
},
)
diff --git a/orange_sensor_tools/CMakeLists.txt b/orange_sensor_tools/CMakeLists.txt
index a107f1b..7487201 100644
--- a/orange_sensor_tools/CMakeLists.txt
+++ b/orange_sensor_tools/CMakeLists.txt
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.8)
+cmake_policy(SET CMP0074 NEW) # ← これを追加
project(orange_sensor_tools)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
diff --git a/pcd_convert/pcd_convert/pcd_height_segmentation.py b/pcd_convert/pcd_convert/pcd_height_segmentation.py
index 28d2610..1fe5e3d 100644
--- a/pcd_convert/pcd_convert/pcd_height_segmentation.py
+++ b/pcd_convert/pcd_convert/pcd_height_segmentation.py
@@ -38,10 +38,11 @@ def __init__(self):
self.pcd_segment_obs_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_obs', qos_profile) #set publish pcd topic name
self.pcd_segment_ground_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_ground', qos_profile) #set publish pcd topic name
self.pcd_segment_step_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_step', qos_profile) #set publish pcd topic name
+ self.pcd_segment_low_step_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_low_step', qos_profile) #set publish pcd topic name
#パラメータ
#set obs range
- self.OBS_HIGHT_MIN = 555/1000; #hight range[m]
+ self.OBS_HIGHT_MIN = 200/1000; #hight range[m]
self.OBS_HIGHT_MAX = 4000/1000; #hight range[m]
self.OBS_MASK_X_MIN = -550/1000; #x mask range[m]
self.OBS_MASK_X_MAX = 400/1000; #x mask range[m]
@@ -51,12 +52,19 @@ def __init__(self):
self.GROUND_HIGHT_MIN = -10/1000; #hight range[m] # IGVC20250601 -150 -> -10
self.GROUND_HIGHT_MAX = 150/1000; #hight range[m]
#set step range
- self.STEP_HIGHT_MIN = 550/1000; #hight range[m]
+ self.STEP_HIGHT_MIN = 140/1000; #hight range[m]
self.STEP_HIGHT_MAX = self.OBS_HIGHT_MIN# 200/1000; #hight range[m]
self.STEP_X_MIN = -1500/1000; #x mask range[m]
- self.STEP_X_MAX = 2000/1000; #x mask range[rosbag2_2024_10_26-03_14_14_20241026_kakunin_bag1m]
+ self.STEP_X_MAX = 1500/1000; #x mask range[rosbag2_2024_10_26-03_14_14_20241026_kakunin_bag1m]
self.STEP_Y_MIN = -5000/1000; #y mask range[m]
self.STEP_Y_MAX = 5000/1000; #y mask range[m]
+ #set low step range
+ self.LOW_STEP_HIGHT_MIN = 70/1000; #hight range[m]
+ self.LOW_STEP_HIGHT_MAX = self.OBS_HIGHT_MIN# 200/1000; #hight range[m]
+ self.LOW_STEP_X_MIN = -1500/1000; #x mask range[m]
+ self.LOW_STEP_X_MAX = 1500/1000; #x mask range[rosbag2_2024_10_26-03_14_14_20241026_kakunin_bag1m]
+ self.LOW_STEP_Y_MIN = -5000/1000; #y mask range[m]
+ self.LOW_STEP_Y_MAX = 5000/1000; #y mask range[m]
def pointcloud2_to_array(self, cloud_msg):
# Extract point cloud data
@@ -98,9 +106,15 @@ def pcd_heigth_segmentation(self, msg):
pcd_step = self.height_segment(points, self.STEP_HIGHT_MIN, self.STEP_HIGHT_MAX)
#pcd_step_height_under = self.height_segment(points, -self.STEP_HIGHT_MAX, -self.STEP_HIGHT_MIN)
#pcd_step = np.insert(pcd_step, len(pcd_step[0,:]), pcd_step_height_under.T, axis=1)
- #pcd_step = self.pcd_serch(pcd_step9, self.STEP_X_MIN, self.STEP_X_MAX, self.STEP_Y_MIN, self.STEP_Y_MAX)
+ pcd_step = self.pcd_serch(pcd_step, self.STEP_X_MIN, self.STEP_X_MAX, self.STEP_Y_MIN, self.STEP_Y_MAX)
pcd_obs = np.insert(pcd_obs, len(pcd_obs[0,:]), pcd_step.T, axis=1)
+ #low step segment
+ pcd_low_step = self.height_segment(points, self.LOW_STEP_HIGHT_MIN, self.LOW_STEP_HIGHT_MAX)
+ #pcd_step_height_under = self.height_segment(points, -self.STEP_HIGHT_MAX, -self.STEP_HIGHT_MIN)
+ #pcd_step = np.insert(pcd_step, len(pcd_step[0,:]), pcd_step_height_under.T, axis=1)
+ pcd_low_step = self.pcd_serch(pcd_low_step, self.LOW_STEP_X_MIN, self.LOW_STEP_X_MAX, self.LOW_STEP_Y_MIN, self.LOW_STEP_Y_MAX)
+
#publish for rviz2
self.pcd_segment_obs = point_cloud_intensity_msg(pcd_obs.T, t_stamp, 'odom')
self.pcd_segment_obs_publisher.publish(self.pcd_segment_obs )
@@ -108,6 +122,8 @@ def pcd_heigth_segmentation(self, msg):
self.pcd_segment_ground_publisher.publish(self.pcd_segment_ground )
self.pcd_segment_step = point_cloud_intensity_msg(pcd_step.T, t_stamp, 'odom')
self.pcd_segment_step_publisher.publish(self.pcd_segment_step )
+ self.pcd_segment_low_step = point_cloud_intensity_msg(pcd_low_step.T, t_stamp, 'odom')
+ self.pcd_segment_low_step_publisher.publish(self.pcd_segment_low_step )
def height_segment(self, pointcloud, height_min, height_max):
pcd_ind = ((height_min <= pointcloud[2,:]) * (pointcloud[2,:] <= height_max ))
diff --git a/pcd_convert/pcd_convert/pcd_reflect_segmentation.py b/pcd_convert/pcd_convert/pcd_reflect_segmentation.py
new file mode 100644
index 0000000..b1bf24b
--- /dev/null
+++ b/pcd_convert/pcd_convert/pcd_reflect_segmentation.py
@@ -0,0 +1,222 @@
+# rclpy (ROS 2のpythonクライアント)の機能を使えるようにします。
+import rclpy
+# rclpy (ROS 2のpythonクライアント)の機能のうちNodeを簡単に使えるようにします。こう書いていない場合、Nodeではなくrclpy.node.Nodeと書く必要があります。
+from rclpy.node import Node
+import std_msgs.msg as std_msgs
+import sensor_msgs.msg as sensor_msgs
+import numpy as np
+import math
+from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy
+import time
+
+
+# C++と同じく、Node型を継承します。
+class PcdReflectSegmentation(Node):
+ # コンストラクタです、PcdReflectSegmentationクラスのインスタンスを作成する際に呼び出されます。
+ def __init__(self):
+ # 継承元のクラスを初期化します。
+ super().__init__('pcd_reflect_segmentation_node')
+
+ qos_profile = QoSProfile(
+ history=QoSHistoryPolicy.KEEP_LAST,
+ reliability=QoSReliabilityPolicy.RELIABLE,
+ durability=QoSDurabilityPolicy.VOLATILE,
+ depth = 1
+ )
+
+ qos_profile_sub = QoSProfile(
+ reliability=QoSReliabilityPolicy.RELIABLE,
+ durability=QoSDurabilityPolicy.VOLATILE,
+ depth = 1
+ )
+
+ # Subscriptionを作成。
+ self.subscription = self.create_subscription(sensor_msgs.PointCloud2, '/pcd_rotation', self.pcd_reflect_segmentation, qos_profile) #set subscribe pcd topic name
+ self.subscription # 警告を回避するために設置されているだけです。削除しても挙動はかわりません。
+
+ # Publisherを作成
+ #self.pcd_segment_paper_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_paper', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_cardboard_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_cardboard', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_asphalt_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_asphalt', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_whiteline_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_whiteline', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_gravel_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_gravel', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_grass_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_grass', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_high_ref_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_high_ref', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_low_ref_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_low_ref', qos_profile) #set publish pcd topic name
+ self.pcd_segment_shibafu_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_shibafu', qos_profile) #set publish pcd topic name
+ #self.pcd_segment_otiba_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_segment_otiba', qos_profile) #set publish pcd topic name
+
+ #パラメータ
+ #set obs range
+ self.OBS_MASK_X_MIN = -550/1000; #x mask range[m]
+ self.OBS_MASK_X_MAX = 200/1000; #x mask range[m]
+ self.OBS_MASK_Y_MIN = -350/1000; #y mask range[m]
+ self.OBS_MASK_Y_MAX = 350/1000; #y mask range[m]
+
+ self.obj_reflect =[
+ # 0 1
+ # median sigma
+ [ 47.00, 6.26], # 0:paper ポットホール(紙)
+ [ 26.00, 2.33], # 1:cardboard 段ボール
+ [ 10.00, 0.92], # 2:asphalt アスファルト
+ [ 155.00, 25.33], # 3:whiteline 白線
+ [ 15.00, 3.29], # 4:gravel 砂利
+ [ 64.00, 8.03], # 5:grass
+ [ 119.05, 10.32], # 6:high_ref
+ [ 3.62, 1.21], # 7:low_ref
+ [ 52.00, 5.25], # 8:shibafu
+ [ 72.00, 16.81], # 9:otiba
+ [ 0, 0] #
+ ]
+ self.sigma = 2
+
+
+ def pointcloud2_to_array(self, cloud_msg):
+ # Extract point cloud data
+ points = np.frombuffer(cloud_msg.data, dtype=np.uint8).reshape(-1, cloud_msg.point_step)
+ x = np.frombuffer(points[:, 0:4].tobytes(), dtype=np.float32)
+ y = np.frombuffer(points[:, 4:8].tobytes(), dtype=np.float32)
+ z = np.frombuffer(points[:, 8:12].tobytes(), dtype=np.float32)
+ intensity = np.frombuffer(points[:, 12:16].tobytes(), dtype=np.float32)
+
+ # Combine into a 4xN matrix
+ point_cloud_matrix = np.vstack((x, y, z, intensity))
+ print(point_cloud_matrix)
+ print(f"point_cloud_matrix ={point_cloud_matrix.shape}")
+ print(f"x ={x.dtype, x.shape}")
+
+ return point_cloud_matrix
+
+ def pcd_reflect_segmentation(self, msg):
+
+ #print stamp message
+ t_stamp = msg.header.stamp
+ print(f"t_stamp ={t_stamp}")
+
+ #get pcd data
+ points = self.pointcloud2_to_array(msg)
+ print(f"points ={points.shape}")
+
+ #obs segment
+ pcd_mask_cut = self.pcd_mask(points, self.OBS_MASK_X_MIN, self.OBS_MASK_X_MAX, self.OBS_MASK_Y_MIN, self.OBS_MASK_Y_MAX)
+ print(f"pcd_mask_cut ={pcd_mask_cut.shape}")
+ print(f"self.obj_reflect ={len(self.obj_reflect)}")
+
+
+ pcd_obj_reflect_ind_list = [] # 列数に基づいて初期化
+ for obj_ind in range(len(self.obj_reflect)-1):
+ if self.obj_reflect[obj_ind][0] < 151:
+ obj_reflect_min = self.obj_reflect[obj_ind][0] - self.sigma * self.obj_reflect[obj_ind][1]
+ obj_reflect_max = self.obj_reflect[obj_ind][0] + self.sigma * self.obj_reflect[obj_ind][1]
+ if obj_reflect_min < 0:
+ obj_reflect_min = 0
+ if obj_reflect_max > 150:
+ obj_reflect_max = 150
+ else:
+ obj_reflect_min = 151
+ obj_reflect_max = 255
+ pcd_obj_reflect_ind = np.array(self.reflect_segment(pcd_mask_cut, obj_reflect_min, obj_reflect_max))
+ pcd_obj_reflect_ind_list.append(pcd_obj_reflect_ind)
+ print(f"pcd_obj_reflect_ind_list ={pcd_obj_reflect_ind_list[0]}")
+
+ #publish for rviz2
+ pcd_segment_paper = pcd_mask_cut[:, pcd_obj_reflect_ind_list[0]]
+
+ #self.pcd_segment_paper = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[0]].T, t_stamp, 'odom')
+ #self.pcd_segment_paper_publisher.publish(self.pcd_segment_paper )
+ #self.pcd_segment_cardboard = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[1]].T, t_stamp, 'odom')
+ #self.pcd_segment_cardboard_publisher.publish(self.pcd_segment_cardboard )
+ #self.pcd_segment_asphalt = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[2]].T, t_stamp, 'odom')
+ #self.pcd_segment_asphalt_publisher.publish(self.pcd_segment_asphalt )
+ #self.pcd_segment_whiteline = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[3]].T, t_stamp, 'odom')
+ #self.pcd_segment_whiteline_publisher.publish(self.pcd_segment_whiteline )
+ #self.pcd_segment_gravel = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[4]].T, t_stamp, 'odom')
+ #self.pcd_segment_gravel_publisher.publish(self.pcd_segment_gravel )
+ #self.pcd_segment_grass = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[5]].T, t_stamp, 'odom')
+ #self.pcd_segment_grass_publisher.publish(self.pcd_segment_grass )
+ #self.pcd_segment_high_ref = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[6]].T, t_stamp, 'odom')
+ #self.pcd_segment_high_ref_publisher.publish(self.pcd_segment_high_ref )
+ #self.pcd_segment_low_ref = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[7]].T, t_stamp, 'odom')
+ #self.pcd_segment_low_ref_publisher.publish(self.pcd_segment_low_ref )
+
+ self.pcd_segment_shibafu = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[8]].T, t_stamp, 'odom')
+ self.pcd_segment_shibafu_publisher.publish(self.pcd_segment_shibafu )
+ #self.pcd_segment_otiba = point_cloud_intensity_msg(pcd_mask_cut[:, pcd_obj_reflect_ind_list[9]].T, t_stamp, 'odom')
+ #self.pcd_segment_otiba_publisher.publish(self.pcd_segment_otiba )
+
+
+
+
+
+ def height_segment(self, pointcloud, height_min, height_max):
+ pcd_ind = ((height_min <= pointcloud[2,:]) * (pointcloud[2,:] <= height_max ))
+ pcd_segment = pointcloud[:, pcd_ind]
+ return pcd_segment
+
+ def reflect_segment(self, pointcloud, reflect_min, reflect_max):
+ pcd_ind = ((reflect_min <= pointcloud[3,:]) * (pointcloud[3,:] <= reflect_max ))
+ return pcd_ind
+
+ def pcd_mask(self, pointcloud, x_min, x_max, y_min, y_max):
+ pcd_ind = (( (x_min <= pointcloud[0,:]) * (pointcloud[0,:] <= x_max)) * ((y_min <= pointcloud[1,:]) * (pointcloud[1,:]) <= y_max ) )
+ pcd_mask = pointcloud[:, ~pcd_ind]
+ return pcd_mask
+
+ def pcd_serch(self, pointcloud, x_min, x_max, y_min, y_max):
+ pcd_ind = (( (x_min <= pointcloud[0,:]) * (pointcloud[0,:] <= x_max)) * ((y_min <= pointcloud[1,:]) * (pointcloud[1,:]) <= y_max ) )
+ pcd_mask = pointcloud[:, pcd_ind]
+ return pcd_mask
+
+
+def point_cloud_intensity_msg(points, t_stamp, parent_frame):
+ # In a PointCloud2 message, the point cloud is stored as an byte
+ # array. In order to unpack it, we also include some parameters
+ # which desribes the size of each individual point.
+ ros_dtype = sensor_msgs.PointField.FLOAT32
+ dtype = np.float32
+ itemsize = np.dtype(dtype).itemsize # A 32-bit float takes 4 bytes.
+ data = points.astype(dtype).tobytes()
+
+ # The fields specify what the bytes represents. The first 4 bytes
+ # represents the x-coordinate, the next 4 the y-coordinate, etc.
+ fields = [
+ sensor_msgs.PointField(name='x', offset=0, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='y', offset=4, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='z', offset=8, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='intensity', offset=12, datatype=ros_dtype, count=1),
+ ]
+
+ # The PointCloud2 message also has a header which specifies which
+ # coordinate frame it is represented in.
+ header = std_msgs.Header(frame_id=parent_frame, stamp=t_stamp)
+
+
+ return sensor_msgs.PointCloud2(
+ header=header,
+ height=1,
+ width=points.shape[0],
+ is_dense=True,
+ is_bigendian=False,
+ fields=fields,
+ point_step=(itemsize * 4), # Every point consists of three float32s.
+ row_step=(itemsize * 4 * points.shape[0]),
+ data=data
+ )
+
+# mainという名前の関数です。C++のmain関数とは異なり、これは処理の開始地点ではありません。
+def main(args=None):
+ # rclpyの初期化処理です。ノードを立ち上げる前に実装する必要があります。
+ rclpy.init(args=args)
+ # クラスのインスタンスを作成
+ pcd_reflect_segmentation = PcdReflectSegmentation()
+ # spin処理を実行、spinをしていないとROS 2のノードはデータを入出力することが出来ません。
+ rclpy.spin(pcd_reflect_segmentation)
+ # 明示的にノードの終了処理を行います。
+ pcd_reflect_segmentation.destroy_node()
+ # rclpyの終了処理、これがないと適切にノードが破棄されないため様々な不具合が起こります。
+ rclpy.shutdown()
+
+# 本スクリプト(publish.py)の処理の開始地点です。
+if __name__ == '__main__':
+ # 関数`main`を実行する。
+ main()
diff --git a/pcd_convert/pcd_convert/pcd_rotation.py b/pcd_convert/pcd_convert/pcd_rotation.py
index bbc1679..0a42f81 100644
--- a/pcd_convert/pcd_convert/pcd_rotation.py
+++ b/pcd_convert/pcd_convert/pcd_rotation.py
@@ -40,15 +40,15 @@ def __init__(self):
#パラメータ
#set LiDAR position
- self.MID360_HIGHT = 980/1000; #hight position[m]
+ self.MID360_HIGHT = 950.8/1000; #hight position[m] 0.9508604675798957
#上下反転 LiDAR init
- self.THETA_INIT_X = 180 #[deg]
- self.THETA_INIT_Y = 0 #[deg]
+ self.THETA_INIT_X = 180.5 #[deg]
+ self.THETA_INIT_Y = 2.663 #[deg] 2.663001576990896
self.THETA_INIT_Z = 0 #[deg]
#initialize calibration
- self.initialize_calibration = 0
+ self.initialize_calibration = 1
self.pcd_buff = np.array([[],[],[],[]]);
self.x1_init_point = 1.5
self.x2_init_point = 2.5
@@ -102,7 +102,7 @@ def pcd_rotation(self, msg):
x2_max = self.x2_init_point + self.x_range;
y_min = self.y_init_point - self.y_range;
y_max = self.y_init_point + self.y_range;
- #print(f"x1_min,x1_max,x2_min,x2_max,y_min,y_max ={ x1_min, x1_max, x2_min, x2_max, y_min, y_max}")
+ print(f"x1_min,x1_max,x2_min,x2_max,y_min,y_max ={ x1_min, x1_max, x2_min, x2_max, y_min, y_max}")
pcd_x1_ind = self.pcd_serch(self.pcd_buff, x1_min, x1_max, y_min, y_max)
pcd_x2_ind = self.pcd_serch(self.pcd_buff, x2_min, x2_max, y_min, y_max)
#print(f"len(self.pcd_buff[0,pcd_x1_ind]), {len(self.pcd_buff[0,pcd_x1_ind])}")
@@ -134,7 +134,8 @@ def pcd_rotation(self, msg):
self.MID360_HIGHT = - intercept; #hight position[m]
self.THETA_INIT_Y = self.THETA_INIT_Y + theta #[deg]
self.initialize_calibration = 1
- print(f"self.MID360_HIGHT ={self.MID360_HIGHT}")
+ #print(f"self.MID360_HIGHT ={self.MID360_HIGHT}")
+ #print(f"self.THETA_INIT_Y ={self.THETA_INIT_Y}")
#add mid height position
diff --git a/pcd_convert/pcd_convert/pcd_rotation_lidar2.py b/pcd_convert/pcd_convert/pcd_rotation_lidar2.py
new file mode 100644
index 0000000..071d957
--- /dev/null
+++ b/pcd_convert/pcd_convert/pcd_rotation_lidar2.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# rclpy (ROS 2のpythonクライアント)の機能を使えるようにします。
+import rclpy
+# rclpy (ROS 2のpythonクライアント)の機能のうちNodeを簡単に使えるようにします。こう書いていない場合、Nodeではなくrclpy.node.Nodeと書く必要があります。
+from rclpy.node import Node
+import std_msgs.msg as std_msgs
+import sensor_msgs.msg as sensor_msgs
+import numpy as np
+import math
+from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy
+import time
+
+
+# C++と同じく、Node型を継承します。
+class PcdRotation(Node):
+ # コンストラクタです、PcdRotationクラスのインスタンスを作成する際に呼び出されます。
+ def __init__(self):
+ # 継承元のクラスを初期化します。
+ super().__init__('pcd_rotation_lidar2_node')
+
+ qos_profile = QoSProfile(
+ history=QoSHistoryPolicy.KEEP_LAST,
+ reliability=QoSReliabilityPolicy.RELIABLE,
+ durability=QoSDurabilityPolicy.VOLATILE,
+ depth = 1
+ )
+
+ qos_profile_sub = QoSProfile(
+ history=QoSHistoryPolicy.KEEP_LAST,
+ reliability=QoSReliabilityPolicy.RELIABLE,
+ durability=QoSDurabilityPolicy.VOLATILE,
+ depth = 10
+ )
+
+ # Subscriptionを作成。 <-- ここを最小限変更して /livox/lidar2 を購読するようにしました
+ self.subscription = self.create_subscription(sensor_msgs.PointCloud2, '/converted_pointcloud2_lidar2', self.pcd_rotation, qos_profile_sub) #set subscribe pcd topic name
+ self.subscription # 警告を回避するために設置されているだけです。削除しても挙動はかわりません。
+
+ # Publisherを作成
+ self.pcd_rotation_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_rotation_lidar2', qos_profile) #set publish pcd topic name
+
+ #パラメータ
+ #set LiDAR position
+ self.MID360_HIGHT = 750/1000; #self.MID360_HIGHT =1.7483812830163088
+
+ #上下反転 LiDAR init
+ self.THETA_INIT_X = 0 #[deg]-180
+ self.THETA_INIT_Y = 0.05435636478316808 #self.THETA_INIT_Y =0.05435636478316808#[deg]
+ self.THETA_INIT_Z = 0 #[deg]
+
+ #initialize calibration
+ self.initialize_calibration = 1
+ self.pcd_buff = np.array([[],[],[],[]]);
+ self.x1_init_point = 1.5
+ self.x2_init_point = 2.5
+ self.y_init_point = 0.0
+ self.x_range = 0.1
+ self.y_range = 0.3
+
+
+
+ def pointcloud2_to_array(self, cloud_msg):
+ # Extract point cloud data
+ points = np.frombuffer(cloud_msg.data, dtype=np.uint8).reshape(-1, cloud_msg.point_step)
+ x = np.frombuffer(points[:, 0:4].tobytes(), dtype=np.float32)
+ y = np.frombuffer(points[:, 4:8].tobytes(), dtype=np.float32)
+ z = np.frombuffer(points[:, 8:12].tobytes(), dtype=np.float32)
+ intensity = np.frombuffer(points[:, 12:16].tobytes(), dtype=np.float32)
+
+ # Combine into a 4xN matrix
+ point_cloud_matrix = np.vstack((x, y, z, intensity))
+ #print(point_cloud_matrix)
+ #print(f"point_cloud_matrix ={point_cloud_matrix.shape}")
+ #print(f"x ={x.dtype, x.shape}")
+
+ return point_cloud_matrix
+
+ def pcd_rotation(self, msg):
+
+ #print stamp message
+ t_stamp = msg.header.stamp
+ #print(f"t_stamp ={t_stamp}")
+
+ #get pcd data
+ points = self.pointcloud2_to_array(msg)
+ #print(f"points ={points.shape}")
+ #for pcd rotation
+ xyz_point = np.vstack([points[0,:],points[1,:],points[2,:]])
+ #print(f"xyz_point ={xyz_point.shape}")
+ pointcloud, rot_matrix = rotation_xyz(xyz_point, self.THETA_INIT_X, self.THETA_INIT_Y, self.THETA_INIT_Z)
+ #add intensity
+ pointcloud_intensity = np.insert(pointcloud, 3, points[3,:], axis=0)
+
+ if self.initialize_calibration == 0:
+ pcd_buff_len = 3* 2*10*4
+ self.pcd_buff = np.insert(self.pcd_buff, len(self.pcd_buff[0,:]), pointcloud_intensity.T, axis=1)
+ print(f"len(self.pcd_buff), {self.pcd_buff.shape}")
+ if (len(self.pcd_buff[0,:]) > pcd_buff_len):
+ #self.pcd_buff = self.pcd_buff[:,(len(self.pcd_buff[0,:])-pcd_buff_len):len(self.pcd_buff[0,:])]
+ x1_min = self.x1_init_point - self.x_range;
+ x1_max = self.x1_init_point + self.x_range;
+ x2_min = self.x2_init_point - self.x_range;
+ x2_max = self.x2_init_point + self.x_range;
+ y_min = self.y_init_point - self.y_range;
+ y_max = self.y_init_point + self.y_range;
+ #print(f"x1_min,x1_max,x2_min,x2_max,y_min,y_max ={ x1_min, x1_max, x2_min, x2_max, y_min, y_max}")
+ pcd_x1_ind = self.pcd_serch(self.pcd_buff, x1_min, x1_max, y_min, y_max)
+ pcd_x2_ind = self.pcd_serch(self.pcd_buff, x2_min, x2_max, y_min, y_max)
+ #print(f"len(self.pcd_buff[0,pcd_x1_ind]), {len(self.pcd_buff[0,pcd_x1_ind])}")
+ #print(f"len(self.pcd_buff[0,pcd_x2_ind]), {len(self.pcd_buff[0,pcd_x2_ind])}")
+ if len(self.pcd_buff[0,pcd_x1_ind]) & len(self.pcd_buff[0,pcd_x2_ind]) > 0:
+ pcd_x1 = self.pcd_buff[:,pcd_x1_ind]
+ pcd_x2 = self.pcd_buff[:,pcd_x2_ind]
+ #pcd_x1_closest_ind = np.abs(pcd_x1[0,:] - self.x1_init_point).argmin()
+ z1_median = np.median(pcd_x1[2,:])
+ pcd_x1_closest_ind = np.abs(pcd_x1[2,:]-z1_median).argmin()
+ #pcd_x2_closest_ind = np.abs(pcd_x2[0,:] - self.x2_init_point).argmin()
+ z2_median = np.median(pcd_x2[2,:])
+ pcd_x2_closest_ind = np.abs(pcd_x2[2,:]-z2_median).argmin()
+ #z= {slope}x + intercept
+ slope = (pcd_x2[2,pcd_x2_closest_ind] - pcd_x1[2,pcd_x1_closest_ind]) / (pcd_x2[0,pcd_x2_closest_ind] - pcd_x1[0,pcd_x1_closest_ind])
+ intercept = pcd_x1[2,pcd_x1_closest_ind] - slope*pcd_x1[0,pcd_x1_closest_ind]
+
+
+ print(f"pcd_x1 x,y,z ={pcd_x1[0,pcd_x1_closest_ind], pcd_x1[1,pcd_x1_closest_ind], pcd_x1[2,pcd_x1_closest_ind]}")
+ print(f"pcd_x2 x,y,z ={pcd_x2[0,pcd_x2_closest_ind], pcd_x2[1,pcd_x2_closest_ind], pcd_x2[2,pcd_x2_closest_ind]}")
+ delta_x = pcd_x2[0,pcd_x2_closest_ind] - 0
+ delta_z = - (intercept - pcd_x2[2,pcd_x2_closest_ind])
+ theta = np.arctan2(delta_z, delta_x) /math.pi*180
+ print(f"delta_x ={delta_x}")
+ print(f"delta_z ={delta_z}")
+ print(f"slope ={slope}")
+ print(f"intercept ={intercept}")
+ print(f"theta ={theta}")
+ self.MID360_HIGHT = - intercept; #hight position[m]
+ self.THETA_INIT_Y = self.THETA_INIT_Y + theta #[deg]
+ self.initialize_calibration = 1
+ #print(f"self.MID360_HIGHT ={self.MID360_HIGHT}")
+ #print(f"self.THETA_INIT_Y ={self.THETA_INIT_Y}")
+
+
+ #add mid height position
+ pointcloud_intensity[2,:] += self.MID360_HIGHT
+ #print(f"pointcloud_intensity ={pointcloud_intensity.shape}")
+
+ #publish for rviz2
+ if self.initialize_calibration == 1:
+ self.pcd_rotation = point_cloud_intensity_msg(pointcloud_intensity.T, t_stamp, 'map')
+ self.pcd_rotation_publisher.publish(self.pcd_rotation )
+
+ def pcd_serch(self, pointcloud, x_min, x_max, y_min, y_max):
+ pcd_ind_x = ((x_min <= pointcloud[0,:]) * (pointcloud[0,:] <= x_max ))
+ pcd_ind_y = ((y_min <= pointcloud[1,:]) * (pointcloud[1,:] <= y_max ))
+ pcd_ind = pcd_ind_x * pcd_ind_y
+ return pcd_ind
+
+def rotation_xyz(pointcloud, theta_x, theta_y, theta_z):
+ rad_x = math.radians(theta_x)
+ rad_y = math.radians(theta_y)
+ rad_z = math.radians(theta_z)
+ rot_x = np.array([[ 1, 0, 0],
+ [ 0, math.cos(rad_x), -math.sin(rad_x)],
+ [ 0, math.sin(rad_x), math.cos(rad_x)]])
+
+ rot_y = np.array([[ math.cos(rad_y), 0, math.sin(rad_y)],
+ [ 0, 1, 0],
+ [-math.sin(rad_y), 0, math.cos(rad_y)]])
+
+ rot_z = np.array([[ math.cos(rad_z), -math.sin(rad_z), 0],
+ [ math.sin(rad_z), math.cos(rad_z), 0],
+ [ 0, 0, 1]])
+ rot_matrix = rot_z.dot(rot_y.dot(rot_x))
+ #print(f"rot_matrix ={rot_matrix}")
+ #print(f"pointcloud ={pointcloud.shape}")
+ rot_pointcloud = rot_matrix.dot(pointcloud)
+ return rot_pointcloud, rot_matrix
+
+def point_cloud_intensity_msg(points, t_stamp, parent_frame):
+ # In a PointCloud2 message, the point cloud is stored as an byte
+ # array. In order to unpack it, we also include some parameters
+ # which desribes the size of each individual point.
+ ros_dtype = sensor_msgs.PointField.FLOAT32
+ dtype = np.float32
+ itemsize = np.dtype(dtype).itemsize # A 32-bit float takes 4 bytes.
+ data = points.astype(dtype).tobytes()
+
+ # The fields specify what the bytes represents. The first 4 bytes
+ # represents the x-coordinate, the next 4 the y-coordinate, etc.
+ fields = [
+ sensor_msgs.PointField(name='x', offset=0, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='y', offset=4, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='z', offset=8, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='intensity', offset=12, datatype=ros_dtype, count=1),
+ ]
+
+ # The PointCloud2 message also has a header which specifies which
+ # coordinate frame it is represented in.
+ header = std_msgs.Header(frame_id=parent_frame, stamp=t_stamp)
+
+
+ return sensor_msgs.PointCloud2(
+ header=header,
+ height=1,
+ width=points.shape[0],
+ is_dense=True,
+ is_bigendian=False,
+ fields=fields,
+ point_step=(itemsize * 4), # Every point consists of three float32s.
+ row_step=(itemsize * 4 * points.shape[0]),
+ data=data
+ )
+
+# mainという名前の関数です。C++のmain関数とは異なり、これは処理の開始地点ではありません。
+def main(args=None):
+ # rclpyの初期化処理です。ノードを立ち上げる前に実装する必要があります。
+ rclpy.init(args=args)
+ # クラスのインスタンスを作成
+ pcd_rotation = PcdRotation()
+ # spin処理を実行、spinをしていないとROS 2のノードはデータを入出力することが出来ません。
+ rclpy.spin(pcd_rotation)
+ # 明示的にノードの終了処理を行います。
+ pcd_rotation.destroy_node()
+ # rclpyの終了処理、これがないと適切にノードが破棄されないため様々な不具合が起こります。
+ rclpy.shutdown()
+
+# 本スクリプト(publish.py)の処理の開始地点です。
+if __name__ == '__main__':
+ # 関数`main`を実行する。
+ main()
+
+
diff --git a/pcd_convert/setup.py b/pcd_convert/setup.py
index b9be1a8..3d9e743 100644
--- a/pcd_convert/setup.py
+++ b/pcd_convert/setup.py
@@ -45,7 +45,9 @@
entry_points={
'console_scripts': [
'pcd_rotation = pcd_convert.pcd_rotation:main',
- 'pcd_height_segmentation = pcd_convert.pcd_height_segmentation:main'
+ 'pcd_height_segmentation = pcd_convert.pcd_height_segmentation:main',
+ 'pcd_reflect_segmentation = pcd_convert.pcd_reflect_segmentation:main',
+ 'pcd_rotation_lidar2 = pcd_convert.pcd_rotation_lidar2:main'
],
},
)
diff --git a/try_navigation/config/config.rviz b/try_navigation/config/config.rviz
index 0079548..d1c2869 100644
--- a/try_navigation/config/config.rviz
+++ b/try_navigation/config/config.rviz
@@ -6,8 +6,16 @@ Panels:
Expanded:
- /Global Options1
- /Status1
+ - /fusion/odom1/Shape1
+ - /fusion/odom1/Covariance1
+ - /GPSodom1/Shape1
+ - /imu_odom1/Shape1
+ - /PointCloud21
+ - /Odometry2/Shape1
+ - /Odometry3/Shape1
+ - /PointCloud22
Splitter Ratio: 0.5
- Tree Height: 549
+ Tree Height: 580
- Class: rviz_common/Selection
Name: Selection
- Class: rviz_common/Tool Properties
@@ -25,7 +33,7 @@ Panels:
Experimental: false
Name: Time
SyncMode: 0
- SyncSource: ""
+ SyncSource: pcd_obs_global
Visualization Manager:
Class: ""
Displays:
@@ -47,10 +55,558 @@ Visualization Manager:
Plane Cell Count: 10
Reference Frame:
Value: true
+ - Angle Tolerance: 0.10000000149011612
+ Class: rviz_default_plugins/Odometry
+ Covariance:
+ Orientation:
+ Alpha: 0.5
+ Color: 255; 255; 127
+ Color Style: Unique
+ Frame: Local
+ Offset: 1
+ Scale: 1
+ Value: true
+ Position:
+ Alpha: 0.30000001192092896
+ Color: 204; 51; 204
+ Scale: 1
+ Value: true
+ Value: true
+ Enabled: true
+ Keep: 0
+ Name: Odometry
+ Position Tolerance: 0.10000000149011612
+ Shape:
+ Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Color: 255; 25; 0
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Value: Arrow
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /odom
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 249; 240; 107
+ Color Transformer: FlatColor
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 4
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: pcd_obs_global
+ Position Transformer: XYZ
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.05000000074505806
+ Style: Flat Squares
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /pcd_obs_global
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 0
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: white_buff
+ Position Transformer: XYZ
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.10000000149011612
+ Style: Flat Squares
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /white_buff
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz_default_plugins/Path
+ Color: 25; 255; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Lines
+ Line Width: 0.029999999329447746
+ Name: Path
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /potential_astar_path
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: ""
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 4096
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: pothole
+ Position Transformer: ""
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Flat Squares
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /pothole_points
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 0.699999988079071
+ Class: rviz_default_plugins/Map
+ Color Scheme: map
+ Draw Behind: false
+ Enabled: true
+ Name: globalmap
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /reflect_map_global
+ Update Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /reflect_map_global_updates
+ Use Timestamp: false
+ Value: true
+ - Alpha: 0.699999988079071
+ Class: rviz_default_plugins/Map
+ Color Scheme: map
+ Draw Behind: false
+ Enabled: true
+ Name: localmap
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /reflect_map_local
+ Update Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /reflect_map_local_updates
+ Use Timestamp: false
+ Value: true
+ - Angle Tolerance: 0.10000000149011612
+ Class: rviz_default_plugins/Odometry
+ Covariance:
+ Orientation:
+ Alpha: 0.5
+ Color: 255; 255; 127
+ Color Style: Unique
+ Frame: Local
+ Offset: 1
+ Scale: 1
+ Value: true
+ Position:
+ Alpha: 0.30000001192092896
+ Color: 204; 51; 204
+ Scale: 1
+ Value: true
+ Value: true
+ Enabled: true
+ Keep: 0
+ Name: fusion/odom
+ Position Tolerance: 0.10000000149011612
+ Shape:
+ Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Color: 220; 138; 221
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Value: Arrow
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /fusion/odom
+ Value: true
+ - Angle Tolerance: 0.10000000149011612
+ Class: rviz_default_plugins/Odometry
+ Covariance:
+ Orientation:
+ Alpha: 0.5
+ Color: 255; 255; 127
+ Color Style: Unique
+ Frame: Local
+ Offset: 1
+ Scale: 1
+ Value: true
+ Position:
+ Alpha: 0.30000001192092896
+ Color: 204; 51; 204
+ Scale: 1
+ Value: true
+ Value: true
+ Enabled: true
+ Keep: 0
+ Name: GPSodom
+ Position Tolerance: 0.10000000149011612
+ Shape:
+ Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Color: 98; 160; 234
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Value: Arrow
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /odom/UM982
+ Value: true
+ - Angle Tolerance: 0.10000000149011612
+ Class: rviz_default_plugins/Odometry
+ Covariance:
+ Orientation:
+ Alpha: 0.5
+ Color: 255; 255; 127
+ Color Style: Unique
+ Frame: Local
+ Offset: 1
+ Scale: 1
+ Value: true
+ Position:
+ Alpha: 0.30000001192092896
+ Color: 204; 51; 204
+ Scale: 1
+ Value: true
+ Value: true
+ Enabled: true
+ Keep: 0
+ Name: imu_odom
+ Position Tolerance: 0.10000000149011612
+ Shape:
+ Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Color: 249; 240; 107
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Value: Arrow
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /odom/wheel_imu
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz_default_plugins/Path
+ Color: 153; 193; 241
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Lines
+ Line Width: 0.029999999329447746
+ Name: waypoint_path
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /waypoint_path
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: ""
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 4096
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: tire
+ Position Transformer: ""
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Flat Squares
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /tire_points
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 3.704611301422119
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: PointCloud2
+ Position Transformer: XYZ
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Flat Squares
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /pcd_obs_global
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Angle Tolerance: 0.10000000149011612
+ Class: rviz_default_plugins/Odometry
+ Covariance:
+ Orientation:
+ Alpha: 0.5
+ Color: 255; 255; 127
+ Color Style: Unique
+ Frame: Local
+ Offset: 1
+ Scale: 1
+ Value: true
+ Position:
+ Alpha: 0.30000001192092896
+ Color: 204; 51; 204
+ Scale: 1
+ Value: true
+ Value: true
+ Enabled: true
+ Keep: 0
+ Name: Odometry
+ Position Tolerance: 0.10000000149011612
+ Shape:
+ Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Color: 255; 190; 111
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Value: Arrow
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /odom/wheel_spimu
+ Value: true
+ - Angle Tolerance: 0.10000000149011612
+ Class: rviz_default_plugins/Odometry
+ Covariance:
+ Orientation:
+ Alpha: 0.5
+ Color: 255; 255; 127
+ Color Style: Unique
+ Frame: Local
+ Offset: 1
+ Scale: 1
+ Value: true
+ Position:
+ Alpha: 0.30000001192092896
+ Color: 204; 51; 204
+ Scale: 1
+ Value: true
+ Value: true
+ Enabled: true
+ Keep: 0
+ Name: Odometry
+ Position Tolerance: 0.10000000149011612
+ Shape:
+ Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Color: 51; 209; 122
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Value: Arrow
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /odom/combine
+ Value: true
+ - Class: rviz_default_plugins/MarkerArray
+ Enabled: true
+ Name: MarkerArray
+ Namespaces:
+ {}
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /wall_follow_markers
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: FlatColor
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: -999999
+ Min Color: 0; 0; 0
+ Min Intensity: 999999
+ Name: PointCloud2
+ Position Transformer: XYZ
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.20000000298023224
+ Style: Flat Squares
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /pcd_test_global
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
Enabled: true
Global Options:
Background Color: 48; 48; 48
- Fixed Frame: map
+ Fixed Frame: odom
Frame Rate: 30
Name: root
Tools:
@@ -93,33 +649,33 @@ Visualization Manager:
Views:
Current:
Class: rviz_default_plugins/Orbit
- Distance: 10
+ Distance: 21.29517364501953
Enable Stereo Rendering:
Stereo Eye Separation: 0.05999999865889549
Stereo Focal Distance: 1
Swap Stereo Eyes: false
Value: false
Focal Point:
- X: 0
- Y: 0
- Z: 0
+ X: 5.204740047454834
+ Y: 6.645839214324951
+ Z: 0.015708111226558685
Focal Shape Fixed Size: true
Focal Shape Size: 0.05000000074505806
Invert Z Axis: false
Name: Current View
Near Clip Distance: 0.009999999776482582
- Pitch: 0.785398006439209
+ Pitch: 1.5647963285446167
Target Frame:
Value: Orbit (rviz)
- Yaw: 0.785398006439209
+ Yaw: 3.1453959941864014
Saved: ~
Window Geometry:
Displays:
collapsed: false
- Height: 846
+ Height: 877
Hide Left Dock: false
- Hide Right Dock: false
- QMainWindow State: 000000ff00000000fd000000040000000000000156000002b0fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002b0000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002b0fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000002b0000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004b00000003efc0100000002fb0000000800540069006d00650100000000000004b0000002fb00fffffffb0000000800540069006d006501000000000000045000000000000000000000023f000002b000000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
+ Hide Right Dock: true
+ QMainWindow State: 000000ff00000000fd000000040000000000000156000002cffc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002cf000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f00000251fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d00000251000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007380000003efc0100000002fb0000000800540069006d0065010000000000000738000002fb00fffffffb0000000800540069006d00650100000000000004500000000000000000000005dc000002cf00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
Selection:
collapsed: false
Time:
@@ -127,7 +683,7 @@ Window Geometry:
Tool Properties:
collapsed: false
Views:
- collapsed: false
- Width: 1200
- X: 60
- Y: 57
+ collapsed: true
+ Width: 1848
+ X: 0
+ Y: 28
diff --git a/try_navigation/launch/try_navigation.launch.py b/try_navigation/launch/try_navigation.launch.py
index e4591ed..ce8cbfb 100644
--- a/try_navigation/launch/try_navigation.launch.py
+++ b/try_navigation/launch/try_navigation.launch.py
@@ -5,6 +5,9 @@
from launch_ros.actions import Node
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.substitutions import LaunchConfiguration
def generate_launch_description():
rviz_config_dir = os.path.join(
@@ -18,7 +21,32 @@ def generate_launch_description():
'livox_to_pointcloud2.launch.py'
)
+ # define config
+ odom = LaunchConfiguration('odom')
+ declare_odom_arg = DeclareLaunchArgument(
+ 'odom',
+ default_value='/fusion/odom',
+ description='Odometry topic name'
+ )
+
+ waypoint_path = LaunchConfiguration('waypoint_path')
+ declare_waypoint_arg = DeclareLaunchArgument(
+ 'waypoint_path',
+ default_value='kbkn_maps/waypoints/tsukuba/2025/papa/tsukuba_waypoint.yaml',
+ description='waypoint name'
+ )
+ #kbkn_maps/waypoints/tsukuba/2025/papa/tsukuba_waypoint.yaml
+ #kbkn_maps/waypoints/hosei/2025/nakaniwa.yaml
+ waypoint_start_index = LaunchConfiguration('waypoint_start_index')
+ declare_waypoint_start_arg = DeclareLaunchArgument(
+ 'waypoint_start_index',
+ default_value= '0',
+ description='waypoint start index'
+ )
+
return LaunchDescription([
+ declare_odom_arg, declare_waypoint_arg, declare_waypoint_start_arg,
+
#rviz2
Node(package='rviz2',
executable='rviz2',
@@ -38,6 +66,14 @@ def generate_launch_description():
output='screen',
arguments=[]
),
+
+ #odom combination
+ Node(package='orange_gnss',
+ executable='odom_combination',
+ name='odom_combination',
+ output='screen',
+ arguments=[],
+ ),
#gps ekf edit
Node(package='try_navigation',
@@ -54,6 +90,15 @@ def generate_launch_description():
output='screen',
arguments=[]
),
+
+ #pcd segmentation
+ Node(package='pcd_convert',
+ executable='pcd_reflect_segmentation',
+ name='pcd_reflect_segmentation_node',
+ output='screen',
+ arguments=[]
+ ),
+
#odom wheel
#Node(package='try_navigation',
# executable='odom_wheel',
@@ -68,6 +113,9 @@ def generate_launch_description():
executable='gps_waypoint',
name='gps_waypoint',
output='screen',
+ parameters=[{'odom': odom},
+ {'waypoint_path': waypoint_path},
+ {'waypoint_start_index': waypoint_start_index}],
arguments=[],
),
# $ ros2 run navigation_control gps_waypoint
@@ -85,15 +133,17 @@ def generate_launch_description():
executable='potential_astar',
name='potential_astar_node',
output='screen',
+ parameters=[{'odom': odom}],
arguments=[],
),
#robot ctrl
- Node(package='try_navigation',
- executable='path_follower',
- name='path_follower_node',
- output='screen',
- arguments=[],
- ),
+ #Node(package='try_navigation',
+ # executable='path_follower',
+ # name='path_follower_node',
+ # output='screen',
+ # parameters=[{'odom': odom}],
+ # arguments=[],
+ #),
#navigation start
Node(package='navigation_control',
@@ -103,12 +153,12 @@ def generate_launch_description():
arguments=[],
),
#takamori Autonav
- Node(package='try_navigation',
- executable='reflection_to_pcd',
- name='reflection_to_pcd',
- output='screen',
- arguments=[],
- ),
+ #Node(package='try_navigation',
+ # executable='reflection_to_pcd',
+ # name='reflection_to_pcd',
+ # output='screen',
+ # arguments=[],
+ #),
#takamori Selfdrive
#Node(package='try_navigation',
# executable='self_drive_line',
diff --git a/try_navigation/setup.py b/try_navigation/setup.py
index 4a20ab2..0226f3c 100644
--- a/try_navigation/setup.py
+++ b/try_navigation/setup.py
@@ -58,7 +58,10 @@
'ekf_myself_match = try_navigation.ekf_myself_match:main',
'ekf_myself_gps = try_navigation.ekf_myself_gps:main',
'ekf_myself_odom = try_navigation.ekf_myself_odom:main',
- 'map_check = try_navigation.map_check:main'
+ 'map_check = try_navigation.map_check:main',
+ 'marker_utils = try_navigation.marker_utils:main',
+ 'pcd_buffer = try_navigation.pcd_buffer:main'
+
],
},
)
diff --git a/try_navigation/try_navigation/ekf_myself_gps.py b/try_navigation/try_navigation/ekf_myself_gps.py
index c7b2699..8cd10c2 100644
--- a/try_navigation/try_navigation/ekf_myself_gps.py
+++ b/try_navigation/try_navigation/ekf_myself_gps.py
@@ -52,6 +52,7 @@ def __init__(self):
self.RR_count_bad = 0
self.RR_count_so = 0
self.kalf_speed_param = 1.05
+ self.kalf_speed_param_noGPS = 1.00
self.gps_rr_flag = 1
self.offsetyaw_bad_gps = 0
@@ -67,7 +68,7 @@ def __init__(self):
self.sub_a = self.create_subscription(
- Odometry, '/odom', self.sensor_a_callback, 10)
+ Odometry, '/odom/combine', self.sensor_a_callback, 10) # /odom/wheel_spimu
self.sub_b = self.create_subscription(
Odometry, '/odom/UM982', self.sensor_b_callback, 10)
#self.sub_b = self.create_subscription(
@@ -88,6 +89,8 @@ def __init__(self):
self.get_logger().info("Start ekf_myself node")
self.get_logger().info("-------------------------")
+ self.is_initialized = False
+
def reset_tf_buffer(self):
# キャッシュのクリアとして、Bufferのインスタンスを再作成
self.br = tf2_ros.TransformBroadcaster(self)
@@ -131,6 +134,15 @@ def sensor_a_callback(self, data):
self.GTheta = self.orientation_to_yaw(
data.pose.pose.orientation.z, data.pose.pose.orientation.w)
+
+ # initialize odom
+ if not self.is_initialized and self.SmpTime is not None and self.SmpTime > 0:
+ self.initialize_odomA(
+ data.pose.pose.position.x,
+ data.pose.pose.position.y,
+ self.GTheta,
+ self.SmpTime
+ )
def sensor_b_callback(self, data):
self.GpsXY = np.array(
@@ -163,13 +175,20 @@ def sensor_b_callback(self, data):
def determination_of_R(self):
if self.GpsXY is not None:
- if (-30= 28: #################### IGVC20250529 use #######################
- if self.Number_of_satellites >= self.set_yaw_satellites_no: #################### IGVC20250530 change #######################
+ #if self.Number_of_satellites >= 28:
+ #GPSの受信衛星整数が多い(≒精度が良い?としている)場合に角度補正を行う
+ #現状はカルマンフィルタとは別に、受信精度がいい時のGPS方位とオドメトリの角度の値をストックし、
+ #そのストックした値の差分の中央値(GPS方位-オドメトリ方位 の10秒分(100回/10Hz))のデータの中央値)を
+ #オフセット角度として保存・更新し、足して補正してる状態。
+ if self.Number_of_satellites >= self.set_yaw_satellites_no and self.gps_rr_flag == 1:
self.GPS_angle_conut += 1
self.GPS_angle_reset_count = 0
yaw_offset1 = self.GPSYaw % (360/180*math.pi)
yaw_offset2 = self.GTheta % (360/180*math.pi)
yaw_offset_val = yaw_offset1 - yaw_offset2
self.diff_yaw_buff.append(yaw_offset_val)
-
else:
self.GPS_angle_reset_count += 1
if self.GPS_angle_reset_count > 20:
self.GPS_angle_conut = 0
self.GPS_angle_reset_count = 0
-
+ #一定回数(self.set_yaw_count)以上になったらカウントリセットし、角度差分の中央値をオフセットとして補正
if self.GPS_angle_conut > self.set_yaw_count:
yaw_offset_list = list(self.diff_yaw_buff)
yaw_offset_median = sorted(yaw_offset_list)[len(yaw_offset_list) // 2]
@@ -434,7 +482,8 @@ def publish_fused_value(self):
self.diff_yaw_buff = []
self.get_logger().info(f"!!!!!!!!!!!!!!!!!!!!!! offsetyaw: {self.offsetyaw}!!!!!!!!!!!!!!!!!!!!!!!!!!")
- if self.GpsXY is not None and self.gps_rr_flag:#self.Number_of_satellites > #22 :
+ #ここからカルマンフィルタ :GPS情報が受信できている(Noneではない)&受信精度が良い区間(sefl.gps_rr_flag=1)時に処理を行う
+ if self.GpsXY is not None and self.gps_rr_flag==1:#self.Number_of_satellites > #22 :
self.get_logger().info(f"!!!!++++++++++ Number_of_satellites: {self.Number_of_satellites}++++++++++!!!!!!!!!")
########## Change the written line #######
#fused_value = self.KalfGPSXY(
@@ -450,7 +499,7 @@ def publish_fused_value(self):
# self.combyaw, self.GTheta, self.GPStheta)
#########################
-
+ #角度はodom等の入力角度に補正。(カルマンフィルタで角度の計算は行っていない :カルマンフィルタの計算では入力と出力で同じ値)
######### edit ###########
#self.robot_yaw = self.GTheta + self.offsetyaw
yaw1 = self.GTheta % (360/180*math.pi)
@@ -464,7 +513,9 @@ def publish_fused_value(self):
self.robot_yaw -= 2 * np.pi
########### Change the written line #######
+ #kalf_speed_paramは速度の神の手調整。速度が正しければ補正はいらないはず。
kalf_speed = self.Speed * self.kalf_speed_param
+ #カルマンフィルタ関数実行
fused_value = self.KalfGPSXY(
kalf_speed, self.SmpTime, self.robot_yaw, self.GpsXY, self.R1, self.R2)
###########################################
@@ -474,6 +525,7 @@ def publish_fused_value(self):
# kalf_speed, self.SmpTime, self.robot_yaw, self.R1, self.R2)
##########################################
+ #カルマンフィルタ結果(fused_value)のxy座標反映。カルマンフィルタで角度の計算は行っていないので、角度オフセット補正した値のまま。
robot_orientation = self.yaw_to_orientation(self.robot_yaw)
self.robot_orientationz = robot_orientation[0]
self.robot_orientationw = robot_orientation[1]
@@ -488,13 +540,15 @@ def publish_fused_value(self):
######## gps reset #########
#self.GpsXY = None
############################
-
+
+ #GPS情報が受信できていない時の処理
else:
########## Change the written line #######
#fused_value = self.KalfXY(
# self.Speed, self.SmpTime, self.GTheta, self.R1, self.R2)
##########################################
+ #角度はodom等の入力角度に補正。(カルマンフィルタで角度の計算は行っていない :カルマンフィルタの計算では入力と出力で同じ値)
######### edit ###########
#self.robot_yaw = self.GTheta + self.offsetyaw
yaw1 = self.GTheta % (360/180*math.pi)
@@ -507,11 +561,14 @@ def publish_fused_value(self):
self.robot_yaw -= 2 * np.pi
########## Change the written line #######
- kalf_speed = self.Speed * self.kalf_speed_param
+ #kalf_speed_paramは速度の神の手調整。速度が正しければ補正はいらないはず。
+ kalf_speed = self.Speed * self.kalf_speed_param_noGPS
+ #GPSなし持カルマンフィルタ関数実行(GPSデータなしの場合のため、デッドレコニングのみ)
fused_value = self.KalfXY(
kalf_speed, self.SmpTime, self.robot_yaw, self.R1, self.R2)
##########################################
+ #カルマンフィルタ結果(fused_value)のxy座標反映。カルマンフィルタで角度の計算は行っていないので、角度オフセット補正した値のまま。
robot_orientation = self.yaw_to_orientation(self.robot_yaw)
self.robot_orientationz = robot_orientation[0]
self.robot_orientationw = robot_orientation[1]
@@ -523,6 +580,9 @@ def publish_fused_value(self):
self.fused_msg.pose.pose.orientation.w = float(
self.robot_orientationw)
+
+ #GPS受信精度が悪い場合は角度オフセットカウントリセット(一定回数以上GPS受信ができている場合のみ角度オフセット実施)
+ #2秒以上(self.GPS_angle_reset_count > 20 : 20/10Hz)GPSがいいの来なかったらリセット
self.GPS_angle_reset_count += 1
if self.GPS_angle_reset_count > 20:
self.GPS_angle_conut = 0
diff --git a/try_navigation/try_navigation/marker_utils.py b/try_navigation/try_navigation/marker_utils.py
new file mode 100644
index 0000000..3bd2ee6
--- /dev/null
+++ b/try_navigation/try_navigation/marker_utils.py
@@ -0,0 +1,227 @@
+
+import rclpy
+from rclpy.node import Node
+import numpy as np
+
+from visualization_msgs.msg import Marker, MarkerArray
+from geometry_msgs.msg import Point
+
+from nav_msgs.msg import Odometry
+
+class MarkerVisualizer(Node):
+
+ def __init__(self):
+ super().__init__('marker_visualizer')
+
+ # ---------- publisher ----------
+ self.marker_pub = self.create_publisher(MarkerArray, '/stop_zones_marker_array', 10)
+
+ #-----------subscriber-----------
+ self.oodm_sub = self.create_subscription(Odometry, '/odom', self.odom_callback,10)
+ self.oodm_sub = self.create_subscription(Odometry, '/fusion/odom', self.fusion_odom_callback,10)
+
+
+ self.pub_rate_hz = 1.0
+ self.timer = self.create_timer(1.0 / self.pub_rate_hz, self.timer_callback)
+
+ self.frame_id = 'odom'
+ self.stop_xy = np.array([
+ [ 64.2, 65.2, 19.0, 39.0, 1.0], #shiyakusyo
+ [100.0, 101.0, 25.0, 45.0, 1.0], #dourotan1
+ [177.7, 178.7, 25.0, 45.0, 1.0], #dourotan2
+ [257.5, 277.5, -60.0, -59.0, 1.0], #singoumaeteisisen1
+ [257.5, 277.5, -66.5, -65.5, 1.0], #singoumae1
+ [256.5, 276.5, -86.2, -85.2, 1.0], #singoumaeteisisen2
+ [270.3, 271.3, -99.0, -79.5, 1.0], #singoumae2
+ [405.0, 425.0, -80.2, -79.2, 1.0], #ekimae oudanhodou1 y-1
+ [545.0, 568.0, -85.0, -60.0, 0.0], #ekimae not stop
+ [410.0, 417.0, -71.5, -70.5, 1.0], #ekimae oudanhodou2 y-2
+ [290.6, 291.6, -98.0, -78.0, 1.0], #singoumaeteisisen3
+ [284.3, 285.3, -98.0, -78.0, 1.0], #singoumae3
+ [259.5, 279.5, -83.7, -82.7, 1.0], #singoumaeteisisen4 12
+ [259.5, 279.5, -80.3, -79.3, 1.0], #singoumae4 13
+ [185.0, 186.0, 25.0, 45.0, 1.0], #dourotan3
+ [107.5, 108.5, 25.0, 45.0, 1.0], #dourotan4
+ [ 64.0, 104.0, -30.0, -25.0, 1.0], #GOAL!!!!
+ [ 999, 999, 999, 999, 0.0]
+ ])
+ self.gps_dr_xy_tsukuba = np.array([
+ [ -60.0, 60.0, 35.0, 110.0, 0],
+ [ 476.0, 600.0, -84.0, -25.0, 1.0],
+ [ 999, 999, 999, 999, 0.0]
+ ])
+ self.gps_dr_xy_nakaniwa = np.array([
+ [ -41.0, 7.0, -52.0, 71.0, 0],
+ [ 999, 999, 999, 999, 0.0]
+ ])
+
+ self.ekf_noGPS_area = np.array([
+ [-60.0, 60.0, 200.0, 210.0, 1.0], #shiyakusyo
+ [260.0, 275.0, -66.0, -40.0, 1.0], #nakaniwa
+ [539.0, 600.0, -84.0, -25.0, 1.0] ]) #
+
+ #------stop marker state------
+ self.stop_markers = []
+ self.next_stop_id = 1000
+ self.prev_moving = True
+ self.current_pose = None
+ self.current_twist = None
+ self.fusion_current_pose = None
+
+ #------ threshold for stop place-------
+ self.linear_vel_threshold = 0.10 #m/s
+ self.angular_vel_threshold = 0.10 #rad / s
+
+ self.get_logger().info('MarkerVisualizer started')
+
+ def timer_callback(self):
+ now = self.get_clock().now().to_msg()
+ ma_stop = create_zone_markers(self.stop_xy, self.frame_id, now, ns='stop_zone', start_id=0)
+ ma_dr = create_zone_markers(self.ekf_noGPS_area, self.frame_id, now, ns='ma_dr', start_id=1)
+
+ ma = MarkerArray()
+ ma.markers.extend(ma_stop.markers)
+ ma.markers.extend(ma_dr.markers)
+ # 永続的な停止マーカーを追加(最新 timestamp に更新)
+ for m in self.stop_markers:
+ m.header.stamp = now
+ ma.markers.append(m)
+
+ if ma.markers:
+ self.marker_pub.publish(ma)
+
+ def fusion_odom_callback(self, msg:Odometry):
+ self.fusion_current_pose = msg.pose.pose
+
+ def odom_callback(self, msg: Odometry):
+ #self.fusion_current_pose = msg.pose.pose
+ self.current_twist = msg.twist.twist
+
+ lin = self.current_twist.linear
+ ang = self.current_twist.angular
+ #lin_speed = (lin.x**2 + lin.y**2 + lin.z**2) ** 0.5
+ #ang_speed = (ang.x**2 + ang.y**2 + ang.z**2) ** 0.5
+
+ #is_moving = not (lin_speed < self.linear_vel_threshold and ang_speed < self.angular_vel_threshold)
+ is_moving = not (abs(lin.x) < self.linear_vel_threshold and abs(ang.z) < self.angular_vel_threshold)
+
+ if self.fusion_current_pose is None:
+ return
+ if not is_moving:
+ print(f"lin : {lin.x:.5f}, ang : {ang.z:.5f}")
+
+ # 動いていた -> 停止へ遷移した瞬間を検出してマーカー追加
+ if self.prev_moving and (not is_moving) and (self.fusion_current_pose is not None):
+ # 停止地点マーカーを作成して保持
+ now = self.get_clock().now().to_msg()
+ m = create_stop_marker(self.fusion_current_pose, self.frame_id, now, self.next_stop_id)
+ self.next_stop_id += 1
+ self.stop_markers.append(m)
+ self.get_logger().info(f'Stop detected, marker added id={m.id} x={m.pose.position.x:.2f} y={m.pose.position.y:.2f}')
+
+ self.prev_moving = is_moving
+
+
+def create_zone_markers(range_xy, frame_id, now, ns , start_id = 0) -> MarkerArray:
+ ma = MarkerArray()
+
+ for idx, row in enumerate(range_xy):
+ xmin, xmax, ymin, ymax, flag = row.tolist()
+
+ # sentinel 行 を skip
+ if xmin >= 999 or ymin >= 999:
+ continue
+
+ base_id = start_id + idx * 10
+ cx = (xmin + xmax) / 2.0
+ cy = (ymin + ymax) / 2.0
+
+ # outline
+ outline = Marker()
+ outline.header.frame_id = frame_id
+ outline.header.stamp = now
+ outline.ns = ns
+ outline.id = base_id + 0
+ outline.type = Marker.LINE_STRIP
+ outline.action = Marker.ADD
+ outline.scale.x = 0.1
+ if flag == 1.0:
+ outline.color.r, outline.color.g, outline.color.b, outline.color.a = 1.0, 0.0, 0.0, 1.0
+ else:
+ outline.color.r, outline.color.g, outline.color.b, outline.color.a = 1.0, 1.0, 0.0, 1.0
+ p1 = Point(x=xmin, y=ymin, z=0.0)
+ p2 = Point(x=xmax, y=ymin, z=0.0)
+ p3 = Point(x=xmax, y=ymax, z=0.0)
+ p4 = Point(x=xmin, y=ymax, z=0.0)
+ outline.points = [p1, p2, p3, p4, p1]
+ ma.markers.append(outline)
+
+ # fill
+ fill = Marker()
+ fill.header.frame_id = frame_id
+ fill.header.stamp = now
+ fill.ns = ns
+ fill.id = base_id + 1
+ fill.type = Marker.CUBE
+ fill.action = Marker.ADD
+ fill.pose.position.x = float(cx)
+ fill.pose.position.y = float(cy)
+ fill.pose.position.z = 0.1
+ fill.pose.orientation.w = 1.0
+ fill.scale.x = float(max(0.001, (xmax - xmin)))
+ fill.scale.y = float(max(0.001, (ymax - ymin)))
+ fill.scale.z = 0.2
+ if flag == 1.0:
+ fill.color.r, fill.color.g, fill.color.b, fill.color.a = 1.0, 0.0, 0.0, 0.15
+ else:
+ fill.color.r, fill.color.g, fill.color.b, fill.color.a = 0.0, 1.0, 0.0, 0.15
+ #ma.markers.append(fill)
+
+ # 3) label
+ text = Marker()
+ text.header.frame_id = frame_id
+ text.header.stamp = now
+ text.ns = ns
+ text.id = base_id + 2
+ text.type = Marker.TEXT_VIEW_FACING
+ text.action = Marker.ADD
+ text.pose.position.x = float(cx)
+ text.pose.position.y = float(cy)
+ text.pose.position.z = 0.4
+ text.pose.orientation.w = 1.0
+ text.scale.z = 0.4
+ text.text = f'zone_{idx} flag={int(flag)}'
+ text.color.r, text.color.g, text.color.b, text.color.a = 1.0, 1.0, 1.0, 1.0
+ ma.markers.append(text)
+ return ma
+
+def create_stop_marker(pose, frame_id, now, mid) -> Marker:
+ m = Marker()
+ m.header.frame_id = frame_id
+ m.header.stamp = now
+ m.ns = 'stop_points'
+ m.id = mid
+ m.type = Marker.SPHERE
+ m.action = Marker.ADD
+ m.pose = pose
+
+ # 少し浮かせる
+ m.pose.position.z = 0.2
+ m.scale.x = 0.4
+ m.scale.y = 0.4
+ m.scale.z = 0.4
+
+ # 色(半透明赤)
+ m.color.r, m.color.g, m.color.b, m.color.a = 1.0, 0.0, 0.0, 0.8
+ return m
+
+def main(args=None):
+ rclpy.init(args=args)
+ node = MarkerVisualizer()
+ try:
+ rclpy.spin(node)
+ except KeyboardInterrupt:
+ pass
+ node.destroy_node()
+ rclpy.shutdown()
+
diff --git a/try_navigation/try_navigation/path_follower.py b/try_navigation/try_navigation/path_follower.py
index f07f857..c9e911a 100644
--- a/try_navigation/try_navigation/path_follower.py
+++ b/try_navigation/try_navigation/path_follower.py
@@ -15,7 +15,26 @@
from geometry_msgs.msg import PoseStamped
from std_msgs.msg import Int32
from std_msgs.msg import String
+import tkinter as tk
+from threading import Thread
+navigation_status = "Initializing..."
+
+# GUI
+def start_gui():
+ global navigation_status
+ root = tk.Tk()
+ root.title("Navigation Status")
+ label = tk.Label(root, text=navigation_status, font=("Helvetica", 32))
+ label.pack(padx=20, pady=20)
+
+ def update_label():
+ label.config(text=navigation_status)
+ root.after(200, update_label)
+
+ update_label()
+ root.mainloop()
+
# C++と同じく、Node型を継承します。
class PathFollower(Node):
# コンストラクタです、PcdRotationクラスのインスタンスを作成する際に呼び出されます。
@@ -42,16 +61,17 @@ def __init__(self):
# Subscriptionを作成。
self.subscription = self.create_subscription(nav_msgs.Path, '/potential_astar_path', self.get_path, qos_profile) #set subscribe pcd topic name
- #self.subscription = self.create_subscription(nav_msgs.Odometry,'/odom/wheel_imu', self.get_odom, qos_profile_sub)
+ #self.subscription = self.create_subscription(nav_msgs.Odometry,'/odom/wheel_imu', self.get_odom, qos_profile_sub) # /odom/wheel_spimu
self.subscription = self.create_subscription(nav_msgs.Odometry,'/fusion/odom', self.get_odom, qos_profile_sub)
#self.subscription = self.create_subscription(nav_msgs.Odometry,'/odom_ekf_match', self.get_odom, qos_profile_sub)
#self.subscription = self.create_subscription(nav_msgs.Odometry,'/odom_ref_slam', self.get_odom_ref, qos_profile_sub)
- self.subscription = self.create_subscription(nav_msgs.Odometry,'/fusion/odom', self.get_odom_ref, qos_profile_sub)
+ self.subscription = self.create_subscription(nav_msgs.Odometry,'/fusion/odom', self.get_odom_ref, qos_profile_sub) #/fusion/odom
self.subscription = self.create_subscription(sensor_msgs.PointCloud2, '/pcd_segment_obs', self.obs_steer, qos_profile)
+ self.step_sub = self.create_subscription(sensor_msgs.PointCloud2, '/pcd_segment_low_step', self.low_obs_steer, qos_profile)
self.goal_sub = self.create_subscription(PoseStamped, '/goal_pose', self.goal_pose_callback, qos_profile)
self.stop_sub = self.create_subscription(String, '/stop_sign_status', self.stop_sign_callback, 10)
self.human_sub = self.create_subscription(String, '/human_status', self.human_callback, 10)
- self.waypoint_number_subscription = self.create_subscription(Int32,'/waypoint_number', self.get_waypoint_number, qos_profile_sub)
+ self.waypoint_number_sub = self.create_subscription(Int32,'/waypoint_number', self.get_waypoint_number, qos_profile_sub)
self.subscription # 警告を回避するために設置されているだけです。削除しても挙動はかわりません。
# タイマーを0.05秒(50ミリ秒)ごとに呼び出す
@@ -60,7 +80,10 @@ def __init__(self):
# Publisherを作成
self.cmd_vel_publisher = self.create_publisher(geometry_msgs.Twist, 'cmd_vel', qos_profile) #set publish pcd topic name
-
+ self.pcd_test_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_test_global', qos_profile)
+ self.pcd_jam_publisher = self.create_publisher(sensor_msgs.PointCloud2, 'pcd_jam', qos_profile)
+ #self.marker_pub = self.create_publisher(MarkerArray, 'wall_follow_markers', 10)
+
#パラメータ init
self.path_plan = np.array([[0],[0],[0]])
@@ -131,14 +154,54 @@ def __init__(self):
#[-69.5, -62, -47.5, -27.5, 1.0], #tyokusen4 ||| [-67.5, -62, -47.5, -27.5, 1.0]before:[-67.0, -62, -47.5, -27.5, 1.0],
#[-55, -35, 41, 46, 1.0], #Goal
-
- [ 999, 999, 999, 999, 0.0] ]) #
+ #[ 10.0, 11.5, -19.0, 39.0, 1.0], #nakaniwa_test normal
+ #[ 30.0, 31.5, -19.0, 39.0, 1.0], #nakaniwa_test normal
+ #[ 10.0, 11.5, -19.0, 39.0, 1.0], #nakaniwa_test normal
+ #[ 0.0, 1.5, 30.0, 65.0, 1.0], #nakaniwa_test omawari
+ #[ 10.0, 11.5, -19.0, 39.0, 1.0], #nakaniwa_test
+ #[ 30.0, 31.5, -19.0, 39.0, 1.0], #nakaniwa_test
+ #[ 0.0, 1.5, 30.0, 65.0, 1.0], #nakaniwa_test
+ #[ 10.0, 11.5, -19.0, 39.0, 1.0], #nakaniwa_test
+ #[ 30.0, 31.5, -19.0, 39.0, 1.0], #nakaniwa_test
+ #[ 0.0, 1.5, 30.0, 65.0, 1.0], #nakaniwa_test
+ [ 64.2, 65.2, 19.0, 39.0, 1.0], #shiyakusyo
+ [100.0, 101.0, 25.0, 45.0, 1.0], #dourotan1
+ [177.7, 178.7, 25.0, 45.0, 1.0], #dourotan2
+ [257.5, 277.5, -60.0, -59.0, 1.0], #singoumaeteisisen1
+ [257.5, 277.5, -66.5, -65.5, 1.0], #singoumae1
+ [256.5, 276.5, -86.2, -85.2, 1.0], #singoumaeteisisen2
+ [270.3, 271.3, -99.0, -79.5, 1.0], #singoumae2
+ [405.0, 425.0, -80.2, -79.2, 1.0], #ekimae oudanhodou1 y-1
+ [545.0, 568.0, -85.0, -60.0, 0.0], #ekimae not stop
+ [410.0, 417.0, -71.5, -70.5, 1.0], #ekimae oudanhodou2 y-2
+ [290.6, 291.6, -98.0, -78.0, 1.0], #singoumaeteisisen3
+ [284.3, 285.3, -98.0, -78.0, 1.0], #singoumae3
+ [259.5, 279.5, -83.7, -82.7, 1.0], #singoumaeteisisen4 12
+ [259.5, 279.5, -80.3, -79.3, 1.0], #singoumae4 13
+ [185.0, 186.0, 25.0, 45.0, 1.0], #dourotan3
+ [107.5, 108.5, 25.0, 45.0, 1.0], #dourotan4
+ [ 64.0, 104.0, -30.0, -25.0, 1.0], #GOAL!!!!
+ [ 999, 999, 999, 999, 0.0] ]) #
self.stop_num = 0;
#obs
self.obs_points = np.array([[],[],[],[]])
+ self.low_step_obs_points = np.array([[],[],[],[]])
self.rh_obs = 0
self.lh_obs = 0
+ self.ch_obs = 0
+ self.t_stamp = 0
+
+ # angle megin for gps heading
+ self.angle_diff = 30
+
+ # jam process
+ self.jam_timer = self.get_clock().now()
+ self.none_jam_timer = self.get_clock().now()
+ self.jam_active = False
+ self.jam_last_print = -1
+ self.c_jam_obs = 0
+
################# IGVC SelfDrive Quolification line stop test #20250530# #################
@@ -165,12 +228,12 @@ def __init__(self):
#################################################################################
################# IGVC SelfDrive Full #20250601# #################
- self.sd_full_flag = 1 #root flag
+ self.sd_full_flag = 0 #root flag
self.waypoint_number = 0
- self.sd_full_human_stop = 1 #sub flag
+ self.sd_full_human_stop = 0 #sub flag
if self.sd_full_human_stop == 1:
self.sd_c_obs_stop_dist = self.sd_human_stop_dist
- self.sd_full_sign_stop = 1 #sub flag
+ self.sd_full_sign_stop = 0 #sub flag
if self.sd_full_sign_stop == 1:
dist = 0.5 + 0.4 + 0.5# eria +top +delay
sd_full_stop_xy = [-32.37441428909107, -16.465277566213718, 0.0]
@@ -181,15 +244,16 @@ def __init__(self):
self.previous_status = None
self.human_status = None
##################################################################
-
+
# actionリクエストの受信時に呼ばれる(tuika)
def listener_callback(self, goal_handle):
+ global navigation_status
self.get_logger().info(f"Received goal with a: {goal_handle.request.a}, b: {goal_handle.request.b}")
# クライアントから送られたaをstop_flagに代入
self.stop_flag = goal_handle.request.a
print(f"stop_flag set to: {self.stop_flag}")
-
+ navigation_status = "GO"
# フィードバックの返信
for i in range(1):
@@ -283,11 +347,23 @@ def robot_ctrl(self):
target_rad = math.atan2(relative_point_rot[1], relative_point_rot[0])
target_theta = (target_rad) * (180 / math.pi)
+ ################### Straight Waypoint ############################
+ if 191 <= self.waypoint_number <= 192:# or 0 <= self.waypoint_number <= 5:
+ target_waypoint = path[:,-1]
+ relative_point_x = target_waypoint[0] - position_x
+ relative_point_y = target_waypoint[1] - position_y
+ relative_point = np.vstack((relative_point_x, relative_point_y, target_waypoint[2]))
+ relative_point_rot, t_point_rot_matrix = rotation_xyz(relative_point, theta_x, theta_y, -theta_z)
+ #relative_point_rot, t_point_rot_matrix = rotation_xyz(relative_point, theta_x, theta_y, -reverse_theta_z)
+ target_rad = math.atan2(relative_point_rot[1], relative_point_rot[0])
+ target_theta = (target_rad) * (180 / math.pi)
+
+ ##################################################################
#set speed
- speed_set = 0.55#55 AutoNav 1.10
+ speed_set = 0.75#55 AutoNav 1.10
speed = speed_set
################# IGVC SelfDrive Full #20250601# #################
@@ -296,15 +372,17 @@ def robot_ctrl(self):
speed = speed_set#0.35
##################################################################
- points = self.obs_points
+ #points = self.obs_points
+ points = np.concatenate([self.obs_points, self.low_step_obs_points], axis=1)
obs_theta = np.arctan2(points[1,:],points[0,:]) * 180/math.pi #arctan2(y,x)
obs_dist = np.sqrt(points[0,:]**2 + points[1,:]**2)
r_obs = (-120 4.0:
+ self.jam_active = True
+ self.jam_timer = now
+ self.jam_last_print = -1
+ print("Front obstacle detected. Timer started.")
+ print(f"none jam timer :{none_elapsed}")
+
+ if self.jam_active:
+ elapsed = (now - self.jam_timer).nanoseconds / 1e9 # 秒(float)
+
+ #just showing passed time per sec
+ sec = int(elapsed)
+ if sec != self.jam_last_print:
+ self.jam_last_print = sec
+ print(f"time: {sec} sec")
+
+ # stop before 20 sec passed
+ if elapsed <= 50.0:
+ speed = 0.0
+ target_rad = 0.0
+ return
+ else: #20 sec passed
+ self.jam_active = False
+ self.none_jam_timer = now
+ print("ch obs persistence exceeded 20 sec -> FORCING EXIT FROM JAM PROCESS!!")
+ else:
+ # ch_obs is none reset
+ if self.jam_active:
+ print("Front obstacle cleared. Timer reset.")
+ self.none_jam_timer = now
+ self.jam_active = False
+
+ #---------------------------
+
+ if ~np.any(ch_obs) :
+ if np.any(lh_obs) and np.any(rh_obs) : #真ん中
+ target_theta = (target_rad) * (180 / math.pi)
+ print("--- Center --- Befor target_theta[deg]:",target_theta)
+ speed = 0.25
+ lh_obs_close = min(lh_obs[1,:]) # y0 lh min
+ rh_obs_close = max(rh_obs[1,:]) # y0 rh min
+ cy = cf
+ cx = (lh_obs_close + rh_obs_close) / 2
+ #c_point = (cx, cy)
+ target_rad = math.atan2(cx, cf)
+ target_theta = (target_rad) * (180 / math.pi)
+ print("--- Center --- After target_theta[deg]:",target_theta)
+
+ #or ((0 <= self.waypoint_number <= 3) and (0 - self.angle_diff <= theta_z <= 0 + self.angle_diff)) \
+ #((25 <= self.waypoint_number <= 28) and (-60 - self.angle_diff <= theta_z <= -60 + self.angle_diff)) \
+ elif ~np.any(lh_obs) and np.any(rh_obs): #右寄り
+ speed = 0.25
+ if (
+ (18 <= self.waypoint_number <= 20 and ((-150 - self.angle_diff <= theta_z <= -150) or (150 <= theta_z <= 150 + self.angle_diff)))
+ or (139 <= self.waypoint_number <= 143 and 0 - self.angle_diff <= theta_z <= 0 + self.angle_diff)
+ or (146 <= self.waypoint_number <= 151 and 0 - self.angle_diff <= theta_z <= 0 + self.angle_diff)
+ or (179 <= self.waypoint_number <= 188 and ((-150 - self.angle_diff <= theta_z <= -150) or (150 <= theta_z <= 150 + self.angle_diff)))
+ or (198 <= self.waypoint_number <= 208 and 90 - self.angle_diff <= theta_z <= 90 + self.angle_diff)
+ or (33 <= self.waypoint_number <= 35 and -20 - self.angle_diff <= theta_z <= -20 + self.angle_diff)
+ ): #1 3 5 6 7 8
+ target_theta = (target_rad) * (180 / math.pi)
+ print("!!!RH!!!! Befor target_theta[deg]:",target_theta)
+ rh_obs_close = max(rh_obs[1,:]) # y0 rh min
+ rh_dist = 0.65 # 0.65
+ cy = cf
+ cx = rh_obs_close + rh_dist
+ target_rad = math.atan2(cx, cf)
+ target_theta = (target_rad) * (180 / math.pi)
+ print("!!!RH!!!! After target_theta[deg]:",target_theta)
+
+ elif np.any(lh_obs) and ~np.any(rh_obs): #左寄り 11 <= self.waypoint_number <= 12 \
+ speed = 0.25
+ if (
+ (65 <= self.waypoint_number <= 72 and -90 - self.angle_diff <= theta_z <= -90 + self.angle_diff)
+ or (162 <= self.waypoint_number <= 168 and ((-150 - self.angle_diff <= theta_z <= -150) or (150 <= theta_z <= 150 + self.angle_diff)))
+ or (172 <= self.waypoint_number <= 175 and ((-150 - self.angle_diff <= theta_z <= -150) or (150 <= theta_z <= 150 + self.angle_diff)))
+ ): #2 4
+ target_theta = (target_rad) * (180 / math.pi)
+ print("!!!LH!!!! Befor target_theta[deg]:",target_theta)
+ lh_obs_close = min(lh_obs[1,:]) # y0 lh min
+ lh_dist = -0.65 # 0.65
+ cy = cf
+ cx = lh_obs_close + lh_dist
+ target_rad = math.atan2(cx, cf)
+ target_theta = (target_rad) * (180 / math.pi)
+ print("!!!LH!!!! After target_theta[deg]:",target_theta)
+
+ #elif ~np.any(rh_obs) and ~np.any(lh_obs):
+ # speed = 0.25
+
lim_steer = 20
#lim_steer = 30 24/11/29 ok
#if abs(target_theta) < 10 and 0.0 < speed and speed < 0.5:
if abs(target_theta) < 10:
if 0.0 < speed:
if speed < 0.5:
- speed = 1.1 # autonav 0.8
+ speed = 0.5 # autonav 0.8
#elif target_theta < -lim_steer:
if target_theta < -lim_steer:
speed = 0.10
target_rad = -lim_steer/180*math.pi
+ #print("over write1")
elif lim_steer < target_theta:
speed = 0.10
target_rad = lim_steer/180*math.pi
-
+ #print("over write1")
if abs(target_theta) > 90:
speed = -0.10
if np.any(c_obs_back) :
speed = 0.10
-
+
################# IGVC SelfDrive Full #20250601# #################
if self.sd_full_flag == 1:
@@ -493,12 +682,13 @@ def robot_ctrl(self):
#################################################################################
+
#elif abs(target_theta) > 90:
# speed = 0.2
#else:
# speed = 0.2
#self.get_logger().info('speed = %f' % (speed))
- target_theta = target_theta +90/180*math.pi
+ target_theta = target_theta +90 ######/180*math.pi
target_rad_pd = self.sensim0(target_rad)
#target_rad_pd = target_rad
@@ -557,6 +747,7 @@ def sensim0(self, steering):
return steering
def get_odom_ref(self, msg):
+ global navigation_status
self.ref_position_x = msg.pose.pose.position.x
self.ref_position_y = msg.pose.pose.position.y
self.ref_position_z = msg.pose.pose.position.z
@@ -572,15 +763,34 @@ def get_odom_ref(self, msg):
self.ref_theta_y = 0 #pitch /math.pi*180
self.ref_theta_z = yaw /math.pi*180
+ if self.waypoint_number >= 225: # after dourotan4
+ if self.stop_num <= 15:
+ self.stop_num = 16
+ elif self.waypoint_number >= 198: # after singou
+ if self.stop_num <= 13:
+ self.stop_num = 14
+ elif self.waypoint_number >= 178: # after ekimae oudanhodou2
+ if self.stop_num <= 9:
+ self.stop_num = 10
+ elif self.waypoint_number >= 138: # after ekimae oudanhodou1
+ if self.stop_num <= 7:
+ self.stop_num = 8
+ elif self.waypoint_number >= 81: # after singou
+ if self.stop_num <= 6:
+ self.stop_num = 7
+ elif self.waypoint_number >= 57: # after dourotan2
+ if self.stop_num <= 2:
+ self.stop_num = 3
if ((self.stop_xy[self.stop_num,0] < self.ref_position_x) and (self.ref_position_x < self.stop_xy[self.stop_num,1]) and (self.stop_xy[self.stop_num,2] < self.ref_position_y) and (self.ref_position_y < self.stop_xy[self.stop_num,3]) ) or ((self.stop_xy[self.stop_num,0] < self.position_x) and (self.position_x < self.stop_xy[self.stop_num,1]) and (self.stop_xy[self.stop_num,2] < self.position_y) and (self.position_y < self.stop_xy[self.stop_num,3]) ):
if self.stop_xy[self.stop_num,4] > 0:
self.get_logger().info('####### stop flag on %f #######' % (self.stop_num))
self.stop_flag = 1;
+ navigation_status = "STOP"
+ #print(self.stop_num)
else:
self.get_logger().info('####### through flag on %f #######' % (self.stop_num))
self.stop_num = self.stop_num + 1;
-
def pointcloud2_to_array(self, cloud_msg):
# Extract point cloud data
@@ -600,6 +810,7 @@ def obs_steer(self, msg):
#print stamp message
t_stamp = msg.header.stamp
#print(f"t_stamp ={t_stamp}")
+ self.t_stamp = t_stamp
#get pcd data
points = self.pointcloud2_to_array(msg)
@@ -608,21 +819,79 @@ def obs_steer(self, msg):
#map_obs
self.obs_points = points
- rh_obs = self.pcd_serch(points, -0.2,1,0,0.7)
+ rh_obs = self.pcd_serch(points, 1.0,1.3,-1.0,0)#1.0,1.3,-0.7,0
if len(rh_obs[0,:]) > 10:
self.rh_obs = 1
else:
self.rh_obs = 0
- lh_obs = self.pcd_serch(points, -0.2,1,-0.7,0)
+ lh_obs = self.pcd_serch(points, 1.0,1.3,0,1.0)#1.0,1.3,0,0.7
if len(lh_obs[0,:]) > 10:
self.lh_obs = 1
else:
self.lh_obs = 0
+
+ ch_obs = self.pcd_serch(points, 1.0,1.3,-0.4,0.4)
+
+ c_jam_obs = self.pcd_serch(points, 0.0,2.0,-0.6,0.6,0.6,1.0) #1.0,1.3,-0.4,0.4 #1.0,2.0,-0.8,0.8
+
+ self.rh_obs = rh_obs
+ self.lh_obs = lh_obs
+ self.ch_obs = ch_obs
+ self.c_jam_obs = c_jam_obs
+
+ #print(lh_obs.shape)
+ #print("x0 min",min(lh_obs[0,:]))
+ #print("x0 max",max(lh_obs[0,:]))
+ #print("y0 min",min(lh_obs[1,:])) ###ok!!!!!!!!!
+ #print("y0 max",max(lh_obs[1,:]))
+
+ #print("y0 rh min",max(rh_obs[1,:]))
+
+ #global test obs rviz2 kesu
+ obs_jam_msg = point_cloud_intensity_msg(c_jam_obs.T, t_stamp, 'odom')
+ obs_test_msg = point_cloud_intensity_msg(ch_obs.T, t_stamp, 'odom')
+ self.pcd_test_publisher.publish(obs_test_msg)
+ self.pcd_test_publisher.publish(obs_jam_msg)
+
+ def low_obs_steer(self, msg):
+
+ #print stamp message
+ #t_stamp = msg.header.stamp
+ #print(f"t_stamp ={t_stamp}")
+ #self.t_stamp = t_stamp
+
+ #get pcd data
+ points = self.pointcloud2_to_array(msg)
+ #print(f"points ={points.shape}")
+
+ #map_obs
+ if (
+ (self.waypoint_number == 45)
+ or (self.waypoint_number == 137)
+ or (self.waypoint_number == 177)
+ or (76 <= self.waypoint_number <= 78)
+ or (148 <= self.waypoint_number <= 153)
+ or (162 <= self.waypoint_number <= 167)
+ or (195 <= self.waypoint_number <= 198)
+ or (227 <= self.waypoint_number <= 232)
+ ): # nakaniwa 14~20
+ self.low_step_obs_points = points
+ #self.get_logger().info(f"#######Add Low Step#######: {self.low_step_obs_points}")
+ else:
+ self.low_step_obs_points = np.array([[],[],[],[]])
+ #self.get_logger().info(f"#######No Low Step#######: {self.low_step_obs_points}")
- def pcd_serch(self, pointcloud, x_min, x_max, y_min, y_max):
- pcd_ind = (( (x_min <= pointcloud[0,:]) * (pointcloud[0,:] <= x_max)) * ((y_min <= pointcloud[1,:]) * (pointcloud[1,:]) <= y_max ) )
- pcd_mask = pointcloud[:, pcd_ind]
- return pcd_mask
+ # pcd_serch を z も考慮する形で置換(点群抽出用ユーティリティ)
+ def pcd_serch(self, pointcloud, x_min, x_max, y_min, y_max, z_min=None, z_max=None):
+ """
+ pointcloud: 4 x N array (x,y,z,intensity)
+ returns: filtered 4 x M array
+ """
+ mask = ((pointcloud[0, :] >= x_min) & (pointcloud[0, :] <= x_max) &
+ (pointcloud[1, :] >= y_min) & (pointcloud[1, :] <= y_max))
+ if z_min is not None and z_max is not None:
+ mask = mask & ((pointcloud[2, :] >= z_min) & (pointcloud[2, :] <= z_max))
+ return pointcloud[:, mask]
def rotation_xyz(pointcloud, theta_x, theta_y, theta_z):
theta_x = math.radians(theta_x)
@@ -658,6 +927,41 @@ def quaternion_to_euler(x, y, z, w):
pitch = np.arctan2(-rot_matrix[2, 0], np.sqrt(rot_matrix[2, 1]**2 + rot_matrix[2, 2]**2))
yaw = np.arctan2(rot_matrix[1, 0], rot_matrix[0, 0])
return roll, pitch, yaw
+
+def point_cloud_intensity_msg(points, t_stamp, parent_frame):
+ # In a PointCloud2 message, the point cloud is stored as an byte
+ # array. In order to unpack it, we also include some parameters
+ # which desribes the size of each individual point.
+ ros_dtype = sensor_msgs.PointField.FLOAT32
+ dtype = np.float32
+ itemsize = np.dtype(dtype).itemsize # A 32-bit float takes 4 bytes.
+ data = points.astype(dtype).tobytes()
+
+ # The fields specify what the bytes represents. The first 4 bytes
+ # represents the x-coordinate, the next 4 the y-coordinate, etc.
+ fields = [
+ sensor_msgs.PointField(name='x', offset=0, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='y', offset=4, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='z', offset=8, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='intensity', offset=12, datatype=ros_dtype, count=1),
+ ]
+
+ # The PointCloud2 message also has a header which specifies which
+ # coordinate frame it is represented in.
+ header = std_msgs.Header(frame_id=parent_frame, stamp=t_stamp)
+
+
+ return sensor_msgs.PointCloud2(
+ header=header,
+ height=1,
+ width=points.shape[0],
+ is_dense=True,
+ is_bigendian=False,
+ fields=fields,
+ point_step=(itemsize * 4), # Every point consists of three float32s.
+ row_step=(itemsize * 4 * points.shape[0]),
+ data=data
+ )
# mainという名前の関数です。C++のmain関数とは異なり、これは処理の開始地点ではありません。
def main(args=None):
@@ -665,6 +969,9 @@ def main(args=None):
rclpy.init(args=args)
# クラスのインスタンスを作成
path_follower = PathFollower()
+ # GUI
+ gui_thread = Thread(target=start_gui, daemon=True)
+ gui_thread.start()
# spin処理を実行、spinをしていないとROS 2のノードはデータを入出力することが出来ません。
rclpy.spin(path_follower)
# 明示的にノードの終了処理を行います。
diff --git a/try_navigation/try_navigation/pcd_buffer.py b/try_navigation/try_navigation/pcd_buffer.py
new file mode 100644
index 0000000..88cebd5
--- /dev/null
+++ b/try_navigation/try_navigation/pcd_buffer.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# rclpy (ROS 2のpythonクライアント)の機能を使えるようにします。
+import rclpy
+# rclpy の Node を使いやすくインポート
+from rclpy.node import Node
+import std_msgs.msg as std_msgs
+import sensor_msgs.msg as sensor_msgs
+import numpy as np
+import math
+from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy
+import time
+import pandas as pd
+from sensor_msgs.msg import PointCloud2, PointField
+from std_msgs.msg import Header
+
+# sensor_msgs_py の point_cloud2 が使えれば利用(環境依存)
+try:
+ from sensor_msgs_py import point_cloud2 as pc2
+except Exception:
+ pc2 = None
+
+
+class PcdBufferNode(Node):
+ """
+ 指定の PointCloud2 トピックを購読して点群をバッファ(グリッド丸め -> 重複削除 -> MAX_POINTSに切り詰め)し、
+ 新しいトピックに publish するノード。
+ """
+
+ def __init__(self):
+ super().__init__('pcd_buffer')
+
+ # QoS 設定
+ qos_profile = QoSProfile(
+ history=QoSHistoryPolicy.KEEP_LAST,
+ reliability=QoSReliabilityPolicy.RELIABLE,
+ durability=QoSDurabilityPolicy.VOLATILE,
+ depth=1
+ )
+
+ qos_profile_sub = QoSProfile(
+ reliability=QoSReliabilityPolicy.RELIABLE,
+ durability=QoSDurabilityPolicy.VOLATILE,
+ depth=1
+ )
+
+ # パラメータ
+ # 入出力トピック
+ self.INPUT_TOPIC = '/pcd_obs_global'
+ self.OUTPUT_TOPIC = 'pcd_obs_global_buff'
+
+ # 丸め精度
+ self.GROUND_PIXEL = 1000.0 / 50.0
+
+ self.MAX_POINTS = 50000
+ self.FRAME_ID = 'odom'
+ # 内部バッファ(4 x N: x, y, z, intensity)
+ self.pcd_obs_global_buff = np.array([[], [], [], []], dtype=np.float32)
+
+ # Subscriptionを作成
+ self.subscription = self.create_subscription(
+ sensor_msgs.PointCloud2,
+ self.INPUT_TOPIC,
+ self.pcd_buffer_callback,
+ qos_profile_sub
+ )
+ self.subscription # 警告回避用
+
+ # Publisherを作成
+ self.pcd_obs_global_buff_publisher = self.create_publisher(sensor_msgs.PointCloud2, self.OUTPUT_TOPIC, qos_profile)
+
+ # ログ
+ self.get_logger().info(f'PcdBufferNode ready. sub:{self.INPUT_TOPIC} pub:{self.OUTPUT_TOPIC} ground_pixel:{self.GROUND_PIXEL} MAX_POINTS:{self.MAX_POINTS}')
+
+ def pointcloud2_to_array(self, cloud_msg):
+ # Extract point cloud data
+ points = np.frombuffer(cloud_msg.data, dtype=np.uint8).reshape(-1, cloud_msg.point_step)
+ x = np.frombuffer(points[:, 0:4].tobytes(), dtype=np.float32)
+ y = np.frombuffer(points[:, 4:8].tobytes(), dtype=np.float32)
+ z = np.frombuffer(points[:, 8:12].tobytes(), dtype=np.float32)
+ intensity = np.frombuffer(points[:, 12:16].tobytes(), dtype=np.float32)
+
+ # Combine into a 4xN matrix
+ point_cloud_matrix = np.vstack((x, y, z, intensity))
+ print(point_cloud_matrix)
+ print(f"point_cloud_matrix ={point_cloud_matrix.shape}")
+ print(f"x ={x.dtype, x.shape}")
+
+ return point_cloud_matrix
+
+ def pcd_buffer_callback(self, msg):
+ """
+ PointCloud2 を受け取りバッファに追加 -> グリッド丸め -> 重複削除 -> 切り詰め -> publish
+ """
+ #print stamp message
+ t_stamp = msg.header.stamp
+ print(f"t_stamp ={t_stamp}")
+
+ # PointCloud2 -> numpy
+ points = self.pointcloud2_to_array(msg)
+ print(f"points ={points.shape}")
+
+ # add buffer----------------------------------------------------------------
+ if hasattr(self, 'pcd_obs_global_buff') and self.pcd_obs_global_buff is not None and self.pcd_obs_global_buff.size != 0:
+ try:
+ self.pcd_obs_global_buff = np.hstack((self.pcd_obs_global_buff, points))
+ except Exception as e:
+ self.get_logger().warning(f'hstack failed: {e}')
+ # フォールバック( concatenate )
+ self.pcd_obs_global_buff = np.concatenate((self.pcd_obs_global_buff, points), axis=1)
+ else:
+ self.pcd_obs_global_buff = points.copy()
+
+ # 3) グリッド丸め(位置を quantize して重複を削除)
+ points_xyz = self.pcd_obs_global_buff[:3, :] # (3, M)
+ points_i = self.pcd_obs_global_buff[3, :] # (M,)
+ points_round = np.round(points_xyz * self.GROUND_PIXEL) / self.GROUND_PIXEL
+
+ # pandas を使って重複削除(丸め後の x,y,z が同じものを 1 つにする)
+ df = pd.DataFrame({
+ 'x': points_round[0, :],
+ 'y': points_round[1, :],
+ 'z': points_round[2, :],
+ })
+ mask_unique = ~df.duplicated()
+ unique_idx = np.where(mask_unique)[0]
+
+ if unique_idx.size == 0:
+ # 重複で何も残らない場合は空バッファ
+ self.pcd_obs_global_buff = np.array([[], [], [], []], dtype=np.float32)
+ else:
+ new_xyz = points_round[:, unique_idx]
+ new_i = points_i[unique_idx]
+ self.pcd_obs_global_buff = np.vstack((new_xyz, new_i))
+
+ # 4) 最大点数を超える場合は末尾(最新)を残す
+ if self.pcd_obs_global_buff.shape[1] > self.MAX_POINTS:
+ self.pcd_obs_global_buff = self.pcd_obs_global_buff[:, -self.MAX_POINTS:].copy()
+
+ # 5) Publish(PointCloud2 へ変換して publish)
+ try:
+ n_by_4 = self.pcd_obs_global_buff.T # (N,4)
+ cloud_msg = point_cloud_intensity_msg(n_by_4, t_stamp, self.FRAME_ID)
+ self.pcd_obs_global_buff_publisher.publish(cloud_msg)
+ except Exception as e:
+ self.get_logger().error(f'publish failed: {e}')
+
+
+def point_cloud_intensity_msg(points, t_stamp, parent_frame):
+ """
+ points: (N,4) numpy array (x,y,z,intensity)
+ t_stamp: builtin_interfaces.msg.Time (そのまま header.stamp に代入)
+ parent_frame: frame id string
+ """
+ if points is None or len(points) == 0:
+ header = std_msgs.Header(frame_id=parent_frame, stamp=t_stamp)
+ return sensor_msgs.PointCloud2(header=header, height=0, width=0, is_dense=True, is_bigendian=False, fields=[], point_step=0, row_step=0, data=b'')
+
+ ros_dtype = sensor_msgs.PointField.FLOAT32
+ dtype = np.float32
+ itemsize = np.dtype(dtype).itemsize # 4 bytes
+ arr = np.asarray(points, dtype=dtype)
+ if arr.ndim == 1:
+ arr = arr.reshape(1, -1)
+ if arr.shape[1] == 3:
+ zeros = np.zeros((arr.shape[0], 1), dtype=dtype)
+ arr = np.hstack((arr, zeros))
+ elif arr.shape[1] > 4:
+ arr = arr[:, :4]
+
+ data = arr.astype(dtype).tobytes()
+
+ fields = [
+ sensor_msgs.PointField(name='x', offset=0, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='y', offset=4, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='z', offset=8, datatype=ros_dtype, count=1),
+ sensor_msgs.PointField(name='intensity', offset=12, datatype=ros_dtype, count=1),
+ ]
+
+ header = std_msgs.Header(frame_id=parent_frame, stamp=t_stamp)
+
+ cloud = sensor_msgs.PointCloud2(
+ header=header,
+ height=1,
+ width=arr.shape[0],
+ is_dense=True,
+ is_bigendian=False,
+ fields=fields,
+ point_step=itemsize * 4,
+ row_step=itemsize * 4 * arr.shape[0],
+ data=data
+ )
+ return cloud
+
+
+# main 関数
+def main(args=None):
+ rclpy.init(args=args)
+ node = PcdBufferNode()
+ try:
+ rclpy.spin(node)
+ except KeyboardInterrupt:
+ pass
+ finally:
+ node.destroy_node()
+ rclpy.shutdown()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/try_navigation/try_navigation/potential_astar.py b/try_navigation/try_navigation/potential_astar.py
index d55fbfb..22fb27a 100644
--- a/try_navigation/try_navigation/potential_astar.py
+++ b/try_navigation/try_navigation/potential_astar.py
@@ -58,10 +58,16 @@ def __init__(self):
depth = 1
)
+ # set parameter (launch can change this parameter)
+ self.declare_parameter('odom', '/fusion/odom')
+
+ # define parameter
+ odom_topic = self.get_parameter('odom').get_parameter_value().string_value
+
# Subscriptionを作成。CustomMsg型,'/livox/lidar'という名前のtopicをsubscribe。
self.subscription = self.create_subscription(sensor_msgs.PointCloud2, '/pcd_segment_obs', self.potential_astar, qos_profile)
#self.subscription = self.create_subscription(nav_msgs.Odometry,'/odom/wheel_imu', self.get_odom, qos_profile_sub)
- self.subscription = self.create_subscription(nav_msgs.Odometry,'/fusion/odom', self.get_odom, qos_profile_sub)
+ self.subscription = self.create_subscription(nav_msgs.Odometry, odom_topic, self.get_odom, qos_profile_sub) # /odom/wheel_spimu
#self.subscription = self.create_subscription(nav_msgs.Odometry,'/odom_fast', self.get_odom, qos_profile_sub)
#self.subscription = self.create_subscription(nav_msgs.Odometry,'/odom_ekf_match', self.get_odom, qos_profile_sub)
self.subscription = self.create_subscription(geometry_msgs.PoseArray,'/current_waypoint', self.get_waypoint, qos_profile_sub)
@@ -73,6 +79,8 @@ def __init__(self):
self.right_subscription = self.create_subscription(sensor_msgs.PointCloud2, '/line_buff_right', self.get_right_obs, qos_profile)
self.left_subscription = self.create_subscription(sensor_msgs.PointCloud2, '/line_buff_left', self.get_left_obs, qos_profile)
self.dot_subscription = self.create_subscription(sensor_msgs.PointCloud2, '/dotted_line', self.get_dot_obs, qos_profile)
+ self.low_step_subscription = self.create_subscription(sensor_msgs.PointCloud2, '/pcd_segment_low_step', self.get_low_step_obs, qos_profile)
+ self.shibafu_subscription = self.create_subscription(sensor_msgs.PointCloud2, '/pcd_segment_shibafu', self.get_shibafu_obs, qos_profile)
self.subscription # 警告を回避するために設置されているだけです。削除しても挙動はかわりません。
#self.timer = self.create_timer(0.05, self.timer_callback)
@@ -96,7 +104,7 @@ def __init__(self):
self.cg=20 #ポテンシャルの引力パラメータ
self.lg=20 #ポテンシャルの引力パラメータ
self.co=11 #ポテンシャルの斥力パラメータ SICKパラ目:co=11;lo=0.55;
- self.lo=0.5#55 #0.5#0.9#ポテンシャルの斥力パラメータ
+ self.lo=0.45#55 #0.5#0.9#ポテンシャルの斥力パラメータ
self.est_xy = [0,0]#自己位置仮入力
self.wp_xy = [10,0]#ウェイポイント仮入力
self.astar_path = [0,10]#ウェイポイント仮入力
@@ -176,6 +184,13 @@ def __init__(self):
[ 0, 0, 0, 0, 1, 0, 0, 0] # waypoint 21 GOAL!!!!!!
]
+ #tukuba_obs_x =np.linspace(-45,25,860);
+ #tukuba_obs_y =np.linspace(97,94,860);
+ #tukuba_obs_z =np.linspace(0,0,860);
+ #self.tsukuba_obs = np.array([tukuba_obs_x, tukuba_obs_y, tukuba_obs_z]);
+ self.tsukuba_obs = np.array([[],[],[]])
+ self.low_step_obs_points = np.array([[],[],[]])
+ self.shibafu_obs_points = np.array([[],[],[]])
################# IGVC SelfDrive Quolification line stop test #20250530# #################
self.sd_line_stop_test = 0
@@ -347,6 +362,28 @@ def get_dot_obs(self, msg):
self.dot_obs_points = np.vstack((points[0,:], points[1,:], points[2,:]))
+ def get_low_step_obs(self, msg):
+ #print stamp message
+ t_stamp = msg.header.stamp
+ #print(f"t_stamp ={t_stamp}")
+
+ #get pcd data
+ points = self.pointcloud2_to_array(msg)
+ #print(f"points ={points.shape}")
+
+ self.low_step_obs_points = np.vstack((points[0,:], points[1,:], points[2,:]))
+
+ def get_shibafu_obs(self, msg):
+ #print stamp message
+ t_stamp = msg.header.stamp
+ #print(f"t_stamp ={t_stamp}")
+
+ #get pcd data
+ points = self.pointcloud2_to_array(msg)
+ #print(f"points ={points.shape}")
+
+ self.shibafu_obs_points = np.vstack((points[0,:], points[1,:], points[2,:]))
+
def potential_astar(self, msg):
#print stamp message
@@ -356,7 +393,7 @@ def potential_astar(self, msg):
#get pcd data
points = self.pointcloud2_to_array(msg)
#print(f"points ={points.shape}")
- print(f"self.pot_obs_points.shape = {self.pot_obs_points.shape}")
+ #print(f"self.pot_obs_points.shape = {self.pot_obs_points.shape}")
position_x=self.position_x; position_y=self.position_y;
@@ -414,6 +451,45 @@ def potential_astar(self, msg):
elif self.obs_info[self.waypoint_number][self.dotline_info] == 1:
dot_line_local = localization_xyz(self.dot_obs_points, position_x, position_y, theta_x, theta_y, theta_z)
+ #low_step_obs add(local)
+ low_step_local = np.array([[],[],[]])
+ if len(self.low_step_obs_points[0,:])>0:
+ if (
+ (self.waypoint_number == 45)
+ or (self.waypoint_number == 137)
+ or (self.waypoint_number == 177)
+ or (76 <= self.waypoint_number <= 78)
+ or (148 <= self.waypoint_number <= 153)
+ or (162 <= self.waypoint_number <= 167)
+ or (195 <= self.waypoint_number <= 198)
+ or (227 <= self.waypoint_number <= 232)
+ ): # nakaniwa 14~20
+ low_step_local = self.low_step_obs_points
+ print("!!!!!!!!Waypoint Low Step!!!!!!!!")
+
+ #shibafu_obs add(local)
+ shibafu_local = np.array([[],[],[]])
+ if len(self.shibafu_obs_points[0,:])>0:
+ if (
+ (self.waypoint_number == 144)
+ or (self.waypoint_number == 170)
+ or (50 <= self.waypoint_number <= 72)
+ or (90 <= self.waypoint_number <= 123)
+ or (200 <= self.waypoint_number <= 225)
+ or (227 <= self.waypoint_number <= 232)
+ ): # no otiba 101~123 nakaniwa 17~26
+ shibafu_local = self.shibafu_obs_points
+ print("!!!!!!!!Waypoint Shibafu!!!!!!!!")
+
+ self_radius = 0.8
+ angle_min = -60 + 180
+ angle_max = 60 + 180
+ angles = np.linspace(np.radians(angle_min),np.radians(angle_max),100)
+ x_radius = self_radius * np.cos(angles)
+ y_radius = self_radius * np.sin(angles)
+ z_radius = 0 * np.cos(angles)
+ self_radius_points = np.vstack((x_radius, y_radius, z_radius))
+
"""
#map_obs add
if len(self.map_obs_points[0,:])>0:
@@ -424,6 +500,37 @@ def potential_astar(self, msg):
else:
relative_point_rot = np.array([[],[],[]])
"""
+
+ # make map_obs x1 x2 y1 y2
+ obs1 = make_obs(-45, 25, 95.5, 92.5) # siyakusyoura minami
+ obs2 = make_obs( 5, 20, 100, 102) # siyakusyoura kita
+ obs3 = make_obs( 28, 20, 108, 102) # siyakusyoura kita2
+ obs4 = make_obs(-74.5,-76.5, 105, 29) # siyakusyo nisi
+ obs5 = make_obs( 61,100, 33.5, 42.5) # siyakusyo oudanhodou
+ obs6 = make_obs( 61,44, 33.5, 36.5) # siyakusyo oudanhodou
+ obs7 = make_obs(272,273,-65,-49.5) # 7-Eleven mae
+ obs8 = make_obs(-21,-39, 25.5, 28.5) # siyakusyo sibahu1
+ obs9 = make_obs(-51,-39, 38.5, 28.5) # siyakusyo sibahu2
+ obs10 = make_obs(38, 38, 62, 96.5) # siyakusyo higasi
+ obs11 = make_obs(465,485,-74.5, -74.5) # eki minami
+ obs12 = make_obs(3,4.8, 19, 23.5) # siyakusyo sibahu
+ obs13 = make_obs(-9,3,20, 19) # siyakusyo sibahu
+ obs14 = make_obs(273.06,269.82, -61.24, -61.25) # singou
+ obs15 = make_obs(270.96,270.52,-57.33, -61.25) # singou
+ obs16 = make_obs(269.96,273.12,-57.33, -57.05) # singou
+
+ self.tsukuba_obs = np.hstack((obs1, obs4, obs5, obs6, obs7, obs8, obs9, obs10, obs11, obs12, obs13, obs14, obs15, obs16))
+ #self.tsukuba_obs = np.hstack((obs7,obs1))
+
+ #map_obs add
+ if len(self.tsukuba_obs[0,:])>0:
+ relative_point_x = self.tsukuba_obs[0,:] - self.position_x
+ relative_point_y = self.tsukuba_obs[1,:] - self.position_y
+ relative_point = np.array((relative_point_x, relative_point_y, self.tsukuba_obs[2,:]))
+ relative_point_rot, t_point_rot_matrix = rotation_xyz(relative_point, self.theta_x, self.theta_y, -self.theta_z)
+ else:
+ relative_point_rot = np.array([[],[],[]])
+
###################################
#obs round&duplicated :grid_size before:28239 after100:24592 after50:8894 after10:3879
@@ -455,30 +562,40 @@ def potential_astar(self, msg):
if dot_line_local.shape[1] > 0:
obs_points = np.insert(obs_points, len(obs_points[0,:]), dot_line_local.T, axis=1)
+
+ if low_step_local.shape[1] > 0:
+ obs_points = np.insert(obs_points, len(obs_points[0,:]), low_step_local.T, axis=1)
+ print("!!!!!!!!Add Low Step!!!!!!!!")
+
+ if shibafu_local.shape[1] > 0:
+ obs_points = np.insert(obs_points, len(obs_points[0,:]), shibafu_local.T, axis=1)
+ print("!!!!!!!!Add Shibafu!!!!!!!!")
- #obs_points = np.insert(obs_points, len(obs_points[0,:]), relative_point_rot.T, axis=1)
+ obs_points = np.insert(obs_points, len(obs_points[0,:]), relative_point_rot.T, axis=1)
+ obs_points = np.insert(obs_points, len(obs_points[0,:]), self_radius_points.T, axis=1)
+ #obs_points = np.insert(obs_points, len(obs_points[0,:]), self.tsukuba_obs.T, axis=1)
points_round = np.round(obs_points * self.obs_pixel) / self.obs_pixel
obs_xy_local = points_round[:,~pd.DataFrame({"x":points_round[0,:], "y":points_round[1,:]}).duplicated()]
obs_xy = np.vstack((obs_xy_local[0,:], obs_xy_local[1,:]))
- print(f"obs_points ={obs_points.shape}")
+ #print(f"obs_points ={obs_points.shape}")
reflect_set = obs_points[2,~pd.DataFrame({"x":points_round[0,:], "y":points_round[1,:]}).duplicated()]
#obs global
obs_xy_rot, obs_rot_matrix = rotation_xyz(obs_xy_local, self.theta_x, self.theta_y, self.theta_z)
obs_x_grobal = obs_xy_rot[0,:] + self.position_x
obs_y_grobal = obs_xy_rot[1,:] + self.position_y
obs_global = np.vstack((obs_x_grobal, obs_y_grobal, obs_xy_local[2,:], reflect_set) , dtype=np.float32)
- print(f"obs_xy ={obs_xy.shape}")
- print(f"obs_global ={obs_global.shape}")
- print(f"obs_global ={obs_global.dtype}")
+ #print(f"obs_xy ={obs_xy.shape}")
+ #print(f"obs_global ={obs_global.shape}")
+ #print(f"obs_global ={obs_global.dtype}")
#set self position
#self.est_xy = [self.position_x, self.position_y]
- print(f"self.est_xy ={self.est_xy}")
+ #print(f"self.est_xy ={self.est_xy}")
astar_path = self.path_plan(obs_xy)
- print(f"astar_path ={astar_path.shape}")
+ #print(f"astar_path ={astar_path.shape}")
astar_path = np.vstack((astar_path, np.zeros([1,len(astar_path[0,:])]) ))
- print(f"astar_path ={astar_path.shape}")
+ #print(f"astar_path ={astar_path.shape}")
astar_path_rot, astar_path_rot_matrix = rotation_xyz(astar_path, self.theta_x, self.theta_y, self.theta_z)
astar_path_x_grobal = astar_path_rot[0,:] + self.position_x
astar_path_y_grobal = astar_path_rot[1,:] + self.position_y
@@ -539,7 +656,7 @@ def path_plan(self, obs_xy):
astar_count = astar_count + 1 #ループカウント
if astar_xy[astar_yn, astar_xn] <0.2:
- self.get_logger().info(f"Goal: astar_path_x, astar_path_y ={astar_path_x, astar_path_y}")
+ #self.get_logger().info(f"Goal: astar_path_x, astar_path_y ={astar_path_x, astar_path_y}")
break
if (astar_count > 100) and (astar_2nd==0):
astar_x = np.arange(-9.9, 9.9, 0.3) +self.est_xy[0] #%%Astarのxを定義
@@ -557,7 +674,7 @@ def path_plan(self, obs_xy):
co=self.co2nd
astar_2nd = 1
if astar_count >200:
- self.get_logger().info("Count Break")
+ #self.get_logger().info("Count Break")
break
#■ process : A-star Return
@@ -586,11 +703,11 @@ def path_plan(self, obs_xy):
astar_judge_y = astar_y[astar_path_y_rev2] - obs_xy[1].reshape(len(obs_xy[1]),1) #y-yo 斥力計算 探索ポイントx近場にある障害物を全て行列使って計算
astar_judge_x2 = ( astar_judge_x * np.ones([len(obs_xy[0]),len(astar_x[astar_path_x_rev2])]) ) ** 2 #(x-xo)^2
astar_judge_y2 = ( astar_judge_y * np.ones([len(obs_xy[1]),len(astar_y[astar_path_y_rev2])]) ) ** 2 #(y-yo)^2
- self.get_logger().info(f"astar_judge_x2 ={len(astar_judge_x2)}")
- self.get_logger().info(f"astar_path_x_rev2 ={len(astar_path_x_rev2)}")
+ #self.get_logger().info(f"astar_judge_x2 ={len(astar_judge_x2)}")
+ #self.get_logger().info(f"astar_path_x_rev2 ={len(astar_path_x_rev2)}")
if len(astar_judge_x2) > 0:
astar_judge_obs_ind = np.minimum.reduce( np.sqrt(astar_judge_x2 + astar_judge_y2) ) <1.8#1.6
- self.get_logger().info(f"astar_judge_obs_ind ={astar_judge_obs_ind}")
+ #self.get_logger().info(f"astar_judge_obs_ind ={astar_judge_obs_ind}")
astar_path_point_x = np.append(np.append(self.est_xy[0],astar_x[astar_path_x_rev2[astar_judge_obs_ind]]),self.wp_xy[0])
astar_path_point_y = np.append(np.append(self.est_xy[1],astar_y[astar_path_y_rev2[astar_judge_obs_ind]]),self.wp_xy[1])
else:
@@ -713,6 +830,30 @@ def path_msg(waypoints, stamp, parent_frame):
wp_msg.poses.append(waypoint)
return wp_msg
+def make_obs(x1, x2, y1, y2, n=860, z=0, thickness=1.5, lines=15): # 1.5m 0.1mkannkaku
+ x = np.linspace(x1, x2, n)
+ y = np.linspace(y1, y2, n)
+ z = np.full(n, z)
+
+ dx = x2 - x1
+ dy = y2 - y1
+ length = np.hypot(dx, dy)
+ nx = -dy / length # housen x
+ ny = dx / length # housen y
+
+ # thickness = hutosa , lines = mitudo
+ offsets = np.linspace(-thickness/2, thickness/2, lines)
+
+ thick_points = []
+ for off in offsets:
+ xx = x + nx * off
+ yy = y + ny * off
+ zz = z.copy()
+ thick_points.append([xx, yy, zz])
+
+ thick_points = np.hstack(thick_points)
+ return thick_points
+ #return np.array([x, y, z])
# mainという名前の関数です。C++のmain関数とは異なり、これは処理の開始地点ではありません。
def main(args=None):