Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
a1bf030
Corrected types
krichardsson May 14, 2025
ddad27f
Improve de-flipper to use all base stations
krichardsson May 14, 2025
6ff42e6
Added IPPE solutions to LhCfPoseSample for reuse and speed up
krichardsson May 14, 2025
f0e6a53
Refactor sample matcher
krichardsson May 14, 2025
6534344
Removed unused parameters
krichardsson May 14, 2025
ca419c5
Moved ippe estimation to LhCfPoseSample
krichardsson May 14, 2025
5276b2a
Added input data container
krichardsson May 15, 2025
7504863
Make sure mandatory samples are not discarded
krichardsson May 15, 2025
5d95092
Moved scaling into estimation manager
krichardsson May 16, 2025
f1ec5cc
Added functions for live sample matching
krichardsson May 16, 2025
24de4a5
Updated tests
krichardsson Jun 5, 2025
7853d2b
Corrected test
krichardsson Jun 5, 2025
3d7eb8e
Styling
krichardsson Jun 5, 2025
6fe04c3
styling
krichardsson Jun 5, 2025
1e81175
basic continuous geo estimation
krichardsson Jun 12, 2025
a524535
Unified solution data and added human readable information
krichardsson Jun 13, 2025
2b813e1
Fix problem in sweep angle reader
krichardsson Jun 14, 2025
da21262
Added base station link map
krichardsson Jun 16, 2025
9094f92
Added unit test
krichardsson Jun 16, 2025
eb6c623
Added matched lighthouse sample stream
krichardsson Jun 17, 2025
088c691
Added user action detector
krichardsson Jun 18, 2025
6cb0c27
Make thread daemon
krichardsson Jun 23, 2025
08ba567
Added missing sample from the origin
krichardsson Jun 23, 2025
482542c
Restored save/load functionality
krichardsson Jun 23, 2025
b28584b
Join optional in solver thread
krichardsson Jun 24, 2025
711dea2
Try to solve when the solver thread is started
krichardsson Jun 25, 2025
1cedb1d
Added sample count methods
krichardsson Jun 27, 2025
7c084ab
Corrected callback
krichardsson Jun 27, 2025
a03c120
Build link stats as early as possible
krichardsson Jun 27, 2025
97a012d
Added script for uploading geometries
krichardsson Jul 1, 2025
17e800d
Added user notification platform service
krichardsson Jul 2, 2025
ab5cfcc
Added timeout to sweep angle reader
krichardsson Jul 2, 2025
bd6576f
Touch up of multi bs estimation script
krichardsson Jul 3, 2025
6d2b1ef
Use crossing beam to calculate error of solution
krichardsson Jul 3, 2025
9688279
Added read/write of geo raw data as yaml
krichardsson Jul 4, 2025
09fbc48
Added session management
krichardsson Jul 4, 2025
f3d91c9
Clean solution before adding new
krichardsson Jul 8, 2025
b57ddc6
Improved information
krichardsson Jul 8, 2025
3f428ab
Added error information per sample in solution
krichardsson Jul 14, 2025
d4be7fa
Reworked sample management and error reporting
krichardsson Jul 14, 2025
77a90cf
Added func to remove samples
krichardsson Jul 15, 2025
b892748
Added error threshold
krichardsson Jul 15, 2025
d4bfb2f
Added cb for start of solution
krichardsson Jul 15, 2025
2e1b594
Added write status method to config writer
krichardsson Jul 15, 2025
dccbe3e
Use UID to identify samples
krichardsson Jul 17, 2025
d665065
Renamed outlier to ambiguous
krichardsson Jul 17, 2025
241fe97
Cleaned up index handling
krichardsson Jul 17, 2025
db45841
Added verification samples
krichardsson Jul 18, 2025
9990e9e
added verification methods
krichardsson Jul 18, 2025
288abb7
Added header
krichardsson Jul 18, 2025
33db4bd
Added threshold for bs links
krichardsson Jul 18, 2025
b207d86
Handle some corner cases
krichardsson Jul 19, 2025
27b1a43
Styling
krichardsson Jul 19, 2025
302498f
Updated tests
krichardsson Jul 19, 2025
8ac1a9b
removed np.float_
krichardsson Jul 19, 2025
8276690
Added base station sample count to LH geo solution
krichardsson Jan 23, 2026
b156d9a
Added contains samples to solution
krichardsson Feb 3, 2026
4a36319
Added support for RPY in Pose
krichardsson Feb 4, 2026
c2ec375
Added one more test for RPY conversion
krichardsson Feb 5, 2026
779f805
Styling
krichardsson Feb 6, 2026
e96816f
Merge commit 'e780010c5a17af95852d5f45114811234b1f32ef' into krichard…
krichardsson Feb 9, 2026
557fea1
First draft of technical documentation
stefanthorstenson May 12, 2026
84a0d17
Doc: Added estimation process overview and where geometry is stored.
stefanthorstenson May 12, 2026
d6ec2af
Merge branch 'master' into krichardsson/umwelt
ArisMorgens Jun 24, 2026
3f0fefa
Added remove base station function
ArisMorgens Jun 25, 2026
cdb722a
Reverted double quote fix
ArisMorgens Jun 25, 2026
b570593
Restricted the sample convertion only between xyz and verification sa…
ArisMorgens Jun 29, 2026
cfacc31
Added back the geo_estimation example
ArisMorgens Jun 30, 2026
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
Prev Previous commit
Next Next commit
Added matched lighthouse sample stream
  • Loading branch information
krichardsson committed Jun 17, 2025
commit eb6c6231d2bfd140d4c8bcb4326ef36a8e6175e4
24 changes: 24 additions & 0 deletions cflib/crazyflie/localization.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Localization():
EXT_POSE_PACKED = 9
LH_ANGLE_STREAM = 10
LH_PERSIST_DATA = 11
LH_MATCHED_ANGLE_STREAM = 12

def __init__(self, crazyflie=None):
"""
Expand Down Expand Up @@ -105,6 +106,8 @@ def _incoming(self, packet):
decoded_data = bool(data[0])
elif pk_type == self.LH_ANGLE_STREAM:
decoded_data = self._decode_lh_angle(data)
elif pk_type == self.LH_MATCHED_ANGLE_STREAM:
decoded_data = self._decode_matched_lh_angle(data)

pk = LocalizationPacket(pk_type, data, decoded_data)
self.receivedLocationPacket.call(pk)
Expand All @@ -128,6 +131,27 @@ def _decode_lh_angle(self, data):

return decoded_data

def _decode_matched_lh_angle(self, data):
decoded_data = {}

raw_data = struct.unpack('<BfhhhfhhhB', data)

decoded_data['basestation'] = raw_data[0]
decoded_data['x'] = [0, 0, 0, 0]
decoded_data['x'][0] = raw_data[1]
decoded_data['x'][1] = raw_data[1] - fp16_to_float(raw_data[2])
decoded_data['x'][2] = raw_data[1] - fp16_to_float(raw_data[3])
decoded_data['x'][3] = raw_data[1] - fp16_to_float(raw_data[4])
decoded_data['y'] = [0, 0, 0, 0]
decoded_data['y'][0] = raw_data[5]
decoded_data['y'][1] = raw_data[5] - fp16_to_float(raw_data[6])
decoded_data['y'][2] = raw_data[5] - fp16_to_float(raw_data[7])
decoded_data['y'][3] = raw_data[5] - fp16_to_float(raw_data[8])
decoded_data['group_id'] = raw_data[9] >> 4
decoded_data['bs_count'] = raw_data[9] & 0x0F

return decoded_data

def send_extpos(self, pos):
"""
Send the current Crazyflie X, Y, Z position. This is going to be
Expand Down
2 changes: 2 additions & 0 deletions cflib/localization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
from .lighthouse_config_manager import LighthouseConfigWriter
from .lighthouse_sweep_angle_reader import LighthouseSweepAngleAverageReader
from .lighthouse_sweep_angle_reader import LighthouseSweepAngleReader
from .lighthouse_sweep_angle_reader import LighthouseMatchedSweepAngleReader
from .param_io import ParamFileManager

__all__ = [
'LighthouseBsGeoEstimator',
'LighthouseBsVector',
'LighthouseSweepAngleAverageReader',
'LighthouseSweepAngleReader',
'LighthouseMatchedSweepAngleReader',
'LighthouseConfigFileManager',
'LighthouseConfigWriter',
'ParamFileManager']
84 changes: 84 additions & 0 deletions cflib/localization/lighthouse_sweep_angle_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from cflib.crazyflie import Crazyflie
from cflib.localization import LighthouseBsVector
from cflib.localization.lighthouse_bs_vector import LighthouseBsVectors
from cflib.localization.lighthouse_cf_pose_sample import LhCfPoseSample


class LighthouseSweepAngleReader():
Expand Down Expand Up @@ -161,3 +162,86 @@ def _average_sample_list(self, sample_list: list[LighthouseBsVector]) -> Lightho

count = len(sample_list)
return LighthouseBsVector(sum_horiz / count, sum_vert / count)


class LighthouseMatchedSweepAngleReader():
"""
Wrapper to simplify reading of matched lighthouse sweep angles from the locSrv stream
"""
MATCHED_STREAM_PARAM = 'locSrv.enLhMtchStm'
MATCHED_STREAM_MIN_BS_PARAM = 'locSrv.minBsLhMtchStm'
MATCHED_STREAM_MAX_TIME_PARAM = 'locSrv.maxTimeLhMtchStm'
NR_OF_SENSORS = 4

def __init__(self, cf: Crazyflie, data_recevied_cb, sample_count: int = 1, min_bs: int = 2, max_time_ms: int = 25):
self._cf = cf
self._cb = data_recevied_cb
self._is_active = False
self._sample_count = sample_count

# The maximum number of base stations is limited in the CF due to memory considerations.
if min_bs > 4:
raise ValueError('Minimum base station count must be 4 or less')
self._min_bs = min_bs

self._max_time_ms = max_time_ms

self._current_group_id = 0
self._angles: dict[int, LighthouseBsVectors] = {}

def start(self):
"""Start reading sweep angles"""
self._cf.loc.receivedLocationPacket.add_callback(self._packet_received_cb)
self._is_active = True
self._angle_stream_activate(True)

def stop(self):
"""Stop reading sweep angles"""
if self._is_active:
self._is_active = False
self._cf.loc.receivedLocationPacket.remove_callback(self._packet_received_cb)
self._angle_stream_activate(False)

def _angle_stream_activate(self, is_active: bool):
value = 0
if is_active:
value = self._sample_count
self._cf.param.set_value(self.MATCHED_STREAM_PARAM, value)

self._cf.param.set_value(self.MATCHED_STREAM_MIN_BS_PARAM, self._min_bs)
self._cf.param.set_value(self.MATCHED_STREAM_MAX_TIME_PARAM, self._max_time_ms)

def _packet_received_cb(self, packet):
if self._is_active:
if packet.type != self._cf.loc.LH_MATCHED_ANGLE_STREAM:
return

base_station_id: int = packet.data['basestation']
horiz_angles: float = packet.data['x']
vert_angles: float = packet.data['y']
group_id: int = packet.data['group_id']
bs_count: int = packet.data['bs_count']

if group_id != self._current_group_id:
if len(self._angles) >= self._min_bs:
# We have enough angles in the previous group even though all angles were not received
# Lost a packet?
self._call_callback()

# Reset
self._current_group_id = group_id
self._angles = {}

vectors: list[LighthouseBsVector] = []
for i in range(self.NR_OF_SENSORS):
vectors.append(LighthouseBsVector(horiz_angles[i], vert_angles[i]))
self._angles[base_station_id] = LighthouseBsVectors(vectors)

if len(self._angles) == bs_count:
# We have received all angles for this group, call the callback
self._call_callback()

def _call_callback(self):
if self._cb:
self._cb(LhCfPoseSample(self._angles))
self._angles = {}