Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature systemtests sim fixed timestamp #411

Merged
merged 66 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
6c731a1
merge to fix timestamp issue
julienthevenoz Jan 17, 2024
cbf6fe3
small progress but still timestamp problem
julienthevenoz Jan 17, 2024
4ab2e16
merge crazyflie tools ?
julienthevenoz Jan 17, 2024
084eba4
finally extracted the CORRECT timestamp
julienthevenoz Jan 17, 2024
d292fc4
added a temporal offset feature in the pdf
julienthevenoz Jan 17, 2024
b248ffc
Merge remote-tracking branch 'refs/remotes/origin/feature-systemtests…
julienthevenoz Jan 17, 2024
63dea8f
yaml file updated to give --sim argument
julienthevenoz Jan 17, 2024
6926506
with argparse and unittest activated
julienthevenoz Jan 18, 2024
9bdb936
github action test
julienthevenoz Jan 18, 2024
da47339
second action test and small cleanup
julienthevenoz Jan 18, 2024
d8956d7
action test
julienthevenoz Jan 18, 2024
dea817e
action test2
julienthevenoz Jan 18, 2024
4ca7693
action test
julienthevenoz Jan 18, 2024
22197ee
debugging the action
julienthevenoz Jan 18, 2024
d5242b0
added print_PIPE function
julienthevenoz Jan 19, 2024
0080468
debug action
julienthevenoz Jan 19, 2024
c622fce
debug action
julienthevenoz Jan 19, 2024
9205ce8
installing cffirmware through pip in the yaml
julienthevenoz Jan 19, 2024
5189df6
another debug action
julienthevenoz Jan 19, 2024
b55c951
another debug
julienthevenoz Jan 21, 2024
5aea2a2
modify pythonpath in yaml
julienthevenoz Jan 21, 2024
73727bf
whats going on
julienthevenoz Jan 21, 2024
492f236
forgot apostrophe
julienthevenoz Jan 21, 2024
2eea3bb
downloading file that poses problem
julienthevenoz Jan 22, 2024
8215396
downloading file that poses problem
julienthevenoz Jan 22, 2024
3173b87
downloading file that poses problem
julienthevenoz Jan 22, 2024
f2b6e0c
debug action
julienthevenoz Jan 22, 2024
15a8b14
debug action
julienthevenoz Jan 22, 2024
1b21f39
debug action
julienthevenoz Jan 22, 2024
cc3a6f2
debug action
julienthevenoz Jan 22, 2024
fae15f3
debug action
julienthevenoz Jan 22, 2024
6933321
tried to change the env variables
julienthevenoz Jan 22, 2024
bd9c0c0
another debug
julienthevenoz Jan 22, 2024
146d53b
action example
julienthevenoz Jan 26, 2024
3bd7db0
exporting new PYTHONPATH
julienthevenoz Jan 26, 2024
03de7f7
exporting new PYTHONPATH with || true
julienthevenoz Jan 26, 2024
fbba8ec
is it working now ?
julienthevenoz Jan 26, 2024
bacf91d
is it working now ?
julienthevenoz Jan 26, 2024
35b391f
yeah I think it works
julienthevenoz Jan 26, 2024
0b259d3
created the full figure8 trajectory with takeoff and landing
julienthevenoz Jan 26, 2024
eb616f2
created the full figure8 with takeoff and landing
julienthevenoz Jan 26, 2024
5df38dc
modified delay
julienthevenoz Jan 26, 2024
a38ece8
adjusted delay a bit better
julienthevenoz Jan 26, 2024
22c8523
adjusting multitrajectory delay
julienthevenoz Jan 29, 2024
9fc2f8b
adjusted m_t delay a bit more
julienthevenoz Jan 29, 2024
5bb63d9
trying to resolve crazyflie_tools dependancy merge
julienthevenoz Jan 30, 2024
d2a683e
addressed wolfgang's requests
julienthevenoz Jan 30, 2024
9967d8a
crazyflie tools deps merge again
julienthevenoz Jan 30, 2024
7d66ee8
adjusted delays better, created an ideal traj file for multi_traj
julienthevenoz Feb 1, 2024
9935c7e
solved .csv not found
julienthevenoz Feb 2, 2024
0a679b9
solved .csv not found
julienthevenoz Feb 2, 2024
b8827a9
changed optitrack IP
julienthevenoz Feb 2, 2024
6f5f8f8
time delay
julienthevenoz Feb 2, 2024
e1e626b
adjusted passing criterias in plotter.py
julienthevenoz Feb 2, 2024
08025cf
adjusted passing criterias in plotter.py
julienthevenoz Feb 2, 2024
684c847
small modifications
julienthevenoz Feb 2, 2024
416656e
modified a test_passed argument
julienthevenoz Feb 2, 2024
905a692
again
julienthevenoz Feb 2, 2024
a1c4928
details
julienthevenoz Feb 2, 2024
2519bc5
testing error if takeoff or landing not found
julienthevenoz Feb 2, 2024
17d68d2
why did unittest tell the github action it passed when fig8 failed ?
julienthevenoz Feb 2, 2024
3e14fff
cleanup
julienthevenoz Feb 2, 2024
bdeb146
testing assert
julienthevenoz Feb 2, 2024
0653cba
cleanup
julienthevenoz Feb 2, 2024
b080d0c
testing if catches error
julienthevenoz Feb 2, 2024
8403bbe
final cleanup
julienthevenoz Feb 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/systemtests_sim.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: System Tests Simulation

on:
push:
branches: [ "main", "feature-systemtests-sim-fixed-timestamp" ]
# manual trigger
workflow_dispatch:

jobs:
build:
runs-on: self-hosted
steps:
- name: Build firmware
id: step1
run: |
ls crazyflie-firmware || git clone --recursive https://github.com/bitcraze/crazyflie-firmware.git
cd crazyflie-firmware
git pull
git submodule sync
git submodule update --init --recursive
make cf2_defconfig
make bindings_python
cd build
python3 setup.py install --user
- name: Create workspace
id: step2
run: |
cd ros2_ws/src || mkdir -p ros2_ws/src
- name: Checkout motion capture package
id: step3
run: |
cd ros2_ws/src
ls motion_capture_tracking || git clone --branch ros2 --recursive https://github.com/IMRCLab/motion_capture_tracking.git
cd motion_capture_tracking
git pull
git submodule sync
git submodule update --recursive --init
- name: Checkout Crazyswarm2
id: step4
uses: actions/checkout@v4
with:
path: ros2_ws/src/crazyswarm2
submodules: 'recursive'
- name: Build workspace
id: step5
run: |
source /opt/ros/humble/setup.bash
cd ros2_ws
colcon build --symlink-install

- name: Flight test
id: step6
run: |
cd ros2_ws
source /opt/ros/humble/setup.bash
. install/local_setup.bash
export PYTHONPATH="${PYTHONPATH}:/home/github/actions-runner/_work/crazyswarm2/crazyswarm2/crazyflie-firmware/build/"
export ROS_LOCALHOST_ONLY=1
julienthevenoz marked this conversation as resolved.
Show resolved Hide resolved
python3 src/crazyswarm2/systemtests/test_flights.py --sim

- name: Upload files
id: step7
if: '!cancelled()'
uses: actions/upload-artifact@v3
with:
name: pdf_rosbags_and_logs
path: |
ros2_ws/results






2 changes: 1 addition & 1 deletion crazyflie/deps/crazyflie_tools
Submodule crazyflie_tools updated 1 files
+1 −1 crazyflie_cpp
2 changes: 1 addition & 1 deletion crazyflie_sim/crazyflie_sim/visualization/rviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def step(self, t, states: list[State], states_desired: list[State], actions: lis
msg.transform.translation.x = state.pos[0]
msg.transform.translation.y = state.pos[1]
msg.transform.translation.z = state.pos[2]
msg.transform.rotation.x = state.quat[1]
msg.transform.rotation.x = state.quat[1]
julienthevenoz marked this conversation as resolved.
Show resolved Hide resolved
msg.transform.rotation.y = state.quat[2]
msg.transform.rotation.z = state.quat[3]
msg.transform.rotation.w = state.quat[0]
Expand Down
22 changes: 22 additions & 0 deletions systemtests/figure8_ideal_traj.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
duration,x^0,x^1,x^2,x^3,x^4,x^5,x^6,x^7,y^0,y^1,y^2,y^3,y^4,y^5,y^6,y^7,z^0,z^1,z^2,z^3,z^4,z^5,z^6,z^7,yaw^0,yaw^1,yaw^2,yaw^3,yaw^4,yaw^5,yaw^6,yaw^7,


####takeoff
2.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000000,0.000000,0.232052,0.184839,0.030911,-0.176192,0.050572,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
####hover
3.65,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.0,0.000000,0.000000,0.0,0.0,0.0,0.0,0.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
####figure8
1.050000,0.000000,-0.000000,0.000000,-0.000000,0.830443,-0.276140,-0.384219,0.180493,-0.000000,0.000000,-0.000000,0.000000,-1.356107,0.688430,0.587426,-0.329106,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.710000,0.396058,0.918033,0.128965,-0.773546,0.339704,0.034310,-0.026417,-0.030049,-0.445604,-0.684403,0.888433,1.493630,-1.361618,-0.139316,0.158875,0.095799,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.620000,0.922409,0.405715,-0.582968,-0.092188,-0.114670,0.101046,0.075834,-0.037926,-0.291165,0.967514,0.421451,-1.086348,0.545211,0.030109,-0.050046,-0.068177,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.700000,0.923174,-0.431533,-0.682975,0.177173,0.319468,-0.043852,-0.111269,0.023166,0.289869,0.724722,-0.512011,-0.209623,-0.218710,0.108797,0.128756,-0.055461,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.560000,0.405364,-0.834716,0.158939,0.288175,-0.373738,-0.054995,0.036090,0.078627,0.450742,-0.385534,-0.954089,0.128288,0.442620,0.055630,-0.060142,-0.076163,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.560000,0.001062,-0.646270,-0.012560,-0.324065,0.125327,0.119738,0.034567,-0.063130,0.001593,-1.031457,0.015159,0.820816,-0.152665,-0.130729,-0.045679,0.080444,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.700000,-0.402804,-0.820508,-0.132914,0.236278,0.235164,-0.053551,-0.088687,0.031253,-0.449354,-0.411507,0.902946,0.185335,-0.239125,-0.041696,0.016857,0.016709,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.620000,-0.921641,-0.464596,0.661875,0.286582,-0.228921,-0.051987,0.004669,0.038463,-0.292459,0.777682,0.565788,-0.432472,-0.060568,-0.082048,-0.009439,0.041158,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
0.710000,-0.923935,0.447832,0.627381,-0.259808,-0.042325,-0.032258,0.001420,0.005294,0.288570,0.873350,-0.515586,-0.730207,-0.026023,0.288755,0.215678,-0.148061,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
1.053185,-0.398611,0.850510,-0.144007,-0.485368,-0.079781,0.176330,0.234482,-0.153567,0.447039,-0.532729,-0.855023,0.878509,0.775168,-0.391051,-0.713519,0.391628,1.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,
####hover
2.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.0,0.000000,0.000000,0.0,0.0,0.0,0.0,0.0,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
####landing
2.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000001,0.000000,0.000000,-0.232049,-0.184841,-0.030916,0.176196,-0.050573,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
5 changes: 4 additions & 1 deletion systemtests/mcap_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ def write_mcap_to_csv(self, inputbag:str, outputfile:str):
f = open(outputfile, 'w+')
writer = csv.writer(f)
for topic, msg, timestamp in self.read_messages(inputbag):
writer.writerow([timestamp, msg.transforms[0].transform.translation.x, msg.transforms[0].transform.translation.y, msg.transforms[0].transform.translation.z])
if topic =="/tf":
t = msg.transforms[0].header.stamp.sec + msg.transforms[0].header.stamp.nanosec *10**(-9)
writer.writerow([t, msg.transforms[0].transform.translation.x, msg.transforms[0].transform.translation.y, msg.transforms[0].transform.translation.z])
f.close()
except FileNotFoundError:
print(f"McapHandler : file {outputfile} not found")
Expand All @@ -62,3 +64,4 @@ def write_mcap_to_csv(self, inputbag:str, outputfile:str):

translator = McapHandler()
translator.write_mcap_to_csv(args.inputbag,args.outputfile)

62 changes: 49 additions & 13 deletions systemtests/plotter_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class Plotter:

def __init__(self):
def __init__(self, sim_backend = False):
self.params = {'experiment':'1','trajectory':'figure8.csv','motors':'standard motors(CF)', 'propellers':'standard propellers(CF)'}
self.bag_times = np.empty([0])
self.bag_x = np.empty([0])
Expand All @@ -21,8 +21,13 @@ def __init__(self):
self.euclidian_dist = np.empty([0])
self.deviation = [] #list of all indexes where euclidian_distance(ideal - recorded) > EPSILON

self.SIM = sim_backend #indicates if we are plotting data from real life test or from a simulated test. Default is false (real life test)
self.EPSILON = 0.05 # euclidian distance in [m] between ideal and recorded trajectory under which the drone has to stay to pass the test
self.DELAY_CONST_FIG8 = 4.75 #this is the delay constant which I found by adding up all the time.sleep() etc in the figure8.py file. This could be implemented better later ?
self.DELAY_CONST_FIG8 = 4.75 #this is the delay constant which I found by adding up all the time.sleep() etc in the figure8.py file.
self.DELAY_CONST_MT = 0
if self.SIM : #It allows to temporally adjust the ideal and real trajectories on the graph. Could this be implemented in a better (not hardcoded) way ?
self.DELAY_CONST_FIG8 = -0.18 #for an unknown reason, the delay constant with the sim_backend is different
self.DELAY_CONST_MT = 5.8
self.ALTITUDE_CONST_FIG8 = 1 #this is the altitude given for the takeoff in figure8.py. I should find a better solution than a symbolic constant ?
self.ALTITUDE_CONST_MULTITRAJ = 1 #takeoff altitude for traj0 in multi_trajectory.py
self.X_OFFSET_CONST_MULTITRAJ = -0.3 #offest on the x axis between ideal and real trajectory. Reason: ideal trajectory (traj0.csv) starts with offset of 0.3m and CrazyflieServer.startTrajectory() is relative to start position
Expand Down Expand Up @@ -91,9 +96,15 @@ def read_csv_and_set_arrays(self, ideal_csvfile, rosbag_csvfile):

no_match_in_idealcsv=[]

delay = 0
if fig8:
delay = self.DELAY_CONST_FIG8
elif m_t:
delay = self.DELAY_CONST_MT

for i in range(bag_arrays_size):
try:
pos = self.ideal_traj_csv.eval(self.bag_times[i] - self.DELAY_CONST_FIG8).pos
pos = self.ideal_traj_csv.eval(self.bag_times[i] - delay).pos
except AssertionError:
no_match_in_idealcsv.append(i)
pos = [0,0,0] #for all recorded datapoints who cannot be matched to a corresponding ideal position we assume the drone is on its ground start position (ie those datapoints are before takeoff or after landing)
Expand All @@ -103,7 +114,8 @@ def read_csv_and_set_arrays(self, ideal_csvfile, rosbag_csvfile):

#special cases
if fig8:
self.ideal_traj_z[i] = self.ALTITUDE_CONST_FIG8 #special case: in fig8 no altitude is given in the trajectory polynomials (idealcsv) but is fixed as the takeoff altitude in figure8.py
# self.ideal_traj_z[i] = self.ALTITUDE_CONST_FIG8 #special case: in fig8 no altitude is given in the trajectory polynomials (idealcsv) but is fixed as the takeoff altitude in figure8.py
pass
elif m_t:
self.ideal_traj_z[i] = pos[2] + self.ALTITUDE_CONST_MULTITRAJ #for multi_trajectory the altitude given in the trajectory polynomials is added to the fixed takeoff altitude specified in multi_trajectory.py
self.ideal_traj_x[i] = pos[0] + self.X_OFFSET_CONST_MULTITRAJ #the x-axis is offset by 0.3 m because ideal start position not being (0,0,0)
Expand Down Expand Up @@ -147,7 +159,6 @@ def adjust_arrays(self):
''' Method that trims the self.bag_* attributes to get rid of the datapoints where the drone is immobile on the ground and makes self.bag_times start at 0 [s]'''

print(f"rosbag initial length {(self.bag_times[-1]-self.bag_times[0]) * 10**-9}s")

#find the takeoff time and landing times
ground_level = self.bag_z[0]
airborne = False
Expand All @@ -160,13 +171,15 @@ def adjust_arrays(self):
takeoff_time = self.bag_times[takeoff_index]
airborne = True
print(f"takeof time is {(takeoff_time-self.bag_times[0]) * 10**-9}")
if airborne and z_coord < ground_level + ground_level*(0.1): #find when it lands again
if airborne and z_coord <= ground_level + ground_level*(0.1): #find when it lands again
landing_index = i
landing_time = self.bag_times[landing_index]
print(f"landing time is {(landing_time-self.bag_times[0]) * 10**-9}")
break
i+=1



assert (takeoff_index != None) and (landing_index != None), "Plotter : couldn't find drone takeoff or landing"


Expand All @@ -184,10 +197,6 @@ def adjust_arrays(self):

assert len(self.bag_times) == len(self.bag_x) == len(self.bag_y) == len(self.bag_z), "Plotter : self.bag_* aren't the same size after trimming"

#rewrite bag_times to start at 0 and be written in [s] instead of [ns]
bag_start_time = self.bag_times[0]
self.bag_times = (self.bag_times-bag_start_time) * (10**-9)
assert self.bag_times[0] == 0
print(f"trimmed bag_times starts: {self.bag_times[0]}s and ends: {self.bag_times[-1]}, size: {len(self.bag_times)}")


Expand All @@ -196,6 +205,11 @@ def create_figures(self, ideal_csvfile:str, rosbag_csvfile:str, pdfname:str):
'''Method that creates the pdf with the plots'''

self.read_csv_and_set_arrays(ideal_csvfile,rosbag_csvfile)
offset_list = self.find_temporal_offset()
if len(offset_list) == 1:
offset_string = f"temporal offset : {offset_list[0]}s \n"
elif len(offset_list) ==2:
offset_string = f"averaged temporal offset : {(offset_list[0]+offset_list[1])/2}s \n"

passed="failed"
if self.test_passed():
Expand Down Expand Up @@ -223,7 +237,7 @@ def create_figures(self, ideal_csvfile:str, rosbag_csvfile:str, pdfname:str):
title_text_parameters = f'Parameters:\n'
for key, value in self.params.items():
title_text_parameters += f" {key}: {value}\n"
title_text_results = f'Results: test {passed}\n' + f'max error : '
title_text_results = f'Results: test {passed}\n' + offset_string + f'max error : '

title_text = text + "\n" + title_text_settings + "\n" + title_text_parameters + "\n" + title_text_results
fig = plt.figure(figsize=(5,8))
Expand Down Expand Up @@ -333,6 +347,28 @@ def test_passed(self) -> bool :
print(f"The deviation between ideal and recorded trajectories is greater than {self.EPSILON}m for {nb_dev_points} "
f"datapoints, which corresponds to a duration of {nb_dev_points*0.01}s")
return False

def find_temporal_offset(self) -> list :
''' Returns a list containing the on-graph temporal offset between real and ideal trajectory. If offset is different for x and y axis, returns both in the same list'''
peak_x = self.bag_x.argmax() #find index of extremum value of real trajectory along x axis
peak_time_x = self.bag_times[peak_x] #find corresponding time
peak_x_ideal = self.ideal_traj_x.argmax() #find index of extremum value of ideal traj along x axis
peak_time_x_ideal = self.bag_times[peak_x_ideal] #find corresponding time
offset_x = peak_time_x_ideal - peak_time_x

peak_y = self.bag_y.argmax() #find index of extremum value of real trajectory along y ayis
peak_time_y = self.bag_times[peak_y] #find corresponding time
peak_y_ideal = self.ideal_traj_y.argmax() #find index of extremum value of ideal traj along y ayis
peak_time_y_ideal = self.bag_times[peak_y_ideal] #find corresponding time
offset_y = peak_time_y_ideal - peak_time_y

if offset_x == offset_y:
print(f"On-graph temporal offset is {offset_x}s, delay const is {self.DELAY_CONST_FIG8} so uncorrected/absolute offset is {offset_x-self.DELAY_CONST_FIG8}")
return [offset_x]
else :
print(f"On-graph temporal offsets are {offset_x} & {offset_y} secs, delay const is {self.DELAY_CONST_FIG8}")
return [offset_x, offset_y]


if __name__=="__main__":

Expand All @@ -351,5 +387,5 @@ def test_passed(self) -> bool :
if args.open:
import subprocess
subprocess.call(["xdg-open", args.pdf])


Loading
Loading