Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremyrhyde committed Jan 10, 2024
2 parents b22b926 + 60b6e8a commit f119a72
Show file tree
Hide file tree
Showing 32 changed files with 2,627 additions and 1,762 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ setup: install-dependencies ensure-submodule-initialized artifact-pull
install-dependencies:
ifneq (, $(shell which brew))
brew update
brew install abseil boost viamrobotics/brews/ceres-solver@2.1 protobuf ninja cairo googletest lua@5.3 pkg-config cmake go@1.20 grpc clang-format
brew install abseil boost viamrobotics/brews/suite-sparse@7.1 viamrobotics/brews/ceres-solver@2.1 protobuf ninja cairo googletest lua@5.3 pkg-config cmake go@1.20 grpc clang-format
brew link lua@5.3
brew install openssl@3 eigen gflags glog sphinx-doc pcl viamrobotics/brews/nlopt-static
else ifneq (, $(shell which apt-get))
Expand Down
28 changes: 6 additions & 22 deletions cartofacade/capi.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,11 @@ const (
type CartoConfig struct {
Camera string
MovementSensor string
MapRateSecond int
DataDir string
ComponentReference string
LidarConfig LidarConfig

CloudStoryEnabled bool
EnableMapping bool
ExistingMap string
EnableMapping bool
ExistingMap string
}

// CartoAlgoConfig contains config values from app
Expand Down Expand Up @@ -392,17 +389,6 @@ func getConfig(cfg CartoConfig) (C.viam_carto_config, error) {
return C.viam_carto_config{}, err
}

// Remove cloud_story_enabled, map_rate_sec, and data_dir from C++ code
// JIRA Ticket: RSDK-52334 https://viam.atlassian.net/browse/RSDK-5334
vcc.cloud_story_enabled = C.bool(true)
vcc.data_dir = goStringToBstring("/tmp/")
if cfg.EnableMapping {
// Set to arbitrarily high value to ensure no maps get saved during operation
vcc.map_rate_sec = C.int(9000)
} else {
vcc.map_rate_sec = C.int(0)
}

vcc.lidar_config = lidarCfg

vcc.enable_mapping = C.bool(cfg.EnableMapping)
Expand Down Expand Up @@ -519,20 +505,16 @@ func toError(status C.int) error {
return errors.New("VIAM_CARTO_LIB_NOT_INITIALIZED")
case C.VIAM_CARTO_UNKNOWN_ERROR:
return errors.New("VIAM_CARTO_UNKNOWN_ERROR")
case C.VIAM_CARTO_DATA_DIR_NOT_PROVIDED:
return errors.New("VIAM_CARTO_DATA_DIR_NOT_PROVIDED")
case C.VIAM_CARTO_SLAM_MODE_INVALID:
return errors.New("VIAM_CARTO_SLAM_MODE_INVALID")
case C.VIAM_CARTO_LIDAR_CONFIG_INVALID:
return errors.New("VIAM_CARTO_LIDAR_CONFIG_INVALID")
case C.VIAM_CARTO_MAP_RATE_SEC_INVALID:
return errors.New("VIAM_CARTO_MAP_RATE_SEC_INVALID")
case C.VIAM_CARTO_COMPONENT_REFERENCE_INVALID:
return errors.New("VIAM_CARTO_COMPONENT_REFERENCE_INVALID")
case C.VIAM_CARTO_LUA_CONFIG_NOT_FOUND:
return errors.New("VIAM_CARTO_LUA_CONFIG_NOT_FOUND")
case C.VIAM_CARTO_DATA_DIR_INVALID_DEPRECATED_STRUCTURE:
return errors.New("VIAM_CARTO_DATA_DIR_INVALID_DEPRECATED_STRUCTURE")
case C.VIAM_CARTO_INTERNAL_STATE_FILE_SYSTEM_ERROR:
return errors.New("VIAM_CARTO_INTERNAL_STATE_FILE_SYSTEM_ERROR")
case C.VIAM_CARTO_MAP_CREATION_ERROR:
return errors.New("VIAM_CARTO_MAP_CREATION_ERROR")
case C.VIAM_CARTO_UNKNOWN_SENSOR_NAME:
Expand Down Expand Up @@ -567,6 +549,8 @@ func toError(status C.int) error {
return errors.New("VIAM_CARTO_NOT_IN_TERMINATABLE_STATE")
case C.VIAM_CARTO_IMU_PROVIDED_AND_IMU_ENABLED_MISMATCH:
return errors.New("VIAM_CARTO_IMU_PROVIDED_AND_IMU_ENABLED_MISMATCH")
case C.VIAM_CARTO_IMU_READING_EMPTY:
return errors.New("VIAM_CARTO_IMU_READING_EMPTY")
case C.VIAM_CARTO_IMU_READING_INVALID:
return errors.New("VIAM_CARTO_IMU_READING_INVALID")
case C.VIAM_CARTO_ODOMETER_READING_INVALID:
Expand Down
7 changes: 4 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ type OptionalConfigParams struct {
LidarDataFrequencyHz int
MovementSensorName string
MovementSensorDataFrequencyHz int
MapRateSec int
EnableMapping bool
ExistingMap string
}

var (
errCameraMustHaveName = errors.New("\"camera[name]\" is required")
errLocalizationInOfflineMode = newError("camera[data_freq_hz] and enable_mapping = false. localization in offline mode not supported.")
errLocalizationInOfflineMode = newError("\"camera[data_freq_hz]\" and enable_mapping = false." +
" Localization in offline mode is not supported.")
)

// Validate creates the list of implicit dependencies.
Expand All @@ -65,7 +65,8 @@ func (config *Config) Validate(path string) ([]string, error) {
deps = append(deps, movementSensorName)
}

if config.ConfigParams["mode"] == "" {
mode, ok := config.ConfigParams["mode"]
if !ok || mode == "" {
return nil, utils.NewConfigValidationFieldRequiredError(path, "config_params[mode]")
}

Expand Down
2 changes: 0 additions & 2 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ func TestGetOptionalParameters(t *testing.T) {
test.That(t, optionalConfigParams.MovementSensorName, test.ShouldEqual, "")
test.That(t, optionalConfigParams.MovementSensorDataFrequencyHz, test.ShouldEqual, 0)
test.That(t, optionalConfigParams.EnableMapping, test.ShouldBeFalse)
test.That(t, optionalConfigParams.MapRateSec, test.ShouldEqual, 0)
test.That(t, optionalConfigParams.ExistingMap, test.ShouldEqual, "")
})

Expand All @@ -150,7 +149,6 @@ func TestGetOptionalParameters(t *testing.T) {
test.That(t, optionalConfigParams.MovementSensorName, test.ShouldEqual, "")
test.That(t, optionalConfigParams.MovementSensorDataFrequencyHz, test.ShouldEqual, 0)
test.That(t, optionalConfigParams.EnableMapping, test.ShouldBeFalse)
test.That(t, optionalConfigParams.MapRateSec, test.ShouldEqual, 0)
test.That(t, optionalConfigParams.ExistingMap, test.ShouldEqual, "")
})

Expand Down
1 change: 1 addition & 0 deletions etc/packaging/appimages/cartographer-module-aarch64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ AppDir:
- libboost-filesystem1.74.0
- libpcl-io1.13
- libnlopt0
- libxcb-shm0

files:
include: []
Expand Down
3 changes: 2 additions & 1 deletion etc/packaging/appimages/cartographer-module-x86_64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ AppDir:
- libboost-filesystem1.74.0
- libpcl-io1.13
- libnlopt0

- libxcb-shm0

files:
include: []
exclude:
Expand Down
2 changes: 1 addition & 1 deletion integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func TestIntegrationCartographer(t *testing.T) {
test.That(t, err, test.ShouldBeNil)
}()

// Set mapRateSec for mapping mode
// Enable mapping mode
enableMapping := true

// Run mapping test
Expand Down
233 changes: 233 additions & 0 deletions postprocess/postprocess.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// Package postprocess contains functionality to postprocess pointcloud maps
package postprocess

import (
"bytes"
"errors"
"image/color"
"math"

"github.com/golang/geo/r3"
"go.viam.com/rdk/pointcloud"
)

// Instruction describes the action of the postprocess step.
type Instruction int

const (
// Add is the instruction for adding points.
Add Instruction = iota
// Remove is the instruction for removing points.
Remove = iota
)

const (
fullConfidence = 100
removalRadius = 100 // mm
xKey = "X"
yKey = "Y"

// ToggleCommand can be used to turn postprocessing on and off.
ToggleCommand = "postprocess_toggle"
// AddCommand can be used to add points to the pointcloud map.
AddCommand = "postprocess_add"
// RemoveCommand can be used to remove points from the pointcloud map.
RemoveCommand = "postprocess_remove"
// UndoCommand can be used to undo last postprocessing step.
UndoCommand = "postprocess_undo"
// PathCommand can be used to specify a pcd that has already been postprocessed.
PathCommand = "postprocess_path"
)

var (
errPointsNotASlice = errors.New("could not parse provided points as a slice")
errPointNotAMap = errors.New("could not parse provided point as a map")
errXNotProvided = errors.New("could X not provided")
errXNotFloat64 = errors.New("could not parse provided X as a float64")
errYNotProvided = errors.New("could X not provided")
errYNotFloat64 = errors.New("could not parse provided X as a float64")
errRemovingPoints = errors.New("unexpected number of points after removal")
errNilUpdatedData = errors.New("cannot provide nil updated data")
)

// Task can be used to construct a postprocessing step.
type Task struct {
Instruction Instruction
Points []r3.Vector
}

// ParseDoCommand parses postprocessing DoCommands into Tasks.
func ParseDoCommand(
unstructuredPoints interface{},
instruction Instruction,
) (Task, error) {
pointSlice, ok := unstructuredPoints.([]interface{})
if !ok {
return Task{}, errPointsNotASlice
}

task := Task{Instruction: instruction}
for _, point := range pointSlice {
pointMap, ok := point.(map[string]interface{})
if !ok {
return Task{}, errPointNotAMap
}

x, ok := pointMap[xKey]
if !ok {
return Task{}, errXNotProvided
}

xFloat, ok := x.(float64)
if !ok {
return Task{}, errXNotFloat64
}

y, ok := pointMap[yKey]
if !ok {
return Task{}, errYNotProvided
}

yFloat, ok := y.(float64)
if !ok {
return Task{}, errXNotFloat64
}

task.Points = append(task.Points, r3.Vector{X: xFloat, Y: yFloat})
}
return task, nil
}

/*
UpdatePointCloud iterated through a list of tasks and adds or removes points from data
and writes the updated pointcloud to updatedData.
*/
func UpdatePointCloud(
data []byte,
updatedData *[]byte,
tasks []Task,
) error {
if updatedData == nil {
return errNilUpdatedData
}

*updatedData = append(*updatedData, data...)

// iterate through tasks and add or remove points
for _, task := range tasks {
switch task.Instruction {
case Add:
err := updatePointCloudWithAddedPoints(updatedData, task.Points)
if err != nil {
return err
}
case Remove:
err := updatePointCloudWithRemovedPoints(updatedData, task.Points)
if err != nil {
return err
}
}
}
return nil
}

func updatePointCloudWithAddedPoints(updatedData *[]byte, points []r3.Vector) error {
if updatedData == nil {
return errNilUpdatedData
}

reader := bytes.NewReader(*updatedData)
pc, err := pointcloud.ReadPCD(reader)
if err != nil {
return err
}

for _, point := range points {
/*
Viam expects pointcloud data with fields "x y z" or "x y z rgb", and for
this to be specified in the pointcloud header in the FIELDS entry. If color
data is included in the pointcloud, Viam's services assume that the color
value encodes a confidence score for that data point. Viam expects the
confidence score to be encoded in the blue parameter of the RGB value, on a
scale from 1-100.
*/
err := pc.Set(point, pointcloud.NewColoredData(color.NRGBA{B: fullConfidence, R: math.MaxUint8}))
if err != nil {
return err
}
}

var buf bytes.Buffer
err = pointcloud.ToPCD(pc, &buf, pointcloud.PCDBinary)
if err != nil {
return err
}

// Initialize updatedData with new points
*updatedData = make([]byte, buf.Len())
updatedReader := bytes.NewReader(buf.Bytes())
_, err = updatedReader.Read(*updatedData)
if err != nil {
return err
}

return nil
}

func updatePointCloudWithRemovedPoints(updatedData *[]byte, points []r3.Vector) error {
if updatedData == nil {
return errNilUpdatedData
}

reader := bytes.NewReader(*updatedData)
pc, err := pointcloud.ReadPCD(reader)
if err != nil {
return err
}

updatedPC := pointcloud.NewWithPrealloc(pc.Size() - len(points))
pointsVisited := 0

filterRemovedPoints := func(p r3.Vector, d pointcloud.Data) bool {
pointsVisited++
// Always return true so iteration continues

for _, point := range points {
// remove all points within the removalRadius from the removed points
if point.Distance(p) <= removalRadius {
return true
}
}

err := updatedPC.Set(p, d)
// end early if point cannot be set
return err == nil
}

pc.Iterate(0, 0, filterRemovedPoints)

// confirm iterate did not have to end early
if pc.Size() != pointsVisited {
/*
Note: this condition will occur if some error occurred while copying valid points
and will be how we can tell that this error occurred: err := updatedPC.Set(p, d)
*/
return errRemovingPoints
}

buf := bytes.Buffer{}
err = pointcloud.ToPCD(updatedPC, &buf, pointcloud.PCDBinary)
if err != nil {
return err
}

// Overwrite updatedData with new points
*updatedData = make([]byte, buf.Len())
updatedReader := bytes.NewReader(buf.Bytes())
_, err = updatedReader.Read(*updatedData)
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit f119a72

Please sign in to comment.