fengtt42 commited on
Commit
0e83c90
·
verified ·
1 Parent(s): 429227d

Upload 23 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ pic/image[[:space:]]1.png filter=lfs diff=lfs merge=lfs -text
AirDroneClient.py ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from dataclasses import dataclass
3
+
4
+ from PyQt5.QtCore import *
5
+ from PyQt5.QtWidgets import *
6
+ import keyboard
7
+
8
+ import DroneController # 假设只需要无人机控制
9
+
10
+ # mobile configuration
11
+ @dataclass
12
+ class Vehicle():
13
+ acc = 1
14
+ vehicle_type = 'drone' # 设置为无人机模式
15
+
16
+ veh = Vehicle()
17
+
18
+ # form application #
19
+ app = QApplication(sys.argv)
20
+
21
+ class AirDroneClientWindow(QWidget):
22
+ button_takeoff = QPushButton()
23
+ button_land = QPushButton()
24
+ button_up = QPushButton()
25
+ button_down = QPushButton()
26
+
27
+ button_f = QPushButton()
28
+ button_b = QPushButton()
29
+ button_l = QPushButton()
30
+ button_r = QPushButton()
31
+
32
+ button_weather = QPushButton()
33
+ button_task = QPushButton()
34
+ button_algorithm = QPushButton()
35
+ button_formation = QPushButton()
36
+ button_keyboard = QPushButton()
37
+
38
+ weather_selection = QComboBox()
39
+
40
+ label_gps_alt = QLabel()
41
+ label_gps_lat = QLabel()
42
+ label_gps_lon = QLabel()
43
+ label_rot_w = QLabel()
44
+ label_rot_y = QLabel()
45
+ label_rot_z = QLabel()
46
+ label_speed = QLabel()
47
+ label_angle_speed = QLabel()
48
+
49
+ refreshinfo = QTimer()
50
+
51
+ def initUI(self):
52
+ self.resize(500, 400) # 调整为更合适的尺寸
53
+ self.setWindowTitle("U21Data-2 Client")
54
+
55
+ #self.set_background()
56
+
57
+ self.setObjectName("AirDroneClientWindow") # 设置对象名称匹配选择器
58
+
59
+
60
+ self.setStyleSheet(self.styleSheet())
61
+
62
+ # 主垂直布局
63
+ main_layout = QVBoxLayout()
64
+
65
+ # 添加Start Point部分
66
+ start_group = QGroupBox("Start Point")
67
+ start_layout = QHBoxLayout()
68
+
69
+ self.start_combo = QComboBox()
70
+ self.start_combo.setEditable(True)
71
+ self.start_combo.lineEdit().setReadOnly(True)
72
+ self.start_combo.lineEdit().setAlignment(Qt.AlignCenter)
73
+ self.start_combo.addItems(["9.8, -279, -22", "213, -429, 19","-315, -444, 12", "-335, 282, 19", "-152, 236, 66", "-430, -91, 13"])
74
+ self.start_combo.setCurrentIndex(-1)
75
+ self.start_combo.lineEdit().setPlaceholderText("StartPoint")
76
+
77
+
78
+ self.weather_selection = QComboBox()
79
+ self.weather_selection.setEditable(True)
80
+ self.weather_selection.lineEdit().setReadOnly(True)
81
+ self.weather_selection.lineEdit().setAlignment(Qt.AlignCenter)
82
+ self.weather_selection.addItems(["Clear", "Rain", "Snow", "Fog", "Overcast","Sand"])
83
+ self.weather_selection.setCurrentIndex(-1)
84
+ self.weather_selection.lineEdit().setPlaceholderText("Weather")
85
+
86
+ for widget in [self.start_combo, self.weather_selection]:
87
+ widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
88
+
89
+ start_layout.addWidget(self.start_combo)
90
+ start_layout.addWidget(self.weather_selection)
91
+ start_group.setLayout(start_layout)
92
+
93
+ # 添加功能按钮部分
94
+ func_group = QGroupBox("Functions")
95
+ func_layout = QHBoxLayout()
96
+
97
+ self.button_keyboard = QComboBox()
98
+ self.button_keyboard.setEditable(True)
99
+ self.button_keyboard.lineEdit().setReadOnly(True)
100
+ self.button_keyboard.lineEdit().setAlignment(Qt.AlignCenter)
101
+ self.button_keyboard.addItems(["Yes", "No"])
102
+ self.button_keyboard.setCurrentIndex(-1)
103
+ self.button_keyboard.lineEdit().setPlaceholderText("Task")
104
+
105
+
106
+
107
+ #Task
108
+ self.task_combo = QComboBox()
109
+ self.task_combo.setEditable(True) # 设置为可编辑
110
+ self.task_combo.lineEdit().setReadOnly(True) # 设置行编辑为只读
111
+ self.task_combo.lineEdit().setAlignment(Qt.AlignCenter) # 文字居中
112
+ self.task_combo.addItems(["Task 1", "Task 2", "Task 3"])
113
+ self.task_combo.setCurrentIndex(-1) # 不选中任何项
114
+ self.task_combo.lineEdit().setPlaceholderText("Task")
115
+
116
+
117
+ # 设置Algorithm下拉框选项
118
+ self.algorithm_combo = QComboBox()
119
+ self.algorithm_combo.setEditable(True) # 设置为可编辑
120
+ self.algorithm_combo.lineEdit().setReadOnly(True) # 设置行编辑为只读
121
+ self.algorithm_combo.lineEdit().setAlignment(Qt.AlignCenter) # 文字居中
122
+ self.algorithm_combo.addItems(["Algorithm 1", "Algorithm 2", "Algorithm 3"])
123
+ self.algorithm_combo.setCurrentIndex(-1) # 不选中任何项
124
+ self.algorithm_combo.lineEdit().setPlaceholderText("Algorithm") # 设置占位文本
125
+
126
+ # 设置Formation下拉框选项
127
+ self.formation_combo = QComboBox()
128
+ self.formation_combo.setEditable(True)
129
+ self.formation_combo.lineEdit().setReadOnly(True)
130
+ self.formation_combo.lineEdit().setAlignment(Qt.AlignCenter)
131
+ self.formation_combo.addItems(["Line Formation", "V Formation", "Square Formation", "Circle Formation"])
132
+ self.formation_combo.setCurrentIndex(-1)
133
+ self.formation_combo.lineEdit().setPlaceholderText("Formation")
134
+
135
+ # 设置控件大小策略
136
+ for widget in [self.task_combo, self.algorithm_combo,
137
+ self.formation_combo, self.button_keyboard]:
138
+ widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
139
+
140
+ # 添加控件到布局
141
+ func_layout.addWidget(self.task_combo)
142
+ func_layout.addWidget(self.algorithm_combo)
143
+ func_layout.addWidget(self.formation_combo)
144
+ func_layout.addWidget(self.button_keyboard)
145
+ func_group.setLayout(func_layout)
146
+
147
+ # 添加飞行控制部分
148
+ flight_group = QGroupBox("Flight Controls")
149
+ flight_layout = QVBoxLayout()
150
+
151
+ # 将Takeoff, Land, Up, Down四个按钮放在同一行
152
+ top_row_layout = QHBoxLayout()
153
+ self.button_takeoff = QPushButton("Takeoff")
154
+ self.button_land = QPushButton("Land")
155
+ self.button_up = QPushButton("Up")
156
+ self.button_down = QPushButton("Down")
157
+
158
+ # 设置按钮大小策略,使它们均匀分布
159
+ for button in [self.button_takeoff, self.button_land,
160
+ self.button_up, self.button_down]:
161
+ button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
162
+
163
+ top_row_layout.addWidget(self.button_takeoff)
164
+ top_row_layout.addWidget(self.button_land)
165
+ top_row_layout.addWidget(self.button_up)
166
+ top_row_layout.addWidget(self.button_down)
167
+ top_row_layout.setSpacing(5) # 设置按钮间距
168
+
169
+ # 前后左右
170
+ move_layout = QGridLayout()
171
+ self.button_f = QPushButton("Move Forward")
172
+ self.button_b = QPushButton("Move Backward")
173
+ self.button_l = QPushButton("Move Left")
174
+ self.button_r = QPushButton("Move Right")
175
+ move_layout.addWidget(self.button_f, 0, 1)
176
+ move_layout.addWidget(self.button_b, 2, 1)
177
+ move_layout.addWidget(self.button_l, 1, 0)
178
+ move_layout.addWidget(self.button_r, 1, 2)
179
+
180
+ flight_layout.addLayout(top_row_layout)
181
+ flight_layout.addLayout(move_layout)
182
+ flight_group.setLayout(flight_layout)
183
+
184
+ # 添加日志信息部分
185
+ log_group = QGroupBox("Log")
186
+ log_layout = QVBoxLayout()
187
+ self.label_gps_alt = QLabel("GPS Altitude: ")
188
+ self.label_gps_lat = QLabel("GPS Latitude: ")
189
+ self.label_gps_lon = QLabel("GPS Longitude: ")
190
+ self.label_rot_w = QLabel("Rotation w: ")
191
+ self.label_rot_y = QLabel("Rotation y: ")
192
+ self.label_rot_z = QLabel("Rotation z: ")
193
+ self.label_speed = QLabel("Linear Speed: ")
194
+ self.label_angle_speed = QLabel("Angle Speed: ")
195
+
196
+ log_layout.addWidget(self.label_gps_alt)
197
+ log_layout.addWidget(self.label_gps_lat)
198
+ log_layout.addWidget(self.label_gps_lon)
199
+ log_layout.addWidget(self.label_rot_w)
200
+ log_layout.addWidget(self.label_rot_y)
201
+ log_layout.addWidget(self.label_rot_z)
202
+ log_layout.addWidget(self.label_speed)
203
+ log_layout.addWidget(self.label_angle_speed)
204
+ log_group.setLayout(log_layout)
205
+
206
+ # 将所有组添加到主布局
207
+ main_layout.addWidget(start_group)
208
+ main_layout.addWidget(func_group)
209
+ main_layout.addWidget(flight_group)
210
+ main_layout.addWidget(log_group)
211
+
212
+ self.setLayout(main_layout)
213
+
214
+ # 连接信号槽
215
+ self.button_takeoff.clicked.connect(DroneController.dronecontrol.TakeOff)
216
+ self.button_land.clicked.connect(DroneController.dronecontrol.Landed)
217
+ self.button_up.clicked.connect(self.up)
218
+ self.button_down.clicked.connect(self.down)
219
+ self.button_f.pressed.connect(self.forward)
220
+ self.button_b.pressed.connect(self.backward)
221
+ self.button_l.pressed.connect(self.left)
222
+ self.button_r.pressed.connect(self.right)
223
+ self.button_f.released.connect(self.stop)
224
+ self.button_b.released.connect(self.stop)
225
+ self.button_l.released.connect(self.stop)
226
+ self.button_r.released.connect(self.stop)
227
+ self.button_keyboard.currentIndexChanged.connect(self.EnableKeyboard)
228
+ self.weather_selection.currentIndexChanged.connect(self.change_weather)
229
+ self.algorithm_combo.currentIndexChanged.connect(self.on_algorithm_changed)
230
+ self.formation_combo.currentIndexChanged.connect(self.on_formation_changed)
231
+ self.task_combo.currentIndexChanged.connect(self.on_task_changed)
232
+ self.start_combo.currentIndexChanged.connect(self.on_start_changed)
233
+
234
+ # 自动刷新信息
235
+ self.refreshinfo.start(1000)
236
+ self.refreshinfo.timeout.connect(self.setInfo)
237
+
238
+ self.show()
239
+
240
+ def setInfo(self):
241
+ self.label_gps_alt.setText("GPS Altitude: " + str(DroneController.dronecontrol.GetState().gps_pos_altitude))
242
+ self.label_gps_lat.setText("GPS Latitude: " + str(DroneController.dronecontrol.GetState().gps_pos_latitude))
243
+ self.label_gps_lon.setText("GPS Longitude: " + str(DroneController.dronecontrol.GetState().gps_pos_longitude))
244
+
245
+ self.label_rot_w.setText("Rotation w: " + str(DroneController.dronecontrol.GetState().ori_x))
246
+ self.label_rot_y.setText("Rotation y: " + str(DroneController.dronecontrol.GetState().ori_y))
247
+ self.label_rot_z.setText("Rotation z: " + str(DroneController.dronecontrol.GetState().ori_z))
248
+
249
+ self.label_speed.setText("Linear Speed: " + str(DroneController.dronecontrol.GetState().linear_speed))
250
+ self.label_angle_speed.setText("Angle Speed: " + str(DroneController.dronecontrol.GetState().a_v_x))
251
+
252
+ # 移动功能
253
+ def forward(self):
254
+ DroneController.dronecontrol.DroneMoveByTime(veh.acc, 0, 0)
255
+
256
+ def backward(self):
257
+ DroneController.dronecontrol.DroneMoveByTime(-veh.acc, 0, 0)
258
+
259
+ def left(self):
260
+ DroneController.dronecontrol.DroneMoveByTime(0, -veh.acc, 0)
261
+
262
+ def right(self):
263
+ DroneController.dronecontrol.DroneMoveByTime(0, veh.acc, 0)
264
+
265
+ def stop(self):
266
+ pass
267
+
268
+ def up(self):
269
+ DroneController.dronecontrol.DroneMoveByTime(0, 0, -veh.acc)
270
+
271
+ def down(self):
272
+ DroneController.dronecontrol.DroneMoveByTime(0, 0, veh.acc)
273
+
274
+ def change_weather(self, index):
275
+ if index >= 0:
276
+ weather = self.weather_selection.currentText()
277
+ if weather == "Snow":
278
+ DroneController.dronecontrol.SnowTeleport()
279
+ elif weather == "Rain":
280
+ DroneController.dronecontrol.RainTeleport()
281
+ elif weather == "Fog":
282
+ DroneController.dronecontrol.FogTeleport()
283
+ elif weather == "Overcast":
284
+ DroneController.dronecontrol.OvercastTeleport()
285
+ elif weather == "Clear":
286
+ DroneController.dronecontrol.ClearTeleport()
287
+ elif weather == "Sand":
288
+ DroneController.dronecontrol.SandTeleport()
289
+
290
+ def on_algorithm_changed(self, index):
291
+ """Algorithm下拉框选择变化时的处理"""
292
+ if index >= 0:
293
+ selected_algorithm = self.algorithm_combo.currentText()
294
+ print(f"Selected Algorithm: {selected_algorithm}")
295
+
296
+
297
+ def on_formation_changed(self, index):
298
+ """Formation下拉框选择变化时的处理"""
299
+ if index >= 0:
300
+ selected_formation = self.formation_combo.currentText()
301
+ print(f"Selected Formation: {selected_formation}")
302
+
303
+
304
+ def on_task_changed(self, index):
305
+ """Task下拉框选择变化时的处理"""
306
+ if index >= 0:
307
+ selected_task = self.task_combo.currentText()
308
+ print(f"Selected Task: {selected_task}")
309
+
310
+
311
+ def on_start_changed(self, index):
312
+ if index >= 0:
313
+ pos = self.start_combo.currentText()
314
+ if pos == "9.8, -279, -22":
315
+ DroneController.dronecontrol.SnowTeleport()
316
+ elif pos == "213, -429, 19":
317
+ DroneController.dronecontrol.OvercastTeleport()
318
+ elif pos == "-315, -444, 12":
319
+ DroneController.dronecontrol.RainTeleport()
320
+ elif pos == "-335, 282, 19":
321
+ DroneController.dronecontrol.SandTeleport()
322
+ elif pos == "-152, 236, 66":
323
+ DroneController.dronecontrol.ClearTeleport()
324
+ elif pos == "-430, -91, 13":
325
+ DroneController.dronecontrol.FogTeleport()
326
+ else:
327
+ print(self.start_combo.currentIndex())
328
+
329
+ def EnableKeyboard(self):
330
+ keyboard.hook(DroneController.dronecontrol.KeyboardControl)
331
+ keyboard.wait()
332
+
333
+ window = AirDroneClientWindow()
334
+ window.initUI()
335
+ window.setInfo()
336
+ sys.exit(app.exec())
AirDroneClient.pyproj ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
2
+ <PropertyGroup>
3
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4
+ <SchemaVersion>2.0</SchemaVersion>
5
+ <ProjectGuid>4e90b183-6758-4994-8334-cfa6a07c9508</ProjectGuid>
6
+ <ProjectHome>.</ProjectHome>
7
+ <StartupFile>DroneController.py</StartupFile>
8
+ <SearchPath>
9
+ </SearchPath>
10
+ <WorkingDirectory>.</WorkingDirectory>
11
+ <OutputPath>.</OutputPath>
12
+ <Name>AirDroneClient</Name>
13
+ <RootNamespace>AirDroneClient</RootNamespace>
14
+ <InterpreterId>MSBuild|env|$(MSBuildProjectFullPath)</InterpreterId>
15
+ </PropertyGroup>
16
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
17
+ <DebugSymbols>true</DebugSymbols>
18
+ <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
19
+ </PropertyGroup>
20
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
21
+ <DebugSymbols>true</DebugSymbols>
22
+ <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
23
+ </PropertyGroup>
24
+ <ItemGroup>
25
+ <Compile Include="AirDroneClient.py" />
26
+ <Compile Include="CarController.py" />
27
+ <Compile Include="DroneController.py" />
28
+ <Compile Include="FileConverter.py" />
29
+ <Compile Include="SeqOutputer.py" />
30
+ </ItemGroup>
31
+ <ItemGroup>
32
+ <Interpreter Include="env\">
33
+ <Id>env</Id>
34
+ <Version>3.11</Version>
35
+ <Description>env (Python 3.11 (64-bit))</Description>
36
+ <InterpreterPath>Scripts\python.exe</InterpreterPath>
37
+ <WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
38
+ <PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
39
+ <Architecture>X64</Architecture>
40
+ </Interpreter>
41
+ </ItemGroup>
42
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
43
+ <!-- Uncomment the CoreCompile target to enable the Build command in
44
+ Visual Studio and specify your pre- and post-build commands in
45
+ the BeforeBuild and AfterBuild targets below. -->
46
+ <!--<Target Name="CoreCompile" />-->
47
+ <Target Name="BeforeBuild">
48
+ </Target>
49
+ <Target Name="AfterBuild">
50
+ </Target>
51
+ </Project>
AirDroneClient.pyproj.user ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <PropertyGroup>
4
+ <ProjectView>ProjectFiles</ProjectView>
5
+ </PropertyGroup>
6
+ </Project>
AirDroneClient.sln ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 
2
+ Microsoft Visual Studio Solution File, Format Version 12.00
3
+ # Visual Studio Version 17
4
+ VisualStudioVersion = 17.7.34202.233
5
+ MinimumVisualStudioVersion = 10.0.40219.1
6
+ Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "AirDroneClient", "AirDroneClient.pyproj", "{4E90B183-6758-4994-8334-CFA6A07C9508}"
7
+ EndProject
8
+ Global
9
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
10
+ Debug|Any CPU = Debug|Any CPU
11
+ Release|Any CPU = Release|Any CPU
12
+ EndGlobalSection
13
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
14
+ {4E90B183-6758-4994-8334-CFA6A07C9508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15
+ {4E90B183-6758-4994-8334-CFA6A07C9508}.Release|Any CPU.ActiveCfg = Release|Any CPU
16
+ EndGlobalSection
17
+ GlobalSection(SolutionProperties) = preSolution
18
+ HideSolutionNode = FALSE
19
+ EndGlobalSection
20
+ GlobalSection(ExtensibilityGlobals) = postSolution
21
+ SolutionGuid = {65CCDCFD-026C-4C90-A618-CF4FBBFB37AD}
22
+ EndGlobalSection
23
+ EndGlobal
CarController.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ import airsim
3
+ import time
4
+
5
+
6
+ @dataclass
7
+ class Carinfo():
8
+ speed_a = 0
9
+ speed_l = 0
10
+
11
+ pos_x = 0
12
+ pos_y = 0
13
+ pos_z = 0
14
+
15
+ rot_x = 0
16
+ rot_y = 0
17
+ rot_z = 0
18
+
19
+
20
+ class CarController():
21
+ def __init__(self):
22
+ # connect to the AirSim simulator
23
+ self.client = airsim.CarClient()
24
+ self.client.confirmConnection()
25
+ self.client.enableApiControl(True)
26
+ self.car_controls = airsim.CarControls()
27
+
28
+ def GoForward(self, v):
29
+ self.car_controls.throttle = v
30
+ self.car_controls.steering = 0
31
+ self.client.setCarControls(self.car_controls)
32
+
33
+ def GoBackwardStart(self, v):
34
+ self.car_controls.throttle = v
35
+ self.car_controls.is_manual_gear = True
36
+ self.car_controls.manual_gear = -1
37
+ self.car_controls.steering = 0
38
+ self.client.setCarControls(self.car_controls)
39
+
40
+ def GoBackwardEnd(self):
41
+ self.car_controls.throttle = 0
42
+ self.car_controls.steering = 0
43
+ self.car_controls.is_manual_gear = False
44
+ self.car_controls.manual_gear = 0
45
+ self.client.setCarControls(self.car_controls)
46
+
47
+ def Steer(self, v, steering):
48
+ self.car_controls.throttle = v
49
+ self.car_controls.steering = steering
50
+ self.client.setCarControls(self.car_controls)
51
+
52
+ def Stop(self):
53
+ self.car_controls.throttle = 0
54
+ self.car_controls.steering = 0
55
+ self.client.setCarControls(self.car_controls)
56
+
57
+ def GetCarPose(self):
58
+ position = self.client.simGetVehiclePose().position
59
+ rotation = self.client.simGetVehiclePose().orientation
60
+
61
+ speed_a = self.client.getCarState().speed
62
+ speed_l = self.client.getCarState().speed
63
+
64
+ carinfo = Carinfo()
65
+
66
+ carinfo.rot_x = rotation.x_val
67
+ carinfo.rot_y = rotation.y_val
68
+ carinfo.rot_z = rotation.z_val
69
+
70
+ carinfo.pos_x = position.x_val
71
+ carinfo.pos_y = position.y_val
72
+ carinfo.pos_z = position.z_val
73
+
74
+ carinfo.speed_a = speed_a
75
+ carinfo.speed_l = speed_l
76
+
77
+ return carinfo
78
+
79
+
80
+ carcontrol = CarController()
DroneController.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from multiprocessing import Value
2
+ import airsim
3
+ import time
4
+ import math
5
+ import keyboard
6
+ import json
7
+
8
+ class DroneInfo():
9
+ gps_pos_altitude = 0
10
+ gps_pos_latitude = 0
11
+ gps_pos_longitude = 0
12
+
13
+ ori_w = 0
14
+ ori_x = 0
15
+ ori_y = 0
16
+ ori_z = 0
17
+
18
+ # linear_velocity
19
+ l_v_x = 0
20
+ l_v_y = 0
21
+ l_v_z = 0
22
+
23
+ # angular_velocity
24
+ a_v_x = 0
25
+ a_v_y = 0
26
+ a_v_z = 0
27
+
28
+ roll = 0
29
+ yaw = 0
30
+ pitch = 0
31
+
32
+ linear_speed = 0
33
+
34
+
35
+ class Drone():
36
+ def __init__(self):
37
+ # connect to the AirSim simulator
38
+ self.client = airsim.MultirotorClient()
39
+ self.client.confirmConnection()
40
+ self.client.enableApiControl(True)
41
+ self.client.armDisarm(True)
42
+
43
+ def TakeOff(self):
44
+ if airsim.LandedState.Landed == 0:
45
+ self.client.takeoffAsync().join()
46
+ print("take off")
47
+ else:
48
+ self.client.hoverAsync().join()
49
+ print("already flying")
50
+
51
+ def Landed(self):
52
+ if airsim.LandedState.Landed == 1:
53
+ print("already landed")
54
+ else:
55
+ self.client.landAsync().join()
56
+ print("now landing")
57
+
58
+ def DroneMoveByTime(self, vx, vy, vz, duration=3):
59
+ print("now speed", vx, vy, vz)
60
+ self.client.moveByVelocityAsync(vx, vy, vz, duration)
61
+ time.sleep(duration)
62
+ print("End Moving")
63
+
64
+ def GetState(self):
65
+ # State = self.client.getMultirotorState()
66
+ State = DroneInfo()
67
+ gps_pos = self.client.getMultirotorState().gps_location
68
+ State.gps_pos_altitude = gps_pos.altitude
69
+ State.gps_pos_longitude = gps_pos.longitude
70
+ State.gps_pos_latitude = gps_pos.latitude
71
+
72
+ orientation = self.client.getMultirotorState().kinematics_estimated.orientation
73
+ State.ori_w = orientation.w_val
74
+ State.ori_x = orientation.x_val
75
+ State.ori_y = orientation.y_val
76
+ State.ori_z = orientation.z_val
77
+
78
+ a_velocity = self.client.getMultirotorState().kinematics_estimated.angular_velocity
79
+ State.a_v_x = a_velocity.x_val
80
+ State.a_v_y = a_velocity.y_val
81
+ State.a_v_z = a_velocity.z_val
82
+
83
+ l_velocity = self.client.getMultirotorState().kinematics_estimated.linear_velocity
84
+ State.l_v_x = l_velocity.x_val
85
+ State.l_v_y = l_velocity.y_val
86
+ State.l_v_z = l_velocity.z_val
87
+
88
+ State.roll = self.client.getMultirotorState().rc_data.roll
89
+ State.yaw = self.client.getMultirotorState().rc_data.yaw
90
+ State.pitch = self.client.getMultirotorState().rc_data.pitch
91
+
92
+ State.linear_speed = math.sqrt(pow(State.l_v_x,2)+pow(State.l_v_y,2)+pow(State.l_v_z,2))
93
+
94
+ return State
95
+
96
+ def Hover(self):
97
+ landed = self.client.getMultirotorState()
98
+
99
+ def MoveToPosition(self,x,y,z,duration=3):
100
+ self.client.moveToPositionAsync(x, y, z, duration).join()
101
+
102
+ def Reset(self):
103
+ self.client.reset()
104
+
105
+ def SnowTeleport(self):
106
+ position = airsim.Vector3r(9.87236677 , -279.41862836, -22.81082173) # snow XYZ 0.01 -Z
107
+ heading = airsim.utils.to_quaternion(0, 0, 0)
108
+ pose = airsim.Pose(position, heading)
109
+ self.client.simSetVehiclePose(pose, True)
110
+
111
+ def OvercastTeleport(self):
112
+ position = airsim.Vector3r(213.24547002 , -429.66393833, 19.260)
113
+ heading = airsim.utils.to_quaternion(0, 0, 0)
114
+ pose = airsim.Pose(position, heading)
115
+ self.client.simSetVehiclePose(pose, True)
116
+
117
+ def RainTeleport(self):
118
+ position = airsim.Vector3r(-315.200 , -444.920, 12.520)
119
+ heading = airsim.utils.to_quaternion(0, 0, 0)
120
+ pose = airsim.Pose(position, heading)
121
+ self.client.simSetVehiclePose(pose, True)
122
+
123
+ def SandTeleport(self):
124
+ position = airsim.Vector3r(-335.50020795 , 282.16650494, 19.50313758)
125
+ heading = airsim.utils.to_quaternion(0, 0, 0)
126
+ pose = airsim.Pose(position, heading)
127
+ self.client.simSetVehiclePose(pose, True)
128
+
129
+ def ClearTeleport(self):
130
+ position = airsim.Vector3r(-152.28332052 , 236.00371837, 66.40114393)
131
+ heading = airsim.utils.to_quaternion(0, 0, 0)
132
+ pose = airsim.Pose(position, heading)
133
+ self.client.simSetVehiclePose(pose, True)
134
+
135
+ def FogTeleport(self):
136
+ position = airsim.Vector3r(-430.39138328 , -91.7420777, 13.86624983)
137
+ heading = airsim.utils.to_quaternion(0, 0, 0)
138
+ pose = airsim.Pose(position, heading)
139
+ self.client.simSetVehiclePose(pose, True)
140
+
141
+
142
+ def CloseAPI(self):
143
+ self.client.enableApiControl(False)
144
+
145
+ def KeyboardControl(self,x):
146
+ w = keyboard.KeyboardEvent('down', 150, 'w') # forward
147
+ s = keyboard.KeyboardEvent('down', 150, 's') # back
148
+ a = keyboard.KeyboardEvent('down', 150, 'a') # left
149
+ d = keyboard.KeyboardEvent('down', 150, 'd') # right
150
+ up = keyboard.KeyboardEvent('down', 150, 'up') # up
151
+ down = keyboard.KeyboardEvent('down', 150, 'down') # down
152
+ left = keyboard.KeyboardEvent('down', 150, 'left') # left
153
+ right = keyboard.KeyboardEvent('down', 150, 'right') # right
154
+ k = keyboard.KeyboardEvent('down', 28, 'k') # get control
155
+ l = keyboard.KeyboardEvent('down', 28, 'l') # release control
156
+ if x.event_type == 'down' and x.name == w.name:
157
+ self.client.moveByVelocityBodyFrameAsync(3, 0, 0, 0.5)
158
+ print("forward")
159
+ elif x.event_type == 'down' and x.name == s.name:
160
+ self.client.moveByVelocityBodyFrameAsync(-3, 0, 0, 0.5)
161
+ print("back")
162
+ elif x.event_type == 'down' and x.name == a.name:
163
+ self.client.moveByVelocityBodyFrameAsync(0, -2, 0, 0.5)
164
+ print("left")
165
+ elif x.event_type == 'down' and x.name == d.name:
166
+ self.client.moveByVelocityBodyFrameAsync(0, 2, 0, 0.5)
167
+ print("right")
168
+ elif x.event_type == 'down' and x.name == up.name:
169
+ self.client.moveByVelocityBodyFrameAsync(0, 0, -0.5, 0.5)
170
+ print("up")
171
+ elif x.event_type == 'down' and x.name == down.name:
172
+ self.client.moveByVelocityBodyFrameAsync(0, 0, 0.5, 0.5)
173
+ print("down")
174
+ elif x.event_type == 'down' and x.name == left.name:
175
+ self.client.rotateByYawRateAsync(-20, 0.5)
176
+ print("turn left")
177
+ elif x.event_type == 'down' and x.name == right.name:
178
+ self.client.rotateByYawRateAsync(20, 0.5)
179
+ print("turn right")
180
+ elif x.event_type == 'down' and x.name == k.name:
181
+ # get control
182
+ self.client.enableApiControl(True)
183
+ print("get control")
184
+ # unlock
185
+ self.client.armDisarm(True)
186
+ print("unlock")
187
+ # Async methods returns Future. Call join() to wait for task to complete.
188
+ self.client.takeoffAsync().join()
189
+ print("takeoff")
190
+ elif x.event_type == 'down' and x.name == l.name:
191
+ keyboard.wait("l")
192
+ keyboard.unhook_all()
193
+ else:
194
+ self.client.moveByVelocityBodyFrameAsync(0, 0, 0, 0.5).join()
195
+ self.client.hoverAsync().join()
196
+ print("hovering")
197
+
198
+ def ReplayRoute(self):
199
+ with open('Route.json', 'r', encoding='utf-8') as file:
200
+ data = json.load(file)
201
+
202
+ location_x = data.get("Location.X", [])
203
+ location_y = data.get("Location.Y", [])
204
+ location_z = data.get("Location.Z", [])
205
+
206
+ # lerp
207
+ all_frames = set()
208
+ for arr in [location_x, location_y, location_z]:
209
+ all_frames.update([item["frame"] for item in arr])
210
+ all_frames = sorted(all_frames)
211
+
212
+ interp_x = interpolate_axis(location_x, all_frames)
213
+ interp_y = interpolate_axis(location_y, all_frames)
214
+ interp_z = interpolate_axis(location_z, all_frames)
215
+
216
+ for i in range(len(all_frames) - 1):
217
+ position = airsim.Vector3r(interp_x[i]/100, interp_y[i]/100, -interp_z[i]/100)
218
+ position_next = airsim.Vector3r(interp_x[i+1]/100, interp_y[i+1]/100, -interp_z[i+1]/100)
219
+ time = (all_frames[i+1] - all_frames[i]) / 30 # 30 FPS
220
+ speed = Calc_distance(position, position_next) / time if time > 0 else 1
221
+ print(position)
222
+ self.client.moveToPositionAsync(position.x_val, position.y_val, position.z_val, speed).join()
223
+
224
+
225
+ def interpolate_axis(axis_list, target_frames):
226
+ if not axis_list:
227
+ return [0.0] * len(target_frames)
228
+ axis_list = sorted(axis_list, key=lambda x: x["frame"])
229
+ frames = [item["frame"] for item in axis_list]
230
+ values = [item["value"] for item in axis_list]
231
+ result = []
232
+ for f in target_frames:
233
+ if f <= frames[0]:
234
+ result.append(values[0])
235
+ elif f >= frames[-1]:
236
+ result.append(values[-1])
237
+ else:
238
+ for i in range(len(frames) - 1):
239
+ if frames[i] <= f <= frames[i+1]:
240
+ t = (f - frames[i]) / (frames[i+1] - frames[i])
241
+ v = values[i] + t * (values[i+1] - values[i])
242
+ result.append(v)
243
+ break
244
+ return result
245
+
246
+
247
+ def Calc_distance(pos1, pos2):
248
+ return math.sqrt((pos1.x_val - pos2.x_val) ** 2 + (pos1.y_val - pos2.y_val) ** 2 + (pos1.z_val - pos2.z_val) ** 2)
249
+
250
+ if __name__ == '__main__':
251
+ dronne = Drone()
252
+ # add sleep to make sure excute at same time
253
+ dronne.ReplayRoute()
254
+
255
+ dronecontrol = Drone()
256
+
FileConverter.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import os
3
+
4
+ def batch_convert_images(input_dir, output_dir):
5
+ # Create the output directory if it doesn't exist
6
+ if not os.path.exists(output_dir):
7
+ os.makedirs(output_dir)
8
+
9
+ # Process each file in the input directory
10
+ for filename in os.listdir(input_dir):
11
+ # Check for .ppm or .pgm files (case-insensitive)
12
+ if filename.lower().endswith('.ppm') or filename.lower().endswith('.pgm'):
13
+ img_path = os.path.join(input_dir, filename)
14
+ img = Image.open(img_path)
15
+
16
+ # Create the new filename with .png extension
17
+ new_filename = os.path.splitext(filename)[0] + '.png'
18
+ save_path = os.path.join(output_dir, new_filename)
19
+
20
+ # Save the image as .png
21
+ img.save(save_path)
22
+ print(f"Converted: {filename} -> {new_filename}")
23
+
24
+ # Example usage
25
+ input_dir = 'E:/THU_Projects/DataSets/2025-03-22-20-29-50/images'
26
+ output_dir = 'E:/THU_Projects/DataSets/2025-03-22-20-29-50/PNG'
27
+ batch_convert_images(input_dir, output_dir)
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
SeqOutputer.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Import built-in modules
2
+ from collections import defaultdict
3
+ import json
4
+ import os
5
+
6
+ # Import local modules
7
+ import unreal
8
+
9
+ DIR = os.path.dirname(os.path.abspath(__file__))
10
+
11
+ def unreal_progress(tasks, label="����", total=None):
12
+ total = total if total else len(tasks)
13
+ with unreal.ScopedSlowTask(total, label) as task:
14
+ task.make_dialog(True)
15
+ for i, item in enumerate(tasks):
16
+ if task.should_cancel():
17
+ break
18
+ task.enter_progress_frame(1, "%s %s/%s" % (label, i, total))
19
+ yield item
20
+
21
+
22
+ def main():
23
+ # NOTE: ��ȡ sequence
24
+ sequence = unreal.load_asset('/Game/RecSeq')
25
+ # NOTE: �ռ� sequence �������е� binding
26
+ binding_dict = defaultdict(list)
27
+ for binding in sequence.get_bindings():
28
+ binding_dict[binding.get_name()].append(binding)
29
+
30
+ # NOTE: ��������Ϊ Face �� binding
31
+ for binding in unreal_progress(binding_dict.get("BP_FlyingPawn", []), "Transform"):
32
+ # NOTE: ��ȡ�ؼ�֡ channel ����
33
+ keys_dict = {}
34
+ for track in binding.get_tracks():
35
+ for section in track.get_sections():
36
+ for channel in unreal_progress(section.get_channels(), "KeyFrame"):
37
+ if not channel.get_num_keys():
38
+ continue
39
+ keys = []
40
+ for key in channel.get_keys():
41
+ frame_time = key.get_time()
42
+ frame = frame_time.frame_number.value + frame_time.sub_frame
43
+ keys.append({"frame": frame, "value": key.get_value()})
44
+
45
+ keys_dict[channel.get_name()] = keys
46
+
47
+ # NOTE: ���� json
48
+ name = binding.get_parent().get_name()
49
+ export_path = os.path.join(DIR, "{0}.json".format(name))
50
+ with open(export_path, "w") as wf:
51
+ json.dump(keys_dict, wf, indent=4)
52
+
53
+ main()
airsim/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from .client import *
2
+ from .utils import *
3
+ from .types import *
4
+
5
+ __version__ = "1.7.0"
airsim/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (232 Bytes). View file
 
airsim/__pycache__/client.cpython-39.pyc ADDED
Binary file (78.2 kB). View file
 
airsim/__pycache__/types.cpython-39.pyc ADDED
Binary file (24.1 kB). View file
 
airsim/__pycache__/utils.cpython-39.pyc ADDED
Binary file (5.21 kB). View file
 
airsim/client.py ADDED
@@ -0,0 +1,1621 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import print_function
2
+
3
+ from .utils import *
4
+ from .types import *
5
+
6
+ import msgpackrpc #install as admin: pip install msgpack-rpc-python
7
+ import numpy as np #pip install numpy
8
+ import msgpack
9
+ import time
10
+ import math
11
+ import logging
12
+
13
+ class VehicleClient:
14
+ def __init__(self, ip = "", port = 41451, timeout_value = 3600):
15
+ if (ip == ""):
16
+ ip = "127.0.0.1"
17
+ self.client = msgpackrpc.Client(msgpackrpc.Address(ip, port), timeout = timeout_value, pack_encoding = 'utf-8', unpack_encoding = 'utf-8')
18
+
19
+ #----------------------------------- Common vehicle APIs ---------------------------------------------
20
+ def reset(self):
21
+ """
22
+ Reset the vehicle to its original starting state
23
+
24
+ Note that you must call `enableApiControl` and `armDisarm` again after the call to reset
25
+ """
26
+ self.client.call('reset')
27
+
28
+ def ping(self):
29
+ """
30
+ If connection is established then this call will return true otherwise it will be blocked until timeout
31
+
32
+ Returns:
33
+ bool:
34
+ """
35
+ return self.client.call('ping')
36
+
37
+ def getClientVersion(self):
38
+ return 1 # sync with C++ client
39
+
40
+ def getServerVersion(self):
41
+ return self.client.call('getServerVersion')
42
+
43
+ def getMinRequiredServerVersion(self):
44
+ return 1 # sync with C++ client
45
+
46
+ def getMinRequiredClientVersion(self):
47
+ return self.client.call('getMinRequiredClientVersion')
48
+
49
+ #basic flight control
50
+ def enableApiControl(self, is_enabled, vehicle_name = ''):
51
+ """
52
+ Enables or disables API control for vehicle corresponding to vehicle_name
53
+
54
+ Args:
55
+ is_enabled (bool): True to enable, False to disable API control
56
+ vehicle_name (str, optional): Name of the vehicle to send this command to
57
+ """
58
+ self.client.call('enableApiControl', is_enabled, vehicle_name)
59
+
60
+ def isApiControlEnabled(self, vehicle_name = ''):
61
+ """
62
+ Returns true if API control is established.
63
+
64
+ If false (which is default) then API calls would be ignored. After a successful call to `enableApiControl`, `isApiControlEnabled` should return true.
65
+
66
+ Args:
67
+ vehicle_name (str, optional): Name of the vehicle
68
+
69
+ Returns:
70
+ bool: If API control is enabled
71
+ """
72
+ return self.client.call('isApiControlEnabled', vehicle_name)
73
+
74
+ def armDisarm(self, arm, vehicle_name = ''):
75
+ """
76
+ Arms or disarms vehicle
77
+
78
+ Args:
79
+ arm (bool): True to arm, False to disarm the vehicle
80
+ vehicle_name (str, optional): Name of the vehicle to send this command to
81
+
82
+ Returns:
83
+ bool: Success
84
+ """
85
+ return self.client.call('armDisarm', arm, vehicle_name)
86
+
87
+ def simPause(self, is_paused):
88
+ """
89
+ Pauses simulation
90
+
91
+ Args:
92
+ is_paused (bool): True to pause the simulation, False to release
93
+ """
94
+ self.client.call('simPause', is_paused)
95
+
96
+ def simIsPause(self):
97
+ """
98
+ Returns true if the simulation is paused
99
+
100
+ Returns:
101
+ bool: If the simulation is paused
102
+ """
103
+ return self.client.call("simIsPaused")
104
+
105
+ def simContinueForTime(self, seconds):
106
+ """
107
+ Continue the simulation for the specified number of seconds
108
+
109
+ Args:
110
+ seconds (float): Time to run the simulation for
111
+ """
112
+ self.client.call('simContinueForTime', seconds)
113
+
114
+ def simContinueForFrames(self, frames):
115
+ """
116
+ Continue (or resume if paused) the simulation for the specified number of frames, after which the simulation will be paused.
117
+
118
+ Args:
119
+ frames (int): Frames to run the simulation for
120
+ """
121
+ self.client.call('simContinueForFrames', frames)
122
+
123
+ def getHomeGeoPoint(self, vehicle_name = ''):
124
+ """
125
+ Get the Home location of the vehicle
126
+
127
+ Args:
128
+ vehicle_name (str, optional): Name of vehicle to get home location of
129
+
130
+ Returns:
131
+ GeoPoint: Home location of the vehicle
132
+ """
133
+ return GeoPoint.from_msgpack(self.client.call('getHomeGeoPoint', vehicle_name))
134
+
135
+ def confirmConnection(self):
136
+ """
137
+ Checks state of connection every 1 sec and reports it in Console so user can see the progress for connection.
138
+ """
139
+ if self.ping():
140
+ print("Connected!")
141
+ else:
142
+ print("Ping returned false!")
143
+ server_ver = self.getServerVersion()
144
+ client_ver = self.getClientVersion()
145
+ server_min_ver = self.getMinRequiredServerVersion()
146
+ client_min_ver = self.getMinRequiredClientVersion()
147
+
148
+ ver_info = "Client Ver:" + str(client_ver) + " (Min Req: " + str(client_min_ver) + \
149
+ "), Server Ver:" + str(server_ver) + " (Min Req: " + str(server_min_ver) + ")"
150
+
151
+ if server_ver < server_min_ver:
152
+ print(ver_info, file=sys.stderr)
153
+ print("AirSim server is of older version and not supported by this client. Please upgrade!")
154
+ elif client_ver < client_min_ver:
155
+ print(ver_info, file=sys.stderr)
156
+ print("AirSim client is of older version and not supported by this server. Please upgrade!")
157
+ else:
158
+ print(ver_info)
159
+ print('')
160
+
161
+ def simSetLightIntensity(self, light_name, intensity):
162
+ """
163
+ Change intensity of named light
164
+
165
+ Args:
166
+ light_name (str): Name of light to change
167
+ intensity (float): New intensity value
168
+
169
+ Returns:
170
+ bool: True if successful, otherwise False
171
+ """
172
+ return self.client.call("simSetLightIntensity", light_name, intensity)
173
+
174
+ def simSwapTextures(self, tags, tex_id = 0, component_id = 0, material_id = 0):
175
+ """
176
+ Runtime Swap Texture API
177
+
178
+ See https://microsoft.github.io/AirSim/retexturing/ for details
179
+
180
+ Args:
181
+ tags (str): string of "," or ", " delimited tags to identify on which actors to perform the swap
182
+ tex_id (int, optional): indexes the array of textures assigned to each actor undergoing a swap
183
+
184
+ If out-of-bounds for some object's texture set, it will be taken modulo the number of textures that were available
185
+ component_id (int, optional):
186
+ material_id (int, optional):
187
+
188
+ Returns:
189
+ list[str]: List of objects which matched the provided tags and had the texture swap perfomed
190
+ """
191
+ return self.client.call("simSwapTextures", tags, tex_id, component_id, material_id)
192
+
193
+ def simSetObjectMaterial(self, object_name, material_name, component_id = 0):
194
+ """
195
+ Runtime Swap Texture API
196
+ See https://microsoft.github.io/AirSim/retexturing/ for details
197
+ Args:
198
+ object_name (str): name of object to set material for
199
+ material_name (str): name of material to set for object
200
+ component_id (int, optional) : index of material elements
201
+
202
+ Returns:
203
+ bool: True if material was set
204
+ """
205
+ return self.client.call("simSetObjectMaterial", object_name, material_name, component_id)
206
+
207
+ def simSetObjectMaterialFromTexture(self, object_name, texture_path, component_id = 0):
208
+ """
209
+ Runtime Swap Texture API
210
+ See https://microsoft.github.io/AirSim/retexturing/ for details
211
+ Args:
212
+ object_name (str): name of object to set material for
213
+ texture_path (str): path to texture to set for object
214
+ component_id (int, optional) : index of material elements
215
+
216
+ Returns:
217
+ bool: True if material was set
218
+ """
219
+ return self.client.call("simSetObjectMaterialFromTexture", object_name, texture_path, component_id)
220
+
221
+
222
+ # time-of-day control
223
+ #time - of - day control
224
+ def simSetTimeOfDay(self, is_enabled, start_datetime = "", is_start_datetime_dst = False, celestial_clock_speed = 1, update_interval_secs = 60, move_sun = True):
225
+ """
226
+ Control the position of Sun in the environment
227
+
228
+ Sun's position is computed using the coordinates specified in `OriginGeopoint` in settings for the date-time specified in the argument,
229
+ else if the string is empty, current date & time is used
230
+
231
+ Args:
232
+ is_enabled (bool): True to enable time-of-day effect, False to reset the position to original
233
+ start_datetime (str, optional): Date & Time in %Y-%m-%d %H:%M:%S format, e.g. `2018-02-12 15:20:00`
234
+ is_start_datetime_dst (bool, optional): True to adjust for Daylight Savings Time
235
+ celestial_clock_speed (float, optional): Run celestial clock faster or slower than simulation clock
236
+ E.g. Value 100 means for every 1 second of simulation clock, Sun's position is advanced by 100 seconds
237
+ so Sun will move in sky much faster
238
+ update_interval_secs (float, optional): Interval to update the Sun's position
239
+ move_sun (bool, optional): Whether or not to move the Sun
240
+ """
241
+ self.client.call('simSetTimeOfDay', is_enabled, start_datetime, is_start_datetime_dst, celestial_clock_speed, update_interval_secs, move_sun)
242
+
243
+ #weather
244
+ def simEnableWeather(self, enable):
245
+ """
246
+ Enable Weather effects. Needs to be called before using `simSetWeatherParameter` API
247
+
248
+ Args:
249
+ enable (bool): True to enable, False to disable
250
+ """
251
+ self.client.call('simEnableWeather', enable)
252
+
253
+ def simSetWeatherParameter(self, param, val):
254
+ """
255
+ Enable various weather effects
256
+
257
+ Args:
258
+ param (WeatherParameter): Weather effect to be enabled
259
+ val (float): Intensity of the effect, Range 0-1
260
+ """
261
+ self.client.call('simSetWeatherParameter', param, val)
262
+
263
+ #camera control
264
+ #simGetImage returns compressed png in array of bytes
265
+ #image_type uses one of the ImageType members
266
+ def simGetImage(self, camera_name, image_type, vehicle_name = '', external = False):
267
+ """
268
+ Get a single image
269
+
270
+ Returns bytes of png format image which can be dumped into abinary file to create .png image
271
+ `string_to_uint8_array()` can be used to convert into Numpy unit8 array
272
+ See https://microsoft.github.io/AirSim/image_apis/ for details
273
+
274
+ Args:
275
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
276
+ image_type (ImageType): Type of image required
277
+ vehicle_name (str, optional): Name of the vehicle with the camera
278
+ external (bool, optional): Whether the camera is an External Camera
279
+
280
+ Returns:
281
+ Binary string literal of compressed png image
282
+ """
283
+ #todo : in future remove below, it's only for compatibility to pre v1.2
284
+ camera_name = str(camera_name)
285
+
286
+ #because this method returns std::vector < uint8>, msgpack decides to encode it as a string unfortunately.
287
+ result = self.client.call('simGetImage', camera_name, image_type, vehicle_name, external)
288
+ if (result == "" or result == "\0"):
289
+ return None
290
+ return result
291
+
292
+ #camera control
293
+ #simGetImage returns compressed png in array of bytes
294
+ #image_type uses one of the ImageType members
295
+ def simGetImages(self, requests, vehicle_name = '', external = False):
296
+ """
297
+ Get multiple images
298
+
299
+ See https://microsoft.github.io/AirSim/image_apis/ for details and examples
300
+
301
+ Args:
302
+ requests (list[ImageRequest]): Images required
303
+ vehicle_name (str, optional): Name of vehicle associated with the camera
304
+ external (bool, optional): Whether the camera is an External Camera
305
+
306
+ Returns:
307
+ list[ImageResponse]:
308
+ """
309
+ responses_raw = self.client.call('simGetImages', requests, vehicle_name, external)
310
+ return [ImageResponse.from_msgpack(response_raw) for response_raw in responses_raw]
311
+
312
+
313
+
314
+ #CinemAirSim
315
+ def simGetPresetLensSettings(self, camera_name, vehicle_name = '', external = False):
316
+ result = self.client.call('simGetPresetLensSettings', camera_name, vehicle_name, external)
317
+ if (result == "" or result == "\0"):
318
+ return None
319
+ return result
320
+
321
+ def simGetLensSettings(self, camera_name, vehicle_name = '', external = False):
322
+ result = self.client.call('simGetLensSettings', camera_name, vehicle_name, external)
323
+ if (result == "" or result == "\0"):
324
+ return None
325
+ return result
326
+
327
+ def simSetPresetLensSettings(self, preset_lens_settings, camera_name, vehicle_name = '', external = False):
328
+ self.client.call("simSetPresetLensSettings", preset_lens_settings, camera_name, vehicle_name, external)
329
+
330
+ def simGetPresetFilmbackSettings(self, camera_name, vehicle_name = '', external = False):
331
+ result = self.client.call('simGetPresetFilmbackSettings', camera_name, vehicle_name, external)
332
+ if (result == "" or result == "\0"):
333
+ return None
334
+ return result
335
+
336
+ def simSetPresetFilmbackSettings(self, preset_filmback_settings, camera_name, vehicle_name = '', external = False):
337
+ self.client.call("simSetPresetFilmbackSettings", preset_filmback_settings, camera_name, vehicle_name, external)
338
+
339
+ def simGetFilmbackSettings(self, camera_name, vehicle_name = '', external = False):
340
+ result = self.client.call('simGetFilmbackSettings', camera_name, vehicle_name, external)
341
+ if (result == "" or result == "\0"):
342
+ return None
343
+ return result
344
+
345
+ def simSetFilmbackSettings(self, sensor_width, sensor_height, camera_name, vehicle_name = '', external = False):
346
+ return self.client.call("simSetFilmbackSettings", sensor_width, sensor_height, camera_name, vehicle_name, external)
347
+
348
+ def simGetFocalLength(self, camera_name, vehicle_name = '', external = False):
349
+ return self.client.call("simGetFocalLength", camera_name, vehicle_name, external)
350
+
351
+ def simSetFocalLength(self, focal_length, camera_name, vehicle_name = '', external = False):
352
+ self.client.call("simSetFocalLength", focal_length, camera_name, vehicle_name, external)
353
+
354
+ def simEnableManualFocus(self, enable, camera_name, vehicle_name = '', external = False):
355
+ self.client.call("simEnableManualFocus", enable, camera_name, vehicle_name, external)
356
+
357
+ def simGetFocusDistance(self, camera_name, vehicle_name = '', external = False):
358
+ return self.client.call("simGetFocusDistance", camera_name, vehicle_name, external)
359
+
360
+ def simSetFocusDistance(self, focus_distance, camera_name, vehicle_name = '', external = False):
361
+ self.client.call("simSetFocusDistance", focus_distance, camera_name, vehicle_name, external)
362
+
363
+ def simGetFocusAperture(self, camera_name, vehicle_name = '', external = False):
364
+ return self.client.call("simGetFocusAperture", camera_name, vehicle_name, external)
365
+
366
+ def simSetFocusAperture(self, focus_aperture, camera_name, vehicle_name = '', external = False):
367
+ self.client.call("simSetFocusAperture", focus_aperture, camera_name, vehicle_name, external)
368
+
369
+ def simEnableFocusPlane(self, enable, camera_name, vehicle_name = '', external = False):
370
+ self.client.call("simEnableFocusPlane", enable, camera_name, vehicle_name, external)
371
+
372
+ def simGetCurrentFieldOfView(self, camera_name, vehicle_name = '', external = False):
373
+ return self.client.call("simGetCurrentFieldOfView", camera_name, vehicle_name, external)
374
+
375
+ #End CinemAirSim
376
+ def simTestLineOfSightToPoint(self, point, vehicle_name = ''):
377
+ """
378
+ Returns whether the target point is visible from the perspective of the inputted vehicle
379
+
380
+ Args:
381
+ point (GeoPoint): target point
382
+ vehicle_name (str, optional): Name of vehicle
383
+
384
+ Returns:
385
+ [bool]: Success
386
+ """
387
+ return self.client.call('simTestLineOfSightToPoint', point, vehicle_name)
388
+
389
+ def simTestLineOfSightBetweenPoints(self, point1, point2):
390
+ """
391
+ Returns whether the target point is visible from the perspective of the source point
392
+
393
+ Args:
394
+ point1 (GeoPoint): source point
395
+ point2 (GeoPoint): target point
396
+
397
+ Returns:
398
+ [bool]: Success
399
+ """
400
+ return self.client.call('simTestLineOfSightBetweenPoints', point1, point2)
401
+
402
+ def simGetWorldExtents(self):
403
+ """
404
+ Returns a list of GeoPoints representing the minimum and maximum extents of the world
405
+
406
+ Returns:
407
+ list[GeoPoint]
408
+ """
409
+ responses_raw = self.client.call('simGetWorldExtents')
410
+ return [GeoPoint.from_msgpack(response_raw) for response_raw in responses_raw]
411
+
412
+ def simRunConsoleCommand(self, command):
413
+ """
414
+ Allows the client to execute a command in Unreal's native console, via an API.
415
+ Affords access to the countless built-in commands such as "stat unit", "stat fps", "open [map]", adjust any config settings, etc. etc.
416
+ Allows the user to create bespoke APIs very easily, by adding a custom event to the level blueprint, and then calling the console command "ce MyEventName [args]". No recompilation of AirSim needed!
417
+
418
+ Args:
419
+ command ([string]): Desired Unreal Engine Console command to run
420
+
421
+ Returns:
422
+ [bool]: Success
423
+ """
424
+ return self.client.call('simRunConsoleCommand', command)
425
+
426
+ #gets the static meshes in the unreal scene
427
+ def simGetMeshPositionVertexBuffers(self):
428
+ """
429
+ Returns the static meshes that make up the scene
430
+
431
+ See https://microsoft.github.io/AirSim/meshes/ for details and how to use this
432
+
433
+ Returns:
434
+ list[MeshPositionVertexBuffersResponse]:
435
+ """
436
+ responses_raw = self.client.call('simGetMeshPositionVertexBuffers')
437
+ return [MeshPositionVertexBuffersResponse.from_msgpack(response_raw) for response_raw in responses_raw]
438
+
439
+ def simGetCollisionInfo(self, vehicle_name = ''):
440
+ """
441
+ Args:
442
+ vehicle_name (str, optional): Name of the Vehicle to get the info of
443
+
444
+ Returns:
445
+ CollisionInfo:
446
+ """
447
+ return CollisionInfo.from_msgpack(self.client.call('simGetCollisionInfo', vehicle_name))
448
+
449
+ def simSetVehiclePose(self, pose, ignore_collision, vehicle_name = ''):
450
+ """
451
+ Set the pose of the vehicle
452
+
453
+ If you don't want to change position (or orientation) then just set components of position (or orientation) to floating point nan values
454
+
455
+ Args:
456
+ pose (Pose): Desired Pose pf the vehicle
457
+ ignore_collision (bool): Whether to ignore any collision or not
458
+ vehicle_name (str, optional): Name of the vehicle to move
459
+ """
460
+ self.client.call('simSetVehiclePose', pose, ignore_collision, vehicle_name)
461
+
462
+ def simGetVehiclePose(self, vehicle_name = ''):
463
+ """
464
+ The position inside the returned Pose is in the frame of the vehicle's starting point
465
+
466
+ Args:
467
+ vehicle_name (str, optional): Name of the vehicle to get the Pose of
468
+
469
+ Returns:
470
+ Pose:
471
+ """
472
+ pose = self.client.call('simGetVehiclePose', vehicle_name)
473
+ return Pose.from_msgpack(pose)
474
+
475
+ def simSetTraceLine(self, color_rgba, thickness=1.0, vehicle_name = ''):
476
+ """
477
+ Modify the color and thickness of the line when Tracing is enabled
478
+
479
+ Tracing can be enabled by pressing T in the Editor or setting `EnableTrace` to `True` in the Vehicle Settings
480
+
481
+ Args:
482
+ color_rgba (list): desired RGBA values from 0.0 to 1.0
483
+ thickness (float, optional): Thickness of the line
484
+ vehicle_name (string, optional): Name of the vehicle to set Trace line values for
485
+ """
486
+ self.client.call('simSetTraceLine', color_rgba, thickness, vehicle_name)
487
+
488
+ def simGetObjectPose(self, object_name):
489
+ """
490
+ The position inside the returned Pose is in the world frame
491
+
492
+ Args:
493
+ object_name (str): Object to get the Pose of
494
+
495
+ Returns:
496
+ Pose:
497
+ """
498
+ pose = self.client.call('simGetObjectPose', object_name)
499
+ return Pose.from_msgpack(pose)
500
+
501
+ def simSetObjectPose(self, object_name, pose, teleport = True):
502
+ """
503
+ Set the pose of the object(actor) in the environment
504
+
505
+ The specified actor must have Mobility set to movable, otherwise there will be undefined behaviour.
506
+ See https://www.unrealengine.com/en-US/blog/moving-physical-objects for details on how to set Mobility and the effect of Teleport parameter
507
+
508
+ Args:
509
+ object_name (str): Name of the object(actor) to move
510
+ pose (Pose): Desired Pose of the object
511
+ teleport (bool, optional): Whether to move the object immediately without affecting their velocity
512
+
513
+ Returns:
514
+ bool: If the move was successful
515
+ """
516
+ return self.client.call('simSetObjectPose', object_name, pose, teleport)
517
+
518
+ def simGetObjectScale(self, object_name):
519
+ """
520
+ Gets scale of an object in the world
521
+
522
+ Args:
523
+ object_name (str): Object to get the scale of
524
+
525
+ Returns:
526
+ airsim.Vector3r: Scale
527
+ """
528
+ scale = self.client.call('simGetObjectScale', object_name)
529
+ return Vector3r.from_msgpack(scale)
530
+
531
+ def simSetObjectScale(self, object_name, scale_vector):
532
+ """
533
+ Sets scale of an object in the world
534
+
535
+ Args:
536
+ object_name (str): Object to set the scale of
537
+ scale_vector (airsim.Vector3r): Desired scale of object
538
+
539
+ Returns:
540
+ bool: True if scale change was successful
541
+ """
542
+ return self.client.call('simSetObjectScale', object_name, scale_vector)
543
+
544
+ def simListSceneObjects(self, name_regex = '.*'):
545
+ """
546
+ Lists the objects present in the environment
547
+
548
+ Default behaviour is to list all objects, regex can be used to return smaller list of matching objects or actors
549
+
550
+ Args:
551
+ name_regex (str, optional): String to match actor names against, e.g. "Cylinder.*"
552
+
553
+ Returns:
554
+ list[str]: List containing all the names
555
+ """
556
+ return self.client.call('simListSceneObjects', name_regex)
557
+
558
+ def simLoadLevel(self, level_name):
559
+ """
560
+ Loads a level specified by its name
561
+
562
+ Args:
563
+ level_name (str): Name of the level to load
564
+
565
+ Returns:
566
+ bool: True if the level was successfully loaded
567
+ """
568
+ return self.client.call('simLoadLevel', level_name)
569
+
570
+ def simListAssets(self):
571
+ """
572
+ Lists all the assets present in the Asset Registry
573
+
574
+ Returns:
575
+ list[str]: Names of all the assets
576
+ """
577
+ return self.client.call('simListAssets')
578
+
579
+ def simSpawnObject(self, object_name, asset_name, pose, scale, physics_enabled=False, is_blueprint=False):
580
+ """Spawned selected object in the world
581
+
582
+ Args:
583
+ object_name (str): Desired name of new object
584
+ asset_name (str): Name of asset(mesh) in the project database
585
+ pose (airsim.Pose): Desired pose of object
586
+ scale (airsim.Vector3r): Desired scale of object
587
+ physics_enabled (bool, optional): Whether to enable physics for the object
588
+ is_blueprint (bool, optional): Whether to spawn a blueprint or an actor
589
+
590
+ Returns:
591
+ str: Name of spawned object, in case it had to be modified
592
+ """
593
+ return self.client.call('simSpawnObject', object_name, asset_name, pose, scale, physics_enabled, is_blueprint)
594
+
595
+ def simDestroyObject(self, object_name):
596
+ """Removes selected object from the world
597
+
598
+ Args:
599
+ object_name (str): Name of object to be removed
600
+
601
+ Returns:
602
+ bool: True if object is queued up for removal
603
+ """
604
+ return self.client.call('simDestroyObject', object_name)
605
+
606
+ def simSetSegmentationObjectID(self, mesh_name, object_id, is_name_regex = False):
607
+ """
608
+ Set segmentation ID for specific objects
609
+
610
+ See https://microsoft.github.io/AirSim/image_apis/#segmentation for details
611
+
612
+ Args:
613
+ mesh_name (str): Name of the mesh to set the ID of (supports regex)
614
+ object_id (int): Object ID to be set, range 0-255
615
+
616
+ RBG values for IDs can be seen at https://microsoft.github.io/AirSim/seg_rgbs.txt
617
+ is_name_regex (bool, optional): Whether the mesh name is a regex
618
+
619
+ Returns:
620
+ bool: If the mesh was found
621
+ """
622
+ return self.client.call('simSetSegmentationObjectID', mesh_name, object_id, is_name_regex)
623
+
624
+ def simGetSegmentationObjectID(self, mesh_name):
625
+ """
626
+ Returns Object ID for the given mesh name
627
+
628
+ Mapping of Object IDs to RGB values can be seen at https://microsoft.github.io/AirSim/seg_rgbs.txt
629
+
630
+ Args:
631
+ mesh_name (str): Name of the mesh to get the ID of
632
+ """
633
+ return self.client.call('simGetSegmentationObjectID', mesh_name)
634
+
635
+ def simAddDetectionFilterMeshName(self, camera_name, image_type, mesh_name, vehicle_name = '', external = False):
636
+ """
637
+ Add mesh name to detect in wild card format
638
+
639
+ For example: simAddDetectionFilterMeshName("Car_*") will detect all instance named "Car_*"
640
+
641
+ Args:
642
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
643
+ image_type (ImageType): Type of image required
644
+ mesh_name (str): mesh name in wild card format
645
+ vehicle_name (str, optional): Vehicle which the camera is associated with
646
+ external (bool, optional): Whether the camera is an External Camera
647
+
648
+ """
649
+ self.client.call('simAddDetectionFilterMeshName', camera_name, image_type, mesh_name, vehicle_name, external)
650
+
651
+ def simSetDetectionFilterRadius(self, camera_name, image_type, radius_cm, vehicle_name = '', external = False):
652
+ """
653
+ Set detection radius for all cameras
654
+
655
+ Args:
656
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
657
+ image_type (ImageType): Type of image required
658
+ radius_cm (int): Radius in [cm]
659
+ vehicle_name (str, optional): Vehicle which the camera is associated with
660
+ external (bool, optional): Whether the camera is an External Camera
661
+ """
662
+ self.client.call('simSetDetectionFilterRadius', camera_name, image_type, radius_cm, vehicle_name, external)
663
+
664
+ def simClearDetectionMeshNames(self, camera_name, image_type, vehicle_name = '', external = False):
665
+ """
666
+ Clear all mesh names from detection filter
667
+
668
+ Args:
669
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
670
+ image_type (ImageType): Type of image required
671
+ vehicle_name (str, optional): Vehicle which the camera is associated with
672
+ external (bool, optional): Whether the camera is an External Camera
673
+
674
+ """
675
+ self.client.call('simClearDetectionMeshNames', camera_name, image_type, vehicle_name, external)
676
+
677
+ def simGetDetections(self, camera_name, image_type, vehicle_name = '', external = False):
678
+ """
679
+ Get current detections
680
+
681
+ Args:
682
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
683
+ image_type (ImageType): Type of image required
684
+ vehicle_name (str, optional): Vehicle which the camera is associated with
685
+ external (bool, optional): Whether the camera is an External Camera
686
+
687
+ Returns:
688
+ DetectionInfo array
689
+ """
690
+ responses_raw = self.client.call('simGetDetections', camera_name, image_type, vehicle_name, external)
691
+ return [DetectionInfo.from_msgpack(response_raw) for response_raw in responses_raw]
692
+
693
+ def simPrintLogMessage(self, message, message_param = "", severity = 0):
694
+ """
695
+ Prints the specified message in the simulator's window.
696
+
697
+ If message_param is supplied, then it's printed next to the message and in that case if this API is called with same message value
698
+ but different message_param again then previous line is overwritten with new line (instead of API creating new line on display).
699
+
700
+ For example, `simPrintLogMessage("Iteration: ", to_string(i))` keeps updating same line on display when API is called with different values of i.
701
+ The valid values of severity parameter is 0 to 3 inclusive that corresponds to different colors.
702
+
703
+ Args:
704
+ message (str): Message to be printed
705
+ message_param (str, optional): Parameter to be printed next to the message
706
+ severity (int, optional): Range 0-3, inclusive, corresponding to the severity of the message
707
+ """
708
+ self.client.call('simPrintLogMessage', message, message_param, severity)
709
+
710
+ def simGetCameraInfo(self, camera_name, vehicle_name = '', external=False):
711
+ """
712
+ Get details about the camera
713
+
714
+ Args:
715
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
716
+ vehicle_name (str, optional): Vehicle which the camera is associated with
717
+ external (bool, optional): Whether the camera is an External Camera
718
+
719
+ Returns:
720
+ CameraInfo:
721
+ """
722
+ #TODO : below str() conversion is only needed for legacy reason and should be removed in future
723
+ return CameraInfo.from_msgpack(self.client.call('simGetCameraInfo', str(camera_name), vehicle_name, external))
724
+
725
+ def simGetDistortionParams(self, camera_name, vehicle_name = '', external = False):
726
+ """
727
+ Get camera distortion parameters
728
+
729
+ Args:
730
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
731
+ vehicle_name (str, optional): Vehicle which the camera is associated with
732
+ external (bool, optional): Whether the camera is an External Camera
733
+
734
+ Returns:
735
+ List (float): List of distortion parameter values corresponding to K1, K2, K3, P1, P2 respectively.
736
+ """
737
+
738
+ return self.client.call('simGetDistortionParams', str(camera_name), vehicle_name, external)
739
+
740
+ def simSetDistortionParams(self, camera_name, distortion_params, vehicle_name = '', external = False):
741
+ """
742
+ Set camera distortion parameters
743
+
744
+ Args:
745
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
746
+ distortion_params (dict): Dictionary of distortion param names and corresponding values
747
+ {"K1": 0.0, "K2": 0.0, "K3": 0.0, "P1": 0.0, "P2": 0.0}
748
+ vehicle_name (str, optional): Vehicle which the camera is associated with
749
+ external (bool, optional): Whether the camera is an External Camera
750
+ """
751
+
752
+ for param_name, value in distortion_params.items():
753
+ self.simSetDistortionParam(camera_name, param_name, value, vehicle_name, external)
754
+
755
+ def simSetDistortionParam(self, camera_name, param_name, value, vehicle_name = '', external = False):
756
+ """
757
+ Set single camera distortion parameter
758
+
759
+ Args:
760
+ camera_name (str): Name of the camera, for backwards compatibility, ID numbers such as 0,1,etc. can also be used
761
+ param_name (str): Name of distortion parameter
762
+ value (float): Value of distortion parameter
763
+ vehicle_name (str, optional): Vehicle which the camera is associated with
764
+ external (bool, optional): Whether the camera is an External Camera
765
+ """
766
+ self.client.call('simSetDistortionParam', str(camera_name), param_name, value, vehicle_name, external)
767
+
768
+ def simSetCameraPose(self, camera_name, pose, vehicle_name = '', external = False):
769
+ """
770
+ - Control the pose of a selected camera
771
+
772
+ Args:
773
+ camera_name (str): Name of the camera to be controlled
774
+ pose (Pose): Pose representing the desired position and orientation of the camera
775
+ vehicle_name (str, optional): Name of vehicle which the camera corresponds to
776
+ external (bool, optional): Whether the camera is an External Camera
777
+ """
778
+ #TODO : below str() conversion is only needed for legacy reason and should be removed in future
779
+ self.client.call('simSetCameraPose', str(camera_name), pose, vehicle_name, external)
780
+
781
+ def simSetCameraFov(self, camera_name, fov_degrees, vehicle_name = '', external = False):
782
+ """
783
+ - Control the field of view of a selected camera
784
+
785
+ Args:
786
+ camera_name (str): Name of the camera to be controlled
787
+ fov_degrees (float): Value of field of view in degrees
788
+ vehicle_name (str, optional): Name of vehicle which the camera corresponds to
789
+ external (bool, optional): Whether the camera is an External Camera
790
+ """
791
+ #TODO : below str() conversion is only needed for legacy reason and should be removed in future
792
+ self.client.call('simSetCameraFov', str(camera_name), fov_degrees, vehicle_name, external)
793
+
794
+ def simGetGroundTruthKinematics(self, vehicle_name = ''):
795
+ """
796
+ Get Ground truth kinematics of the vehicle
797
+
798
+ The position inside the returned KinematicsState is in the frame of the vehicle's starting point
799
+
800
+ Args:
801
+ vehicle_name (str, optional): Name of the vehicle
802
+
803
+ Returns:
804
+ KinematicsState: Ground truth of the vehicle
805
+ """
806
+ kinematics_state = self.client.call('simGetGroundTruthKinematics', vehicle_name)
807
+ return KinematicsState.from_msgpack(kinematics_state)
808
+ simGetGroundTruthKinematics.__annotations__ = {'return': KinematicsState}
809
+
810
+ def simSetKinematics(self, state, ignore_collision, vehicle_name = ''):
811
+ """
812
+ Set the kinematics state of the vehicle
813
+
814
+ If you don't want to change position (or orientation) then just set components of position (or orientation) to floating point nan values
815
+
816
+ Args:
817
+ state (KinematicsState): Desired Pose pf the vehicle
818
+ ignore_collision (bool): Whether to ignore any collision or not
819
+ vehicle_name (str, optional): Name of the vehicle to move
820
+ """
821
+ self.client.call('simSetKinematics', state, ignore_collision, vehicle_name)
822
+
823
+ def simGetGroundTruthEnvironment(self, vehicle_name = ''):
824
+ """
825
+ Get ground truth environment state
826
+
827
+ The position inside the returned EnvironmentState is in the frame of the vehicle's starting point
828
+
829
+ Args:
830
+ vehicle_name (str, optional): Name of the vehicle
831
+
832
+ Returns:
833
+ EnvironmentState: Ground truth environment state
834
+ """
835
+ env_state = self.client.call('simGetGroundTruthEnvironment', vehicle_name)
836
+ return EnvironmentState.from_msgpack(env_state)
837
+ simGetGroundTruthEnvironment.__annotations__ = {'return': EnvironmentState}
838
+
839
+
840
+ #sensor APIs
841
+ def getImuData(self, imu_name = '', vehicle_name = ''):
842
+ """
843
+ Args:
844
+ imu_name (str, optional): Name of IMU to get data from, specified in settings.json
845
+ vehicle_name (str, optional): Name of vehicle to which the sensor corresponds to
846
+
847
+ Returns:
848
+ ImuData:
849
+ """
850
+ return ImuData.from_msgpack(self.client.call('getImuData', imu_name, vehicle_name))
851
+
852
+ def getBarometerData(self, barometer_name = '', vehicle_name = ''):
853
+ """
854
+ Args:
855
+ barometer_name (str, optional): Name of Barometer to get data from, specified in settings.json
856
+ vehicle_name (str, optional): Name of vehicle to which the sensor corresponds to
857
+
858
+ Returns:
859
+ BarometerData:
860
+ """
861
+ return BarometerData.from_msgpack(self.client.call('getBarometerData', barometer_name, vehicle_name))
862
+
863
+ def getMagnetometerData(self, magnetometer_name = '', vehicle_name = ''):
864
+ """
865
+ Args:
866
+ magnetometer_name (str, optional): Name of Magnetometer to get data from, specified in settings.json
867
+ vehicle_name (str, optional): Name of vehicle to which the sensor corresponds to
868
+
869
+ Returns:
870
+ MagnetometerData:
871
+ """
872
+ return MagnetometerData.from_msgpack(self.client.call('getMagnetometerData', magnetometer_name, vehicle_name))
873
+
874
+ def getGpsData(self, gps_name = '', vehicle_name = ''):
875
+ """
876
+ Args:
877
+ gps_name (str, optional): Name of GPS to get data from, specified in settings.json
878
+ vehicle_name (str, optional): Name of vehicle to which the sensor corresponds to
879
+
880
+ Returns:
881
+ GpsData:
882
+ """
883
+ return GpsData.from_msgpack(self.client.call('getGpsData', gps_name, vehicle_name))
884
+
885
+ def getDistanceSensorData(self, distance_sensor_name = '', vehicle_name = ''):
886
+ """
887
+ Args:
888
+ distance_sensor_name (str, optional): Name of Distance Sensor to get data from, specified in settings.json
889
+ vehicle_name (str, optional): Name of vehicle to which the sensor corresponds to
890
+
891
+ Returns:
892
+ DistanceSensorData:
893
+ """
894
+ return DistanceSensorData.from_msgpack(self.client.call('getDistanceSensorData', distance_sensor_name, vehicle_name))
895
+
896
+ def getLidarData(self, lidar_name = '', vehicle_name = ''):
897
+ """
898
+ Args:
899
+ lidar_name (str, optional): Name of Lidar to get data from, specified in settings.json
900
+ vehicle_name (str, optional): Name of vehicle to which the sensor corresponds to
901
+
902
+ Returns:
903
+ LidarData:
904
+ """
905
+ return LidarData.from_msgpack(self.client.call('getLidarData', lidar_name, vehicle_name))
906
+
907
+ def simGetLidarSegmentation(self, lidar_name = '', vehicle_name = ''):
908
+ """
909
+ NOTE: Deprecated API, use `getLidarData()` API instead
910
+ Returns Segmentation ID of each point's collided object in the last Lidar update
911
+
912
+ Args:
913
+ lidar_name (str, optional): Name of Lidar sensor
914
+ vehicle_name (str, optional): Name of the vehicle wth the sensor
915
+
916
+ Returns:
917
+ list[int]: Segmentation IDs of the objects
918
+ """
919
+ logging.warning("simGetLidarSegmentation API is deprecated, use getLidarData() API instead")
920
+ return self.getLidarData(lidar_name, vehicle_name).segmentation
921
+
922
+ #Plotting APIs
923
+ def simFlushPersistentMarkers(self):
924
+ """
925
+ Clear any persistent markers - those plotted with setting `is_persistent=True` in the APIs below
926
+ """
927
+ self.client.call('simFlushPersistentMarkers')
928
+
929
+ def simPlotPoints(self, points, color_rgba=[1.0, 0.0, 0.0, 1.0], size = 10.0, duration = -1.0, is_persistent = False):
930
+ """
931
+ Plot a list of 3D points in World NED frame
932
+
933
+ Args:
934
+ points (list[Vector3r]): List of Vector3r objects
935
+ color_rgba (list, optional): desired RGBA values from 0.0 to 1.0
936
+ size (float, optional): Size of plotted point
937
+ duration (float, optional): Duration (seconds) to plot for
938
+ is_persistent (bool, optional): If set to True, the desired object will be plotted for infinite time.
939
+ """
940
+ self.client.call('simPlotPoints', points, color_rgba, size, duration, is_persistent)
941
+
942
+ def simPlotLineStrip(self, points, color_rgba=[1.0, 0.0, 0.0, 1.0], thickness = 5.0, duration = -1.0, is_persistent = False):
943
+ """
944
+ Plots a line strip in World NED frame, defined from points[0] to points[1], points[1] to points[2], ... , points[n-2] to points[n-1]
945
+
946
+ Args:
947
+ points (list[Vector3r]): List of 3D locations of line start and end points, specified as Vector3r objects
948
+ color_rgba (list, optional): desired RGBA values from 0.0 to 1.0
949
+ thickness (float, optional): Thickness of line
950
+ duration (float, optional): Duration (seconds) to plot for
951
+ is_persistent (bool, optional): If set to True, the desired object will be plotted for infinite time.
952
+ """
953
+ self.client.call('simPlotLineStrip', points, color_rgba, thickness, duration, is_persistent)
954
+
955
+ def simPlotLineList(self, points, color_rgba=[1.0, 0.0, 0.0, 1.0], thickness = 5.0, duration = -1.0, is_persistent = False):
956
+ """
957
+ Plots a line strip in World NED frame, defined from points[0] to points[1], points[2] to points[3], ... , points[n-2] to points[n-1]
958
+
959
+ Args:
960
+ points (list[Vector3r]): List of 3D locations of line start and end points, specified as Vector3r objects. Must be even
961
+ color_rgba (list, optional): desired RGBA values from 0.0 to 1.0
962
+ thickness (float, optional): Thickness of line
963
+ duration (float, optional): Duration (seconds) to plot for
964
+ is_persistent (bool, optional): If set to True, the desired object will be plotted for infinite time.
965
+ """
966
+ self.client.call('simPlotLineList', points, color_rgba, thickness, duration, is_persistent)
967
+
968
+ def simPlotArrows(self, points_start, points_end, color_rgba=[1.0, 0.0, 0.0, 1.0], thickness = 5.0, arrow_size = 2.0, duration = -1.0, is_persistent = False):
969
+ """
970
+ Plots a list of arrows in World NED frame, defined from points_start[0] to points_end[0], points_start[1] to points_end[1], ... , points_start[n-1] to points_end[n-1]
971
+
972
+ Args:
973
+ points_start (list[Vector3r]): List of 3D start positions of arrow start positions, specified as Vector3r objects
974
+ points_end (list[Vector3r]): List of 3D end positions of arrow start positions, specified as Vector3r objects
975
+ color_rgba (list, optional): desired RGBA values from 0.0 to 1.0
976
+ thickness (float, optional): Thickness of line
977
+ arrow_size (float, optional): Size of arrow head
978
+ duration (float, optional): Duration (seconds) to plot for
979
+ is_persistent (bool, optional): If set to True, the desired object will be plotted for infinite time.
980
+ """
981
+ self.client.call('simPlotArrows', points_start, points_end, color_rgba, thickness, arrow_size, duration, is_persistent)
982
+
983
+
984
+ def simPlotStrings(self, strings, positions, scale = 5, color_rgba=[1.0, 0.0, 0.0, 1.0], duration = -1.0):
985
+ """
986
+ Plots a list of strings at desired positions in World NED frame.
987
+
988
+ Args:
989
+ strings (list[String], optional): List of strings to plot
990
+ positions (list[Vector3r]): List of positions where the strings should be plotted. Should be in one-to-one correspondence with the strings' list
991
+ scale (float, optional): Font scale of transform name
992
+ color_rgba (list, optional): desired RGBA values from 0.0 to 1.0
993
+ duration (float, optional): Duration (seconds) to plot for
994
+ """
995
+ self.client.call('simPlotStrings', strings, positions, scale, color_rgba, duration)
996
+
997
+ def simPlotTransforms(self, poses, scale = 5.0, thickness = 5.0, duration = -1.0, is_persistent = False):
998
+ """
999
+ Plots a list of transforms in World NED frame.
1000
+
1001
+ Args:
1002
+ poses (list[Pose]): List of Pose objects representing the transforms to plot
1003
+ scale (float, optional): Length of transforms' axes
1004
+ thickness (float, optional): Thickness of transforms' axes
1005
+ duration (float, optional): Duration (seconds) to plot for
1006
+ is_persistent (bool, optional): If set to True, the desired object will be plotted for infinite time.
1007
+ """
1008
+ self.client.call('simPlotTransforms', poses, scale, thickness, duration, is_persistent)
1009
+
1010
+ def simPlotTransformsWithNames(self, poses, names, tf_scale = 5.0, tf_thickness = 5.0, text_scale = 10.0, text_color_rgba = [1.0, 0.0, 0.0, 1.0], duration = -1.0):
1011
+ """
1012
+ Plots a list of transforms with their names in World NED frame.
1013
+
1014
+ Args:
1015
+ poses (list[Pose]): List of Pose objects representing the transforms to plot
1016
+ names (list[string]): List of strings with one-to-one correspondence to list of poses
1017
+ tf_scale (float, optional): Length of transforms' axes
1018
+ tf_thickness (float, optional): Thickness of transforms' axes
1019
+ text_scale (float, optional): Font scale of transform name
1020
+ text_color_rgba (list, optional): desired RGBA values from 0.0 to 1.0 for the transform name
1021
+ duration (float, optional): Duration (seconds) to plot for
1022
+ """
1023
+ self.client.call('simPlotTransformsWithNames', poses, names, tf_scale, tf_thickness, text_scale, text_color_rgba, duration)
1024
+
1025
+ def cancelLastTask(self, vehicle_name = ''):
1026
+ """
1027
+ Cancel previous Async task
1028
+
1029
+ Args:
1030
+ vehicle_name (str, optional): Name of the vehicle
1031
+ """
1032
+ self.client.call('cancelLastTask', vehicle_name)
1033
+
1034
+ #Recording APIs
1035
+ def startRecording(self):
1036
+ """
1037
+ Start Recording
1038
+
1039
+ Recording will be done according to the settings
1040
+ """
1041
+ self.client.call('startRecording')
1042
+
1043
+ def stopRecording(self):
1044
+ """
1045
+ Stop Recording
1046
+ """
1047
+ self.client.call('stopRecording')
1048
+
1049
+ def isRecording(self):
1050
+ """
1051
+ Whether Recording is running or not
1052
+
1053
+ Returns:
1054
+ bool: True if Recording, else False
1055
+ """
1056
+ return self.client.call('isRecording')
1057
+
1058
+ def simSetWind(self, wind):
1059
+ """
1060
+ Set simulated wind, in World frame, NED direction, m/s
1061
+
1062
+ Args:
1063
+ wind (Vector3r): Wind, in World frame, NED direction, in m/s
1064
+ """
1065
+ self.client.call('simSetWind', wind)
1066
+
1067
+ def simCreateVoxelGrid(self, position, x, y, z, res, of):
1068
+ """
1069
+ Construct and save a binvox-formatted voxel grid of environment
1070
+
1071
+ Args:
1072
+ position (Vector3r): Position around which voxel grid is centered in m
1073
+ x, y, z (int): Size of each voxel grid dimension in m
1074
+ res (float): Resolution of voxel grid in m
1075
+ of (str): Name of output file to save voxel grid as
1076
+
1077
+ Returns:
1078
+ bool: True if output written to file successfully, else False
1079
+ """
1080
+ return self.client.call('simCreateVoxelGrid', position, x, y, z, res, of)
1081
+
1082
+ #Add new vehicle via RPC
1083
+ def simAddVehicle(self, vehicle_name, vehicle_type, pose, pawn_path = ""):
1084
+ """
1085
+ Create vehicle at runtime
1086
+
1087
+ Args:
1088
+ vehicle_name (str): Name of the vehicle being created
1089
+ vehicle_type (str): Type of vehicle, e.g. "simpleflight"
1090
+ pose (Pose): Initial pose of the vehicle
1091
+ pawn_path (str, optional): Vehicle blueprint path, default empty wbich uses the default blueprint for the vehicle type
1092
+
1093
+ Returns:
1094
+ bool: Whether vehicle was created
1095
+ """
1096
+ return self.client.call('simAddVehicle', vehicle_name, vehicle_type, pose, pawn_path)
1097
+
1098
+ def listVehicles(self):
1099
+ """
1100
+ Lists the names of current vehicles
1101
+
1102
+ Returns:
1103
+ list[str]: List containing names of all vehicles
1104
+ """
1105
+ return self.client.call('listVehicles')
1106
+
1107
+ def getSettingsString(self):
1108
+ """
1109
+ Fetch the settings text being used by AirSim
1110
+
1111
+ Returns:
1112
+ str: Settings text in JSON format
1113
+ """
1114
+ return self.client.call('getSettingsString')
1115
+
1116
+ #----------------------------------- Multirotor APIs ---------------------------------------------
1117
+ class MultirotorClient(VehicleClient, object):
1118
+ def __init__(self, ip = "", port = 41451, timeout_value = 3600):
1119
+ super(MultirotorClient, self).__init__(ip, port, timeout_value)
1120
+
1121
+ def takeoffAsync(self, timeout_sec = 20, vehicle_name = ''):
1122
+ """
1123
+ Takeoff vehicle to 3m above ground. Vehicle should not be moving when this API is used
1124
+
1125
+ Args:
1126
+ timeout_sec (int, optional): Timeout for the vehicle to reach desired altitude
1127
+ vehicle_name (str, optional): Name of the vehicle to send this command to
1128
+
1129
+ Returns:
1130
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1131
+ """
1132
+ return self.client.call_async('takeoff', timeout_sec, vehicle_name)
1133
+
1134
+ def landAsync(self, timeout_sec = 60, vehicle_name = ''):
1135
+ """
1136
+ Land the vehicle
1137
+
1138
+ Args:
1139
+ timeout_sec (int, optional): Timeout for the vehicle to land
1140
+ vehicle_name (str, optional): Name of the vehicle to send this command to
1141
+
1142
+ Returns:
1143
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1144
+ """
1145
+ return self.client.call_async('land', timeout_sec, vehicle_name)
1146
+
1147
+ def goHomeAsync(self, timeout_sec = 3e+38, vehicle_name = ''):
1148
+ """
1149
+ Return vehicle to Home i.e. Launch location
1150
+
1151
+ Args:
1152
+ timeout_sec (int, optional): Timeout for the vehicle to reach desired altitude
1153
+ vehicle_name (str, optional): Name of the vehicle to send this command to
1154
+
1155
+ Returns:
1156
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1157
+ """
1158
+ return self.client.call_async('goHome', timeout_sec, vehicle_name)
1159
+
1160
+ #APIs for control
1161
+ def moveByVelocityBodyFrameAsync(self, vx, vy, vz, duration, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(), vehicle_name = ''):
1162
+ """
1163
+ Args:
1164
+ vx (float): desired velocity in the X axis of the vehicle's local NED frame.
1165
+ vy (float): desired velocity in the Y axis of the vehicle's local NED frame.
1166
+ vz (float): desired velocity in the Z axis of the vehicle's local NED frame.
1167
+ duration (float): Desired amount of time (seconds), to send this command for
1168
+ drivetrain (DrivetrainType, optional):
1169
+ yaw_mode (YawMode, optional):
1170
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1171
+
1172
+ Returns:
1173
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1174
+ """
1175
+ return self.client.call_async('moveByVelocityBodyFrame', vx, vy, vz, duration, drivetrain, yaw_mode, vehicle_name)
1176
+
1177
+ def moveByVelocityZBodyFrameAsync(self, vx, vy, z, duration, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(), vehicle_name = ''):
1178
+ """
1179
+ Args:
1180
+ vx (float): desired velocity in the X axis of the vehicle's local NED frame
1181
+ vy (float): desired velocity in the Y axis of the vehicle's local NED frame
1182
+ z (float): desired Z value (in local NED frame of the vehicle)
1183
+ duration (float): Desired amount of time (seconds), to send this command for
1184
+ drivetrain (DrivetrainType, optional):
1185
+ yaw_mode (YawMode, optional):
1186
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1187
+
1188
+ Returns:
1189
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1190
+ """
1191
+
1192
+ return self.client.call_async('moveByVelocityZBodyFrame', vx, vy, z, duration, drivetrain, yaw_mode, vehicle_name)
1193
+
1194
+ def moveByAngleZAsync(self, pitch, roll, z, yaw, duration, vehicle_name = ''):
1195
+ logging.warning("moveByAngleZAsync API is deprecated, use moveByRollPitchYawZAsync() API instead")
1196
+ return self.client.call_async('moveByRollPitchYawZ', roll, -pitch, -yaw, z, duration, vehicle_name)
1197
+
1198
+ def moveByAngleThrottleAsync(self, pitch, roll, throttle, yaw_rate, duration, vehicle_name = ''):
1199
+ logging.warning("moveByAngleThrottleAsync API is deprecated, use moveByRollPitchYawrateThrottleAsync() API instead")
1200
+ return self.client.call_async('moveByRollPitchYawrateThrottle', roll, -pitch, -yaw_rate, throttle, duration, vehicle_name)
1201
+
1202
+ def moveByVelocityAsync(self, vx, vy, vz, duration, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(), vehicle_name = ''):
1203
+ """
1204
+ Args:
1205
+ vx (float): desired velocity in world (NED) X axis
1206
+ vy (float): desired velocity in world (NED) Y axis
1207
+ vz (float): desired velocity in world (NED) Z axis
1208
+ duration (float): Desired amount of time (seconds), to send this command for
1209
+ drivetrain (DrivetrainType, optional):
1210
+ yaw_mode (YawMode, optional):
1211
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1212
+
1213
+ Returns:
1214
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1215
+ """
1216
+ return self.client.call_async('moveByVelocity', vx, vy, vz, duration, drivetrain, yaw_mode, vehicle_name)
1217
+
1218
+ def moveByVelocityZAsync(self, vx, vy, z, duration, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(), vehicle_name = ''):
1219
+ return self.client.call_async('moveByVelocityZ', vx, vy, z, duration, drivetrain, yaw_mode, vehicle_name)
1220
+
1221
+ def moveOnPathAsync(self, path, velocity, timeout_sec = 3e+38, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(),
1222
+ lookahead = -1, adaptive_lookahead = 1, vehicle_name = ''):
1223
+ return self.client.call_async('moveOnPath', path, velocity, timeout_sec, drivetrain, yaw_mode, lookahead, adaptive_lookahead, vehicle_name)
1224
+
1225
+ def moveToPositionAsync(self, x, y, z, velocity, timeout_sec = 3e+38, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(),
1226
+ lookahead = -1, adaptive_lookahead = 1, vehicle_name = ''):
1227
+ return self.client.call_async('moveToPosition', x, y, z, velocity, timeout_sec, drivetrain, yaw_mode, lookahead, adaptive_lookahead, vehicle_name)
1228
+
1229
+ def moveToGPSAsync(self, latitude, longitude, altitude, velocity, timeout_sec = 3e+38, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(),
1230
+ lookahead = -1, adaptive_lookahead = 1, vehicle_name = ''):
1231
+ return self.client.call_async('moveToGPS', latitude, longitude, altitude, velocity, timeout_sec, drivetrain, yaw_mode, lookahead, adaptive_lookahead, vehicle_name)
1232
+
1233
+ def moveToZAsync(self, z, velocity, timeout_sec = 3e+38, yaw_mode = YawMode(), lookahead = -1, adaptive_lookahead = 1, vehicle_name = ''):
1234
+ return self.client.call_async('moveToZ', z, velocity, timeout_sec, yaw_mode, lookahead, adaptive_lookahead, vehicle_name)
1235
+
1236
+ def moveByManualAsync(self, vx_max, vy_max, z_min, duration, drivetrain = DrivetrainType.MaxDegreeOfFreedom, yaw_mode = YawMode(), vehicle_name = ''):
1237
+ """
1238
+ - Read current RC state and use it to control the vehicles.
1239
+
1240
+ Parameters sets up the constraints on velocity and minimum altitude while flying. If RC state is detected to violate these constraints
1241
+ then that RC state would be ignored.
1242
+
1243
+ Args:
1244
+ vx_max (float): max velocity allowed in x direction
1245
+ vy_max (float): max velocity allowed in y direction
1246
+ vz_max (float): max velocity allowed in z direction
1247
+ z_min (float): min z allowed for vehicle position
1248
+ duration (float): after this duration vehicle would switch back to non-manual mode
1249
+ drivetrain (DrivetrainType): when ForwardOnly, vehicle rotates itself so that its front is always facing the direction of travel. If MaxDegreeOfFreedom then it doesn't do that (crab-like movement)
1250
+ yaw_mode (YawMode): Specifies if vehicle should face at given angle (is_rate=False) or should be rotating around its axis at given rate (is_rate=True)
1251
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1252
+ Returns:
1253
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1254
+ """
1255
+ return self.client.call_async('moveByManual', vx_max, vy_max, z_min, duration, drivetrain, yaw_mode, vehicle_name)
1256
+
1257
+ def rotateToYawAsync(self, yaw, timeout_sec = 3e+38, margin = 5, vehicle_name = ''):
1258
+ return self.client.call_async('rotateToYaw', yaw, timeout_sec, margin, vehicle_name)
1259
+
1260
+ def rotateByYawRateAsync(self, yaw_rate, duration, vehicle_name = ''):
1261
+ return self.client.call_async('rotateByYawRate', yaw_rate, duration, vehicle_name)
1262
+
1263
+ def hoverAsync(self, vehicle_name = ''):
1264
+ return self.client.call_async('hover', vehicle_name)
1265
+
1266
+ def moveByRC(self, rcdata = RCData(), vehicle_name = ''):
1267
+ return self.client.call('moveByRC', rcdata, vehicle_name)
1268
+
1269
+ #low - level control API
1270
+ def moveByMotorPWMsAsync(self, front_right_pwm, rear_left_pwm, front_left_pwm, rear_right_pwm, duration, vehicle_name = ''):
1271
+ """
1272
+ - Directly control the motors using PWM values
1273
+
1274
+ Args:
1275
+ front_right_pwm (float): PWM value for the front right motor (between 0.0 to 1.0)
1276
+ rear_left_pwm (float): PWM value for the rear left motor (between 0.0 to 1.0)
1277
+ front_left_pwm (float): PWM value for the front left motor (between 0.0 to 1.0)
1278
+ rear_right_pwm (float): PWM value for the rear right motor (between 0.0 to 1.0)
1279
+ duration (float): Desired amount of time (seconds), to send this command for
1280
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1281
+ Returns:
1282
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1283
+ """
1284
+ return self.client.call_async('moveByMotorPWMs', front_right_pwm, rear_left_pwm, front_left_pwm, rear_right_pwm, duration, vehicle_name)
1285
+
1286
+ def moveByRollPitchYawZAsync(self, roll, pitch, yaw, z, duration, vehicle_name = ''):
1287
+ """
1288
+ - z is given in local NED frame of the vehicle.
1289
+ - Roll angle, pitch angle, and yaw angle set points are given in **radians**, in the body frame.
1290
+ - The body frame follows the Front Left Up (FLU) convention, and right-handedness.
1291
+
1292
+ - Frame Convention:
1293
+ - X axis is along the **Front** direction of the quadrotor.
1294
+
1295
+ | Clockwise rotation about this axis defines a positive **roll** angle.
1296
+ | Hence, rolling with a positive angle is equivalent to translating in the **right** direction, w.r.t. our FLU body frame.
1297
+
1298
+ - Y axis is along the **Left** direction of the quadrotor.
1299
+
1300
+ | Clockwise rotation about this axis defines a positive **pitch** angle.
1301
+ | Hence, pitching with a positive angle is equivalent to translating in the **front** direction, w.r.t. our FLU body frame.
1302
+
1303
+ - Z axis is along the **Up** direction.
1304
+
1305
+ | Clockwise rotation about this axis defines a positive **yaw** angle.
1306
+ | Hence, yawing with a positive angle is equivalent to rotated towards the **left** direction wrt our FLU body frame. Or in an anticlockwise fashion in the body XY / FL plane.
1307
+
1308
+ Args:
1309
+ roll (float): Desired roll angle, in radians.
1310
+ pitch (float): Desired pitch angle, in radians.
1311
+ yaw (float): Desired yaw angle, in radians.
1312
+ z (float): Desired Z value (in local NED frame of the vehicle)
1313
+ duration (float): Desired amount of time (seconds), to send this command for
1314
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1315
+
1316
+ Returns:
1317
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1318
+ """
1319
+ return self.client.call_async('moveByRollPitchYawZ', roll, -pitch, -yaw, z, duration, vehicle_name)
1320
+
1321
+ def moveByRollPitchYawThrottleAsync(self, roll, pitch, yaw, throttle, duration, vehicle_name = ''):
1322
+ """
1323
+ - Desired throttle is between 0.0 to 1.0
1324
+ - Roll angle, pitch angle, and yaw angle are given in **degrees** when using PX4 and in **radians** when using SimpleFlight, in the body frame.
1325
+ - The body frame follows the Front Left Up (FLU) convention, and right-handedness.
1326
+
1327
+ - Frame Convention:
1328
+ - X axis is along the **Front** direction of the quadrotor.
1329
+
1330
+ | Clockwise rotation about this axis defines a positive **roll** angle.
1331
+ | Hence, rolling with a positive angle is equivalent to translating in the **right** direction, w.r.t. our FLU body frame.
1332
+
1333
+ - Y axis is along the **Left** direction of the quadrotor.
1334
+
1335
+ | Clockwise rotation about this axis defines a positive **pitch** angle.
1336
+ | Hence, pitching with a positive angle is equivalent to translating in the **front** direction, w.r.t. our FLU body frame.
1337
+
1338
+ - Z axis is along the **Up** direction.
1339
+
1340
+ | Clockwise rotation about this axis defines a positive **yaw** angle.
1341
+ | Hence, yawing with a positive angle is equivalent to rotated towards the **left** direction wrt our FLU body frame. Or in an anticlockwise fashion in the body XY / FL plane.
1342
+
1343
+ Args:
1344
+ roll (float): Desired roll angle.
1345
+ pitch (float): Desired pitch angle.
1346
+ yaw (float): Desired yaw angle.
1347
+ throttle (float): Desired throttle (between 0.0 to 1.0)
1348
+ duration (float): Desired amount of time (seconds), to send this command for
1349
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1350
+
1351
+ Returns:
1352
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1353
+ """
1354
+ return self.client.call_async('moveByRollPitchYawThrottle', roll, -pitch, -yaw, throttle, duration, vehicle_name)
1355
+
1356
+ def moveByRollPitchYawrateThrottleAsync(self, roll, pitch, yaw_rate, throttle, duration, vehicle_name = ''):
1357
+ """
1358
+ - Desired throttle is between 0.0 to 1.0
1359
+ - Roll angle, pitch angle, and yaw rate set points are given in **radians**, in the body frame.
1360
+ - The body frame follows the Front Left Up (FLU) convention, and right-handedness.
1361
+
1362
+ - Frame Convention:
1363
+ - X axis is along the **Front** direction of the quadrotor.
1364
+
1365
+ | Clockwise rotation about this axis defines a positive **roll** angle.
1366
+ | Hence, rolling with a positive angle is equivalent to translating in the **right** direction, w.r.t. our FLU body frame.
1367
+
1368
+ - Y axis is along the **Left** direction of the quadrotor.
1369
+
1370
+ | Clockwise rotation about this axis defines a positive **pitch** angle.
1371
+ | Hence, pitching with a positive angle is equivalent to translating in the **front** direction, w.r.t. our FLU body frame.
1372
+
1373
+ - Z axis is along the **Up** direction.
1374
+
1375
+ | Clockwise rotation about this axis defines a positive **yaw** angle.
1376
+ | Hence, yawing with a positive angle is equivalent to rotated towards the **left** direction wrt our FLU body frame. Or in an anticlockwise fashion in the body XY / FL plane.
1377
+
1378
+ Args:
1379
+ roll (float): Desired roll angle, in radians.
1380
+ pitch (float): Desired pitch angle, in radians.
1381
+ yaw_rate (float): Desired yaw rate, in radian per second.
1382
+ throttle (float): Desired throttle (between 0.0 to 1.0)
1383
+ duration (float): Desired amount of time (seconds), to send this command for
1384
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1385
+
1386
+ Returns:
1387
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1388
+ """
1389
+ return self.client.call_async('moveByRollPitchYawrateThrottle', roll, -pitch, -yaw_rate, throttle, duration, vehicle_name)
1390
+
1391
+ def moveByRollPitchYawrateZAsync(self, roll, pitch, yaw_rate, z, duration, vehicle_name = ''):
1392
+ """
1393
+ - z is given in local NED frame of the vehicle.
1394
+ - Roll angle, pitch angle, and yaw rate set points are given in **radians**, in the body frame.
1395
+ - The body frame follows the Front Left Up (FLU) convention, and right-handedness.
1396
+
1397
+ - Frame Convention:
1398
+ - X axis is along the **Front** direction of the quadrotor.
1399
+
1400
+ | Clockwise rotation about this axis defines a positive **roll** angle.
1401
+ | Hence, rolling with a positive angle is equivalent to translating in the **right** direction, w.r.t. our FLU body frame.
1402
+
1403
+ - Y axis is along the **Left** direction of the quadrotor.
1404
+
1405
+ | Clockwise rotation about this axis defines a positive **pitch** angle.
1406
+ | Hence, pitching with a positive angle is equivalent to translating in the **front** direction, w.r.t. our FLU body frame.
1407
+
1408
+ - Z axis is along the **Up** direction.
1409
+
1410
+ | Clockwise rotation about this axis defines a positive **yaw** angle.
1411
+ | Hence, yawing with a positive angle is equivalent to rotated towards the **left** direction wrt our FLU body frame. Or in an anticlockwise fashion in the body XY / FL plane.
1412
+
1413
+ Args:
1414
+ roll (float): Desired roll angle, in radians.
1415
+ pitch (float): Desired pitch angle, in radians.
1416
+ yaw_rate (float): Desired yaw rate, in radian per second.
1417
+ z (float): Desired Z value (in local NED frame of the vehicle)
1418
+ duration (float): Desired amount of time (seconds), to send this command for
1419
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1420
+
1421
+ Returns:
1422
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1423
+ """
1424
+ return self.client.call_async('moveByRollPitchYawrateZ', roll, -pitch, -yaw_rate, z, duration, vehicle_name)
1425
+
1426
+ def moveByAngleRatesZAsync(self, roll_rate, pitch_rate, yaw_rate, z, duration, vehicle_name = ''):
1427
+ """
1428
+ - z is given in local NED frame of the vehicle.
1429
+ - Roll rate, pitch rate, and yaw rate set points are given in **radians**, in the body frame.
1430
+ - The body frame follows the Front Left Up (FLU) convention, and right-handedness.
1431
+
1432
+ - Frame Convention:
1433
+ - X axis is along the **Front** direction of the quadrotor.
1434
+
1435
+ | Clockwise rotation about this axis defines a positive **roll** angle.
1436
+ | Hence, rolling with a positive angle is equivalent to translating in the **right** direction, w.r.t. our FLU body frame.
1437
+
1438
+ - Y axis is along the **Left** direction of the quadrotor.
1439
+
1440
+ | Clockwise rotation about this axis defines a positive **pitch** angle.
1441
+ | Hence, pitching with a positive angle is equivalent to translating in the **front** direction, w.r.t. our FLU body frame.
1442
+
1443
+ - Z axis is along the **Up** direction.
1444
+
1445
+ | Clockwise rotation about this axis defines a positive **yaw** angle.
1446
+ | Hence, yawing with a positive angle is equivalent to rotated towards the **left** direction wrt our FLU body frame. Or in an anticlockwise fashion in the body XY / FL plane.
1447
+
1448
+ Args:
1449
+ roll_rate (float): Desired roll rate, in radians / second
1450
+ pitch_rate (float): Desired pitch rate, in radians / second
1451
+ yaw_rate (float): Desired yaw rate, in radians / second
1452
+ z (float): Desired Z value (in local NED frame of the vehicle)
1453
+ duration (float): Desired amount of time (seconds), to send this command for
1454
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1455
+
1456
+ Returns:
1457
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1458
+ """
1459
+ return self.client.call_async('moveByAngleRatesZ', roll_rate, -pitch_rate, -yaw_rate, z, duration, vehicle_name)
1460
+
1461
+ def moveByAngleRatesThrottleAsync(self, roll_rate, pitch_rate, yaw_rate, throttle, duration, vehicle_name = ''):
1462
+ """
1463
+ - Desired throttle is between 0.0 to 1.0
1464
+ - Roll rate, pitch rate, and yaw rate set points are given in **radians**, in the body frame.
1465
+ - The body frame follows the Front Left Up (FLU) convention, and right-handedness.
1466
+
1467
+ - Frame Convention:
1468
+ - X axis is along the **Front** direction of the quadrotor.
1469
+
1470
+ | Clockwise rotation about this axis defines a positive **roll** angle.
1471
+ | Hence, rolling with a positive angle is equivalent to translating in the **right** direction, w.r.t. our FLU body frame.
1472
+
1473
+ - Y axis is along the **Left** direction of the quadrotor.
1474
+
1475
+ | Clockwise rotation about this axis defines a positive **pitch** angle.
1476
+ | Hence, pitching with a positive angle is equivalent to translating in the **front** direction, w.r.t. our FLU body frame.
1477
+
1478
+ - Z axis is along the **Up** direction.
1479
+
1480
+ | Clockwise rotation about this axis defines a positive **yaw** angle.
1481
+ | Hence, yawing with a positive angle is equivalent to rotated towards the **left** direction wrt our FLU body frame. Or in an anticlockwise fashion in the body XY / FL plane.
1482
+
1483
+ Args:
1484
+ roll_rate (float): Desired roll rate, in radians / second
1485
+ pitch_rate (float): Desired pitch rate, in radians / second
1486
+ yaw_rate (float): Desired yaw rate, in radians / second
1487
+ throttle (float): Desired throttle (between 0.0 to 1.0)
1488
+ duration (float): Desired amount of time (seconds), to send this command for
1489
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1490
+
1491
+ Returns:
1492
+ msgpackrpc.future.Future: future. call .join() to wait for method to finish. Example: client.METHOD().join()
1493
+ """
1494
+ return self.client.call_async('moveByAngleRatesThrottle', roll_rate, -pitch_rate, -yaw_rate, throttle, duration, vehicle_name)
1495
+
1496
+ def setAngleRateControllerGains(self, angle_rate_gains=AngleRateControllerGains(), vehicle_name = ''):
1497
+ """
1498
+ - Modifying these gains will have an affect on *ALL* move*() APIs.
1499
+ This is because any velocity setpoint is converted to an angle level setpoint which is tracked with an angle level controllers.
1500
+ That angle level setpoint is itself tracked with and angle rate controller.
1501
+ - This function should only be called if the default angle rate control PID gains need to be modified.
1502
+
1503
+ Args:
1504
+ angle_rate_gains (AngleRateControllerGains):
1505
+ - Correspond to the roll, pitch, yaw axes, defined in the body frame.
1506
+ - Pass AngleRateControllerGains() to reset gains to default recommended values.
1507
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1508
+ """
1509
+ self.client.call('setAngleRateControllerGains', *(angle_rate_gains.to_lists()+(vehicle_name,)))
1510
+
1511
+ def setAngleLevelControllerGains(self, angle_level_gains=AngleLevelControllerGains(), vehicle_name = ''):
1512
+ """
1513
+ - Sets angle level controller gains (used by any API setting angle references - for ex: moveByRollPitchYawZAsync(), moveByRollPitchYawThrottleAsync(), etc)
1514
+ - Modifying these gains will also affect the behaviour of moveByVelocityAsync() API.
1515
+ This is because the AirSim flight controller will track velocity setpoints by converting them to angle set points.
1516
+ - This function should only be called if the default angle level control PID gains need to be modified.
1517
+ - Passing AngleLevelControllerGains() sets gains to default airsim values.
1518
+
1519
+ Args:
1520
+ angle_level_gains (AngleLevelControllerGains):
1521
+ - Correspond to the roll, pitch, yaw axes, defined in the body frame.
1522
+ - Pass AngleLevelControllerGains() to reset gains to default recommended values.
1523
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1524
+ """
1525
+ self.client.call('setAngleLevelControllerGains', *(angle_level_gains.to_lists()+(vehicle_name,)))
1526
+
1527
+ def setVelocityControllerGains(self, velocity_gains=VelocityControllerGains(), vehicle_name = ''):
1528
+ """
1529
+ - Sets velocity controller gains for moveByVelocityAsync().
1530
+ - This function should only be called if the default velocity control PID gains need to be modified.
1531
+ - Passing VelocityControllerGains() sets gains to default airsim values.
1532
+
1533
+ Args:
1534
+ velocity_gains (VelocityControllerGains):
1535
+ - Correspond to the world X, Y, Z axes.
1536
+ - Pass VelocityControllerGains() to reset gains to default recommended values.
1537
+ - Modifying velocity controller gains will have an affect on the behaviour of moveOnSplineAsync() and moveOnSplineVelConstraintsAsync(), as they both use velocity control to track the trajectory.
1538
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1539
+ """
1540
+ self.client.call('setVelocityControllerGains', *(velocity_gains.to_lists()+(vehicle_name,)))
1541
+
1542
+
1543
+ def setPositionControllerGains(self, position_gains=PositionControllerGains(), vehicle_name = ''):
1544
+ """
1545
+ Sets position controller gains for moveByPositionAsync.
1546
+ This function should only be called if the default position control PID gains need to be modified.
1547
+
1548
+ Args:
1549
+ position_gains (PositionControllerGains):
1550
+ - Correspond to the X, Y, Z axes.
1551
+ - Pass PositionControllerGains() to reset gains to default recommended values.
1552
+ vehicle_name (str, optional): Name of the multirotor to send this command to
1553
+ """
1554
+ self.client.call('setPositionControllerGains', *(position_gains.to_lists()+(vehicle_name,)))
1555
+
1556
+ #query vehicle state
1557
+ def getMultirotorState(self, vehicle_name = ''):
1558
+ """
1559
+ The position inside the returned MultirotorState is in the frame of the vehicle's starting point
1560
+
1561
+ Args:
1562
+ vehicle_name (str, optional): Vehicle to get the state of
1563
+
1564
+ Returns:
1565
+ MultirotorState:
1566
+ """
1567
+ return MultirotorState.from_msgpack(self.client.call('getMultirotorState', vehicle_name))
1568
+ getMultirotorState.__annotations__ = {'return': MultirotorState}
1569
+ #query rotor states
1570
+ def getRotorStates(self, vehicle_name = ''):
1571
+ """
1572
+ Used to obtain the current state of all a multirotor's rotors. The state includes the speeds,
1573
+ thrusts and torques for all rotors.
1574
+
1575
+ Args:
1576
+ vehicle_name (str, optional): Vehicle to get the rotor state of
1577
+
1578
+ Returns:
1579
+ RotorStates: Containing a timestamp and the speed, thrust and torque of all rotors.
1580
+ """
1581
+ return RotorStates.from_msgpack(self.client.call('getRotorStates', vehicle_name))
1582
+ getRotorStates.__annotations__ = {'return': RotorStates}
1583
+
1584
+ #----------------------------------- Car APIs ---------------------------------------------
1585
+ class CarClient(VehicleClient, object):
1586
+ def __init__(self, ip = "", port = 41451, timeout_value = 3600):
1587
+ super(CarClient, self).__init__(ip, port, timeout_value)
1588
+
1589
+ def setCarControls(self, controls, vehicle_name = ''):
1590
+ """
1591
+ Control the car using throttle, steering, brake, etc.
1592
+
1593
+ Args:
1594
+ controls (CarControls): Struct containing control values
1595
+ vehicle_name (str, optional): Name of vehicle to be controlled
1596
+ """
1597
+ self.client.call('setCarControls', controls, vehicle_name)
1598
+
1599
+ def getCarState(self, vehicle_name = ''):
1600
+ """
1601
+ The position inside the returned CarState is in the frame of the vehicle's starting point
1602
+
1603
+ Args:
1604
+ vehicle_name (str, optional): Name of vehicle
1605
+
1606
+ Returns:
1607
+ CarState:
1608
+ """
1609
+ state_raw = self.client.call('getCarState', vehicle_name)
1610
+ return CarState.from_msgpack(state_raw)
1611
+
1612
+ def getCarControls(self, vehicle_name=''):
1613
+ """
1614
+ Args:
1615
+ vehicle_name (str, optional): Name of vehicle
1616
+
1617
+ Returns:
1618
+ CarControls:
1619
+ """
1620
+ controls_raw = self.client.call('getCarControls', vehicle_name)
1621
+ return CarControls.from_msgpack(controls_raw)
airsim/pfm.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ import re
4
+ import sys
5
+ import pdb
6
+
7
+
8
+ def read_pfm(file):
9
+ """ Read a pfm file """
10
+ file = open(file, 'rb')
11
+
12
+ color = None
13
+ width = None
14
+ height = None
15
+ scale = None
16
+ endian = None
17
+
18
+ header = file.readline().rstrip()
19
+ header = str(bytes.decode(header, encoding='utf-8'))
20
+ if header == 'PF':
21
+ color = True
22
+ elif header == 'Pf':
23
+ color = False
24
+ else:
25
+ raise Exception('Not a PFM file.')
26
+
27
+ pattern = r'^(\d+)\s(\d+)\s$'
28
+ temp_str = str(bytes.decode(file.readline(), encoding='utf-8'))
29
+ dim_match = re.match(pattern, temp_str)
30
+ if dim_match:
31
+ width, height = map(int, dim_match.groups())
32
+ else:
33
+ temp_str += str(bytes.decode(file.readline(), encoding='utf-8'))
34
+ dim_match = re.match(pattern, temp_str)
35
+ if dim_match:
36
+ width, height = map(int, dim_match.groups())
37
+ else:
38
+ raise Exception('Malformed PFM header: width, height cannot be found')
39
+
40
+ scale = float(file.readline().rstrip())
41
+ if scale < 0: # little-endian
42
+ endian = '<'
43
+ scale = -scale
44
+ else:
45
+ endian = '>' # big-endian
46
+
47
+ data = np.fromfile(file, endian + 'f')
48
+ shape = (height, width, 3) if color else (height, width)
49
+
50
+ data = np.reshape(data, shape)
51
+ # DEY: I don't know why this was there.
52
+ file.close()
53
+
54
+ return data, scale
55
+
56
+
57
+ def write_pfm(file, image, scale=1):
58
+ """ Write a pfm file """
59
+ file = open(file, 'wb')
60
+
61
+ color = None
62
+
63
+ if image.dtype.name != 'float32':
64
+ raise Exception('Image dtype must be float32.')
65
+
66
+ if len(image.shape) == 3 and image.shape[2] == 3: # color image
67
+ color = True
68
+ elif len(image.shape) == 2 or len(image.shape) == 3 and image.shape[2] == 1: # greyscale
69
+ color = False
70
+ else:
71
+ raise Exception('Image must have H x W x 3, H x W x 1 or H x W dimensions.')
72
+
73
+ file.write(bytes('PF\n', 'UTF-8') if color else bytes('Pf\n', 'UTF-8'))
74
+ temp_str = '%d %d\n' % (image.shape[1], image.shape[0])
75
+ file.write(bytes(temp_str, 'UTF-8'))
76
+
77
+ endian = image.dtype.byteorder
78
+
79
+ if endian == '<' or endian == '=' and sys.byteorder == 'little':
80
+ scale = -scale
81
+
82
+ temp_str = '%f\n' % scale
83
+ file.write(bytes(temp_str, 'UTF-8'))
84
+
85
+ image.tofile(file)
airsim/types.py ADDED
@@ -0,0 +1,580 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import print_function
2
+ import msgpackrpc #install as admin: pip install msgpack-rpc-python
3
+ import numpy as np #pip install numpy
4
+ import math
5
+
6
+ class MsgpackMixin:
7
+ def __repr__(self):
8
+ from pprint import pformat
9
+ return "<" + type(self).__name__ + "> " + pformat(vars(self), indent=4, width=1)
10
+
11
+ def to_msgpack(self, *args, **kwargs):
12
+ return self.__dict__
13
+
14
+ @classmethod
15
+ def from_msgpack(cls, encoded):
16
+ obj = cls()
17
+ #obj.__dict__ = {k.decode('utf-8'): (from_msgpack(v.__class__, v) if hasattr(v, "__dict__") else v) for k, v in encoded.items()}
18
+ obj.__dict__ = { k : (v if not isinstance(v, dict) else getattr(getattr(obj, k).__class__, "from_msgpack")(v)) for k, v in encoded.items()}
19
+ #return cls(**msgpack.unpack(encoded))
20
+ return obj
21
+
22
+ class _ImageType(type):
23
+ @property
24
+ def Scene(cls):
25
+ return 0
26
+ def DepthPlanar(cls):
27
+ return 1
28
+ def DepthPerspective(cls):
29
+ return 2
30
+ def DepthVis(cls):
31
+ return 3
32
+ def DisparityNormalized(cls):
33
+ return 4
34
+ def Segmentation(cls):
35
+ return 5
36
+ def SurfaceNormals(cls):
37
+ return 6
38
+ def Infrared(cls):
39
+ return 7
40
+ def OpticalFlow(cls):
41
+ return 8
42
+ def OpticalFlowVis(cls):
43
+ return 9
44
+
45
+ def __getattr__(self, key):
46
+ if key == 'DepthPlanner':
47
+ print('\033[31m'+"DepthPlanner has been (correctly) renamed to DepthPlanar. Please use ImageType.DepthPlanar instead."+'\033[0m')
48
+ raise AttributeError
49
+
50
+ class ImageType(metaclass=_ImageType):
51
+ Scene = 0
52
+ DepthPlanar = 1
53
+ DepthPerspective = 2
54
+ DepthVis = 3
55
+ DisparityNormalized = 4
56
+ Segmentation = 5
57
+ SurfaceNormals = 6
58
+ Infrared = 7
59
+ OpticalFlow = 8
60
+ OpticalFlowVis = 9
61
+
62
+ class DrivetrainType:
63
+ MaxDegreeOfFreedom = 0
64
+ ForwardOnly = 1
65
+
66
+ class LandedState:
67
+ Landed = 0
68
+ Flying = 1
69
+
70
+ class WeatherParameter:
71
+ Rain = 0
72
+ Roadwetness = 1
73
+ Snow = 2
74
+ RoadSnow = 3
75
+ MapleLeaf = 4
76
+ RoadLeaf = 5
77
+ Dust = 6
78
+ Fog = 7
79
+ Enabled = 8
80
+
81
+ class Vector2r(MsgpackMixin):
82
+ x_val = 0.0
83
+ y_val = 0.0
84
+
85
+ def __init__(self, x_val = 0.0, y_val = 0.0):
86
+ self.x_val = x_val
87
+ self.y_val = y_val
88
+
89
+ class Vector3r(MsgpackMixin):
90
+ x_val = 0.0
91
+ y_val = 0.0
92
+ z_val = 0.0
93
+
94
+ def __init__(self, x_val = 0.0, y_val = 0.0, z_val = 0.0):
95
+ self.x_val = x_val
96
+ self.y_val = y_val
97
+ self.z_val = z_val
98
+
99
+ @staticmethod
100
+ def nanVector3r():
101
+ return Vector3r(np.nan, np.nan, np.nan)
102
+
103
+ def containsNan(self):
104
+ return (math.isnan(self.x_val) or math.isnan(self.y_val) or math.isnan(self.z_val))
105
+
106
+ def __add__(self, other):
107
+ return Vector3r(self.x_val + other.x_val, self.y_val + other.y_val, self.z_val + other.z_val)
108
+
109
+ def __sub__(self, other):
110
+ return Vector3r(self.x_val - other.x_val, self.y_val - other.y_val, self.z_val - other.z_val)
111
+
112
+ def __truediv__(self, other):
113
+ if type(other) in [int, float] + np.sctypes['int'] + np.sctypes['uint'] + np.sctypes['float']:
114
+ return Vector3r( self.x_val / other, self.y_val / other, self.z_val / other)
115
+ else:
116
+ raise TypeError('unsupported operand type(s) for /: %s and %s' % ( str(type(self)), str(type(other))) )
117
+
118
+ def __mul__(self, other):
119
+ if type(other) in [int, float] + np.sctypes['int'] + np.sctypes['uint'] + np.sctypes['float']:
120
+ return Vector3r(self.x_val*other, self.y_val*other, self.z_val*other)
121
+ else:
122
+ raise TypeError('unsupported operand type(s) for *: %s and %s' % ( str(type(self)), str(type(other))) )
123
+
124
+ def dot(self, other):
125
+ if type(self) == type(other):
126
+ return self.x_val*other.x_val + self.y_val*other.y_val + self.z_val*other.z_val
127
+ else:
128
+ raise TypeError('unsupported operand type(s) for \'dot\': %s and %s' % ( str(type(self)), str(type(other))) )
129
+
130
+ def cross(self, other):
131
+ if type(self) == type(other):
132
+ cross_product = np.cross(self.to_numpy_array(), other.to_numpy_array())
133
+ return Vector3r(cross_product[0], cross_product[1], cross_product[2])
134
+ else:
135
+ raise TypeError('unsupported operand type(s) for \'cross\': %s and %s' % ( str(type(self)), str(type(other))) )
136
+
137
+ def get_length(self):
138
+ return ( self.x_val**2 + self.y_val**2 + self.z_val**2 )**0.5
139
+
140
+ def distance_to(self, other):
141
+ return ( (self.x_val-other.x_val)**2 + (self.y_val-other.y_val)**2 + (self.z_val-other.z_val)**2 )**0.5
142
+
143
+ def to_Quaternionr(self):
144
+ return Quaternionr(self.x_val, self.y_val, self.z_val, 0)
145
+
146
+ def to_numpy_array(self):
147
+ return np.array([self.x_val, self.y_val, self.z_val], dtype=np.float32)
148
+
149
+ def __iter__(self):
150
+ return iter((self.x_val, self.y_val, self.z_val))
151
+
152
+ class Quaternionr(MsgpackMixin):
153
+ w_val = 0.0
154
+ x_val = 0.0
155
+ y_val = 0.0
156
+ z_val = 0.0
157
+
158
+ def __init__(self, x_val = 0.0, y_val = 0.0, z_val = 0.0, w_val = 1.0):
159
+ self.x_val = x_val
160
+ self.y_val = y_val
161
+ self.z_val = z_val
162
+ self.w_val = w_val
163
+
164
+ @staticmethod
165
+ def nanQuaternionr():
166
+ return Quaternionr(np.nan, np.nan, np.nan, np.nan)
167
+
168
+ def containsNan(self):
169
+ return (math.isnan(self.w_val) or math.isnan(self.x_val) or math.isnan(self.y_val) or math.isnan(self.z_val))
170
+
171
+ def __add__(self, other):
172
+ if type(self) == type(other):
173
+ return Quaternionr( self.x_val+other.x_val, self.y_val+other.y_val, self.z_val+other.z_val, self.w_val+other.w_val )
174
+ else:
175
+ raise TypeError('unsupported operand type(s) for +: %s and %s' % ( str(type(self)), str(type(other))) )
176
+
177
+ def __mul__(self, other):
178
+ if type(self) == type(other):
179
+ t, x, y, z = self.w_val, self.x_val, self.y_val, self.z_val
180
+ a, b, c, d = other.w_val, other.x_val, other.y_val, other.z_val
181
+ return Quaternionr( w_val = a*t - b*x - c*y - d*z,
182
+ x_val = b*t + a*x + d*y - c*z,
183
+ y_val = c*t + a*y + b*z - d*x,
184
+ z_val = d*t + z*a + c*x - b*y)
185
+ else:
186
+ raise TypeError('unsupported operand type(s) for *: %s and %s' % ( str(type(self)), str(type(other))) )
187
+
188
+ def __truediv__(self, other):
189
+ if type(other) == type(self):
190
+ return self * other.inverse()
191
+ elif type(other) in [int, float] + np.sctypes['int'] + np.sctypes['uint'] + np.sctypes['float']:
192
+ return Quaternionr( self.x_val / other, self.y_val / other, self.z_val / other, self.w_val / other)
193
+ else:
194
+ raise TypeError('unsupported operand type(s) for /: %s and %s' % ( str(type(self)), str(type(other))) )
195
+
196
+ def dot(self, other):
197
+ if type(self) == type(other):
198
+ return self.x_val*other.x_val + self.y_val*other.y_val + self.z_val*other.z_val + self.w_val*other.w_val
199
+ else:
200
+ raise TypeError('unsupported operand type(s) for \'dot\': %s and %s' % ( str(type(self)), str(type(other))) )
201
+
202
+ def cross(self, other):
203
+ if type(self) == type(other):
204
+ return (self * other - other * self) / 2
205
+ else:
206
+ raise TypeError('unsupported operand type(s) for \'cross\': %s and %s' % ( str(type(self)), str(type(other))) )
207
+
208
+ def outer_product(self, other):
209
+ if type(self) == type(other):
210
+ return ( self.inverse()*other - other.inverse()*self ) / 2
211
+ else:
212
+ raise TypeError('unsupported operand type(s) for \'outer_product\': %s and %s' % ( str(type(self)), str(type(other))) )
213
+
214
+ def rotate(self, other):
215
+ if type(self) == type(other):
216
+ if other.get_length() == 1:
217
+ return other * self * other.inverse()
218
+ else:
219
+ raise ValueError('length of the other Quaternionr must be 1')
220
+ else:
221
+ raise TypeError('unsupported operand type(s) for \'rotate\': %s and %s' % ( str(type(self)), str(type(other))) )
222
+
223
+ def conjugate(self):
224
+ return Quaternionr(-self.x_val, -self.y_val, -self.z_val, self.w_val)
225
+
226
+ def star(self):
227
+ return self.conjugate()
228
+
229
+ def inverse(self):
230
+ return self.star() / self.dot(self)
231
+
232
+ def sgn(self):
233
+ return self/self.get_length()
234
+
235
+ def get_length(self):
236
+ return ( self.x_val**2 + self.y_val**2 + self.z_val**2 + self.w_val**2 )**0.5
237
+
238
+ def to_numpy_array(self):
239
+ return np.array([self.x_val, self.y_val, self.z_val, self.w_val], dtype=np.float32)
240
+
241
+ def __iter__(self):
242
+ return iter((self.x_val, self.y_val, self.z_val, self.w_val))
243
+
244
+ class Pose(MsgpackMixin):
245
+ position = Vector3r()
246
+ orientation = Quaternionr()
247
+
248
+ def __init__(self, position_val = None, orientation_val = None):
249
+ position_val = position_val if position_val is not None else Vector3r()
250
+ orientation_val = orientation_val if orientation_val is not None else Quaternionr()
251
+ self.position = position_val
252
+ self.orientation = orientation_val
253
+
254
+ @staticmethod
255
+ def nanPose():
256
+ return Pose(Vector3r.nanVector3r(), Quaternionr.nanQuaternionr())
257
+
258
+ def containsNan(self):
259
+ return (self.position.containsNan() or self.orientation.containsNan())
260
+
261
+ def __iter__(self):
262
+ return iter((self.position, self.orientation))
263
+
264
+ class CollisionInfo(MsgpackMixin):
265
+ has_collided = False
266
+ normal = Vector3r()
267
+ impact_point = Vector3r()
268
+ position = Vector3r()
269
+ penetration_depth = 0.0
270
+ time_stamp = 0.0
271
+ object_name = ""
272
+ object_id = -1
273
+
274
+ class GeoPoint(MsgpackMixin):
275
+ latitude = 0.0
276
+ longitude = 0.0
277
+ altitude = 0.0
278
+
279
+ class YawMode(MsgpackMixin):
280
+ is_rate = True
281
+ yaw_or_rate = 0.0
282
+ def __init__(self, is_rate = True, yaw_or_rate = 0.0):
283
+ self.is_rate = is_rate
284
+ self.yaw_or_rate = yaw_or_rate
285
+
286
+ class RCData(MsgpackMixin):
287
+ timestamp = 0
288
+ pitch, roll, throttle, yaw = (0.0,)*4 #init 4 variable to 0.0
289
+ switch1, switch2, switch3, switch4 = (0,)*4
290
+ switch5, switch6, switch7, switch8 = (0,)*4
291
+ is_initialized = False
292
+ is_valid = False
293
+ def __init__(self, timestamp = 0, pitch = 0.0, roll = 0.0, throttle = 0.0, yaw = 0.0, switch1 = 0,
294
+ switch2 = 0, switch3 = 0, switch4 = 0, switch5 = 0, switch6 = 0, switch7 = 0, switch8 = 0, is_initialized = False, is_valid = False):
295
+ self.timestamp = timestamp
296
+ self.pitch = pitch
297
+ self.roll = roll
298
+ self.throttle = throttle
299
+ self.yaw = yaw
300
+ self.switch1 = switch1
301
+ self.switch2 = switch2
302
+ self.switch3 = switch3
303
+ self.switch4 = switch4
304
+ self.switch5 = switch5
305
+ self.switch6 = switch6
306
+ self.switch7 = switch7
307
+ self.switch8 = switch8
308
+ self.is_initialized = is_initialized
309
+ self.is_valid = is_valid
310
+
311
+ class ImageRequest(MsgpackMixin):
312
+ camera_name = '0'
313
+ image_type = ImageType.Scene
314
+ pixels_as_float = False
315
+ compress = False
316
+
317
+ def __init__(self, camera_name, image_type, pixels_as_float = False, compress = True):
318
+ # todo: in future remove str(), it's only for compatibility to pre v1.2
319
+ self.camera_name = str(camera_name)
320
+ self.image_type = image_type
321
+ self.pixels_as_float = pixels_as_float
322
+ self.compress = compress
323
+
324
+
325
+ class ImageResponse(MsgpackMixin):
326
+ image_data_uint8 = np.uint8(0)
327
+ image_data_float = 0.0
328
+ camera_position = Vector3r()
329
+ camera_orientation = Quaternionr()
330
+ time_stamp = np.uint64(0)
331
+ message = ''
332
+ pixels_as_float = 0.0
333
+ compress = True
334
+ width = 0
335
+ height = 0
336
+ image_type = ImageType.Scene
337
+
338
+ class CarControls(MsgpackMixin):
339
+ throttle = 0.0
340
+ steering = 0.0
341
+ brake = 0.0
342
+ handbrake = False
343
+ is_manual_gear = False
344
+ manual_gear = 0
345
+ gear_immediate = True
346
+
347
+ def __init__(self, throttle = 0, steering = 0, brake = 0,
348
+ handbrake = False, is_manual_gear = False, manual_gear = 0, gear_immediate = True):
349
+ self.throttle = throttle
350
+ self.steering = steering
351
+ self.brake = brake
352
+ self.handbrake = handbrake
353
+ self.is_manual_gear = is_manual_gear
354
+ self.manual_gear = manual_gear
355
+ self.gear_immediate = gear_immediate
356
+
357
+
358
+ def set_throttle(self, throttle_val, forward):
359
+ if (forward):
360
+ self.is_manual_gear = False
361
+ self.manual_gear = 0
362
+ self.throttle = abs(throttle_val)
363
+ else:
364
+ self.is_manual_gear = False
365
+ self.manual_gear = -1
366
+ self.throttle = - abs(throttle_val)
367
+
368
+ class KinematicsState(MsgpackMixin):
369
+ position = Vector3r()
370
+ orientation = Quaternionr()
371
+ linear_velocity = Vector3r()
372
+ angular_velocity = Vector3r()
373
+ linear_acceleration = Vector3r()
374
+ angular_acceleration = Vector3r()
375
+
376
+ class EnvironmentState(MsgpackMixin):
377
+ position = Vector3r()
378
+ geo_point = GeoPoint()
379
+ gravity = Vector3r()
380
+ air_pressure = 0.0
381
+ temperature = 0.0
382
+ air_density = 0.0
383
+
384
+ class CarState(MsgpackMixin):
385
+ speed = 0.0
386
+ gear = 0
387
+ rpm = 0.0
388
+ maxrpm = 0.0
389
+ handbrake = False
390
+ collision = CollisionInfo()
391
+ kinematics_estimated = KinematicsState()
392
+ timestamp = np.uint64(0)
393
+
394
+ class MultirotorState(MsgpackMixin):
395
+ collision = CollisionInfo()
396
+ kinematics_estimated = KinematicsState()
397
+ gps_location = GeoPoint()
398
+ timestamp = np.uint64(0)
399
+ landed_state = LandedState.Landed
400
+ rc_data = RCData()
401
+ ready = False
402
+ ready_message = ""
403
+ can_arm = False
404
+
405
+ class RotorStates(MsgpackMixin):
406
+ timestamp = np.uint64(0)
407
+ rotors = []
408
+
409
+ class ProjectionMatrix(MsgpackMixin):
410
+ matrix = []
411
+
412
+ class CameraInfo(MsgpackMixin):
413
+ pose = Pose()
414
+ fov = -1
415
+ proj_mat = ProjectionMatrix()
416
+
417
+ class LidarData(MsgpackMixin):
418
+ point_cloud = 0.0
419
+ time_stamp = np.uint64(0)
420
+ pose = Pose()
421
+ segmentation = 0
422
+
423
+ class ImuData(MsgpackMixin):
424
+ time_stamp = np.uint64(0)
425
+ orientation = Quaternionr()
426
+ angular_velocity = Vector3r()
427
+ linear_acceleration = Vector3r()
428
+
429
+ class BarometerData(MsgpackMixin):
430
+ time_stamp = np.uint64(0)
431
+ altitude = Quaternionr()
432
+ pressure = Vector3r()
433
+ qnh = Vector3r()
434
+
435
+ class MagnetometerData(MsgpackMixin):
436
+ time_stamp = np.uint64(0)
437
+ magnetic_field_body = Vector3r()
438
+ magnetic_field_covariance = 0.0
439
+
440
+ class GnssFixType(MsgpackMixin):
441
+ GNSS_FIX_NO_FIX = 0
442
+ GNSS_FIX_TIME_ONLY = 1
443
+ GNSS_FIX_2D_FIX = 2
444
+ GNSS_FIX_3D_FIX = 3
445
+
446
+ class GnssReport(MsgpackMixin):
447
+ geo_point = GeoPoint()
448
+ eph = 0.0
449
+ epv = 0.0
450
+ velocity = Vector3r()
451
+ fix_type = GnssFixType()
452
+ time_utc = np.uint64(0)
453
+
454
+ class GpsData(MsgpackMixin):
455
+ time_stamp = np.uint64(0)
456
+ gnss = GnssReport()
457
+ is_valid = False
458
+
459
+ class DistanceSensorData(MsgpackMixin):
460
+ time_stamp = np.uint64(0)
461
+ distance = 0.0
462
+ min_distance = 0.0
463
+ max_distance = 0.0
464
+ relative_pose = Pose()
465
+
466
+ class Box2D(MsgpackMixin):
467
+ min = Vector2r()
468
+ max = Vector2r()
469
+
470
+ class Box3D(MsgpackMixin):
471
+ min = Vector3r()
472
+ max = Vector3r()
473
+
474
+ class DetectionInfo(MsgpackMixin):
475
+ name = ''
476
+ geo_point = GeoPoint()
477
+ box2D = Box2D()
478
+ box3D = Box3D()
479
+ relative_pose = Pose()
480
+
481
+ class PIDGains():
482
+ """
483
+ Struct to store values of PID gains. Used to transmit controller gain values while instantiating
484
+ AngleLevel/AngleRate/Velocity/PositionControllerGains objects.
485
+
486
+ Attributes:
487
+ kP (float): Proportional gain
488
+ kI (float): Integrator gain
489
+ kD (float): Derivative gain
490
+ """
491
+ def __init__(self, kp, ki, kd):
492
+ self.kp = kp
493
+ self.ki = ki
494
+ self.kd = kd
495
+
496
+ def to_list(self):
497
+ return [self.kp, self.ki, self.kd]
498
+
499
+ class AngleRateControllerGains():
500
+ """
501
+ Struct to contain controller gains used by angle level PID controller
502
+
503
+ Attributes:
504
+ roll_gains (PIDGains): kP, kI, kD for roll axis
505
+ pitch_gains (PIDGains): kP, kI, kD for pitch axis
506
+ yaw_gains (PIDGains): kP, kI, kD for yaw axis
507
+ """
508
+ def __init__(self, roll_gains = PIDGains(0.25, 0, 0),
509
+ pitch_gains = PIDGains(0.25, 0, 0),
510
+ yaw_gains = PIDGains(0.25, 0, 0)):
511
+ self.roll_gains = roll_gains
512
+ self.pitch_gains = pitch_gains
513
+ self.yaw_gains = yaw_gains
514
+
515
+ def to_lists(self):
516
+ return [self.roll_gains.kp, self.pitch_gains.kp, self.yaw_gains.kp], [self.roll_gains.ki, self.pitch_gains.ki, self.yaw_gains.ki], [self.roll_gains.kd, self.pitch_gains.kd, self.yaw_gains.kd]
517
+
518
+ class AngleLevelControllerGains():
519
+ """
520
+ Struct to contain controller gains used by angle rate PID controller
521
+
522
+ Attributes:
523
+ roll_gains (PIDGains): kP, kI, kD for roll axis
524
+ pitch_gains (PIDGains): kP, kI, kD for pitch axis
525
+ yaw_gains (PIDGains): kP, kI, kD for yaw axis
526
+ """
527
+ def __init__(self, roll_gains = PIDGains(2.5, 0, 0),
528
+ pitch_gains = PIDGains(2.5, 0, 0),
529
+ yaw_gains = PIDGains(2.5, 0, 0)):
530
+ self.roll_gains = roll_gains
531
+ self.pitch_gains = pitch_gains
532
+ self.yaw_gains = yaw_gains
533
+
534
+ def to_lists(self):
535
+ return [self.roll_gains.kp, self.pitch_gains.kp, self.yaw_gains.kp], [self.roll_gains.ki, self.pitch_gains.ki, self.yaw_gains.ki], [self.roll_gains.kd, self.pitch_gains.kd, self.yaw_gains.kd]
536
+
537
+ class VelocityControllerGains():
538
+ """
539
+ Struct to contain controller gains used by velocity PID controller
540
+
541
+ Attributes:
542
+ x_gains (PIDGains): kP, kI, kD for X axis
543
+ y_gains (PIDGains): kP, kI, kD for Y axis
544
+ z_gains (PIDGains): kP, kI, kD for Z axis
545
+ """
546
+ def __init__(self, x_gains = PIDGains(0.2, 0, 0),
547
+ y_gains = PIDGains(0.2, 0, 0),
548
+ z_gains = PIDGains(2.0, 2.0, 0)):
549
+ self.x_gains = x_gains
550
+ self.y_gains = y_gains
551
+ self.z_gains = z_gains
552
+
553
+ def to_lists(self):
554
+ return [self.x_gains.kp, self.y_gains.kp, self.z_gains.kp], [self.x_gains.ki, self.y_gains.ki, self.z_gains.ki], [self.x_gains.kd, self.y_gains.kd, self.z_gains.kd]
555
+
556
+ class PositionControllerGains():
557
+ """
558
+ Struct to contain controller gains used by position PID controller
559
+
560
+ Attributes:
561
+ x_gains (PIDGains): kP, kI, kD for X axis
562
+ y_gains (PIDGains): kP, kI, kD for Y axis
563
+ z_gains (PIDGains): kP, kI, kD for Z axis
564
+ """
565
+ def __init__(self, x_gains = PIDGains(0.25, 0, 0),
566
+ y_gains = PIDGains(0.25, 0, 0),
567
+ z_gains = PIDGains(0.25, 0, 0)):
568
+ self.x_gains = x_gains
569
+ self.y_gains = y_gains
570
+ self.z_gains = z_gains
571
+
572
+ def to_lists(self):
573
+ return [self.x_gains.kp, self.y_gains.kp, self.z_gains.kp], [self.x_gains.ki, self.y_gains.ki, self.z_gains.ki], [self.x_gains.kd, self.y_gains.kd, self.z_gains.kd]
574
+
575
+ class MeshPositionVertexBuffersResponse(MsgpackMixin):
576
+ position = Vector3r()
577
+ orientation = Quaternionr()
578
+ vertices = 0.0
579
+ indices = 0.0
580
+ name = ''
airsim/utils.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np #pip install numpy
2
+ import math
3
+ import time
4
+ import sys
5
+ import os
6
+ import inspect
7
+ import types
8
+ import re
9
+ import logging
10
+
11
+ from .types import *
12
+
13
+
14
+ def string_to_uint8_array(bstr):
15
+ return np.fromstring(bstr, np.uint8)
16
+
17
+ def string_to_float_array(bstr):
18
+ return np.fromstring(bstr, np.float32)
19
+
20
+ def list_to_2d_float_array(flst, width, height):
21
+ return np.reshape(np.asarray(flst, np.float32), (height, width))
22
+
23
+ def get_pfm_array(response):
24
+ return list_to_2d_float_array(response.image_data_float, response.width, response.height)
25
+
26
+
27
+ def get_public_fields(obj):
28
+ return [attr for attr in dir(obj)
29
+ if not (attr.startswith("_")
30
+ or inspect.isbuiltin(attr)
31
+ or inspect.isfunction(attr)
32
+ or inspect.ismethod(attr))]
33
+
34
+
35
+
36
+ def to_dict(obj):
37
+ return dict([attr, getattr(obj, attr)] for attr in get_public_fields(obj))
38
+
39
+
40
+ def to_str(obj):
41
+ return str(to_dict(obj))
42
+
43
+
44
+ def write_file(filename, bstr):
45
+ """
46
+ Write binary data to file.
47
+ Used for writing compressed PNG images
48
+ """
49
+ with open(filename, 'wb') as afile:
50
+ afile.write(bstr)
51
+
52
+ # helper method for converting getOrientation to roll/pitch/yaw
53
+ # https:#en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
54
+
55
+ def to_eularian_angles(q):
56
+ z = q.z_val
57
+ y = q.y_val
58
+ x = q.x_val
59
+ w = q.w_val
60
+ ysqr = y * y
61
+
62
+ # roll (x-axis rotation)
63
+ t0 = +2.0 * (w*x + y*z)
64
+ t1 = +1.0 - 2.0*(x*x + ysqr)
65
+ roll = math.atan2(t0, t1)
66
+
67
+ # pitch (y-axis rotation)
68
+ t2 = +2.0 * (w*y - z*x)
69
+ if (t2 > 1.0):
70
+ t2 = 1
71
+ if (t2 < -1.0):
72
+ t2 = -1.0
73
+ pitch = math.asin(t2)
74
+
75
+ # yaw (z-axis rotation)
76
+ t3 = +2.0 * (w*z + x*y)
77
+ t4 = +1.0 - 2.0 * (ysqr + z*z)
78
+ yaw = math.atan2(t3, t4)
79
+
80
+ return (pitch, roll, yaw)
81
+
82
+
83
+ def to_quaternion(pitch, roll, yaw):
84
+ t0 = math.cos(yaw * 0.5)
85
+ t1 = math.sin(yaw * 0.5)
86
+ t2 = math.cos(roll * 0.5)
87
+ t3 = math.sin(roll * 0.5)
88
+ t4 = math.cos(pitch * 0.5)
89
+ t5 = math.sin(pitch * 0.5)
90
+
91
+ q = Quaternionr()
92
+ q.w_val = t0 * t2 * t4 + t1 * t3 * t5 #w
93
+ q.x_val = t0 * t3 * t4 - t1 * t2 * t5 #x
94
+ q.y_val = t0 * t2 * t5 + t1 * t3 * t4 #y
95
+ q.z_val = t1 * t2 * t4 - t0 * t3 * t5 #z
96
+ return q
97
+
98
+
99
+ def wait_key(message = ''):
100
+ ''' Wait for a key press on the console and return it. '''
101
+ if message != '':
102
+ print (message)
103
+
104
+ result = None
105
+ if os.name == 'nt':
106
+ import msvcrt
107
+ result = msvcrt.getch()
108
+ else:
109
+ import termios
110
+ fd = sys.stdin.fileno()
111
+
112
+ oldterm = termios.tcgetattr(fd)
113
+ newattr = termios.tcgetattr(fd)
114
+ newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
115
+ termios.tcsetattr(fd, termios.TCSANOW, newattr)
116
+
117
+ try:
118
+ result = sys.stdin.read(1)
119
+ except IOError:
120
+ pass
121
+ finally:
122
+ termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
123
+
124
+ return result
125
+
126
+
127
+ def read_pfm(file):
128
+ """ Read a pfm file """
129
+ file = open(file, 'rb')
130
+
131
+ color = None
132
+ width = None
133
+ height = None
134
+ scale = None
135
+ endian = None
136
+
137
+ header = file.readline().rstrip()
138
+ header = str(bytes.decode(header, encoding='utf-8'))
139
+ if header == 'PF':
140
+ color = True
141
+ elif header == 'Pf':
142
+ color = False
143
+ else:
144
+ raise Exception('Not a PFM file.')
145
+
146
+ temp_str = str(bytes.decode(file.readline(), encoding='utf-8'))
147
+ dim_match = re.match(r'^(\d+)\s(\d+)\s$', temp_str)
148
+ if dim_match:
149
+ width, height = map(int, dim_match.groups())
150
+ else:
151
+ raise Exception('Malformed PFM header.')
152
+
153
+ scale = float(file.readline().rstrip())
154
+ if scale < 0: # little-endian
155
+ endian = '<'
156
+ scale = -scale
157
+ else:
158
+ endian = '>' # big-endian
159
+
160
+ data = np.fromfile(file, endian + 'f')
161
+ shape = (height, width, 3) if color else (height, width)
162
+
163
+ data = np.reshape(data, shape)
164
+ # DEY: I don't know why this was there.
165
+ file.close()
166
+
167
+ return data, scale
168
+
169
+
170
+ def write_pfm(file, image, scale=1):
171
+ """ Write a pfm file """
172
+ file = open(file, 'wb')
173
+
174
+ color = None
175
+
176
+ if image.dtype.name != 'float32':
177
+ raise Exception('Image dtype must be float32.')
178
+
179
+ if len(image.shape) == 3 and image.shape[2] == 3: # color image
180
+ color = True
181
+ elif len(image.shape) == 2 or len(image.shape) == 3 and image.shape[2] == 1: # grayscale
182
+ color = False
183
+ else:
184
+ raise Exception('Image must have H x W x 3, H x W x 1 or H x W dimensions.')
185
+
186
+ file.write('PF\n'.encode('utf-8') if color else 'Pf\n'.encode('utf-8'))
187
+ temp_str = '%d %d\n' % (image.shape[1], image.shape[0])
188
+ file.write(temp_str.encode('utf-8'))
189
+
190
+ endian = image.dtype.byteorder
191
+
192
+ if endian == '<' or endian == '=' and sys.byteorder == 'little':
193
+ scale = -scale
194
+
195
+ temp_str = '%f\n' % scale
196
+ file.write(temp_str.encode('utf-8'))
197
+
198
+ image.tofile(file)
199
+
200
+
201
+ def write_png(filename, image):
202
+ """ image must be numpy array H X W X channels
203
+ """
204
+ import cv2 # pip install opencv-python
205
+
206
+ ret = cv2.imwrite(filename, image)
207
+ if not ret:
208
+ logging.error(f"Writing PNG file {filename} failed")
create_ir_segmentation_map.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy
2
+ import cv2
3
+ import time
4
+ import sys
5
+ import os
6
+ import random
7
+ from airsim import *
8
+
9
+ def radiance(absoluteTemperature, emissivity, dx=0.01, response=None):
10
+ """
11
+ title::
12
+ radiance
13
+
14
+ description::
15
+ Calculates radiance and integrated radiance over a bandpass of 8 to 14
16
+ microns, given temperature and emissivity, using Planck's Law.
17
+
18
+ inputs::
19
+ absoluteTemperature
20
+ temperture of object in [K]
21
+
22
+ either a single temperature or a numpy
23
+ array of temperatures, of shape (temperatures.shape[0], 1)
24
+ emissivity
25
+ average emissivity (number between 0 and 1 representing the
26
+ efficiency with which it emits radiation; if 1, it is an ideal
27
+ blackbody) of object over the bandpass
28
+
29
+ either a single emissivity or a numpy array of emissivities, of
30
+ shape (emissivities.shape[0], 1)
31
+ dx
32
+ discrete spacing between the wavelengths for evaluation of
33
+ radiance and integration [default is 0.1]
34
+ response
35
+ optional response of the camera over the bandpass of 8 to 14
36
+ microns [default is None, for no response provided]
37
+
38
+ returns::
39
+ radiance
40
+ discrete spectrum of radiance over bandpass
41
+ integratedRadiance
42
+ integration of radiance spectrum over bandpass (to simulate
43
+ the readout from a sensor)
44
+
45
+ author::
46
+ Elizabeth Bondi
47
+ """
48
+ wavelength = numpy.arange(8,14,dx)
49
+ c1 = 1.19104e8 # (2 * 6.62607*10^-34 [Js] *
50
+ # (2.99792458 * 10^14 [micron/s])^2 * 10^12 to convert
51
+ # denominator from microns^3 to microns * m^2)
52
+ c2 = 1.43879e4 # (hc/k) [micron * K]
53
+ if response is not None:
54
+ radiance = response * emissivity * (c1 / ((wavelength**5) * \
55
+ (numpy.exp(c2 / (wavelength * absoluteTemperature )) - 1)))
56
+ else:
57
+ radiance = emissivity * (c1 / ((wavelength**5) * (numpy.exp(c2 / \
58
+ (wavelength * absoluteTemperature )) - 1)))
59
+ if absoluteTemperature.ndim > 1:
60
+ return radiance, numpy.trapz(radiance, dx=dx, axis=1)
61
+ else:
62
+ return radiance, numpy.trapz(radiance, dx=dx)
63
+
64
+
65
+ def get_new_temp_emiss_from_radiance(tempEmissivity, response):
66
+ """
67
+ title::
68
+ get_new_temp_emiss_from_radiance
69
+
70
+ description::
71
+ Transform tempEmissivity from [objectName, temperature, emissivity]
72
+ to [objectName, "radiance"] using radiance calculation above.
73
+
74
+ input::
75
+ tempEmissivity
76
+ numpy array containing the temperature and emissivity of each
77
+ object (e.g., each row has: [objectName, temperature, emissivity])
78
+ response
79
+ camera response (same input as radiance, set to None if lacking
80
+ this information)
81
+
82
+ returns::
83
+ tempEmissivityNew
84
+ tempEmissivity, now with [objectName, "radiance"]; note that
85
+ integrated radiance (L) is divided by the maximum and multiplied
86
+ by 255 in order to simulate an 8 bit digital count observed by the
87
+ thermal sensor, since radiance and digital count are linearly
88
+ related, so it's [objectName, simulated thermal digital count]
89
+
90
+ author::
91
+ Elizabeth Bondi
92
+ """
93
+ numObjects = tempEmissivity.shape[0]
94
+
95
+ L = radiance(tempEmissivity[:,1].reshape((-1,1)).astype(numpy.float64),
96
+ tempEmissivity[:,2].reshape((-1,1)).astype(numpy.float64),
97
+ response=response)[1].flatten()
98
+ L = ((L / L.max()) * 255).astype(numpy.uint8)
99
+
100
+ tempEmissivityNew = numpy.hstack((
101
+ tempEmissivity[:,0].reshape((numObjects,1)),
102
+ L.reshape((numObjects,1))))
103
+
104
+ return tempEmissivityNew
105
+
106
+ def set_segmentation_ids(segIdDict, tempEmissivityNew, client):
107
+ """
108
+ title::
109
+ set_segmentation_ids
110
+
111
+ description::
112
+ Set stencil IDs in environment so that stencil IDs correspond to
113
+ simulated thermal digital counts (e.g., if elephant has a simulated
114
+ digital count of 219, set stencil ID to 219).
115
+
116
+ input::
117
+ segIdDict
118
+ dictionary mapping environment object names to the object names in
119
+ the first column of tempEmissivityNew
120
+ tempEmissivityNew
121
+ numpy array containing object names and corresponding simulated
122
+ thermal digital count
123
+ client
124
+ connection to AirSim (e.g., client = MultirotorClient() for UAV)
125
+
126
+ author::
127
+ Elizabeth Bondi
128
+ """
129
+
130
+ #First set everything to 0.
131
+ success = client.simSetSegmentationObjectID("[\w]*", 0, True);
132
+ if not success:
133
+ print('There was a problem setting all segmentation object IDs to 0. ')
134
+ sys.exit(1)
135
+
136
+ #Next set all objects of interest provided to corresponding object IDs
137
+ #segIdDict values MUST match tempEmissivityNew labels.
138
+ for key in segIdDict:
139
+ objectID = int(tempEmissivityNew[numpy.where(tempEmissivityNew == \
140
+ segIdDict[key])[0],1][0])
141
+
142
+ success = client.simSetSegmentationObjectID("[\w]*"+key+"[\w]*",
143
+ objectID, True);
144
+ if not success:
145
+ print('There was a problem setting {0} segmentation object ID to {1!s}, or no {0} was found.'.format(key, objectID))
146
+
147
+ time.sleep(0.1)
148
+
149
+
150
+ if __name__ == '__main__':
151
+
152
+ #Connect to AirSim, UAV mode.
153
+ client = MultirotorClient()
154
+ client.confirmConnection()
155
+
156
+ #Multiple same object settings
157
+ cubeList = client.simListSceneObjects('.*?Cube.*?')
158
+ cubeDict = {}
159
+ for x in cubeList:
160
+ cubeDict[x] = 'tree'
161
+
162
+
163
+ segIdDict = {
164
+ 'Floor':'soil',
165
+ }
166
+
167
+ segIdDict.update(cubeDict)
168
+ #Choose temperature values for winter or summer.
169
+ #"""
170
+ #winter
171
+ tempEmissivity = numpy.array([['elephant',290,0.96],
172
+ ['zebra',298,0.98],
173
+ ['rhinoceros',291,0.96],
174
+ ['hippopotamus',290,0.96],
175
+ ['crocodile',295,0.96],
176
+ ['human',292,0.985],
177
+ ['tree',273,0.952],
178
+ ['grass',273,0.958],
179
+ ['soil',278,0.914],
180
+ ['shrub',300,0.3],
181
+ ['truck',273,0.8],
182
+ ['water',273,0.96]])
183
+ #"""
184
+ """
185
+ #summer
186
+ tempEmissivity = numpy.array([['elephant',298,0.96],
187
+ ['zebra',307,0.98],
188
+ ['rhinoceros',299,0.96],
189
+ ['hippopotamus',298,0.96],
190
+ ['crocodile',303,0.96],
191
+ ['human',301,0.985],
192
+ ['tree',293,0.952],
193
+ ['grass',293,0.958],
194
+ ['soil',288,0.914],
195
+ ['shrub',293,0.986],
196
+ ['truck',293,0.8],
197
+ ['water',293,0.96]])
198
+ """
199
+
200
+ #Read camera response.
201
+ response = None
202
+ camResponseFile = 'camera_response.npy'
203
+ try:
204
+ numpy.load(camResponseFile)
205
+ except:
206
+ print("{} not found. Using default response.".format(camResponseFile))
207
+
208
+ #Calculate radiance.
209
+ tempEmissivityNew = get_new_temp_emiss_from_radiance(tempEmissivity,
210
+ response)
211
+
212
+ #Set IDs in AirSim environment.
213
+ set_segmentation_ids(segIdDict, tempEmissivityNew, client)
pic/image 1.png ADDED

Git LFS Details

  • SHA256: ffd6e69551175f022fea75fdb42ce5649104984493873a9e979f9b799fd1cf24
  • Pointer size: 132 Bytes
  • Size of remote file: 1.53 MB
pic/image 2.png ADDED
pic/image 3.png ADDED
pic/image_1.jpg ADDED