Skip to content

Commit

Permalink
[DATA-568] Add cartographer integration tests (#1544)
Browse files Browse the repository at this point in the history
  • Loading branch information
tessavitabile authored Nov 2, 2022
1 parent 2389c9e commit ddf6889
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 10 deletions.
74 changes: 74 additions & 0 deletions .artifact/tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -50580,6 +50580,14 @@
"hash": "8ecf7ccd4c22d4606783db10a26018c4",
"size": 145250924
},
"locating_in_map.lua": {
"hash": "94180b8051257132b0e6eaab763c303a",
"size": 1017
},
"mapping_new_map.lua": {
"hash": "8156d4670834d97e5d226c8488ef6f85",
"size": 939
},
"mock_camera_short": {
"depth": {
"0.png": {
Expand Down Expand Up @@ -50818,6 +50826,68 @@
}
}
},
"mock_lidar": {
"0.pcd": {
"hash": "ff60d0b3ae61004a1ba2031eb7c6b832",
"size": 39791
},
"1.pcd": {
"hash": "db5bd806efd2bbb61c161d7c609395f9",
"size": 39772
},
"10.pcd": {
"hash": "ae63f9547ae2811edbad8fe09249dc41",
"size": 39848
},
"11.pcd": {
"hash": "b63f819b53bf30461513e20fec59d370",
"size": 40302
},
"12.pcd": {
"hash": "d5b1504da753c256648bf12cc290c449",
"size": 39988
},
"13.pcd": {
"hash": "3a928b03dbe1f805e2865ae1c8824058",
"size": 40042
},
"14.pcd": {
"hash": "dc0e197912d86ae4222eae2875482885",
"size": 40270
},
"2.pcd": {
"hash": "2a13f11e8e855f9b4c03acdee8cc64f9",
"size": 40001
},
"3.pcd": {
"hash": "12556c1a825b34f835b65c13dd037bc9",
"size": 40060
},
"4.pcd": {
"hash": "f56068feb87e1f6e9b4e9ee3da1bf2a9",
"size": 39755
},
"5.pcd": {
"hash": "c0b725cf8a6aeebd0f03fb2c7d8d6c80",
"size": 39754
},
"6.pcd": {
"hash": "a0f6bd9c9732a84a0be19fbc49c03dfe",
"size": 39720
},
"7.pcd": {
"hash": "c716a579022d25f6f92c8a80659a420c",
"size": 39947
},
"8.pcd": {
"hash": "0f990165d2b1a9b6fed12dae7580c898",
"size": 39989
},
"9.pcd": {
"hash": "9b30a870e4ffb599b5213165a5cb44cb",
"size": 39853
}
},
"mock_mono_camera": {
"rgb": {
"0.png": {
Expand Down Expand Up @@ -50881,6 +50951,10 @@
"size": 813831
}
}
},
"updating_a_map.lua": {
"hash": "2b044a23bdc0f4da1b78a9fd0c727497",
"size": 1082
}
},
"transform": {
Expand Down
44 changes: 39 additions & 5 deletions services/slam/builtin/builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ import (
)

const (
validDataRateMS = 200
validDataRateMS = 200
numCartographerPointClouds = 15
)

var (
orbslamIntCameraMutex sync.Mutex
orbslamIntCameraReleaseImagesChan chan int = make(chan int, 2)
orbslamIntWebcamReleaseImageChan chan int = make(chan int, 1)
orbslamIntSynchronizeCamerasChan chan int = make(chan int)
orbslamIntCameraMutex sync.Mutex
orbslamIntCameraReleaseImagesChan chan int = make(chan int, 2)
orbslamIntWebcamReleaseImageChan chan int = make(chan int, 1)
orbslamIntSynchronizeCamerasChan chan int = make(chan int)
cartographerIntLidarReleasePointCloudChan chan int = make(chan int, 1)
)

func getNumOrbslamImages(mode slam.Mode) int {
Expand Down Expand Up @@ -389,6 +391,38 @@ func setupInjectRobot() *inject.Robot {
}
}
return cam, nil
case camera.Named("cartographer_int_lidar"):
var index uint64
cam.NextPointCloudFunc = func(ctx context.Context) (pointcloud.PointCloud, error) {
select {
case <-cartographerIntLidarReleasePointCloudChan:
i := atomic.AddUint64(&index, 1) - 1
if i >= numCartographerPointClouds {
return nil, errors.New("No more cartographer point clouds")
}
file, err := os.Open(artifact.MustPath("slam/mock_lidar/" + strconv.FormatUint(i, 10) + ".pcd"))
if err != nil {
return nil, err
}
pointCloud, err := pointcloud.ReadPCD(file)
if err != nil {
return nil, err
}
return pointCloud, nil
default:
return nil, errors.Errorf("Lidar not ready to return point cloud %v", index)
}
}
cam.StreamFunc = func(ctx context.Context, errHandlers ...gostream.ErrorHandler) (gostream.VideoStream, error) {
return nil, errors.New("lidar not camera")
}
cam.ProjectorFunc = func(ctx context.Context) (transform.Projector, error) {
return nil, transform.NewNoIntrinsicsError("")
}
cam.PropertiesFunc = func(ctx context.Context) (camera.Properties, error) {
return camera.Properties{}, nil
}
return cam, nil
default:
return nil, rdkutils.NewResourceNotFoundError(name)
}
Expand Down
153 changes: 153 additions & 0 deletions services/slam/builtin/cartographer_int_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package builtin_test

import (
"context"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
"testing"

"github.com/edaniels/golog"
"go.viam.com/rdk/services/slam"
"go.viam.com/rdk/services/slam/builtin"
"go.viam.com/rdk/services/slam/internal"
"go.viam.com/test"
"go.viam.com/utils"
"go.viam.com/utils/artifact"
)

// Creates the lua files required by the cartographer binary.
func createLuaFiles(name string) error {
if err := os.Mkdir(name+"/config/lua_files", os.ModePerm); err != nil {
return err
}
for _, luaFile := range []string{"locating_in_map.lua", "mapping_new_map.lua", "updating_a_map.lua"} {
source, err := os.Open(artifact.MustPath("slam/" + luaFile))
if err != nil {
return err
}
defer source.Close()
destination, err := os.Create(name + "/config/lua_files/" + luaFile)
if err != nil {
return err
}
defer destination.Close()
_, err = io.Copy(destination, source)
if err != nil {
return err
}
}
return nil
}

// Checks the cartographer position and map.
func testCartographerPositionAndMap(t *testing.T, svc slam.Service) {
t.Helper()

actualMIME, _, pointcloud, err := svc.GetMap(context.Background(), "test", "pointcloud/pcd", nil, false)
test.That(t, err, test.ShouldBeNil)
test.That(t, actualMIME, test.ShouldResemble, "pointcloud/pcd")
test.That(t, pointcloud.Size(), test.ShouldEqual, 200)

// TODO DATA-701 test GetPosition
}

func TestCartographerIntegration(t *testing.T) {
_, err := exec.LookPath("carto_grpc_server")
if err != nil {
t.Log("Skipping test because carto_grpc_server binary was not found")
t.Skip()
}

name, err := createTempFolderArchitecture()
test.That(t, err, test.ShouldBeNil)
createLuaFiles(name)

t.Log("Testing online mode")

attrCfg := &builtin.AttrConfig{
Algorithm: "cartographer",
Sensors: []string{"cartographer_int_lidar"},
ConfigParams: map[string]string{
"mode": "2d",
"v": "1",
},
DataDirectory: name,
}

// Release point cloud for service validation
cartographerIntLidarReleasePointCloudChan <- 1
// Create slam service using a real cartographer binary
svc, err := createSLAMService(t, attrCfg, golog.NewTestLogger(t), true, true)
test.That(t, err, test.ShouldBeNil)

// Release point cloud, since cartographer looks for the second most recent point cloud
cartographerIntLidarReleasePointCloudChan <- 1
// Wait for cartographer to finish processing data
logReader := svc.(internal.Service).GetSLAMProcessBufferedLogReader()
for i := 0; i < numCartographerPointClouds-2; i++ {
t.Logf("Find log line for point cloud %v", i)
cartographerIntLidarReleasePointCloudChan <- 1
for {
line, err := logReader.ReadString('\n')
test.That(t, err, test.ShouldBeNil)
if strings.Contains(line, "Passed sensor data to SLAM") {
break
}
}
}

testCartographerPositionAndMap(t, svc)

// Close out slam service
test.That(t, utils.TryClose(context.Background(), svc), test.ShouldBeNil)
// Don't clear out the directory, since we will re-use the data for the next run
closeOutSLAMService(t, "")

// Delete the last .pcd file in the data directory, so that offline mode runs on the
// same data as online mode. (Online mode will not read the last .pcd file, since it
// always processes the second-most-recent .pcd file, in case the most-recent .pcd
// file is currently being written.)
files, err := ioutil.ReadDir(name + "/data/")
test.That(t, err, test.ShouldBeNil)
lastFileName := files[len(files)-1].Name()
test.That(t, os.Remove(name+"/data/"+lastFileName), test.ShouldBeNil)

// Test offline mode using the data generated in the online test
t.Log("Testing offline mode")

attrCfg = &builtin.AttrConfig{
Algorithm: "cartographer",
Sensors: []string{},
ConfigParams: map[string]string{
"mode": "2d",
"v": "1",
},
DataDirectory: name,
}

// Create slam service using a real cartographer binary
svc, err = createSLAMService(t, attrCfg, golog.NewTestLogger(t), true, true)
test.That(t, err, test.ShouldBeNil)

// Wait for cartographer to finish processing data
logReader = svc.(internal.Service).GetSLAMProcessBufferedLogReader()
for {
line, err := logReader.ReadString('\n')
test.That(t, err, test.ShouldBeNil)
if strings.Contains(line, "Finished processing offline data") {
break
}
}

testCartographerPositionAndMap(t, svc)

// Close out slam service
test.That(t, utils.TryClose(context.Background(), svc), test.ShouldBeNil)
// Clear out directory
closeOutSLAMService(t, name)

// TODO DATA-701 test localization and a priori map loading
}
13 changes: 8 additions & 5 deletions services/slam/builtin/orbslam_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func releaseImages(t *testing.T, mode slam.Mode) {

// Checks that we can get position and map, and that there are more than zero map points.
// Doesn't check precise values due to variations in orbslam results.
func testPositionAndMap(t *testing.T, svc slam.Service) {
func testOrbslamPositionAndMap(t *testing.T, svc slam.Service) {
t.Helper()

position, err := svc.Position(context.Background(), "test")
Expand Down Expand Up @@ -154,14 +154,17 @@ func integrationTestHelper(t *testing.T, mode slam.Mode) {
}
}

testPositionAndMap(t, svc)
testOrbslamPositionAndMap(t, svc)

// Close out slam service
test.That(t, utils.TryClose(context.Background(), svc), test.ShouldBeNil)
// Don't clear out the directory, since we will re-use the config and data for the next run
closeOutSLAMService(t, "")

// Delete the last image or image pair, so that offline mode runs on the same set of images
// Delete the last image (or image pair) in the data directory, so that offline mode runs on
// the same data as online mode. (Online mode will not read the last image (or image pair),
// since it always processes the second-most-recent image (or image pair), in case the
// most-recent image (or image pair) is currently being written.)
var directories []string
switch mode {
case slam.Mono:
Expand Down Expand Up @@ -215,7 +218,7 @@ func integrationTestHelper(t *testing.T, mode slam.Mode) {
test.That(t, strings.Contains(line, "Fail to track local map!"), test.ShouldBeFalse)
}

testPositionAndMap(t, svc)
testOrbslamPositionAndMap(t, svc)

// Wait for the final map to be saved
for {
Expand Down Expand Up @@ -287,7 +290,7 @@ func integrationTestHelper(t *testing.T, mode slam.Mode) {
}
}

testPositionAndMap(t, svc)
testOrbslamPositionAndMap(t, svc)

// Close out slam service
test.That(t, utils.TryClose(context.Background(), svc), test.ShouldBeNil)
Expand Down

0 comments on commit ddf6889

Please sign in to comment.