Local control algorithm test only
This commit is contained in:
parent
e301e7cf67
commit
060cf2b078
@ -1,5 +1,5 @@
|
||||
# auto drive misson center webots simulation:
|
||||
该分支修复了原LQR算法中矩阵错误问题,并对参数进行调教,增加stanley前轮转向控制算法,并适配webots仿真环境。后续实车测试优先使用该分支
|
||||
lhy本地控制测试分支,无本地控制测试需求请勿使用
|
||||
## 1, `ubuntu20.04`, `ubuntu22.04`
|
||||
## 2, 安装 `webots`
|
||||
`https://github.com/cyberbotics/webots/tags`, 版本: `ubuntu20.04: R2023a, ubuntu22.04:R2025a`
|
||||
|
||||
217
path_generate_json.py
Normal file
217
path_generate_json.py
Normal file
@ -0,0 +1,217 @@
|
||||
import json
|
||||
import math
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# ============================================================
|
||||
# 参数配置
|
||||
# ============================================================
|
||||
|
||||
# 起点:采用你提供的第一个路径点
|
||||
START_LAT = 30.80062100422322
|
||||
START_LON = 121.21541599992909
|
||||
|
||||
TURN_RADIUS = 1.5
|
||||
MISSION_SPEED = 0.32
|
||||
ARC_SPACING = 0.3
|
||||
|
||||
# 地球半径(m)
|
||||
EARTH_RADIUS = 6378137.0
|
||||
|
||||
|
||||
#
|
||||
first_line_length = 6.0
|
||||
first_arc_rad = math.pi # 180° 半圆
|
||||
|
||||
second_line_length = 8.0
|
||||
second_arc_rad = math.pi / 2 # 90° 四分之一圆
|
||||
|
||||
third_line_length = 5.0
|
||||
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 工具函数
|
||||
# ============================================================
|
||||
|
||||
def normalize_yaw(yaw):
|
||||
"""将 yaw 归一化到 [-pi, pi]。"""
|
||||
return math.atan2(math.sin(yaw), math.cos(yaw))
|
||||
|
||||
|
||||
def local_to_latlon(x, y):
|
||||
"""
|
||||
局部 ENU 坐标转经纬度。
|
||||
x: East,向东为正,单位 m
|
||||
y: North,向北为正,单位 m
|
||||
"""
|
||||
lat0 = math.radians(START_LAT)
|
||||
|
||||
lat = START_LAT + math.degrees(y / EARTH_RADIUS)
|
||||
lon = START_LON + math.degrees(
|
||||
x / (EARTH_RADIUS * math.cos(lat0))
|
||||
)
|
||||
|
||||
return lat, lon
|
||||
|
||||
|
||||
def add_point(points, x, y, yaw):
|
||||
"""添加一个 [latitude, longitude, yaw] 路径点。"""
|
||||
lat, lon = local_to_latlon(x, y)
|
||||
|
||||
points.append([
|
||||
round(lat, 12),
|
||||
round(lon, 12),
|
||||
round(normalize_yaw(yaw), 9)
|
||||
])
|
||||
|
||||
|
||||
def add_arc(points, cx, cy, radius,
|
||||
start_angle, sweep_angle, spacing):
|
||||
"""
|
||||
添加圆弧采样点,不重复添加圆弧起点。
|
||||
yaw 为圆弧的切线方向,单位 rad。
|
||||
"""
|
||||
arc_length = abs(radius * sweep_angle)
|
||||
num_segments = max(1, math.ceil(arc_length / spacing))
|
||||
|
||||
for i in range(1, num_segments + 1):
|
||||
ratio = i / num_segments
|
||||
angle = start_angle + sweep_angle * ratio
|
||||
|
||||
x = cx + radius * math.cos(angle)
|
||||
y = cy + radius * math.sin(angle)
|
||||
|
||||
# CCW 左转时,切线方向 = 圆心角 + pi/2
|
||||
yaw = angle + (
|
||||
math.pi / 2 if sweep_angle > 0
|
||||
else -math.pi / 2
|
||||
)
|
||||
|
||||
add_point(points, x, y, yaw)
|
||||
|
||||
|
||||
|
||||
coordinates = []
|
||||
|
||||
# 1. 第一段直线:长度 6 m
|
||||
# 起点 (0, 0),终点 (6, 0)
|
||||
add_point(coordinates, 0, 0, 0.0)
|
||||
add_point(coordinates, first_line_length, 0, 0.0)
|
||||
|
||||
# 第一段圆弧起始索引
|
||||
first_turn_index = len(coordinates) - 1
|
||||
|
||||
# 2. 第一段半圆:R=1,逆时针 180°
|
||||
# 圆心 (6, 1),从 -90° 转到 90°
|
||||
add_arc(
|
||||
coordinates,
|
||||
cx=first_line_length,
|
||||
cy=TURN_RADIUS,
|
||||
radius=TURN_RADIUS,
|
||||
start_angle=-math.pi / 2,
|
||||
sweep_angle= first_arc_rad,
|
||||
spacing=ARC_SPACING
|
||||
)
|
||||
|
||||
# 3. 第二段直线:长度 8 m
|
||||
# 半圆终点 (6, 2),直线终点 (-2, 2)
|
||||
second_line_end_x = first_line_length - second_line_length
|
||||
second_line_end_y = 2 * TURN_RADIUS
|
||||
|
||||
add_point(
|
||||
coordinates,
|
||||
second_line_end_x,
|
||||
second_line_end_y,
|
||||
math.pi
|
||||
)
|
||||
# 第二段圆弧起始索引
|
||||
second_turn_index = len(coordinates) - 1
|
||||
|
||||
# 4. 第二段四分之一圆:顺时针 90°
|
||||
# 与第一段圆弧方向相反
|
||||
second_arc_cx = second_line_end_x
|
||||
second_arc_cy = second_line_end_y + TURN_RADIUS
|
||||
|
||||
add_arc(
|
||||
coordinates,
|
||||
cx=second_arc_cx,
|
||||
cy=second_arc_cy,
|
||||
radius=TURN_RADIUS,
|
||||
start_angle=-math.pi / 2,
|
||||
sweep_angle=-second_arc_rad,
|
||||
spacing=ARC_SPACING
|
||||
)
|
||||
|
||||
# 5. 最后一段直线:长度 5 m
|
||||
|
||||
third_line_start_x = second_arc_cx
|
||||
third_line_start_y = second_arc_cy
|
||||
|
||||
add_point(
|
||||
coordinates,
|
||||
third_line_start_x - TURN_RADIUS,
|
||||
third_line_start_y + third_line_length,
|
||||
math.pi / 2
|
||||
)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 构造符合现有项目的 GeoJSON
|
||||
# ============================================================
|
||||
|
||||
path_json = {
|
||||
"type": "FeatureCollection",
|
||||
"properties": {
|
||||
"turnRadius": TURN_RADIUS,
|
||||
"misionSpeed": MISSION_SPEED
|
||||
},
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"turnInds": [
|
||||
first_turn_index,
|
||||
second_turn_index
|
||||
],
|
||||
"mission": []
|
||||
},
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": coordinates
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 保存文件
|
||||
# ============================================================
|
||||
|
||||
output_file = "test_path.json"
|
||||
|
||||
with open(output_file, "w", encoding="utf-8") as f:
|
||||
json.dump(
|
||||
path_json,
|
||||
f,
|
||||
ensure_ascii=False,
|
||||
indent=2
|
||||
)
|
||||
|
||||
print(f"JSON 文件已生成: {output_file}")
|
||||
print(f"路径点数量: {len(coordinates)}")
|
||||
print(f"turnInds: {path_json['features'][0]['properties']['turnInds']}")
|
||||
print(f"起点: {coordinates[0]}")
|
||||
print(f"终点: {coordinates[-1]}")
|
||||
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
ax.plot([p[0] for p in coordinates], [p[1] for p in coordinates], "-o", markersize=3, label="Path")
|
||||
|
||||
ax.set_xlabel("X (m)")
|
||||
ax.set_ylabel("Y (m)")
|
||||
ax.axis("equal")
|
||||
ax.grid(True)
|
||||
ax.legend()
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
37893
path_visualizer.log
Normal file
37893
path_visualizer.log
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,12 +10,12 @@ import matplotlib.pyplot as plt
|
||||
# 参数
|
||||
# ============================================================
|
||||
|
||||
LOG_PATTERN = "logs/mission_center_logger*.txt"
|
||||
LOG_PATTERN = "path_visualizer.log"
|
||||
|
||||
# 每 5 个完整数据取 1 个
|
||||
SAMPLE_STEP = 40
|
||||
SAMPLE_STEP = 1
|
||||
|
||||
CSV_FILE = "recalculated_tracking_error.csv"
|
||||
CSV_FILE = "tracking_error.csv"
|
||||
|
||||
|
||||
# ============================================================
|
||||
@ -74,6 +74,9 @@ current_path_pattern = re.compile(
|
||||
r"current_path_yaw:([-+0-9.eE]+)"
|
||||
)
|
||||
|
||||
yaw_command_pattern = re.compile(
|
||||
r"current_cmd_angular_z:([-+0-9.eE]+)"
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# 3. 函数:角度归一化
|
||||
@ -178,6 +181,7 @@ def calculate_errors(
|
||||
# ============================================================
|
||||
|
||||
all_data = []
|
||||
yaw_command_data = []
|
||||
|
||||
total_raw_count = 0
|
||||
|
||||
@ -202,6 +206,7 @@ for file in files:
|
||||
# --------------------------------------------------------
|
||||
|
||||
pending_pose = None
|
||||
pending_yaw = None
|
||||
|
||||
|
||||
with file.open(
|
||||
@ -222,7 +227,13 @@ for file in files:
|
||||
line
|
||||
)
|
||||
|
||||
|
||||
m_yaw = yaw_command_pattern.search(line)
|
||||
if m_yaw:
|
||||
pending_yaw = float(m_yaw.group(1))
|
||||
if file_data_count % SAMPLE_STEP == 0:
|
||||
if abs(pending_yaw) >= 2.0:
|
||||
pending_yaw = 0 # 避免添加接近零的值
|
||||
yaw_command_data.append(pending_yaw)
|
||||
if m_pose:
|
||||
|
||||
pending_pose = {
|
||||
@ -351,6 +362,7 @@ for file in files:
|
||||
pending_pose = None
|
||||
|
||||
|
||||
|
||||
print(
|
||||
f"原始完整数据:{file_data_count}"
|
||||
)
|
||||
@ -447,6 +459,12 @@ alpha_deg = np.array([
|
||||
for d in all_data
|
||||
])
|
||||
|
||||
yaw_command = yaw_command_data# 确保长度一致
|
||||
|
||||
print(
|
||||
f"yaw size: {len(yaw_command)}"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 8. 计算车辆累计行驶距离
|
||||
@ -524,13 +542,18 @@ with open(
|
||||
|
||||
"heading_error_deg",
|
||||
|
||||
"yaw_command",
|
||||
|
||||
])
|
||||
|
||||
|
||||
for i in range(
|
||||
len(all_data)
|
||||
):
|
||||
|
||||
if yaw_command[i] is None:
|
||||
print(f"yaw_command[{i}] is None")
|
||||
print(f"i = {i}")
|
||||
print(f"yaw_command length = {len(yaw_command)}")
|
||||
writer.writerow([
|
||||
|
||||
f"{distance_m[i]:.8f}",
|
||||
@ -545,6 +568,8 @@ with open(
|
||||
|
||||
f"{alpha_deg[i]:.8f}",
|
||||
|
||||
f"{yaw_command[i]:.8f}"
|
||||
|
||||
])
|
||||
|
||||
|
||||
@ -630,7 +655,43 @@ plt.grid(True)
|
||||
plt.tight_layout()
|
||||
|
||||
plt.show()
|
||||
# ============================================================
|
||||
# 14. yaw command rad
|
||||
# ============================================================
|
||||
|
||||
plt.figure(
|
||||
figsize=(12, 5)
|
||||
)
|
||||
|
||||
plt.plot(
|
||||
distance_m,
|
||||
yaw_command,
|
||||
linewidth=1.2
|
||||
)
|
||||
|
||||
plt.axhline(
|
||||
0.0,
|
||||
linestyle="--",
|
||||
linewidth=0.8
|
||||
)
|
||||
|
||||
plt.xlabel(
|
||||
"Distance (m)"
|
||||
)
|
||||
|
||||
plt.ylabel(
|
||||
"Yaw Command (rad)"
|
||||
)
|
||||
|
||||
plt.title(
|
||||
"Recalculated Yaw Command"
|
||||
)
|
||||
|
||||
plt.grid(True)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
plt.show()
|
||||
|
||||
# ============================================================
|
||||
# 15. 车辆轨迹 + 路径点
|
||||
|
||||
@ -23,6 +23,10 @@
|
||||
#include <tf2/LinearMath/Quaternion.h>
|
||||
#include <std_msgs/msg/int32.hpp>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/sinks/rotating_file_sink.h>
|
||||
|
||||
class PathVisualizer : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
@ -38,6 +42,8 @@ private:
|
||||
double w);
|
||||
double normalize_angle(double angle);
|
||||
bool downloadPathJson(const std::string &url,std::string &json_data);
|
||||
bool downloadPathJson(const std::string &file_path,std::string &json_data,
|
||||
bool is_local_file);
|
||||
bool loadPathFromJson(const std::string &json_data);
|
||||
|
||||
double gps_to_x(double lat,double lon);
|
||||
@ -101,7 +107,7 @@ private:
|
||||
// contorl paramss
|
||||
double max_speed_ = 0.8;
|
||||
double max_yaw_rate_ = 2.0;
|
||||
double lookahead_distance_ =0.6;
|
||||
double lookahead_distance_ =0.4;
|
||||
double max_interval_distance_ = 0.2;
|
||||
double origin_lat_;
|
||||
double origin_lon_;
|
||||
@ -113,10 +119,15 @@ private:
|
||||
|
||||
bool origin_set_= false;
|
||||
bool path_transed = false;
|
||||
std::string file_path_;
|
||||
std::string file_name_;
|
||||
std::string path_url_ ;
|
||||
std::string json_data_;
|
||||
std::unique_ptr<GeodeticConverter> geodetic_converter_;
|
||||
|
||||
// logger
|
||||
std::shared_ptr<spdlog::logger> logger_;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#include "sweet_webots_robot/PathVisualizer.hpp"
|
||||
|
||||
|
||||
|
||||
PathVisualizer::PathVisualizer(const std::string& file_name):
|
||||
Node("path_visualizer"),
|
||||
target_index_(0),
|
||||
@ -7,6 +9,7 @@ has_pose_(false)
|
||||
{
|
||||
file_name_ = file_name;
|
||||
path_url_ = "https://test.sweetai.cn:4433/load/locationCmdPath/" + file_name_;
|
||||
file_path_ = "/home/sweetai/auto_drive_misson_center_webots_simulation/test_path.json";
|
||||
RCLCPP_INFO(this->get_logger(),"PathVisualizer started");
|
||||
// pub
|
||||
cmd_pub_ = this->create_publisher<geometry_msgs::msg::Twist>("/twist_cmd_vel",10);
|
||||
@ -39,9 +42,35 @@ has_pose_(false)
|
||||
// urdf pose
|
||||
tf_broadcaster_ = std::make_unique<tf2_ros::TransformBroadcaster>(*this);
|
||||
// path get
|
||||
downloadPathJson(path_url_,json_data_);
|
||||
downloadPathJson(file_path_,json_data_,true);
|
||||
loadPathFromJson(json_data_);
|
||||
|
||||
// logger初始化
|
||||
auto console_sink =
|
||||
std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
|
||||
auto file_sink =
|
||||
std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
|
||||
"./path_visualizer.log",
|
||||
10 * 1024 * 1024, // 10 MB
|
||||
5 // 保留5个文件
|
||||
);
|
||||
|
||||
std::vector<spdlog::sink_ptr> sinks{
|
||||
console_sink,
|
||||
file_sink
|
||||
};
|
||||
|
||||
logger_ = std::make_shared<spdlog::logger>(
|
||||
"path_visualizer",
|
||||
sinks.begin(),
|
||||
sinks.end()
|
||||
);
|
||||
|
||||
logger_->set_level(spdlog::level::debug);
|
||||
|
||||
logger_->info("PathVisualizer started");
|
||||
|
||||
|
||||
}
|
||||
using json = nlohmann::json;
|
||||
@ -122,6 +151,60 @@ void PathVisualizer::transformPathToVehicleStart(double vehicle_initial_yaw)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
bool PathVisualizer::downloadPathJson(const std::string &file_path,
|
||||
std::string &json_data,
|
||||
bool is_local_file)
|
||||
{
|
||||
json_data.clear();
|
||||
|
||||
// 打开本地 JSON 文件
|
||||
std::ifstream file(file_path);
|
||||
if (!file.is_open())
|
||||
{
|
||||
RCLCPP_ERROR(
|
||||
this->get_logger(),
|
||||
"Failed to open JSON file: %s",
|
||||
file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 读取文件全部内容
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
|
||||
if (file.bad())
|
||||
{
|
||||
RCLCPP_ERROR(
|
||||
this->get_logger(),
|
||||
"Failed to read JSON file: %s",
|
||||
file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
json_data = buffer.str();
|
||||
|
||||
if (json_data.empty())
|
||||
{
|
||||
RCLCPP_ERROR(
|
||||
this->get_logger(),
|
||||
"JSON file is empty: %s",
|
||||
file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
RCLCPP_INFO(
|
||||
this->get_logger(),
|
||||
"Path JSON loaded successfully, size = %zu, path = %s",
|
||||
json_data.size(),
|
||||
file_path.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
bool PathVisualizer::downloadPathJson(const std::string &url,
|
||||
std::string &json_data)
|
||||
{
|
||||
@ -171,6 +254,14 @@ bool PathVisualizer::loadPathFromJson(
|
||||
{
|
||||
// 解析 JSON
|
||||
json data =json::parse(json_data);
|
||||
// 检查 properties
|
||||
if (!data.contains("properties") ||
|
||||
!data["properties"].is_object())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(),"JSON does not contain valid properties");
|
||||
return false;
|
||||
}
|
||||
max_speed_ = data["properties"]["misionSpeed"].get<double>();
|
||||
// 检查 features
|
||||
if (!data.contains("features") ||
|
||||
!data["features"].is_array() ||
|
||||
@ -601,10 +692,11 @@ void PathVisualizer::control(){
|
||||
"control has not pose.");
|
||||
return;
|
||||
}
|
||||
|
||||
double speed=max_speed_;
|
||||
publishVehicleInfoRviz();
|
||||
publishLocalPose();
|
||||
target_index_= findClosestPoint(forward_index_limit_);
|
||||
int current_index = target_index_;
|
||||
int target =findLookAheadPoint();
|
||||
|
||||
double target_x =path_x_[target];
|
||||
@ -617,15 +709,38 @@ void PathVisualizer::control(){
|
||||
double local_x =cos(yaw_)*dx+sin(yaw_)*dy;
|
||||
double local_y =-sin(yaw_)*dx+cos(yaw_)*dy;
|
||||
|
||||
double target_angle = std::atan2(dy, dx);
|
||||
double alpha = target_angle - yaw_;
|
||||
while (alpha > M_PI) alpha -= 2 * M_PI;
|
||||
while (alpha < -M_PI) alpha += 2 * M_PI;
|
||||
|
||||
/*Pure Pursuit*/
|
||||
double Ld =sqrt(local_x*local_x+local_y*local_y);
|
||||
double curvature =2.0*local_y/(Ld*Ld);
|
||||
// double Ld =sqrt(local_x*local_x+local_y*local_y);
|
||||
// double w_cmd =2.0*speed*sin(alpha)/(Ld);
|
||||
|
||||
/*Stanley*/
|
||||
double curent_path_x = path_x_[current_index];
|
||||
double curent_path_y = path_y_[current_index];
|
||||
double current_path_yaw =path_yaw_[current_index];
|
||||
|
||||
double dx_path = x_ - curent_path_x;
|
||||
double dy_path = y_ - curent_path_y;
|
||||
double error_lat = -dx_path * sin(current_path_yaw) + dy_path * cos(current_path_yaw);
|
||||
double w_cmd =1.0 * alpha - 1.5 * std::atan2(error_lat,speed);
|
||||
|
||||
geometry_msgs::msg::Twist cmd;
|
||||
|
||||
double speed=max_speed_;
|
||||
cmd.linear.x =speed;
|
||||
cmd.angular.z =speed*curvature;
|
||||
logger_->debug("current_pose_x:{}, current_pose_y:{},current_pose_yaw:{}",
|
||||
x_, y_,yaw_
|
||||
);
|
||||
logger_->debug("current_path_x:{}, current_path_y:{},current_path_yaw:{}",
|
||||
target_x,
|
||||
target_y,
|
||||
target_yaw
|
||||
);
|
||||
cmd.linear.x = speed;
|
||||
cmd.angular.z = w_cmd;
|
||||
|
||||
|
||||
if(cmd.angular.z >max_yaw_rate_)
|
||||
{
|
||||
@ -637,7 +752,8 @@ void PathVisualizer::control(){
|
||||
cmd.angular.z=-max_yaw_rate_;
|
||||
}
|
||||
|
||||
//cmd_pub_->publish(cmd);
|
||||
cmd_pub_->publish(cmd);
|
||||
logger_->debug("current_cmd_angular_z:{}",cmd.angular.z);
|
||||
|
||||
// RCLCPP_INFO_THROTTLE(
|
||||
// this->get_logger(),
|
||||
|
||||
164
test_path.json
Normal file
164
test_path.json
Normal file
@ -0,0 +1,164 @@
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"properties": {
|
||||
"turnRadius": 1.5,
|
||||
"misionSpeed": 0.32
|
||||
},
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"turnInds": [
|
||||
1,
|
||||
18
|
||||
],
|
||||
"mission": []
|
||||
},
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
30.800621004223,
|
||||
121.215415999929,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
30.800621004223,
|
||||
121.215478749384,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
30.800621263136,
|
||||
121.215481809837,
|
||||
0.196349541
|
||||
],
|
||||
[
|
||||
30.800622029926,
|
||||
121.215484752678,
|
||||
0.392699082
|
||||
],
|
||||
[
|
||||
30.800623275125,
|
||||
121.215487464816,
|
||||
0.589048623
|
||||
],
|
||||
[
|
||||
30.80062495088,
|
||||
121.215489842025,
|
||||
0.785398163
|
||||
],
|
||||
[
|
||||
30.800626992794,
|
||||
121.21549179295,
|
||||
0.981747704
|
||||
],
|
||||
[
|
||||
30.800629322397,
|
||||
121.215493242618,
|
||||
1.178097245
|
||||
],
|
||||
[
|
||||
30.800631850163,
|
||||
121.215494135319,
|
||||
1.374446786
|
||||
],
|
||||
[
|
||||
30.800634478952,
|
||||
121.215494436748,
|
||||
1.570796327
|
||||
],
|
||||
[
|
||||
30.800637107742,
|
||||
121.215494135319,
|
||||
1.767145868
|
||||
],
|
||||
[
|
||||
30.800639635508,
|
||||
121.215493242618,
|
||||
1.963495408
|
||||
],
|
||||
[
|
||||
30.800641965111,
|
||||
121.21549179295,
|
||||
2.159844949
|
||||
],
|
||||
[
|
||||
30.800644007025,
|
||||
121.215489842025,
|
||||
2.35619449
|
||||
],
|
||||
[
|
||||
30.80064568278,
|
||||
121.215487464816,
|
||||
2.552544031
|
||||
],
|
||||
[
|
||||
30.800646927979,
|
||||
121.215484752678,
|
||||
2.748893572
|
||||
],
|
||||
[
|
||||
30.800647694769,
|
||||
121.215481809837,
|
||||
2.945243113
|
||||
],
|
||||
[
|
||||
30.800647953682,
|
||||
121.215478749384,
|
||||
3.141592654
|
||||
],
|
||||
[
|
||||
30.800647953682,
|
||||
121.215395083444,
|
||||
3.141592654
|
||||
],
|
||||
[
|
||||
30.800648212595,
|
||||
121.215392022991,
|
||||
2.945243113
|
||||
],
|
||||
[
|
||||
30.800648979384,
|
||||
121.21538908015,
|
||||
2.748893572
|
||||
],
|
||||
[
|
||||
30.800650224583,
|
||||
121.215386368012,
|
||||
2.552544031
|
||||
],
|
||||
[
|
||||
30.800651900339,
|
||||
121.215383990803,
|
||||
2.35619449
|
||||
],
|
||||
[
|
||||
30.800653942253,
|
||||
121.215382039878,
|
||||
2.159844949
|
||||
],
|
||||
[
|
||||
30.800656271855,
|
||||
121.21538059021,
|
||||
1.963495408
|
||||
],
|
||||
[
|
||||
30.800658799622,
|
||||
121.215379697509,
|
||||
1.767145868
|
||||
],
|
||||
[
|
||||
30.800661428411,
|
||||
121.21537939608,
|
||||
1.570796327
|
||||
],
|
||||
[
|
||||
30.800706344175,
|
||||
121.21537939608,
|
||||
1.570796327
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user