Skip to content

Commit 2c794af

Browse files
authored
Merge pull request #881 from JuliaRobotics/master
v0.13.2-rc1
2 parents af02ca7 + 421ecff commit 2c794af

27 files changed

+719
-222
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
- release**
99
jobs:
1010
test:
11-
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
11+
name: Julia ${{ matrix.version }} - ${{ matrix.arch }} - ${{ matrix.group }} - ${{ matrix.os }}
1212
runs-on: ${{ matrix.os }}
1313
env:
1414
JULIA_PKG_SERVER: ""
@@ -23,6 +23,9 @@ jobs:
2323
- ubuntu-latest
2424
arch:
2525
- x64
26+
group:
27+
- 'basic_functional_group'
28+
- 'test_cases_group'
2629
steps:
2730
- uses: actions/checkout@v2
2831
- uses: julia-actions/setup-julia@v1
@@ -45,6 +48,8 @@ jobs:
4548
git config --global user.email [email protected]
4649
- uses: julia-actions/julia-runtest@latest
4750
continue-on-error: ${{ matrix.version == 'nightly' }}
51+
env:
52+
IIF_TEST_GROUP: ${{ matrix.group }}
4853
- uses: julia-actions/julia-processcoverage@v1
4954
- uses: codecov/codecov-action@v1
5055
with:

NEWS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# NEWS Caesar.jl
2+
3+
Major changes and news in Caesar.jl.
4+
5+
## Changes in v0.13
6+
7+
- Finally adding a NEWS.md to Caesar.jl.
8+
- Standardize all Manifolds.jl representations to use `SciML/ArrayPartition` instead of `Manifold/ProductRepr`.
9+
- Add `RobotOS / PyCall` based interface for writing bagfiles with `RosbagWriter`.
10+
- Add `_PCL` export functions to go from `Caesar._PCL.PointCloud` out to `PCLPointCloud2` and `ROS.PointCloud2` types.
11+
- Refactored `ScatterAlign` to support both `ScatterAlignPose2` and `ScatterAlignPose3`.
12+
- Improved `_PCL.apply` to now transform both 2D and 3D pointclouds using `Manifolds.SpecialEuclidean(2)` and `Manifolds.SpecialEuclidean(3)`.
13+
- Added more testing on `_PCL` conversions and transforms.

Project.toml

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name = "Caesar"
22
uuid = "62eebf14-49bc-5f46-9df9-f7b7ef379406"
33
keywords = ["SLAM", "state-estimation", "MM-iSAM", "MM-iSAMv2", "inference", "robotics", "ROS"]
44
desc = "Non-Gaussian simultaneous localization and mapping"
5-
version = "0.13.1"
5+
version = "0.13.2"
66

77
[deps]
88
ApproxManifoldProducts = "9bbbb610-88a1-53cd-9763-118ce10c1f89"
@@ -44,35 +44,35 @@ Unmarshal = "cbff2730-442d-58d7-89d1-8e530c41eb02"
4444
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
4545

4646
[compat]
47-
ApproxManifoldProducts = "0.4.21, 0.5"
47+
ApproxManifoldProducts = "0.6"
4848
AprilTags = "0.8, 0.9"
49-
Combinatorics = "0.7, 0.8, 0.9, 1"
49+
Combinatorics = "1"
5050
CoordinateTransformations = "0.5, 0.6"
51-
DataStructures = "0.16, 0.17, 0.18"
52-
DistributedFactorGraphs = "0.17, 0.18"
53-
Distributions = "0.22, 0.23, 0.24, 0.25"
54-
DocStringExtensions = "0.7, 0.8, 0.9"
51+
DataStructures = "0.17, 0.18"
52+
DistributedFactorGraphs = "0.18"
53+
Distributions = "0.25"
54+
DocStringExtensions = "0.8, 0.9"
5555
FFTW = "1"
5656
FileIO = "1"
57-
ImageCore = "0.7, 0.8, 0.9"
58-
ImageMagick = "0.7, 1.0, 1.1"
59-
IncrementalInference = "0.26, 0.27, 0.28, 0.29"
57+
ImageCore = "0.8, 0.9"
58+
ImageMagick = "1"
59+
IncrementalInference = "0.30"
6060
JLD2 = "0.3, 0.4"
61-
JSON = "0.19, 0.20, 0.21"
61+
JSON = "0.20, 0.21"
6262
JSON2 = "0.3, 0.4"
6363
KernelDensityEstimate = "0.5"
64-
Manifolds = "0.6.3, 0.7, 0.8"
65-
NLsolve = "3, 4"
64+
Manifolds = "0.8"
65+
NLsolve = "4"
6666
Optim = "1"
67-
ProgressMeter = "0.9, 1"
68-
Reexport = "0.2, 1"
69-
Requires = "0.5, 1"
70-
RoME = "0.17, 0.18, 0.19"
67+
ProgressMeter = "1"
68+
Reexport = "1"
69+
Requires = "1"
70+
RoME = "0.20"
7171
Rotations = "1.1"
7272
StaticArrays = "1"
73-
TensorCast = "0.3, 0.4"
73+
TensorCast = "0.4"
7474
TimeZones = "1.3.1, 1.4"
75-
TransformUtils = "0.2.2"
75+
TransformUtils = "0.2.14"
7676
Unmarshal = "0.3, 0.4"
7777
YAML = "0.3, 0.4"
7878
julia = "1.6"
@@ -81,15 +81,17 @@ julia = "1.6"
8181
AprilTags = "f0fec3d5-a81e-5a6a-8c28-d2b34f3659de"
8282
BSON = "fbb218c0-5317-5bc6-957e-2ee96dd4b1f0"
8383
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
84+
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
8485
Gadfly = "c91e804a-d5a3-530f-b6f0-dfbca275c004"
8586
GraphPlot = "a2cc645c-3eea-5389-862e-a155d0052231"
8687
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
8788
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
8889
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
8990
RoMEPlotting = "238d586b-a4bf-555c-9891-eda6fc5e55a2"
9091
RobotOS = "22415677-39a4-5241-a37a-00beabbbdae8"
92+
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
9193
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
9294
ZMQ = "c2297ded-f4af-51ae-bb23-16f91089e4e1"
9395

9496
[targets]
95-
test = ["AprilTags", "BSON", "Colors", "Gadfly", "GraphPlot", "Images", "PyCall", "Random", "RobotOS", "Test", "ZMQ"]
97+
test = ["AprilTags", "BSON", "Colors", "FixedPointNumbers", "Gadfly", "GraphPlot", "Images", "PyCall", "Random", "RobotOS", "Serialization", "Test", "ZMQ"]

docs/src/examples/custom_variables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Another good example to look at is RoME's [`Pose2`](@ref) with 3 degrees of free
2424
@defVariable(
2525
Pose2,
2626
SpecialEuclidean(2),
27-
ProductRepr(MVector{2}(0.0,0.0), MMatrix{2,2}(1.0,0.0,0.0,1.0))
27+
ArrayPartition(MVector{2}(0.0,0.0), MMatrix{2,2}(1.0,0.0,0.0,1.0))
2828
)
2929
```
3030

docs/src/refs/literature.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ Newly created page to list related references and additional literature pertaini
7070

7171
[2.15f] Žefran, M., Kumar, V. and Croke, C., 1996, August. [Choice of Riemannian metrics for rigid body kinematics](https://www.cis.upenn.edu/~cis610/SE3-Croke-Kumar.pdf). In International Design Engineering Technical Conferences and Computers and Information in Engineering Conference (Vol. 97584, p. V02BT02A030). American Society of Mechanical Engineers.
7272

73+
[2.15g] Chirikjian, G.S. and Zhou, S., 1998. [Metrics on motion and deformation of solid models](https://asmedigitalcollection.asme.org/mechanicaldesign/article-abstract/120/2/252/429661/Metrics-on-Motion-and-Deformation-of-Solid-Models).
74+
7375
[2.16] Kaess, M. and Dellaert, F., 2009. [Covariance recovery from a square root information matrix for data association](https://apps.dtic.mil/dtic/tr/fulltext/u2/a537233.pdf). Robotics and autonomous systems, 57(12), pp.1198-1210.
7476

7577
[2.17] Bishop, C.M., 2006. Pattern recognition and machine learning. New York: Springer. ISBN 978-0-387-31073-2.

src/3rdParty/_PCL/_PCL.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module _PCL
55

66
using ..Colors
7+
import ..Caesar: _FastTransform3D
78

89
using Dates
910
using DocStringExtensions
@@ -17,6 +18,7 @@ import Base: getindex, setindex!, resize!, cat, convert, sizeof, hasproperty, ge
1718

1819
# gets overloaded
1920
import Manifolds: apply
21+
import IncrementalInference: ArrayPartition
2022

2123
## hold off on exports, users can in the mean-time use/import via e.g. _PCL.PointXYZ
2224
# export PointT, PointXYZ, PointXYZRGB, PointXYZRGBA

src/3rdParty/_PCL/entities/PCLTypes.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ References
8080
end
8181

8282
Base.convert(::Type{<:_PCL_POINTFIELD_FORMAT}, val::Integer) = (en=instances(_PCL_POINTFIELD_FORMAT); en[findfirst(Int.(en) .== Int.(convert(UInt8, val)))])
83+
Base.convert(::Type{<:Integer}, ff::_PCL_POINTFIELD_FORMAT) = findfirst(==(ff), instances(_PCL_POINTFIELD_FORMAT))
8384

8485
struct asType{T} end
8586

@@ -125,7 +126,8 @@ Base.@kwdef struct Header
125126
""" The sequence number """
126127
seq::UInt32 = UInt32(0)
127128
""" A timestamp associated with the time when the data was acquired.
128-
The value represents microseconds since 1970-01-01 00:00:00 (the UNIX epoch). """
129+
The value represents microseconds since 1970-01-01 00:00:00 (the UNIX epoch).
130+
Suggest: making this relative to UCT to account for timezones in future. """
129131
stamp::UInt64 = UInt64(0)
130132
""" Coordinate frame ID. """
131133
frame_id::String = ""

src/3rdParty/_PCL/services/PointCloud.jl

Lines changed: 101 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Base.getindex(pc::PointCloud, i) = pc.points[i]
5757
Base.setindex!(pc::PointCloud, pt::PointT, idx) = (pc.points[idx] = pt)
5858
Base.resize!(pc::PointCloud, s::Integer) = resize!(pc.points, s)
5959

60+
Base.length(pc::PointCloud) = length(pc.points)
61+
6062
## not very Julian translations from C++ above
6163
## ==============================================================================================
6264

@@ -146,7 +148,7 @@ function createMapping(T,msg_fields::AbstractVector{<:PointField}, field_map::Ms
146148

147149
# Coalesce adjacent fields into single copy where possible
148150
if 1 < length(field_map)
149-
# TODO check accending vs descending order
151+
# TODO check accending vs descending order>
150152
sort!(field_map, by = x->x.serialized_offset)
151153

152154
# something strange with how C++ does and skips the while loop, disabling the coalescing for now
@@ -167,7 +169,7 @@ function createMapping(T,msg_fields::AbstractVector{<:PointField}, field_map::Ms
167169
# else
168170
# i += 1
169171
# j += 1
170-
172+
171173
# _jmap = field_map[j]
172174
# _jend = field_map[end]
173175
# end
@@ -177,6 +179,14 @@ function createMapping(T,msg_fields::AbstractVector{<:PointField}, field_map::Ms
177179

178180
return field_map
179181
end
182+
# pc2.fields
183+
# 6-element Vector{Caesar._PCL.PointField}:
184+
# Caesar._PCL.PointField("x", 0x00000000, Caesar._PCL._PCL_FLOAT32, 0x00000001)
185+
# Caesar._PCL.PointField("y", 0x00000004, Caesar._PCL._PCL_FLOAT32, 0x00000001)
186+
# Caesar._PCL.PointField("z", 0x00000008, Caesar._PCL._PCL_FLOAT32, 0x00000001)
187+
# Caesar._PCL.PointField("intensity", 0x00000010, Caesar._PCL._PCL_FLOAT32, 0x00000001)
188+
# Caesar._PCL.PointField("timestamp", 0x00000018, Caesar._PCL._PCL_FLOAT64, 0x00000001)
189+
# Caesar._PCL.PointField("ring", 0x00000020, Caesar._PCL._PCL_UINT16, 0x00000001)
180190

181191

182192
# https://pointclouds.org/documentation/conversions_8h_source.html#l00166
@@ -193,13 +203,14 @@ function PointCloud(
193203
cloudsize = msg.width*msg.height
194204
# cloud_data = Vector{UInt8}(undef, cloudsize)
195205

196-
# NOTE assume all fields use the same data type
206+
# FIXME cannot assume all fields use the same data type
197207
# off script conversion for XYZ_ data only
198208
datatype = asType{msg.fields[1].datatype}()
199209
len = trunc(Int, length(msg.data)/field_map[1].size)
200210
data_ = Vector{datatype}(undef, len)
201211
read!(IOBuffer(msg.data), data_)
202212
mat = reshape(data_, :, cloudsize)
213+
# see createMapping above
203214

204215

205216
# Check if we can copy adjacent points in a single memcpy. We can do so if there
@@ -214,7 +225,7 @@ function PointCloud(
214225
error("copy of just one field_map not implemented yet")
215226
else
216227
# If not, memcpy each group of contiguous fields separately
217-
@assert msg.height == 1 "only decoding msg.height=1 messages, update converter here."
228+
@assert msg.height == 1 "only decoding msg::PCLPointCloud2.height=1 messages, update converter here."
218229
for row in 1:msg.height
219230
# TODO check might have an off by one error here
220231
# row_data = row * msg.row_step + 1 # msg.data[(row-1) * msg.row_step]
@@ -223,12 +234,11 @@ function PointCloud(
223234
# the slow way of building the point.data entry
224235
ptdata = zeros(datatype, 4)
225236
for (i,mapping) in enumerate(field_map)
237+
# @info "test" i mapping maxlog=3
226238
midx = trunc(Int,mapping.serialized_offset/mapping.size) + 1
227-
# TODO, why the weird index reversal?
239+
# Copy individual points as columns from mat -- i.e. memcpy
240+
# TODO copy only works if all elements have the same type -- suggestion, ptdata::ArrayPartition instead
228241
ptdata[i] = mat[midx, col]
229-
# @info "DO COPY" mapping
230-
# memcpy (cloud_data + mapping.struct_offset, msg_data + mapping.serialized_offset, mapping.size);
231-
# @info "copy" mapping.struct_offset mapping.serialized_offset mapping.size
232242
end
233243
pt = T(;data=SVector(ptdata...))
234244
push!(cloud.points, pt)
@@ -241,22 +251,89 @@ function PointCloud(
241251
end
242252

243253

254+
"""
255+
$SIGNATURES
256+
257+
Convert a pcl::PointCloud<T> object to a PCLPointCloud2 binary data blob.
258+
In: cloud the input pcl::PointCloud<T>
259+
Out: msg the resultant PCLPointCloud2 binary blob
260+
261+
DevNotes:
262+
- TODO, remove hacks, e.g. fields [x,y,z,intensity,]-only.
263+
"""
264+
function PCLPointCloud2(cloud::PointCloud{T,P,R}; datatype = _PCL_FLOAT32) where {T,P,R}
265+
# Ease the user's burden on specifying width/height for unorganized datasets
266+
if (cloud.width == 0 && cloud.height == 0)
267+
height = 1;
268+
width = length(cloud)
269+
else
270+
@assert (length(cloud) == cloud.width * cloud.height) "Input `cloud::PointCloud`'s width*height is not equal to length(cloud)"
271+
height = cloud.height;
272+
width = cloud.width;
273+
end
274+
275+
# // Fill point cloud binary data (padding and all)
276+
# std::size_t data_size = sizeof (PointT) * cloud.size ();
277+
# msg.data.resize (data_size);
278+
# if (data_size)
279+
# {
280+
# memcpy(&msg.data[0], &cloud[0], data_size);
281+
# }
282+
283+
fields = Vector{PointField}(undef, 4)
284+
# TODO assuming all(fields[_].count==1)
285+
fields[1] = PointField("x", UInt32(0), datatype, UInt32(1))
286+
fields[2] = PointField("y", UInt32(4), datatype, UInt32(1))
287+
fields[3] = PointField("z", UInt32(8), datatype, UInt32(1))
288+
fields[4] = PointField("intensity", UInt32(12), datatype, UInt32(1))
289+
_nflds = length(fields)
290+
# # Fill fields metadata
291+
# msg.fields.clear ();
292+
# for_each_type<typename traits::fieldList<PointT>::type> (detail::FieldAdder<PointT>(msg.fields));
293+
294+
# NOTE get the last possible byte location of all fields, assume no padding after last used byte
295+
_fend = fields[end]
296+
point_step = _fend.offset + sizeof(asType{_fend.datatype}()) # = sizeof (PointT);
297+
# msg.row_step = (sizeof (PointT) * msg.width);
298+
# # todo msg.is_bigendian = ?;
299+
300+
# flatten all point fields as binary
301+
data = Vector{UInt8}(undef, point_step*width)
302+
for (i,pt) in enumerate(cloud.points)
303+
offset = (point_step*(i-1)+1)
304+
# NOTE assume continuous data block in struct (all of same datatype, FIXME)
305+
data[offset:(offset-1+point_step)] .= reinterpret(UInt8, view(pt.data, 1:_nflds))
306+
end
307+
308+
# @error("noise")
309+
return PCLPointCloud2(;
310+
header = cloud.header,
311+
height,
312+
width,
313+
fields,
314+
data,
315+
# is_bigendian = ,
316+
point_step,
317+
row_step = sizeof(T)*width,
318+
is_dense = cloud.is_dense,
319+
)
320+
end
321+
322+
244323
## =========================================================================================================
245324
## Coordinate transformations using Manifolds.jl
246325
## =========================================================================================================
247326

248327

249328
# 2D, do similar or better for 3D
250329
# FIXME, to optimize, this function will likely be slow
251-
function apply( M_::typeof(SpecialEuclidean(2)),
252-
rPp::Union{<:ProductRepr,<:Manifolds.ArrayPartition},
253-
pc::PointCloud{T} ) where T
330+
# TODO, consolidate with transformPointcloud(::ScatterAlign,..) function
331+
function apply( M_::Union{<:typeof(SpecialEuclidean(2)),<:typeof(SpecialEuclidean(3))},
332+
rPp::Manifolds.ArrayPartition,
333+
pc::PointCloud{T} ) where T
254334
#
255335

256-
rTp = affine_matrix(M_, rPp)
257-
pV = MVector(0.0,0.0,1.0)
258-
_data = MVector(0.0,0.0,0.0,0.0)
259-
336+
# allocate destination
260337
_pc = PointCloud(;header=pc.header,
261338
points = Vector{T}(),
262339
width=pc.width,
@@ -266,13 +343,18 @@ function apply( M_::typeof(SpecialEuclidean(2)),
266343
sensor_orientation_=pc.sensor_orientation_ )
267344
#
268345

346+
ft3 = _FastTransform3D(M_, rPp, 0f0)
347+
nc = M_ isa typeof(SpecialEuclidean(3)) ? 3 : 2
348+
_data = MVector(0f0,0f0,0f0,0f0)
349+
269350
# rotate the elements from the old point cloud into new static memory locations
270351
# NOTE these types must match the types use for PointCloud and PointXYZ
352+
# TODO not the world's fastest implementation
271353
for pt in pc.points
272-
pV[1] = pt.x
273-
pV[2] = pt.y
274-
_data[1:3] .= rTp*pV
275-
push!(_pc.points, PointXYZ(;color=pt.color, data=SVector{4,eltype(pt.data)}(_data[1], _data[2], pt.data[3:4]...)) )
354+
_data[1:nc] = ft3(pt.data[1:nc])
355+
_data[4] = pt.data[4]
356+
npt = PointXYZ(;color=pt.color, data=SVector{4,eltype(pt.data)}(_data...))
357+
push!(_pc.points, npt )
276358
end
277359

278360
# return the new point cloud

0 commit comments

Comments
 (0)